Springcloud-Ribbon

162 阅读4分钟

简介

  Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。

在SpringCloud中一般配合Eureka使用,从Eureka中获取服务列表,根据负载均衡算法(轮询,随机)来选择使用的哪个服务提供者的服务

负载均衡(loadblance): 将用户请求平摊到分配到多个服务器,类似于用3个100宽带提供一个300m宽带的效果

分为:

集中式LB

在服务的消费方和提供方之间使用独立的负载均衡设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责吧访问请求通过某中策略转发至服务的提供方

进程式LB 将负载均衡逻辑集成到消费方,消费方从服务注册中心获知那些地址可用,然后自己再从这些地址中选择一个合适的服务器,Ribbon就属于进程式LB,ribbon只是一个类库,集成在消费方进程,消费方通过它来获取到服务提供方的地址

即:ribbon是一个类库,在消费者方面进行对服务列表的选择(轮询,随机)

2.png

使用

  1. 导入依赖
<!--因与Eureka配合所以需要导入Eureka的依赖包-->
 <dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-ribbon</artifactId>
     <version>1.4.7.RELEASE</version> 
</dependency>
<dependency>
     <groupId>com.netflix.ribbon</groupId>
     <artifactId>ribbon-eureka</artifactId>
     <version>2.2.5</version>
 </dependency>
  1. 编写配置文件
eureka:
  client:
    register-with-eureka: false #客户端只要拿,不需要向注册中心注册
    service-url:
    #从多个注册中心获取
      defaultZone: http://eureka1:7001/eureka,http://eureka1:7002/eureka,http://eureka1:7003/eureka
  1. 开启LoadBalanced 由于Client访问是通过RestTemplate来访问的 因此我们只需在RestTemplate上添加注解让其拥有负载均衡的功能即可
@Configuration
public class ConfigBean {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}
  1. 修改Controller 之前private static final String url="http://localhost:";被我们写死了

现在修改为private static final String url="http://PROVIDER-NAME";根据服务的id来获取

由于我们之前的三个服务提供者都叫PROVIDER-NAME,因此client有三个选择,会根据LB算法来抉择

@RestController
public class ConsumerController {
    @Autowired
    private RestTemplate template;
    private static final String url="http://PROVIDER-NAME";
    @RequestMapping("/consumer/get/{id}")
    public Dept getByID(@PathVariable long id){
                                                //请求的路径,返回的对象
        Dept getEntity = template.getForObject(url + "/dev/" + id, Dept.class);
        return getEntity;
    }
    @RequestMapping("/consumer/add")
    public boolean add(String dname){
        Dept dept = new Dept();
        dept.setDname(dname);
        System.out.println(dept);
        //请求的路径,传递的参数,返回的对象
        return template.postForObject(url+ "/dev/add",dept,Boolean.class);
    }
    @RequestMapping("/consumer/list")
    public List<Dept> list(){
        //请求的路径,返回的对象
        return template.postForObject(url+"/dev/list",void.class,List.class);
    }

}

  1. 由于使用了Eureka,因此需在启动类上添加注解
@SpringBootApplication
@EnableEurekaClient
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class,args);
    }
}

运行后访问 http://localhost/consumer/list 由于Ribbon默认是轮询的策略,因此每次访问都是不同的服务提供者 我们也可以自定义自己的LB策略

如何自定义LB策略

IRule接口是Riboon中负责负载均衡策略的组件有多个实现类 .png

  1. 继承IRule,照猫画虎将RandomRule中的代码抄过来,在"//-----"之间编写我们的代码
public class MyRandomRule extends AbstractLoadBalancerRule {
    /**
     * Randomly choose from all living servers
     */
    private static int currentindex=0;//谁在调用
    private static int total=0;//调用了几次
//    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
    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;
            }

//            int index = chooseRandomInt(serverCount); //源代码,在活着的服务数之间中生成随机数
//            server = upList.get(index);//源代码;根据随机数获取服务
            //------------------------------------
            //每个服务调用五次后,调用下一个服务
            if (total<5){
                server=upList.get(currentindex);
                total++;
            }else {
                total=1;
                currentindex++;
                if (currentindex>=upList.size())
                    currentindex=0;
                server=upList.get(currentindex);
            }
            System.out.println("--------"+server);
            //------------------------------------
            if (server == null) {
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }
            server = null;
            Thread.yield();
        }

        return server;

    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

	@Override
	public Server choose(Object key) {
		return choose(getLoadBalancer(), key);
	}

	@Override
	public void initWithNiwsConfig(IClientConfig clientConfig) {
		// TODO Auto-generated method stub

	}
}

  1. 注册到spring中
@Configuration
public class MyRuleConfig {
    @Bean
    public IRule myRule(){
        return new MyRandomRule();
    }
}
  1. 修改启动类
@SpringBootApplication
@EnableEurekaClient
//在微服务启动时自动去加载我们自定义的配置
@RibbonClient(name="xxx",configuration = MyRuleConfig.class)//
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class,args);
    }
}


注意:我们自定义的Ribbon相关配置不应该与启动类在同一个包下 7.png

debug

No instances available for provider

旧的pom.xml中对于ribbon的依赖是

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

后面新增了一个依赖

<dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-eureka</artifactId>
            <version>2.2.5</version>
</dependency>