Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 的客户端组件提供一系列完整的配置项,如:连接超时、重试等。
在配置文件中列出 LoadBalancer (简称LB:负载均衡) 后面所有的机器,Ribbon 会自动的帮助你基于某种规则 (如简单轮询,随机连接等等) 去连接这些机器,也可以使用 Ribbon 实现自定义的负载均衡算法。
负载均衡:简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高可用)。
负载均衡简单分类:
Ribbon 就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
要实现Ribbon负载均衡至少需要一个服务注册中心、至少两个服务提供者、一个服务消费者。
负载均衡流程:
服务提供者在服务注册中心注册
服务消费者向服务注册中心发出请求
服务注册中心将服务提供者地址提供给消费者
消费者根据负载均衡策略挑选服务
前置条件:学习Eureka搭建服务,学习restful发送请求
使用Eureka章节搭建的Eureka服务端
负载均衡至少需要两个服务提供者
步骤:
新建maven子模块:springcloud-eureka-client-8001、springcloud-eureka-client-8002
添加依赖:两个服务提供者依赖相同
编写application.yaml
spring:
application:
name: springcloud-eureka-client #服务注册中心的服务名,提供相同服务的提供者需要同名
server:
port: 8001
eureka:
instance:
instance-id: client8001
prefer-ip-address: true #访问路径可以显示ip
client:
service-url:
defaultZone: http://127.0.0.1:2001/eureka #表示eureka服务器的部署位置
register-with-eureka: true
fetch-registry: true
spring:
application:
name: springcloud-eureka-client #服务注册中心的服务名,提供相同服务的提供者需要同名
server:
port: 8002
eureka:
instance:
instance-id: client8002
prefer-ip-address: true #访问路径可以显示ip
client:
service-url:
defaultZone: http://127.0.0.1:2001/eureka #表示eureka服务器的部署位置
register-with-eureka: true
fetch-registry: true
编写controller
springcloud-eureka-client-8001
@ResponseBody
@Controller
public class TestController {
@GetMapping(value = "/payment/get")
public String getPaymentById()
{
return "get8001";
}
}
springcloud-eureka-client-8002
@ResponseBody
@Controller
public class TestController {
@GetMapping(value = "/payment/get")
public String getPaymentById()
{
return "get8002";
}
}
编写启动类:两个服务提供者启动类相同
@EnableDiscoveryClient
@EnableEurekaClient
@SpringBootApplication
public class SpringCloudEurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaClientApplication.class,args);
}
}
步骤:
新建maven子模块:springcloud-eureka-consumer
添加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>1.4.6.RELEASEversion>
dependency>
spring:
application:
name: springcloud-eureka-consumer
server:
port: 80
eureka:
instance:
instance-id: consumer80
prefer-ip-address: true #访问路径可以显示ip
client:
service-url:
defaultZone: http://127.0.0.1:2001/eureka #表示eureka服务器的部署位置
register-with-eureka: true
fetch-registry: true
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced//使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@Configuration
public class MySelfRule {
@Bean //lRule:根据特定算法中从服务列表中选取一个要访问的服务
public IRule myRule(){
return new RandomRule();//随机访问
}
}
@ResponseBody
@Controller
public class TestController {
//载均衡服务提供者的服务名
public static final String PAYMENT_URL="http://SPRINGCLOUD-EUREKA-CLIENT";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/get")
public String getPayment(){
//RestTemplate根据负载均衡策略向服务提供者发送请求
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/",String.class);
}
}
@EnableEurekaClient
@SpringBootApplication
/**
name:负载均衡服务提供者的服务名
configuration:负载均衡策略类的class
*/
@RibbonClient(name = "SPRINGCLOUD-EUREKA-CLIENT", configuration = MySelfRule.class)
public class SpringCloudEurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaClientApplication.class,args);
}
}
自定义算法类必须继承 AbstractLoadBalancerRule类
@Configuration
public class CustomerRule extends AbstractLoadBalancerRule{
//自定义逻辑:调用同一个服务5次后,再轮询调用下一个服务
/*
total = 0 // 当total==5,调用下一个服务
currentIndex = 0 // 当前对外提供服务的服务器下标
*/
private int total = 0; // 总共被调用的次数,目前要求每台被调用5次
private int currentIndex = 0; // 当前提供服务的机器号
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers(); //当前存活的服务
List<Server> allList = lb.getAllServers(); //获取全部的服务
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
if(total < 5)
{
server = upList.get(currentIndex);
total++;
}else {
total = 0;
currentIndex++;
if(currentIndex >= upList.size())
{
currentIndex = 0;
}
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
@EnableEurekaClient
@SpringBootApplication
//修改configuration,指定为自定义配置类
@RibbonClient(name = "SPRINGCLOUD-EUREKA-CLIENT", configuration = CustomerRule.class)
public class SpringCloudEurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaClientApplication.class,args);
}
}