1 什么是负载均衡
通俗的讲,负载均衡就是将负载(⼯作任务,访问请求)进⾏分摊到多个操作单元(服务器,组件)上进⾏执⾏。根据负载均衡发⽣位置的不同,⼀般分为服务端负载均衡和客户端负载均衡。
- 服务端负载均衡指的是发⽣在服务提供者⼀⽅,⽐如常⻅的Nginx负载均衡;
- 客户端负载均衡指的是发⽣在服务请求的⼀⽅,也就是在发送请求之前已经选好了由哪个服务端实例处理请求;
我们在微服务调⽤中⼀般会选择客户端负载均衡,也就是在服务调⽤⽅来决定服务由哪个提供者执行。
2 自定义负载均衡
2.1 在idea中再启动⼀个 shop-product 微服务,设置其端⼝为8082
2.2 在nacos查看微服务的启动情况
2.3 修改 OrderServiceImpl 的代码,实现负载均衡
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@Override
public Order createOrder(Long productId,Long userId) {
log.info("接收到{}号商品的下单请求,接下来调⽤商品微服务查询此商品信息",productId);
//从nacos中获取服务地址
//⾃定义规则实现随机挑选服务
List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
int index = new Random().nextInt(instances.size());
ServiceInstance instance = instances.get(index);
String url = instance.getHost()+":"+instance.getPort();
log.info(">>从nacos中获取到的微服务地址为:" + url);
//远程调⽤商品微服务,查询商品信息
Product product = restTemplate.getForObject(
"http://"+url+"/product/"+productId,Product.class);
log.info("查询到{}号商品的信息,内容是:{}", productId, JSON.toJSONString(product));
//创建订单并保存
Order order = new Order();
order.setUid(userId);
order.setUsername("叩丁狼教育");
order.setPid(productId);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderDao.save(order);
log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
return order;
}
}
2.4 启动两个服务提供者和⼀个服务消费者,多访问⼏次消费者测试效
2.5 基于Ribbon实现负载均衡
Ribbon是用于客户端负载均衡的工具,是Spring Cloud的⼀个组件,它可以让我们使⽤⼀个注解就能轻松的实现负载均衡;
Ribbon只负责负载均衡,那么发送请求,还需要Spring提供的RestTemplate。默认情况下,使用RestTemplate是无需进行配置的,Spring会自动注入。但是如果配合Ribbon使用时,就需要手动声明了。
1) 在RestTemplate的⽣成⽅法上添加@LoadBalanced注解
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
2) 修改OrderServiceImpl服务调⽤的⽅法
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private RestTemplate restTemplate;
@Override
public Order createOrder(Long productId,Long userId) {
log.info("接收到{}号商品的下单请求,接下来调⽤商品微服务查询此商品信息", productId);
//远程调⽤商品微服务,查询商品信息
Product product = restTemplate.getForObject(
"http://product-service/product/"+productId,Product.class);
log.info("查询到{}号商品的信息,内容是:{}", productId, JSON.toJSONString(product));
//创建订单并保存
Order order = new Order();
order.setUid(userId);
order.setUsername("叩丁狼教育");
order.setPid(productId);
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderDao.save(order);
log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
return order;
}
}
3) 为了更直观看到请求是进⾏负载均衡了,我们修改⼀下ProductController代码
@RestController
@Slf4j
public class ProductController {
@Autowired
private ProductService productService;
@Value("${server.port}")
private String port;
//商品信息查询
@RequestMapping("/product/{pid}")
public Product findByPid(@PathVariable("pid") Long pid) {
log.info("接下来要进⾏{}号商品信息的查询", pid);
Product product = productService.findByPid(pid);
product.setPname(product.getPname()+",data from "+port);
log.info("商品信息查询成功,内容为{}", JSON.toJSONString(product));
return product;
}
}
4) 调⽤订单保存的⽅法,查看⽇志
默认情况下,Ribbon负载均衡采取的是ZoneAvoidanceRule的策略,复合判断server所在区域的性能和server的可⽤性来选择server。在没有区域的环境下,采用类似于轮询(RandomRule)的策略。
3 Ribbon⽀持的负载均衡策略
Ribbon内置了多种负载均衡策略,内部负载均衡的顶级接⼝为com.netflix.loadbalancer.IRule。具体的负载策略如下图所示:
我们可以通过修改配置来调整Ribbon的负载均衡策略,在order-server项⽬的application.yml中增加。如下配置: