springCloud Alibaba之负载均衡

  • Post author:
  • Post category:其他

往期分享:
微服务项目搭建
springCloud Alibaba之Nacos组件

由于部分代码放在上面两篇文章中,所以直接看这边文章的代码可能看不到什么东西。

远程调用负载均衡 Ribbon

什么是负载均衡?

负载均衡就是根据负载均衡策略,把负载分摊给多个操作单元去执行

负载均衡流程图:

在这里插入图片描述

基于Ribbon实现负载均衡

Ribbon是Spring Cloud的一个组件, 它可以让我们使用一个注解就能轻松的搞定负载均衡

  1. 在RestTemplate 的生成方法上添加@LoadBalanced注解

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
    	return new RestTemplate();
    }
    
  2. 修改OrderServiceImpl服务调用的方法

    @Service
    public class OrderServiceImpl implements IOrderService {
        @Autowired
        private RestTemplate restTemplate;
        @Autowired
        private DiscoveryClient discoveryClient;
    
        @Override
        public Order createOrder(Long productId, Long userId) {
            Order order = new Order();
            order.setUid(userId);
            order.setOid(1L);
            order.setNumber(1);
            order.setUsername("James");
    
            Product product = restTemplate.getForObject("http://product-service/get?pid="
                                       +productId,Product.class);
            
            order.setPid(productId);
            order.setPname(product.getPname());
            order.setPprice(product.getPprice());
    
            return order;
        }
    }
    
  3. 为了更直观看到请求是进行负载均衡了,我们修改一下ProductController代码

@RestController
@Slf4j
public class ProductController {
    @Autowired
    private ProductService productService;
    @Value("${server.port}")
    private String port;

    //商品信息查询
    @RequestMapping("/product")
    public Product findByPid(@RequestParam("pid") Long pid) {
        log.info("接下来要进行{}号商品信息的查询", pid);
        Product product = productService.findByPid(pid);
        product.setPname(product.getPname()+" data from port:"+port);
        log.info("商品信息查询成功,内容为{}", JSON.toJSONString(product));
        return product;
    }
}

4.当我重复访问地址:http://localhost:8082/save?pid=1&uid=1的时候,发现在交替访问端口为8081和8083的两个product-service服务,其实是因为默认的负载均衡策略就是(ZoneAvoidanceRule的策略)
在这里插入图片描述

负载均衡策略:

com.netflix.loadbalancer.IRule , 具体的负载策略如下图所示:

策略名 策略描述 实现说明
BestAvailableRule 选择一个最小的并发请求的server 逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server
AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例; 使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态
WeightedResponseTimeRule 根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。 一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权
RetryRule 对选定的负载均衡策略机上重试机制。 在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server
RoundRobinRule 轮询方式轮询选择server 轮询index,选择index对应位置的server
RandomRule 随机选择一个server 在index上随机,选择index对应位置的server
ZoneAvoidanceRule(默认) 复合判断server所在区域的性能和server的可用性选择server 使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。

我们可以通过修改配置来调整Ribbon的负载均衡策略,在order-server项目的application.yml中增加如下配置:

product-service: # 调用的提供者的名称
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

5.当我再次重复访问地址:http://localhost:8082/save?pid=1&uid=1的时候,发现在随机访问端口为8081和8083的两个product-service服务,证明上面配置的RandomRule策略是起作用了
在这里插入图片描述

Ribbon实现负载均衡的原理

ribbon实现的关键点是为ribbon定制的RestTemplate,ribbon利用了RestTemplate的拦截器机制,在拦截器中实现ribbon的负载均衡。负载均衡的基本实现就是利用applicationName从服务注册中心获取可用的服务地址列表,然后通过一定算法负载,决定使用哪一个服务地址来进行http调用。

大白话版:

1.通过反射获取请求传过来的参数,url=http://product-service/get?pid=1

2.按照规则去进行切割,得到product-service服务的名称

3.在启动项目时就从注册中心拉取了一份服务地址的清单,127.0.0.1:8081 127.0.0.1:8083

4.根据负载均衡策略选择一个节点的服务,再把product-service替换成该服务对应的 IP:端口,

如选择了8081,那么替换的到http://127.0.0.1:8081/get?pid=1

5.最后还是通过RestTemplate去发送请求

远程调用Feign

什么是Feign

​ Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务

一样简单, 只需要创建一个接口并添加一个注解即可。

​ Nacos很好的兼容了Feign, Feign默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负

载均衡的效果。

订单微服务集成Feign

  1. 在shop-order-server项目的pom文件加入Fegin的依赖

    <!--fegin组件-->
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. 在启动类OrderServer.java上添加Fegin的扫描注解,注意扫描路径(默认扫描当前包及其子包)

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    public class OrderServer {
        public static void main(String[] args) {
            SpringApplication.run(OrderServer.class,args);
        }
    }
    
  3. 在shop-order-server项目中新增接口ProductFeignApi

    package cn.test.feign;
    //name的名称一定要和商品服务的服务名保持一致
    @FeignClient(name = "product-service")
    public interface ProductFeignApi {
        @RequestMapping("/product")
        public Product findByPid(@RequestParam("pid") Long pid);
    }
    
  4. 修改OrderServiceImpl.java的远程调用方法

    @Service
    @Slf4j
    public class OrderServiceImpl implements OrderService {
        @Autowired
        private OrderDao orderDao;
        @Autowired
        private ProductFeignApi productFeignApi;
        @Override
        public Order createOrder(Long productId,Long userId) {
           //测试是否超时重试
            System.out.println("测试是否超时重试");
    
            Order order = new Order();
            order.setUid(userId);
            order.setOid(1L);
            order.setNumber(1);
            order.setUsername("James");
    
            //通过Ribbon实现远程调用
            //Product product = restTemplate.getForObject("http://product-service/product?pid="+productId,Product.class);
    
            //通过Feign实现远程调用
            Product product = productFeignApi.findByPid(productId);
            System.out.println("查询成功:product " + JSON.toJSONString(product));
            order.setPid(productId);
            order.setPname(product.getPname());
            order.setPprice(product.getPprice());
            
            return order;
        }
    }
    
  5. 重启订单服务,并验证.

Feign的重要属性

超时配置

feign:
  client:
    config:
      default:
        connectTimeout: 3000
        readTimeout: 3000

重试次数配置

https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties

product-service: # 调用的提供者的名称
  ribbon:
    MaxAutoRetries: 0
    MaxAutoRetriesNextServer: 0

Feign实现远程调用的原理

1.通过反射拿到代理类实现的接口 ProductFeignApi

2.通过反射拿到接口上注解@ Feignclient注解,井且把注解中的值取出来, product-service

3.通过反射拿到接口中方法井且拿到接口中方法上的注解@ Requestmapping,井且把值给取出来,/get

4.把方法中参数注解中的值也获取出来id

5.拼接路径http://product-service/get?pid=1

6.根据服务名上本地去找 product-service对应节点信息 localhost:8081 localhost:8083

7.根据你配置 ribbon的负载均衡策选择一个节点, localhost:8081

8.把服务名称替换成http://localhost::8081/get?pid=1

拿到接口中方法井且拿到接口中方法上的注解@ Requestmapping,井且把值给取出来,/get

9.使用 Resttemplate发送请求


版权声明:本文为Seati_原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。