spring cloud概念和常见组件 

Spring Cloud 是一款基于 Spring Boot 实现的微服务框架,它将市面上成熟的、经过验证的微服务框架整合起来,并通过 Spring Boot 的思想进行再封装,屏蔽调其中复杂的配置和实现原理,最终为开发人员提供了一套简单易懂、易部署和易维护的分布式系统开发工具包。

Spring Cloud 中包含了 spring-cloud-config、spring-cloud-bus 等近 20 个子项目,提供了服务治理、服务网关、智能路由、负载均衡、断路器、监控跟踪、分布式消息队列、配置管理等领域的解决方案。

注册中心Euraka

Eureka 两大组件

Eureka 采用 CS(Client/Server,客户端/服务器) 架构,它包括以下两大组件:

Eureka Server:Eureka 服务注册中心,主要用于提供服务注册功能。当微服务启动时,会将自己的服务注册到 Eureka Server。Eureka Server 维护了一个可用服务列表,存储了所有注册到 Eureka Server 的可用服务的信息,这些可用服务可以在 Eureka Server 的管理界面中直观看到。Eureka Client:Eureka 客户端,通常指的是微服务系统中各个微服务,主要用于和 Eureka Server 进行交互。在微服务应用启动后,Eureka Client 会向 Eureka Server 发送心跳(默认周期为 30 秒)。若 Eureka Server 在多个心跳周期内没有接收到某个 Eureka Client 的心跳,Eureka Server 将它从可用服务列表中移除(默认 90 秒)。 

注:“心跳”指的是一段定时发送的自定义信息,让对方知道自己“存活”,以确保连接的有效性。大部分 CS 架构的应用程序都采用了心跳机制,服务端和客户端都可以发心跳。通常情况下是客户端向服务器端发送心跳包,服务端用于判断客户端是否在线。

Eureka 实现服务注册发现的角色和流程

如下图所示  

上图中共涉及到以下 3 个角色:

服务注册中心(Register Service):它是一个 Eureka Server,用于提供服务注册和发现功能。服务提供者(Provider Service):它是一个 Eureka Client,用于提供服务。它将自己提供的服务注册到服务注册中心,以供服务消费者发现。服务消费者(Consumer Service):它是一个 Eureka Client,用于消费服务。它可以从服务注册中心获取服务列表,调用所需的服务。

Eureka 实现服务注册与发现的流程如下:

搭建一个 Eureka Server 作为服务注册中心;服务提供者 Eureka Client 启动时,会把当前服务器的信息以服务名(spring.application.name)的方式注册到服务注册中心;服务消费者 Eureka Client 启动时,也会向服务注册中心注册;服务消费者还会获取一份可用服务列表,该列表中包含了所有注册到服务注册中心的服务信息(包括服务提供者和自身的信息);在获得了可用服务列表后,服务消费者通过 HTTP 或消息中间件(比如Openfeign)远程调用服务提供者提供的服务。

服务注册中心(Eureka Server)所扮演的角色十分重要,它是服务提供者和服务消费者之间的桥梁。服务提供者只有将自己的服务注册到服务注册中心才可能被服务消费者调用,而服务消费者也只有通过服务注册中心获取可用服务列表后,才能调用所需的服务。

示例

创建service服务注册中心

创一个Spring boot项目,引入euraka服务端依赖

org.springframework.boot

spring-boot-starter-web

org.springframework.cloud

spring-cloud-starter-netflix-eureka-server

配置文件

server:

port: 7001 #该 Module 的端口号

eureka:

instance:

hostname: localhost #eureka服务端的实例名称,

client:

register-with-eureka: false #false表示不向注册中心注册自己。

fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务

service-url:

defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机版服务注册中心

启动类加注解@EnableEurekaServer

@SpringBootApplication

@EnableEurekaServer //开启 Eureka server,接受其他微服务的注册

public class MicroServiceCloudEureka7001Application {

public static void main(String[] args) {

SpringApplication.run(MicroServiceCloudEureka7001Application.class, args);

}

}

启动 项目,使用浏览器访问 Eureka 服务注册中心主页,地址为“http://localhost:7001/”,结果如下图

创建client

创建springboot项目,引入与euraka客户端的依赖

org.springframework.cloud

spring-cloud-starter-netflix-eureka-client

配置文件

server:

port: 8001 #服务端口号

spring:

application:

