Spring Cloud Alibaba Ribbon组件的使用

Ribbon组件在Spring Cloud的作用是实现负载均衡,这里简单的说一下负载均衡的概念,负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。如下图:

Spring Cloud Alibaba Ribbon组件的使用_第1张图片

 Nacos为我们提供了注册中心和配置中心,在集群模式下,Ribbon可以将请求通过一些策略分摊到各个Nacos,在讲具体实现之前,先来简单的自定义模拟一下负载均衡的实现:

自定义模拟负载均衡:

创建两个服务提供者工程:

ribbon_provider_1

pom依赖:导入nacos启动器和springboot的web启动器,同时注入common公共属性工程(这里不展示,common工程只声明了一个User实体类)

         
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        

配置文件:application.yaml

server:
  port: 9090
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.40.141:8848
  application:
    name: ribbon-provider

controller:

@RestController
@RequestMapping("/provider")
public class ProviderController {
    @Autowired
    private UserService userService;

    @RequestMapping("/getUserById/{id}")
    public User getUserById(@PathVariable Integer id){
        return userService.getUserById(id);
    }
}

service:

public interface UserService {
    User getUserById(Integer id);
}
@Service
public class UserServiceImpl implements UserService {

    @Override
    public User getUserById(Integer id) {
        return new User(id,"张三-1",18);
    }
}

启动类:

@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApp {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApp.class,args);
    }
}

ribbon_provider_2:

配置文件:application.yaml

server:
  port: 9091
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.40.141:8848
  application:
    name: ribbon-provider

Controller、Service与启动类和ribbon_provider_1保持一致(Service稍作区分,打印的User对象name属性为"张三-2")

创建服务消费者ribbon_consumer:

pom依赖(与ribbon_provider一样):

         
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        

application.yaml配置文件

server:
  port: 80
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.40.141:8848
  application:
    name: ribbon-consumer

controller:

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    //访问Rest服务的客户端
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    private int currentIndex;

    @RequestMapping(value = "/getUserById/{id}")
    public User getUserById(@PathVariable  Integer id){
        //获取nacos中注册的指定服务信息
        List serviceList = discoveryClient.getInstances("ribbon-provider");
        //通过Random工具类获取随机数
        currentIndex = new Random().nextInt(serviceList.size());
        //随机获取服务
        ServiceInstance serviceInstance = serviceList.get(currentIndex);
        //拼接访问服务的url
        String serviceUrl = serviceInstance.getHost() + ":" + serviceInstance.getPort();
        String url = "http://"+serviceUrl+"/provider/getUserById/"+id;
        //调用服务
        return restTemplate.getForObject(url,User.class);
    }
}

配置类(注册RestTemplate到ioc容器):

@Configuration
public class ConfigBean {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

启动类:

@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApp {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp.class,args);
    }
}

启动消费者跟提供者工程,然后启动nacos服务,浏览器进入nacos后台(关于nacos上篇文章已详细记载)查看注册的服务

Spring Cloud Alibaba Ribbon组件的使用_第2张图片

 看到服务提供者实例数为2,服务消费者实例数为1,注册成功!

访问localhost/consumer/getUserById/1测试

Spring Cloud Alibaba Ribbon组件的使用_第3张图片

查看响应结果是访问了ribbon_provider_2

继续刷新(这里模拟的策略为随机,可能连续几次访问的都是ribbon_provider_2)

Spring Cloud Alibaba Ribbon组件的使用_第4张图片

 负载均衡的随机分配模拟成功!

Ribbon入门使用:

首先,nacos里面已经集成了ribbon,所以不用额外的再去引入依赖。

Ribbon实现负载均衡的核心在于IRule接口,这个接口给出了负载均衡的规范,并且官方提供了多个实现类,每个实现类可以近似的看做是负载均衡的一个实现策略。

Spring Cloud Alibaba Ribbon组件的使用_第5张图片

如上图中的RandomRule就是随机分配策略,RoundRobinRule是轮询策略

简单的看下RandomRule源码如何实现:

    //该注解的作用是告诉JVM,忽略掉一些警告信息
    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
    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;
                }
                //调用自身的chooseRandomInt方法获取一个随机数
                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);
    }

 源码中的核心业务逻辑跟模拟随机分配的实现很相似,都是获得所有服务的列表再通过随机索引去获取服务并调用。

 了解这些原理后,改造一下上面的模拟实现案例,通过Ribbon来实操一波。

1.修改ribbon_consumer的ConfigBean配置类:

在RestTemplate的注册方法上加上@LoadBalanced注解,这样在通过ioc容器获取restTemplate对象时,Ribbon会通过拦截器将请求拦截并处理,拿到注册中心的所有可用服务,通过获取到的服务信息(ip,port)替换掉serviceId,同时在ioc容器中查找IRule是否已注册,如果已注册则根据IRule的类型来实施负载策略,否则通过实施默认策略。

    @Bean
	/**
	 * 添加了@LoadBalanced注解之后,Ribbon会给restTemplate请求添加一个拦截器,在拦截器中获取
	 * 注册中心的所有可用服务,通过获取到的服务信息(ip,port)替换 serviceId 实现负载请求。
	 */
	@LoadBalanced //开启负载均衡
	public RestTemplate getRestTemplate(){
		return new RestTemplate();
	}

	//随机策略
	@Bean
	public IRule iRule() {
		return new RandomRule();
	}

修改ribbon_consumer的controller:

@RequestMapping(value = "/getUserById/{id}")
    public User getUserById(@PathVariable("id")  Integer id){
        //在ribbon中不再使用host + ":" + port的方式获取url,而是通过serviceId(nacos中注册服务的名称)查找
        String serviceUrl = "ribbon-provider";
        String url = "http://"+serviceUrl+"/provider/getUserById/"+id;
        //调用服务
        return restTemplate.getForObject(url,User.class);
    }

 启动测试:

浏览器的地址栏输入localhost/consumer/getUserById/2测试访问

Spring Cloud Alibaba Ribbon组件的使用_第6张图片

Spring Cloud Alibaba Ribbon组件的使用_第7张图片

 至此Ribbon的入门案例结束

你可能感兴趣的:(ribbon,spring,cloud,java)