开启掘金成长之旅!这是我参与「掘金日新计划 · 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的负载均衡模块的功能。