前言
本篇主要介绍的是 SpringCloud 中的服务消费者 (Feign) 和负载均衡 (Ribbon) 功能的实现以及使用 Feign 结合 Ribbon 实现负载均衡.
SpringCloud Feign
Feign 介绍
Feign 是一个声明式的 web Service 客户端, 它使得编写 Web Serivce 客户端变得更加简单. 我们只需要使用 Feign 来创建一个接口并用注解来配置它既可完成. 它具备可插拔的注解支持, 包括 Feign 注解和 JAX-RS 注解. Feign 也支持可插拔的编码器和解码器. Spring Cloud 为 Feign 增加了对 Spring MVC 注解的支持, 还整合了 Ribbon 和 Eureka 来提供均衡负载的 HTTP 客户端实现.
开发准备
开发环境
- JDK:1.8
- SpringBoot:2.1.1.RELEASE
- SpringCloud:Finchley
注: 不一定非要用上述的版本, 可以根据情况进行相应的调整. 需要注意的是 SpringBoot2.x 以后, jdk 的版本必须是 1.8 以上!
确认了开发环境之后, 我们再来添加相关的 pom 依赖.
- <dependencies>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-openfeign</artifactId>
- </dependency>
- </dependencies>
注: 基于 SpringBoot1.x 以上 SpringCloud 是 Dalston 版本的 eureka 依赖是 <artifactId>spring-cloud-starter-eureka</artifactId>,feign 依赖是 <artifactId>spring-cloud-starter-feign</artifactId> , 少了个 netflix .SpringCloud 的版本命名方式是通过伦敦的地方来命名的, 版本顺序是根据首字母的顺序来的.
SpringCloud Feign 示例
服务端
首先建立一个 springcloud-feign-eureka 服务的工程, 用于做注册中心. 配置和之前的基本一样. application.properties 添加如下的配置:
配置信息:
- spring.application.name=springcloud-feign-eureka-server
- server.port=8001
- eureka.client.register-with-eureka=false
- eureka.client.fetch-registry=false
- eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
配置说明:
spring.application.name: 这个是指定服务名称.
server.port: 服务指定的端口.
eureka.client.register-with-eureka: 表示是否将自己注册到 Eureka Server, 默认是 true.
eureka.client.fetch-registry: 表示是否从 Eureka Server 获取注册信息, 默认为 true.
eureka.client.serviceUrl.defaultZone: 这个是设置与 Eureka Server 交互的地址, 客户端的查询服务和注册服务都需要依赖这个地址.
完成配置信息的添加后, 我们再来看代码如何实现.
在服务端这边只需要在 SpringBoot 启动类添加 @EnableEurekaServer 注解就可以了, 该注解表示此服务是一个服务注册中心服务.
代码示例:
- @EnableEurekaServer
- @SpringBootApplication
- public class FeignEurekaApplication {
- public static void main(String[] args) {
- SpringApplication.run(FeignEurekaApplication.class, args);
- System.out.println("feign 注册中心服务启动...");
- }
- }
客户端
这里我们定义两个消费者, springcloud-feign-consumer 和 springcloud-feign-consumer2, 一个使用 feign 做转发, 另一个为一个普通的项目. 添加如上的依赖之后, 在 application.properties 添加如下的配置:
consumer 配置信息:
- spring.application.name=springcloud-feign-consumer
- server.port=9002
- eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
consumer2 配置信息:
- spring.application.name=springcloud-feign-consumer2
- server.port=9003
- eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
配置说明:
spring.application.name: 这个是客户端的服务名称. 如果有多个服务使用同一个名称但是访问地址不同, 结合 ribbon 使用, 则可以实现负载均衡功能.
server.port: 服务指定的端口.
eureka.client.serviceUrl.defaultZone: 注册中心服务端的地址.
springcloud-feign-consumer
springcloud-feign-consumer 服务因为要实现 fegin 的功能, 因此需要在启动类上添加 @EnableFeignClients 该注解, 使用该注解表示启用 feign 进行远程调用.
启动类代码示例:
- @SpringBootApplication
- @EnableDiscoveryClient
- @EnableFeignClients
- public class FeignConsumerApplication {
- public static void main(String[] args) {
- SpringApplication.run(FeignConsumerApplication.class, args);
- System.out.println("feign 第一个消费者服务启动...");
- }
- }
需要定义转发的服务, 这里使用 @FeignClient 注解来实现, 该注解表示需要转发服务的名称, 该名称在 application.properties 中进行配置.
这里我们把请求转发到第二个服务 springcloud-feign-consumer2.
转发类代码示例:
- @FeignClient(name= "springcloud-feign-consumer2")
- public interface HelloRemote {
- @RequestMapping(value = "/hello")
- public String hello(@RequestParam(value = "name") String name);
- }
我们还需要提供一个入口供外部调用, 然后调用上述的的方法进行转发.
控制层代码示例:
- @RestController
- public class ConsumerController {
- @Autowired
- HelloRemote helloRemote;
- @RequestMapping("/hello/{name}")
- public String index(@PathVariable("name") String name) {
- System.out.println("接受到请求参数:"+name+", 进行转发到其他服务");
- return helloRemote.hello(name);
- }
- }
- springcloud-feign-consumer2
这个服务的代码就比较简单了, 只是一个普通的服务, 提供一个接口然后打印信息即可.
启动类代码示例:
- @SpringBootApplication
- @EnableDiscoveryClient
- public class FeignConsumerApplication2 {
- public static void main(String[] args) {
- SpringApplication.run(FeignConsumerApplication2.class, args);
- System.out.println("feign 第二个消费者服务启动...");
- }
- }
控制层代码示例:
- @RestController
- public class ConsumerController {
- @RequestMapping("/hello")
- public String index(@RequestParam String name) {
- return name+",Hello World";
- }
- }
功能测试
完成如上的工程开发之后, 我们依次启动服务端和客户端的三个程序, 然后在浏览器界面输入: http://localhost:8001/, 即可查看注册中心的信息.
在浏览器输入:
http://localhost:9003/hello?name=pancm
返回:
pancm,Hello World
说明可以直接访问第二个消费服务.
然后再输入:
http://localhost:9002/hello/pancm
通过第一个消费服务调用第二个服务.
控制台打印:
接受到请求参数: pancm, 进行转发到其他服务
界面返回结果:
pancm,Hello World
示例图:
出现以上结果说明客户端已经成功的通过 feign 调用了远程服务 hello, 并且将结果返回到了浏览器.
SpringCloud Ribbon
Ribbon 介绍
Ribbon 是 Netflix 发布的开源项目, 主要功能是提供客户端的软件负载均衡算法, 将 Netflix 的中间层服务连接在一起. Ribbon 客户端组件提供一系列完善的配置项如连接超时, 重试等. 简单的说, 就是在配置文件中列出 Load Balancer 后面所有的机器, Ribbon 会自动的帮助你基于某种规则 (如简单轮询, 随即连接等) 去连接这些机器. 我们也很容易使用 Ribbon 实现自定义的负载均衡算法. 简单地说, Ribbon 是一个客户端负载均衡器.
开发准备
开发环境
- JDK:1.8
- SpringBoot:2.1.1.RELEASE
- SpringCloud:Finchley
注: 不一定非要用上述的版本, 可以根据情况进行相应的调整. 需要注意的是 SpringBoot2.x 以后, jdk 的版本必须是 1.8 以上!
确认了开发环境之后, 我们再来添加相关的 pom 依赖.
- <dependencies>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-actuator</artifactId>
- </dependency>
- </dependencies>
注: 基于 SpringBoot1.x 以上 SpringCloud 是 Dalston 版本的 eureka 依赖是 <artifactId>spring-cloud-starter-eureka</artifactId>,feign 依赖是 <artifactId>spring-cloud-starter-ribbon</artifactId> , 少了个 netflix .SpringCloud 的版本命名方式是通过伦敦的地方来命名的, 版本顺序是根据首字母的顺序来的.
SpringCloud Ribbon 示例
服务端
首先建立一个 springcloud-ribbon-eureka 服务的工程, 用于做注册中心. 配置和代码之前的基本一样, 除了端口和打印信息, 这里就不在说明了.
客户端
这里我们定义三个服务, 一个服务使用 Ribbon 做负载均衡, 另外两个做普通的服务, 服务名称依次为 springcloud-ribbon-consumer,springcloud-ribbon-consumer2 和 springcloud-ribbon-consumer3. 添加如上的依赖之后, 在 application.properties 添加如下的配置:
consumer 配置信息:
- spring.application.name=springcloud-ribbon-consumer
- server.port=9006
- eureka.client.serviceUrl.defaultZone=http://localhost:8003/eureka/
consumer2 配置信息:
- spring.application.name=springcloud-ribbon-consumer2
- server.port=9007
- eureka.client.serviceUrl.defaultZone=http://localhost:8003/eureka/
consumer3 配置信息:
这里的服务名称和另一个服务的名称保持一致才能实现负载均衡功能.
- spring.application.name=springcloud-ribbon-consumer2
- server.port=9008
- eureka.client.serviceUrl.defaultZone=http://localhost:8003/eureka/
配置说明:
spring.application.name: 这个是客户端的服务名称. 如果有多个服务使用同一个名称但是访问地址不同, 结合 ribbon 使用, 则可以实现负载均衡功能.
server.port: 服务指定的端口.
eureka.client.serviceUrl.defaultZone: 注册中心服务端的地址.
springcloud-ribbon-consumer
使用 Ribbon 实现负载均衡, 只需要在启动类中实例化 RestTemplate, 通过 @LoadBalanced 注解开启均衡负载能力..
启动类代码示例:
- @SpringBootApplication
- @EnableDiscoveryClient
- public class RibbonConsumerApplication {
- public static void main(String[] args) {
- SpringApplication.run(RibbonConsumerApplication.class, args);
- System.out.println("ribbon 第一个消费者服务启动...");
- }
- @Bean
- @LoadBalanced
- public RestTemplate restTemplate() {
- return new RestTemplate();
- }
- }
需要定义转发的服务, 这里使用 RestTemplate 来进行调用, 调用另外一个服务的名称.
转发类代码示例:
- @RestController
- public class ConsumerController {
- @Autowired
- RestTemplate restTemplate;
- @RequestMapping("/hello")
- public String hello() {
- // 进行远程调用
- return restTemplate.getForObject("http://springcloud-ribbon-consumer2/hello/?name=xuwujing", String.class);
- }
- }
springcloud-ribbon-consumer2 和 springcloud-ribbon-consumer3 代码基本和 fegin 中的 springcloud-feign-consumer 一样, 因此这里就不在贴代码了.
功能测试
完成如上的工程开发之后, 我们依次启动服务端和客户端的四个程序, 然后在浏览器界面输入: http://localhost:8003/, 即可查看注册中心的信息.
在浏览器输入:
http://localhost:9006//hello
然后进行重复访问, 返回如下结果:
xuwujing,Hello World!
xuwujing,Hello World! 这是另一个服务!
xuwujing,Hello World!
xuwujing,Hello World! 这是另一个服务!
xuwujing,Hello World!
xuwujing,Hello World! 这是另一个服务!
说明已经实现了负载均衡功能了.
我们从上述的结果中发现了一点, 这个调用貌似是有规律的, 两个服务进行来回调用. 那么根据这个我们发现了, 负载均衡是由策略的. 上述的这个示例的策略就是其中的一个 RoundRobinRule 轮询策略.
这里就顺便说下 Ribbon 的策略, Ribbon 一共有 7 中策略, 默认使用的策略是轮询. 策略说明如下表格:
策略名 | 策略声明 | 策略描述 | 实现说明 |
---|---|---|---|
BestAvailableRule | public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule | 选择一个最小的并发请求的 server | 逐个考察 Server,如果 Server 被 tripped 了,则忽略,在选择其中 ActiveRequestsCount 最小的 server |
AvailabilityFilteringRule | public class AvailabilityFilteringRule extends PredicateBasedRule | 过滤掉那些因为一直连接失败的被标记为 circuit tripped 的后端 server,并过滤掉那些高并发的的后端 server(active connections 超过配置的阈值) | 使用一个 AvailabilityPredicate 来包含过滤 server 的逻辑,其实就就是检查 status 里记录的各个 server 的运行状态 |
WeightedResponseTimeRule | public class WeightedResponseTimeRule extends RoundRobinRule | 根据响应时间分配一个 weight,响应时间越长,weight 越小,被选中的可能性越低。 | 一个后台线程定期的从 status 里面读取评价响应时间,为每个 server 计算一个 weight.Weight 的计算也比较简单 responsetime 减去每个 server 自己平均的 responsetime 是 server 的权重。当刚开始运行,没有形成 status 时,使用 roubine 策略选择 server。 |
RetryRule | public class RetryRule extends AbstractLoadBalancerRule | 对选定的负载均衡策略机上重试机制。 | 在一个配置时间段内当选择 server 不成功,则一直尝试使用 subRule 的方式选择一个可用的 server |
RoundRobinRule | public class RoundRobinRule extends AbstractLoadBalancerRule | roundRobin 方式轮询选择 server | 轮询 index,选择 index 对应位置的 server |
RandomRule | public class RandomRule extends AbstractLoadBalancerRule | 随机选择一个 server | 在 index 上随机,选择 index 对应位置的 server |
ZoneAvoidanceRule | public class ZoneAvoidanceRule extends PredicateBasedRule | 复合判断 server 所在区域的性能和 server 的可用性选择 server | 使用 ZoneAvoidancePredicate 和 AvailabilityPredicate 来判断是否选择某个 server,前一个判断判定一个 zone 的运行性能是否可用,剔除不可用的 zone(的所有 server),AvailabilityPredicate 用于过滤掉连接数过多的 Server。 |
这里也顺便说明如何指定一个策略使用方法.
最简单的方式
在 application.properties 添加如下配置:
springcloud-ribbon-consumer2.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
该配置的意思是为 springcloud-ribbon-consumer2 服务指定随机策略.
另一种方式
新建一个类, 通过 @Bean 注解指定策略.
- @Configuration
- public class RibbonConfiguration{
- @Bean
- public IRule ribbonRule(){
- return new RandomRule();
- }
- }
然后再通过 @RibbonClien 注解指定服务.
- @RibbonClient(name="springcloud-ribbon-consumer2", configuration=RibbonConfiguration.class)
- public class HelloRibbon{
- }
注: 如果有的服务没有在 Eureka 进行注册, 可以使用 ribbon.listOfServers 方式在配置文件中来指定服务.
例如:
springcloud-ribbon-consumer2.ribbon.listOfServers:localhost:9007,localhost:9008
添加好了该配置之后, 我们重启 springcloud-ribbon-consumer 服务, 然后依旧重复访问 http://localhost:9006//hello 该地址,
访问的结果如下:
xuwujing,Hello World!
xuwujing,Hello World! 这是另一个服务!
xuwujing,Hello World! 这是另一个服务!
- xuwujing,Hello World!
- xuwujing,Hello World!
- xuwujing,Hello World!
可以看到已经成功实现了随机访问的策略!
SpringCloud Fegin 结合 Ribbon 实现负载均衡
Fegin 包含了 Ribbon, 可以直接实现负载均衡功能. 这里我们就在 Ribbon 的项目稍微进行改造下实现该功能.
首先在 pom 文件添加 Fegin 的依赖包.
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-openfeign</artifactId>
- </dependency>
然后在 springcloud-ribbon-consumer 项目的启动类上添加 @EnableFeignClients 注解, 启用 feign 进行远程调用.
添加完成之后, 新建一个类, 实现 feign 远程调用.
代码如下:
- @FeignClient(name= "springcloud-ribbon-consumer2")
- public interface HelloRemote {
- @RequestMapping(value = "/hello")
- public String hello(@RequestParam(value = "name") String name);
- }
最后在提供一个新的接口供外部调用. 这里就直接在之前的代码上新加一个接口了.
代码如下:
- @RestController
- public class ConsumerController {
- @Autowired
- RestTemplate restTemplate;
- @RequestMapping("/hello")
- public String hello() {
- return restTemplate.getForObject("http://springcloud-ribbon-consumer2/hello/?name=xuwujing", String.class);
- }
- @Autowired
- HelloRemote helloRemote;
- @RequestMapping("/hello/{name}")
- public String index(@PathVariable("name") String name) {
- System.out.println("接受到请求参数:"+name+", 进行转发到其他服务!");
- return helloRemote.hello(name);
- }
- }
添加完之后, 重启 springcloud-ribbon-consumer 服务, 然后依旧重复访问 http://localhost:9006//hello/pancm 该地址,
访问的结果如下:
pancm,Hello World!
pancm,Hello World! 这是另一个服务!
pancm,Hello World!
pancm,Hello World! 这是另一个服务!
pancm,Hello World!
pancm,Hello World! 这是另一个服务!
示例图:
其他
项目地址
基于 SpringBoot1.x,SpringCloud 的 Dalston 版本开发的地址 https://github.com/xuwujing/springcloud-study :https://github.com/xuwujing/springcloud-study
基于 SpringBoot1.x,SpringCloud 的 Dalston 版本开发的地址 https://github.com/xuwujing/springcloud-study-old : https://github.com/xuwujing/springcloud-study-old
如果感觉项目不错, 希望能给个 star, 谢谢!
音乐推荐
一首非常棒的纯音乐 NEXT TO YOU , 强烈推荐!
原创不易, 如果感觉不错, 希望给个推荐! 您的支持是我写作的最大动力!
来源: https://www.cnblogs.com/xuwujing/p/10273989.html