Ribbon

第一章 Ribbon简介

Ribbon Netflix是公司开源的个负载均衡的组件,是将负载均衡逻辑封装在客户端中,并且运行在客户端的进程里。  

在Spring Cloud 构建的微服务系统中, Ribbon 作为服务消费者的负载均衡,有两种使用方式:

1)RestTemplate相结合

2)Feign相结合(Feign默认集成了Ribbon

 

两种负载均衡方式:

1)独立进程单元,通过负载均衡策略,将请求转发到不同的执行单元上(Ngnix

2)将负载均衡逻辑以代码的方式封装到服务消费者的客户端上,服务消费者客户端维护了一份服务提供者的列表信息,通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的(Ribbon

 

 

第二章 RestTemplate

 

RestTemplatespring Resources中一个访问第三方RESTful API接口的网络请求框架

 

支持XMLJSON,默认实现了序列化,可以自动将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 nameconsumer调用时用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实现过程:

  1. 通过@EnableFeignClients注解开启FeignClient功能,开启对@FeignClient注解的包扫描。
  2. 根据Feign规则实现接口,并加上@FeignClient注解。
  3. 程序启动后,会扫描所有@FeignClient的注解的类,并将这些信息注入IOC容器中。
  4. 接口被调用时,通过JDK代理来生成具体的RequestTemplate模板对象。
  5. 根据RequestTemplate生成Http请求的request对象
  6. Client 处理Request对象,client的网络请求框架可以是HttpURLConnectionHttpClientOkHttp
  7. 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();
    }
}

 

汇总:

 

posted @ 2021-02-26 00:01  無_茗  阅读(81)  评论(0)    收藏  举报