Ribbon
第一章 Ribbon简介
Ribbon Netflix是公司开源的一个负载均衡的组件,是将负载均衡逻辑封装在客户端中,并且运行在客户端的进程里。
在Spring Cloud 构建的微服务系统中, Ribbon 作为服务消费者的负载均衡,有两种使用方式:
1)和RestTemplate相结合
2)和Feign相结合(Feign默认集成了Ribbon)
两种负载均衡方式:
1)独立进程单元,通过负载均衡策略,将请求转发到不同的执行单元上(Ngnix)
2)将负载均衡逻辑以代码的方式封装到服务消费者的客户端上,服务消费者客户端维护了一份服务提供者的列表信息,通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的(Ribbon)
第二章 RestTemplate
RestTemplate是spring Resources中一个访问第三方RESTful API接口的网络请求框架,
支持XML,JSON,默认实现了序列化,可以自动将JSON转换为实体
主要方法:
headForHeaders(),getForObject(),postForObject(),put(),delete()
例:
private RestTemplate restTemplate = new RestTemplate(); @RequestMapping("/cust/get/{id}") public CustInfo getCustInfoById(@PathVariable("id") Long id) { log.debug("Consumer_CustInfoController getCustInfoById is running..."); return restTemplate.getForObject(PROVIDER_PRIFIX + "/cust_info/selectid/" + id, CustInfo.class); }
第三章 Ribbon client
3.1. 起步依赖
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-eureka</artifactid>
</dependency>
<dependency>
<groupid>org.springframework.cloud</groupid
<artifactid>spring-cloud-starter-ribbon</artifactid>
</dependency>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactId>
</dependency>
新版本Hoxton.SR6引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
spring-cloud-starter-netflix-eureka-client中包含了spring-cloud-netflix-ribbon
3.2. Ribbon实例
3.2.1. Eureka client 中RestTemplate
@Configuration public class BeanConfiguration { @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
3.2.2. Eureka client 中controller
private static final String PROVIDER_PRIFIX = "http://EUREKA-PROVIDER"; @Autowired private RestTemplate restTemplate; @RequestMapping("/cust/getProviderInfo") public String getProviderServiceInfo() { return restTemplate.getForObject(PROVIDER_PRIFIX + "/cust_info/getServerInfo", String.class); }
3.2.3. Provider yml配置
需要设置application name,consumer调用时用http://EUREKA-PROVIDER
spring:
application:
name: eureka-provider
===================================
Provider1:
server: port: 9001 #数据源配置 spring: application: name: eureka-provider datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC username: root password: xxxx #日志信息 logging: level: com.kai.cloud: debug #mybatis: mybatis: mapper-locations: classpath:mappers/*.xml eureka: client: registerWithEureka: true fetchRegistry: true serviceUrl: defaultZone: http://eureka-server-7001:7001/eureka/ instance: instance-id: provider-9001 prefer-ip-address: true info: app.name: spring-cloud build.artifactId: $project.artifactId$ build.version: $project.version$
Provider2:
server: port: 9002 #数据源配置 spring: application: name: eureka-provider datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC username: root password: xxxx #日志信息 logging: level: com.kai.cloud: debug #mybatis: mybatis: mapper-locations: classpath:mappers/*.xml eureka: client: registerWithEureka: true fetchRegistry: true serviceUrl: defaultZone: http://eureka-server-7001:7001/eureka/ instance: instance-id: provider-9002 prefer-ip-address: true info: app.name: spring-cloud build.artifactId: $project.artifactId$ build.version: $project.version$
Provider3:
server: port: 9003 #数据源配置 spring: application: name: eureka-provider datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC username: root password: chenkai123 #日志信息 logging: level: com.kai.cloud: debug #mybatis: mybatis: mapper-locations: classpath:mappers/*.xml eureka: client: registerWithEureka: true fetchRegistry: true serviceUrl: defaultZone: http://eureka-server-7001:7001/eureka/ instance: instance-id: provider-9003 prefer-ip-address: true info: app.name: spring-cloud build.artifactId: $project.artifactId$ build.version: $project.version$
第四章 Ribbon 分析
负载均衡器的核 类为 LoadBalancerClient, LoadBalancerCiient 可以获取负载均衡的服务提
供者的实例信息:
@Autowired
LoadBalancerClient loadBalancerClient;
public void test() {
ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKA-PROVIDER");
}
Ribbon.eureka.enable:false 来禁止调用Eureka client获取注册列表信息。
Stores.ribbon.listOfServers: xxx.com,xxx2.com 来配置服务实例的URL

org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
@Override public ServiceInstance choose(String serviceId) { return choose(serviceId, null); } /** * New: Select a server using a 'key'. * @param serviceId of the service to choose an instance for * @param hint to specify the service instance * @return the selected {@link ServiceInstance} */ public ServiceInstance choose(String serviceId, Object hint) { Server server = getServer(getLoadBalancer(serviceId), hint); if (server == null) { return null; } return new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server)); } protected Server getServer(ILoadBalancer loadBalancer, Object hint) { if (loadBalancer == null) { return null; } // Use 'default' on a null hint, or just pass it on? return loadBalancer.chooseServer(hint != null ? hint : "default"); } protected ILoadBalancer getLoadBalancer(String serviceId) { return this.clientFactory.getLoadBalancer(serviceId); }
转为调用ILoadBalancer chooseServer方法,

DynamicServerListLoadBalancer public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList<T> serverList, ServerListFilter<T> filter, ServerListUpdater serverListUpdater) { super(clientConfig, rule, ping); this.serverListImpl = serverList; this.filter = filter; this.serverListUpdater = serverListUpdater; if (filter instanceof AbstractServerListFilter) { ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats()); } restOfInit(clientConfig); }
IRule:

第五章 自定义Rule
定义configuration:
@Configuration public class SelfBalanceRule { @Bean public IRule getSelfRule() { return new MyBalanceRule(); } }
定义rule:
@Slf4j public class MyBalanceRule extends AbstractLoadBalancerRule{ private int total = 0; private int chooseIndex = 0; public Server choose(ILoadBalancer lb,Object key) { Server server = null; int count=0; //设置10次循环查找,避免死循环 while(server==null && count++<10) { if(Thread.interrupted()) { return null; } List<Server> allList = lb.getAllServers();//获取所有的server集合 List<Server> reachList = lb.getReachableServers();//获取可用的server集合 //判断是否有必要进行下一步 int serverCount = allList.size(); int reachCount = reachList.size(); if(serverCount == 0 || reachCount == 0) { return null; } if ( total<3 ) { total++; } else { total = 0; chooseIndex++; if (chooseIndex > reachCount) { chooseIndex = 0; } } server = reachList.get(chooseIndex); if (server == null) { Thread.yield(); continue; } if(server.isAlive()) { return server; } } if(count>=10) { log.warn("no available server for the self balance:" + lb); } return server; } @Override public Server choose(Object key) { return choose(getLoadBalancer(),key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { } }
第六章 和Feign相结合(Feign默认集成了Ribbon)
feign实现过程:
- 通过@EnableFeignClients注解开启FeignClient功能,开启对@FeignClient注解的包扫描。
- 根据Feign规则实现接口,并加上@FeignClient注解。
- 程序启动后,会扫描所有@FeignClient的注解的类,并将这些信息注入IOC容器中。
- 接口被调用时,通过JDK代理来生成具体的RequestTemplate模板对象。
- 根据RequestTemplate生成Http请求的request对象
- Client 处理Request对象,client的网络请求框架可以是HttpURLConnection、HttpClient、OkHttp。
- Client被封装到LoadBalanceClient中,这个类结合Ribbon做了负载均衡。
6.1. 起步依赖
老版本为:spring-cloud-starter- feign
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
6.2. Feign实例
6.2.1. 实现FeignClient接口
@FeignClient(value = "EUREKA-PROVIDER", configuration = FeignConfig.class) public interface CustEurekaClientFeign { @GetMapping("/cust_info/selectid/{id}") public String getCustInfoById(@RequestParam(value="id") Long id); @GetMapping("/cust_info/getServerInfo") public String getServerInfo(); }
6.2.2. 配置FeignConfig远程调用失败后进行重试
@Configuration public class FeignConfig { //feign在远程调用失败后进行重试 @Bean public Retryer feignRetryer() { return new Retryer.Default(100, 1, 5); } }
6.2.3. Service调用CustEurekaClientFeign
@Service public class CustServiceImpl implements CustService{ @Autowired CustEurekaClientFeign custEurekaClientFeign; @Override public String getCustInfo(Long id) throws Exception { return custEurekaClientFeign.getCustInfoById(id); } @Override public String getServerInfo() throws Exception { return custEurekaClientFeign.getServerInfo(); } }
6.2.4. Controller调用service
@RestController public class ConsumerController { @Autowired private CustService custService; @RequestMapping("/cust/getCustInfo/{id}") public String getCustInfo(@PathVariable("id") Long id) throws Exception { return custService.getCustInfo(id); } @RequestMapping("/cust/getServerInfo") public String getServerInfo() throws Exception { return custService.getServerInfo(); } }
汇总:


浙公网安备 33010602011771号