上回说到了商品微服务可以从注册中心拿到商品微服务的信息,进行下单操作.
请看下面的代码
//获取商品微服务集合和信息
List<ServiceInstance> instances = discoveryClient.getInstances("service-product2021");
//获取第一个实例
ServiceInstance serviceInstance = instances.get(0);
//host ip地址
String host = serviceInstance.getHost();
//port 端口号
int port = serviceInstance.getPort();
Product product = restTemplate.getForObject("http://" + host + ":" + port + "/findProductById/" + pid, Product.class);
好像是没有什么问题,可以正常请求,但是如果将来商品微服务集群了,那我们是调用第一个实例呐还是第二个实例呐?应该来说都不行!因为一旦集群了就说明都要被调用,如何被调用呐?此时又引入了一个概念-负载均衡。也就说均衡的调用每个服务,分担到每个服务上执行。根据负载均衡发生的位置不同,一般分为服务端负载均衡和客户端负载均衡。
服务端负载均衡指的是发生在服务提供者一方,如常见的Nginx
客户端负载均衡指的是调用方/消费者,请求时就确定了调用哪个服务,通常在微服务中大多会选择此方式。
此时我们给shop-product再添加一个服务,选择springboot服务。
下边我们自定义一个小小算法来实现负载均衡。
/**
* 随机获取请求服务
* @param instances
* @return
*/
private String randomServer(List<ServiceInstance> instances) {
Map<String, Integer> map = new HashMap<>();
//key:192.168.9.186:9001
//value:0
for (int i = 0; i < instances.size(); i++) {
StringBuilder stringBuilder = new StringBuilder();
//host ip地址
stringBuilder.append(instances.get(i).getHost());
stringBuilder.append(":");
//port 端口号
stringBuilder.append(instances.get(i).getPort());
map.put(stringBuilder.toString(), i);
}
Set<String> keySet = map.keySet();
List<String> list = new ArrayList<>();
list.addAll(keySet);
Random random = new Random();
int i = random.nextInt(keySet.size());
String s = list.get(i);
return s;
}
就这样无论服务提供者有多少个实例,我们都可以随机生成一个server,
@RequestMapping("/createOrder/{pid}")
public Order createOrder(@PathVariable Integer pid) {
//获取商品微服务集合和信息
List<ServiceInstance> instances = discoveryClient.getInstances("service-product2021");
String s = randomServer(instances);
//获取第一个实例
// ServiceInstance serviceInstance = instances.get(0);
//host ip地址
// String host = serviceInstance.getHost();
//port 端口号
// int port = serviceInstance.getPort();
Product product = restTemplate.getForObject("http://" +s + "/findProductById/" + pid, Product.class);
Order order = new Order();
order.setNumber(2);
order.setUid(1);
order.setUsername("chenchen");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
return orderService.createOrder(order);
}
再去测试,看控制台已经实现随机负载均衡
2021-05-13 22:28:14.743 INFO 22540 --- [nio-8092-exec-1] com.chenchen.controller.OrderController : 服务:192.168.0.101:8082
2021-05-13 22:28:15.111 INFO 22540 --- [nio-8092-exec-1] c.c.service.impl.OrderServiceImpl : 下单成功,订单信息Order(oid=291, uid=1, username=chenchen, pid=3, pname=苹果, pprice=3000.0, number=2)
2021-05-13 22:28:16.739 INFO 22540 --- [nio-8092-exec-2] com.chenchen.controller.OrderController : 服务:192.168.0.101:8082
2021-05-13 22:28:16.756 INFO 22540 --- [nio-8092-exec-2] c.c.service.impl.OrderServiceImpl : 下单成功,订单信息Order(oid=292, uid=1, username=chenchen, pid=3, pname=苹果, pprice=3000.0, number=2)
2021-05-13 22:28:16.962 INFO 22540 --- [nio-8092-exec-5] com.chenchen.controller.OrderController : 服务:192.168.0.101:8081
2021-05-13 22:28:16.978 INFO 22540 --- [nio-8092-exec-5] c.c.service.impl.OrderServiceImpl : 下单成功,订单信息Order(oid=293, uid=1, username=chenchen, pid=3, pname=苹果, pprice=3000.0, number=2)
2021-05-13 22:28:17.185 INFO 22540 --- [nio-8092-exec-7] com.chenchen.controller.OrderController : 服务:192.168.0.101:8081
2021-05-13 22:28:17.204 INFO 22540 --- [nio-8092-exec-7] c.c.service.impl.OrderServiceImpl : 下单成功,订单信息Order(oid=294, uid=1, username=chenchen, pid=3, pname=苹果, pprice=3000.0, number=2)
2021-05-13 22:28:17.371 INFO 22540 --- [nio-8092-exec-9] com.chenchen.controller.OrderController : 服务:192.168.0.101:8082
2021-05-13 22:28:17.384 INFO 22540 --- [nio-8092-exec-9] c.c.service.impl.OrderServiceImpl : 下单成功,订单信息Order(oid=295, uid=1, username=chenchen, pid=3, pname=苹果, pprice=3000.0, number=2)
2021-05-13 22:28:17.539 INFO 22540 --- [nio-8092-exec-1] com.chenchen.controller.OrderController : 服务:192.168.0.101:8082
2021-05-13 22:28:17.550 INFO 22540 --- [nio-8092-exec-1] c.c.service.impl.OrderServiceImpl : 下单成功,订单信息Order(oid=296, uid=1, username=chenchen, pid=3, pname=苹果, pprice=3000.0, number=2)
鉴于我们自己实现的负载均衡方式,单一且容错性不高,我们还是使用官方的组件Ribbon,它提供了一个注解@LoadBalanced自动实现负载均衡
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
然后改造我们的controller
@RequestMapping("/createOrder/{pid}")
public Order createOrder(@PathVariable Integer pid) {
//使用微服务的名字,从注册中心Nacos中获取
String url = "service-product2021";
Product product = restTemplate.getForObject("http://" + url + "/findProductById/" + pid, Product.class);
Order order = new Order();
order.setNumber(2);
order.setUid(1);
order.setUsername("chenchen");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
return orderService.createOrder(order);
}
负载均衡的策略有很多,如 随机、轮询、轮询加权、Hash、随机加权等,如果不指定默认使用轮询方式。如果要修改策略,只需要在服务调用方shop-order中application.yml添加配置即可。
service-product: #配置负载均衡 service-product服务提供者的名字 默认采用轮询策略
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule