Ribbon 是 Netflix 开源的项目,主要用于为提供客户端软件提供负载均衡算法。Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一个基于 HTTP 和 TCP 的客户端负载均衡器。
创建两个项目:order-service(订单服务,作为服务提供者)、user-center(用户中心,作为服务消费者)。并在 user-center(服务消费者)端使用 Spring Cloud Ribbon 做客户端负载均衡。
在 order-service/pom.xml 中声明依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
在 order-service/src/main/resources/application.yml 配置两个端口以便启动两个不同实例用于测试:
spring: application: name: order-service profiles: active: service1 --- spring: profiles: service1 server: port: 8881 --- spring: profiles: service2 server: port: 8882
编写查询用户订单的服务方法:
@RestController @RequestMapping("/order") public class OrderController{ @Autowired private OrderRepository orderRepository; @GetMapping("/find/{uid:[1-9]//d+}") publicList<Order>findByUid(@PathVariable Integer uid){ System.out.println("------------------------------------------------"); System.out.println("------------------ 方法被调用 ------------------"); System.out.println("------------------------------------------------"); return orderRepository.findByUid(uid); } }
配置根路径的访问许可,以便客户端负载均衡器能够 PING 通本服务:
@Configuration public class WebConfigextends WebMvcConfigurerAdapter{ @Override public void addViewControllers(ViewControllerRegistry registry){ registry.addStatusController("/", HttpStatus.OK); } }
编写 Spring Boot 应用启动类:
@SpringBootApplication public class Application{ public static void main(String[] args){ SpringApplication.run(Application.class); } }
在 user-center/pom.xml 中声明依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency>
在 user-center/src/main/resources/application.yml 配置客户端负载均衡:
server: port: 8888 spring: application: name: user-center order-service: ribbon: eureka: enabled: false # 微服务的服务器列表 listOfServers: localhost:8881,localhost:8882 # 刷新微服务的服务器列表信息间隔的毫秒数 ServerListRefreshInterval: 3000
默认情况下,Spring Cloud Ribbon 使用 NoOpPing 作为 IPing 的实现,NoOpPing 实际上并没有 PING 服务器而是一直返回 true。而 PingUrl 则会通过 PING 服务器根路径地址来检查每一台服务器的状态以确认服务是否还在线。默认情况下,Spring Cloud Ribbon 使用 ZoneAvoidanceRule 作为 IRule 的实现,ZoneAvoidanceRule 基于 AZ(可用区)过滤服务器最大程度避免区域服务器故障。由于本地环境测试,这里采用 AvailabilityFilteringRule,它使用 Ribbon 内置的断路器功能来过滤掉处于开路状态(无法 PING 通)的服务器。
public class RibbonConfiguration{ @Autowired private IClientConfig ribbonClientConfig; @Bean publicIPingribbonPing(IClientConfig config){ return new PingUrl(); } @Bean publicIRuleribbonRule(IClientConfig config){ return new AvailabilityFilteringRule(); } }
编写用户订单查询服务:
@RestController @RequestMapping("/user") public class UserController{ @Autowired private RestTemplate restTemplate; @GetMapping("/orders/{uid:[1-9]//d+}") publicList<Order>orders(@PathVariable Integer uid){ return restTemplate.getForObject(String.format("http://order-service/order/find/%d", uid), List.class); } }
编写 Spring Boot 应用启动类,使用 @LoadBalanced
注解,为 RestTemplate 开启负载均衡的能力:
@SpringBootApplication @RibbonClient(name = "order-service", configuration = RibbonConfiguration.class) public class Application{ @Bean @LoadBalanced publicRestTemplateproviceRestTemplate(){ return new RestTemplate(); } public static void main(String[] args){ SpringApplication.run(Application.class); } }
启动两个服务提供者服务:
$ java -jar order-service-0.0.1-SNAPSHOT.jar $ java -Dspring.profiles.active=service2 -jar order-service-0.0.1-SNAPSHOT.jar
启动一个服务消费者服务:
$ java -jar user-center-0.0.1-SNAPSHOT.jar
你可以尝试多次访问用户订单服务,或尝试途中关闭其中一个服务提供者服务再访问用户订单服务,以便查看其效果。
$ curl -i -X GET http://localhost:8888/user/orders/1001
创建三个项目:eureka-server(服务注册中心)、order-service(订单服务,作为服务提供者)、user-center(用户中心,作为服务消费者)。并在 user-center(服务消费者)端使用 Spring Cloud Ribbon 做客户端负载均衡。
在 eureka-server/pom.xml 中声明依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency>
eureka-server/src/main/resources/application.yml 配置示例:
server: port: 8761 spring: application: name: eureka-server eureka: instance: hostname: localhost server: renewal-percent-threshold: 0.49 client: register-with-eureka: false fetch-registry: false service-url: default-zone: http://${eureka.instance.hostname}:${server.port}/eureka/ logging: level: com.netflix.eureka: 'off' com.netflix.discovery: 'off'
Spring Boot 应用启动类:
@SpringBootApplication public class Application{ public static void main(String[] args){ SpringApplication.run(Application.class); } }
在 order-service/pom.xml 中声明依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
在 order-service/src/main/resources/application.yml 配置两个端口以便启动两个不同实例用于测试:
spring: application: name: order-service profiles: active: service1 eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ --- spring: profiles: service1 server: port: 8881 --- spring: profiles: service2 server: port: 8882
编写查询用户订单的服务方法:
@RestController @RequestMapping("/order") public class OrderController{ @Autowired private OrderRepository orderRepository; @GetMapping("/find/{uid:[1-9]//d+}") publicList<Order>findByUid(@PathVariable Integer uid){ System.out.println("------------------------------------------------"); System.out.println("------------------ 方法被调用 ------------------"); System.out.println("------------------------------------------------"); return orderRepository.findByUid(uid); } }
编写 Spring Boot 应用启动类:
@SpringBootApplication public class Application{ public static void main(String[] args){ SpringApplication.run(Application.class); } }
在 user-center/pom.xml 中声明依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
user-center/src/main/resources/application.yml 配置示例:
server: port: 8888 spring: application: name: user-center eureka: client: register-with-eureka: false serviceUrl: defaultZone: http://localhost:8761/eureka/
编写用户订单查询服务:
@RestController @RequestMapping("/user") public class UserController{ @Autowired private RestTemplate restTemplate; @GetMapping("/orders/{uid:[1-9]//d+}") publicList<Order>orders(@PathVariable Integer uid){ return restTemplate.getForObject(String.format("http://order-service/order/find/%d", uid), List.class); } }
编写 Spring Boot 应用启动类,使用 @LoadBalanced
注解,为 RestTemplate 开启负载均衡的能力:
@SpringBootApplication @RibbonClient(name = "order-service", configuration = RibbonConfiguration.class) public class Application{ @Bean @LoadBalanced publicRestTemplateproviceRestTemplate(){ return new RestTemplate(); } public static void main(String[] args){ SpringApplication.run(Application.class); } }
启动服务注册中心:
$ java -jar eureka-server-0.0.1-SNAPSHOT.jar
启动两个服务提供者服务:
$ java -jar order-service-0.0.1-SNAPSHOT.jar $ java -Dspring.profiles.active=service2 -jar order-service-0.0.1-SNAPSHOT.jar
启动一个服务消费者服务:
$ java -jar user-center-0.0.1-SNAPSHOT.jar
你可以尝试多次访问用户订单服务,或尝试途中关闭其中一个服务提供者服务再访问用户订单服务,以便查看其效果。
$ curl -i -X GET http://localhost:8888/user/orders/1001
示例项目开发环境:Java-8、Maven-3、IntelliJ IDEA-2017、Spring Cloud-Dalston.SR1
完整示例项目链接: spring-cloud-netflix-ribbon-sample 、 spring-cloud-netflix-ribbon-with-eureka-sample
参考文档文献链接: client-side-load-balancing 、 spring-cloud-ribbon