微服务-负载均衡介绍

118 阅读8分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情

什么是负载均衡

负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行,根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡

服务端负载均衡

服务端负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡

客户端负载均衡

客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求

我们在微服务调用关系中一般会选择客户端负载均衡,也就是在服务调用的一方来决定服务由哪个提供者执行

代码自定义实现负载均衡

模拟出来一台商品服务提供者(8082)

实现之前为了方便测试,我们需要再模拟出来一台商品服务提供者(8082),这个通过IDEA来操作

添加一个springboot配置

添加启动类知道是商品的启动类并且修改端口为8082

来到nacos客户端控制台发现商品微服务实例增加了一个,就是我们刚才创建的

可以通过点击详情查看具体的信息

1 随机数进行负载均衡

在实例范围内随机挑选一个进行使用



 //通过随机数选择实例 创建一个随机数在实例中的随机数

int index=new Random().nextInt(instances.size()); //根据实例个数生成的随机数 保证在实例中间



 //自定义负载均衡

ServiceInstance serviceInstance = instances.get(index);

重新启动商品微服务,发现实例是随机调用的

package com.wyh.controller;



import com.alibaba.fastjson.JSON;

import com.wyh.entity.Order;

import com.wyh.entity.Product;

import com.wyh.service.OrderService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cloud.client.ServiceInstance;

import org.springframework.cloud.client.discovery.DiscoveryClient;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.client.RestTemplate;



import java.util.List;

import java.util.Random;



 /**

 *  @program:  springcloud-alibaba

 *  @description:  order controller

 *  @author:  魏一鹤

 *  @createDate:  2022-05-01 22:52

 **/

@RestController

@Slf4j

public class OrderController {

    @Autowired

    private OrderService orderService;

    @Autowired

    private RestTemplate restTemplate;

    @Autowired

    private DiscoveryClient discoveryClient;



    //下单->自定义负载均衡

 @RequestMapping( "/order/prod/{pid}" )

    public Order order(@PathVariable Integer pid) {



        log.info( "接收到{}号商品的下单请求,接下来调用商品微服务查询此商品信息" ,pid);

        //调用商品微服务查询商品信息

 //使用用restTemplate调用商品微服务 有两个参数 url访问地址和返回值

 //订单微服务通过nacos调用商品微服务



 //获取全部商品微服务实例,参数就是nacos客户端服务列表的服务名

 List<ServiceInstance> instances = discoveryClient.getInstances( "service-product" );



        //通过随机数选择实例 创建一个随机数在实例中的随机数

  int index=new Random().nextInt(instances.size()); //根据实例个数生成的随机数 保证在实例中间



 //自定义负载均衡

 ServiceInstance serviceInstance = instances.get(index);

        //获取商品微服务地址

 String host = serviceInstance.getHost();

        //获取商品微服务端口

  int port = serviceInstance.getPort();

        //把获取到商品微服务的地址+端口进行嵌入

 Product product = restTemplate.getForObject( "http://" +host+ ":" +port+ "/product/" + pid, Product.class);

        log.info( "查询到{}号商品信息,内容是{}" ,pid, JSON.toJSONString(product));

        //组装order订单信息

 Order order=new Order();

        //用户信息 模拟即可

 order.setUid(1);

        order.setUsername( "测试用户" );

        //商品信息 从查询到的product中获取

 order.setPid(pid);

        order.setPname(product.getPname());

        order.setPprice(product.getPprice());

        //购买数量 模拟即可

 order.setNumber(1);

        //下单 创建订单信息

  orderService.createOrder(order);

        log.info( "创建信息成功,订单信息为:{}" ,JSON.toJSONString(order));

        return order;

    }

}

2 基于ribbon实现负载均衡

我们上面使用随机数实现负载均衡是一种思路,但是真实的负载均衡其实有很多策略的,比如说轮询,加强轮询,哈希,使用ribbon就可以实现

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

ribbon默认使用轮询做负载均衡,即一个给你处理,一个给我处理

第一步 在RestTemplate的生成方法上添加@LoadBalanced注解

package com.wyh;



import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;

import org.springframework.context.annotation.Bean;

import org.springframework.web.client.RestTemplate;



 /**

 *  @program:  springcloud-alibaba

 *  @description:  order启动类

 *  @author:  魏一鹤

 *  @createDate:  2022-05-01 23:00

 **/



@EnableDiscoveryClient //开启nacos客户端注解

@SpringBootApplication

public class OrderApplication {

    public static void main(String[] args){

        SpringApplication.run(OrderApplication.class);

    }





    @Bean  //把restTemplate注入到spring并且使用restTemplate调用微服务

  @LoadBalanced   //开启ribbon负载均衡

  public RestTemplate restTemplate() {

        return new RestTemplate();

    }

}

第二步 修改服务调用的方法

package com.wyh.controller;



import com.alibaba.fastjson.JSON;

import com.wyh.entity.Order;

import com.wyh.entity.Product;

import com.wyh.service.OrderService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cloud.client.ServiceInstance;

import org.springframework.cloud.client.discovery.DiscoveryClient;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.client.RestTemplate;



import java.util.List;

import java.util.Random;



 /**

 *  @program:  springcloud-alibaba

 *  @description:  order controller

 *  @author:  魏一鹤

 *  @createDate:  2022-05-01 22:52

 **/

@RestController

@Slf4j

public class OrderController {

    @Autowired

    private OrderService orderService;

    @Autowired

    private RestTemplate restTemplate;

    @Autowired

    private DiscoveryClient discoveryClient;



    //下单->ribbon实现负载均衡

 @RequestMapping( "/order/prod/{pid}" )

    public Order order(@PathVariable Integer pid) {



        log.info( "接收到{}号商品的下单请求,接下来调用商品微服务查询此商品信息" ,pid);

        //调用商品微服务查询商品信息

 //使用用restTemplate调用商品微服务 有两个参数 url访问地址和返回值

 //订单微服务通过nacos调用商品微服务



 Product product = restTemplate.getForObject( "http://service-product/product/" + pid, Product.class);

        log.info( "查询到{}号商品信息,内容是{}" ,pid, JSON.toJSONString(product));

        //组装order订单信息

 Order order=new Order();

        //用户信息 模拟即可

 order.setUid(1);

        order.setUsername( "测试用户" );

        //商品信息 从查询到的product中获取

 order.setPid(pid);

        order.setPname(product.getPname());

        order.setPprice(product.getPprice());

        //购买数量 模拟即可

 order.setNumber(1);

        //下单 创建订单信息

  orderService.createOrder(order);

        log.info( "创建信息成功,订单信息为:{}" ,JSON.toJSONString(order));

        return order;

    }

}

3 Ribbon支持的负载均衡策略

Ribbon的默认内置了7种负载均衡策略:默认采用第一种RoundRobinRule轮询策略

1、RoundRobinRule

轮询策略,Rabbon默认采⽤的策略,若经过⼀轮轮询没有找到可⽤的provider(提供者),其最多轮询10轮,若最终

没有找到,则返回NULL。

举例:当前有3个提供者A,B,C,先挨个轮询1遍,A,B,C都不访问(1轮),在A,B,C访问⼀遍(2轮次),⼀共试10轮

如果还不能访问,则返回NULL。

2、RandomRule

随机策略,从所有可⽤的provider(提供者)中选择⼀个。

3、RetryRule

重试策略,先按照RoundRobinRule策略获取provider(策略者)能获取到直接返回,若获取失败,则在指定的时限内重试,

默认的时限为500毫秒。【RoundRobinRule轮询策略,默认是10轮,⽽RetryRule我给你500毫秒,你可以⼀直重试,直到找到为⽌】

4、BestAvailableRule

最可⽤策略。选择并发量最⼩的provider(提供者), 即连接的消费者数量最少的provider 。

5、AvailabilityFilteringRule

可⽤过滤算法。该算法规则是:过滤掉处于熔断状态的provider与已经超过连接极限的provider,对剩余provider采⽤轮询策略。

6、ZoneAvoidanceRule

zone回避策略。根据provider所在zone及provider的可⽤性,对provider进⾏选择。

7、WeightedResponseTimeRule

“权重响应时间”策略。根据每个provider的平均响应时间计算其权重,响应时间越快权重越⼤,被选中的机率就越⾼。

4 修改rabbon的负载均衡策略

很简单,修改application文件即可,根据不同的策略进行不同的修改

 #端口server:



server:

  port: 8091



 #项目名称



spring:

  application:

    name: service-order



  # 数据库

  datasource:

    driver-class-name: com.mysql.cj.jdbc.Driver

    url: jdbc:mysql://localhost:3306/shop?serverTimezone=GMT&allowPublicKeyRetrieval=true&useSSL=false&characterEncoding=utf8

    username: root

    password: root

  # jpa

  jpa:

    properties:

      hibernate:

        #jpa根据实体创建对应的数据库表.有的话就创建,没有的话就不创建

  hbm2ddl:

          auto: update

          #索引方式

  dialect: org.hibernate.dialect.MYSQL5InnoDBDialect

  #配置nacos服务

  cloud:

    nacos:

      discovery:

        server-addr: localhost:8848



 #修改ribbon负载均衡的策略,这里改为随机数 在服务消费者配置服务提供者的服务名

stock-service:

  ribbon:

    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

5 ribbon和Nginx的区别

服务器端负载均衡 Nginx

nginx 是客户端所有请求统一交给 nginx,由 nginx 进行实现负载均衡请求转发,属于服务器端负载均衡。

既请求由 nginx 服务器端进行转发。

2.客户端负载均衡 Ribbon

Ribbon 是从 eureka 注册中心服务器端上获取服务注册信息列表,缓存到本地,然后在本地实现轮询负载均衡策略。

既在客户端实现负载均衡。

应用场景的区别:

(1)Nginx适合于服务器端实现负载均衡比如 Tomcat ,Ribbon适合与在微服务中RPC远程调用实现本地服务负载均衡,比如 Dubbo、SpringCloud 中都是采用本地负载均衡。

spring cloud的Netflix中提供了两个组件实现软负载均衡调用:ribbon和feign。

(2)Ribbon

是一个基于 HTTP 和 TCP 客户端的负载均衡器

它可以在客户端配置 ribbonServerList(服务端列表),然后轮询请求以实现均衡负载。

springcloud的ribbon和nginx有什么区别?哪个性能好?

nginx性能好,但ribbon可以剔除不健康节点,nginx剔除节点比较复杂。ribbon还可以配合熔断器一起工作

ribbon是客户端负载均衡,nginx是服务端负载均衡。客户端负载均衡,所有客户端节点都维护自己要访问的服务端清单。服务端负载均衡的软件模块会维护一个可用的服务清单

ribbon 是一个客户端负载均衡器,可以简单的理解成类似于 nginx的负载均衡模块的功能。