Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具
,简单的说,Ribbon是Netflix 发布的开源项目,主要功能是提供客户端软件的负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供了一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮你基于某种规则(如简单轮训,随机连接等)去连接这些机器。我们也很容易实现自定义的负载均衡算法。
这里是Eureka作为服务注册中心,为Ribbon提供服务端信息的获取,比如说服务的IP地址和端口,使用前面搭建好的项目(eureka-server)。
调用不同的服务端,会返回对应服务端的接口。
@RestController
@RequestMapping("/api")
public class PoroviderController {@Value("${server.port}")private String serverPort;@PostMapping("/sayHello")public String sayHello(String name) {return "我是服务端" + serverPort + ",你好" + name;}
}
新建一个spring-boot工程,取名为consumer-ribbon,在pom文件引入ribbon需要的依赖。
org.springframework.cloud spring-cloud-starter-eureka
org.springframework.cloud spring-cloud-starter-ribbon
server:port: 8091servlet:context-path: /ribbon
eureka:client:# false表示不向注册中心注册自己register-with-eureka: falseservice-url:defaultZone: http://localhost:8761/eureka/
spring:application:name: consumer-ribbon
@Configuration
public class RestTemplateConfig {/*** @return org.springframework.web.client.RestTemplate* @description 注入一个可以进行负载均衡的RestTemple用于服务问调用* @author fengfan* @date 2022/5/18 14:43*/@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}}
@Configuration
public class RibbonConfig {/*** @return com.netflix.loadbalancer.IRule* @description 配置随机负载策略* @author fengfan* @date 2022/5/20 16:04*/@Beanpublic IRule iRule(){return new RandomRule();}
}
# 服务提供方的单独配置设置(服务名称大小写必须和注册的一样)
EUREKA-PROVIDER:ribbon:ConnectTimeout: 1000 #服务请求连接超时时间(毫秒)ReadTimeout: 3000 #服务请求处理超时时间(毫秒)OkToRetryOnAllOperations: true #对超时请求启用重试机制MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数MaxAutoRetries: 1 # 切换实例后重试最大次数NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法
@Configuration
public class MyRuleConfig {@Beanpublic IRule iRule() {return new RandomRule();}
}
@SpringBootApplication
@EnableEurekaClient
/*** 在启动改微服务的时候就能去加载我们自定义的Ribbon配置类,从而配置生效* 注意:MyRuleConfig 必须是 @Configuration ,但请注意,它不在主应用程序上下文* 的 @ComponentScan 中,否则将由所有 @RibbonClients 共享。* 如果您使用 @ComponentScan (或 @SpringBootApplication ),* 则需要采取措施避免包含(例如将其放在一个单独的,不重叠的包中,或者指定要在 @ComponentScan )*/
@RibbonClient(name = "EUREKA-PROVIDER", configuration = MyRuleConfig.class)
public class ConsumerRibbonApplication {public static void main(String[] args) {SpringApplication.run(ConsumerRibbonApplication.class, args);}}
这里自己将RandomRule抄一份,实际情况根据需求自己实现
public class ZDYRule extends AbstractLoadBalancerRule {@Overridepublic void initWithNiwsConfig(IClientConfig iClientConfig) {}@Overridepublic Server choose(Object o) {return this.choose(this.getLoadBalancer(), o);}public Server choose(ILoadBalancer lb, Object key) {if (lb == null) {return null;} else {Server server = null;while(server == null) {if (Thread.interrupted()) {return null;}List upList = lb.getReachableServers();List allList = lb.getAllServers();int serverCount = allList.size();if (serverCount == 0) {return null;}int index = this.chooseRandomInt(serverCount);server = (Server)upList.get(index);if (server == null) {Thread.yield();} else {if (server.isAlive()) {return server;}server = null;Thread.yield();}}return server;}}protected int chooseRandomInt(int serverCount) {return ThreadLocalRandom.current().nextInt(serverCount);}
}
@Configuration
public class MyRuleConfig {@Beanpublic IRule iRule() {return new ZDYRule();}
}
@RestController
@RequestMapping("/api")
public class ConsumerRibbonController {@Resourceprivate RestTemplate restTemplate;@PostMapping("/getServerInfo")public String getServerInfo(){MultiValueMap body = new LinkedMultiValueMap<>();body.add("name", "consumer8081");ResponseEntity responseEntity = restTemplate.postForEntity("http://EUREKA-PROVIDER/provider/api/sayHello", body, String.class);return responseEntity.getBody();}
}
多次请求的服务端,端口随机变化,证明配置成功。