name: microServiceCloudProviderDept #微服务名称,对外暴漏的微服务名称,十分重要

可以创建若干service、controller方法模拟服务;

启动类加注解@EnableEurekaClient

@SpringBootApplication

@EnableEurekaClient // Spring cloud Eureka 客户端,自动将本服务注册到 Eureka Server 注册中心中

public class MicroServiceCloudProviderDept8001Application {

public static void main(String[] args) {

SpringApplication.run(MicroServiceCloudProviderDept8001Application.class, args);

}

}

启动项目,使用浏览器访再次问 Eureka 服务注册中心主页(http://localhost:7001/),如下图

负载均衡Ribbon

负载均衡(Load Balance) ,简单点说就是将用户的请求平摊分配到多个服务器上运行,以达到扩展服务器带宽、增强数据处理能力、增加吞吐量、提高网络的可用性和灵活性的目的。 常见的负载均衡方式有两种:

服务端负载均衡客户端负载均衡

服务端负载均衡

服务端负载均衡是最常见的负载均衡方式,其工作原理如下图。  

服务端负载均衡是在客户端和服务端之间建立一个独立的负载均衡服务器,该服务器既可以是硬件设备(例如 F5),也可以是软件(例如 Nginx)。这个负载均衡服务器维护了一份可用服务端清单,然后通过心跳机制来删除故障的服务端节点,以保证清单中的所有服务节点都是可以正常访问的。 当客户端发送请求时,该请求不会直接发送到服务端进行处理,而是全部交给负载均衡服务器,由负载均衡服务器按照某种算法(例如轮询、随机等),从其维护的可用服务清单中选择一个服务端,然后进行转发。 服务端负载均衡具有以下特点:

需要建立一个独立的负载均衡服务器。负载均衡是在客户端发送请求后进行的,因此客户端并不知道到底是哪个服务端提供的服务。可用服务端清单存储在负载均衡服务器上。

客户端负载均衡

相较于服务端负载均衡,客户端服务在均衡则是一个比较小众的概念。客户端负载均衡的工作原理如下图。  

客户端负载均衡是将负载均衡逻辑以代码的形式封装到客户端上,即负载均衡器位于客户端。客户端通过服务注册中心(例如 Eureka Server)获取到一份服务端提供的可用服务清单。有了服务清单后,负载均衡器会在客户端发送请求前通过负载均衡算法选择一个服务端实例再进行访问,以达到负载均衡的目的; 客户端负载均衡也需要心跳机制去维护服务端清单的有效性,这个过程需要配合服务注册中心一起完成。 客户端负载均衡具有以下特点:

负载均衡器位于客户端,不需要单独搭建一个负载均衡服务器。负载均衡是在客户端发送请求前进行的,因此客户端清楚地知道是哪个服务端提供的服务。客户端都维护了一份可用服务清单,而这份清单都是从服务注册中心获取的。

Ribbon 就是一个基于 HTTP 和 TCP 的客户端负载均衡器,当我们将 Ribbon 和 Eureka 一起使用时,Ribbon 会从 Eureka Server(服务注册中心)中获取服务端列表,然后通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的。

负载均衡策略

Spring Cloud Ribbon 提供了一个 IRule 接口,该接口主要用来定义负载均衡策略,它有 7 个默认实现类,每一个实现类都是一种负载均衡策略。  

序号实现类负载均衡策略1RoundRobinRule按照线性轮询策略,即按照一定的顺序依次选取服务实例2RandomRule随机选取一个服务实例3RetryRule按照 RoundRobinRule(轮询)的策略来获取服务,如果获取的服务实例为 null 或已经失效,则在指定的时间之内不断地进行重试(重试时获取服务的策略还是 RoundRobinRule 中定义的策略),如果超过指定时间依然没获取到服务实例则返回 null 。4WeightedResponseTimeRuleWeightedResponseTimeRule 是 RoundRobinRule 的一个子类,它对 RoundRobinRule 的功能进行了扩展。 根据平均响应时间,来计算所有服务实例的权重,响应时间越短的服务实例权重越高,被选中的概率越大。刚启动时,如果统计信息不足,则使用线性轮询策略,等信息足够时,再切换到 WeightedResponseTimeRule。5BestAvailableRule继承自 ClientConfigEnabledRoundRobinRule。先过滤点故障或失效的服务实例,然后再选择并发量最小的服务实例。6AvailabilityFilteringRule先过滤掉故障或失效的服务实例,然后再选择并发量较小的服务实例。7ZoneAvoidanceRule默认的负载均衡策略,综合判断服务所在区域(zone)的性能和服务(server)的可用性,来选择服务实例。在没有区域的环境下,该策略与轮询(RandomRule)策略类似。

实现、切换、自定义负载均衡策略的代码实现

Ribbon:Spring Cloud负载均衡与服务调用组件(非常详细)

远程调用Openfeign

理解远程调用

远程调用和本地调用是相对的,那我们先说本地调用更好理解些,本地调用就是同一个 Service 里面的方法 A 调用方法 B。

那远程调用就是不同 Service 之间的方法调用。Service 级的方法调用,就是我们自己构造请求 URL和请求参数,就可以发起远程调用了。

在服务之间调用的话,我们都是基于 HTTP 协议,一般用到的远程服务框架有 HttpClient,RestTemplet 等。其调用流程如下:

但是这种虚线方框中的构造请求的过程是很繁琐的,需要先构建http头部,再构建http body,最后再发送http请求,比如:

那有没有更简便的方式呢?Feign 就是来简化我们发起远程调用的代码的,那简化到什么程度呢?简化成就像调用本地方法那样简单。

openfeign实现远程调用

服务提供者

先创建一个springboot项目模拟服务提供者,写3个cotroller方法,分别测试Json参数、对象参数、普通参数

@RestController

public class Controller {

@RequestMapping("/getUser3")

public User getUser3(@RequestBody User user) throws InterruptedException {

return user;

}

@RequestMapping("/getUser2")

public User getUser2(User user) throws InterruptedException {

return user;

}

@RequestMapping("/getUsers")

public String[] getUsers(String ids) throws InterruptedException {

return ids.split(",");

}

}

服务调用者

引入依赖

org.springframework.cloud

spring-cloud-starter-openfeign

org.springframework.boot

spring-boot-starter-web

启动类添加注解@EnableFeignClients

@EnableFeignClients

@SpringBootApplication

public class FeignConsumerApplication {

public static void main(String[] args) {

SpringApplication.run(FeignConsumerApplication.class, args);

}

}

创建服务调用接口

创建一个接口,添加@FeignClient("provider",url="localhost:8080")注解,provider是服务名,url是服务调用地址,在接口中定义调用方法的请求方式、参数、返回值等信

@FeignClient"provider",url="localhost:8080")

@Service

public interface Service {

//必须用@RequestParam注解,否则提供者接收不到

@GetMapping("/getUsers")

String[] get(@RequestParam("ids") String ids);

//传递对象参数用@SpringQueryMap注解

@GetMapping("/getUser2")

User get2(@SpringQueryMap User user);

//传递Json参数用@RequestBody注解

@GetMapping("/getUser3")

User get3(@RequestBody User user);

}

在controller中调用service接口中方法

@RestController

public class TestController {

//注入上面定义的接口

@Autowired

private Service service;

//Controller层方法

@GetMapping("/getUsers")

public String[] getUsers(String ids){

//调用接口的方法

return service.get(ids);

}

}

OpenFeign 的核心流程

先看下 OpenFeign 的核心流程图:

1、在 Spring 项目启动阶段,服务 A 的OpenFeign 框架会发起一个主动的扫包流程。2、从指定的目录下扫描并加载所有被 @FeignClient 注解修饰的接口,然后将这些接口转换成 Bean,统一交给 Spring 来管理。3、根据这些接口会经过 MVC Contract 协议解析,将方法上的注解都解析出来,放到 MethodMetadata 元数据中。4、基于上面加载的每一个 FeignClient 接口,会生成一个动态代理对象,指向了一个包含对应方法的 MethodHandler 的 HashMap。MethodHandler 对元数据有引用关系。生成的动态代理对象会被添加到 Spring 容器中,并注入到对应的服务里。5、服务 A 调用接口,准备发起远程调用。6、从动态代理对象 Proxy 中找到一个 MethodHandler 实例,生成 Request,包含有服务的请求 URL(不包含服务的 IP)。7、经过负载均衡算法找到一个服务的 IP 地址,拼接出请求的 URL8、服务 B 处理服务 A 发起的远程调用请求,执行业务逻辑后,返回响应给服务 A。

相关链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: