上一篇讲解的通过RestTemplate完成远程调用存在什么问题?
微服务架构与单体应用架构的区别—以及微服务工程的基础搭建–springcloud – 掘金 (juejin.cn)
1.把提供者的地址写死在代码中,提供者部署的服务发生改变。那么消费者也要改变。
- 如果提供者是一个集群,那么消费者如何负载均衡的调用
我们如果想解决上述问题 我们需要使用注册中心。
注册中心
服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。
服务注册: 在服务治理框架中,都会构建一个 **注册中心 **,每个服务单元向注册中心登记自己提供服
务的详细信息。并在注册中心形成一张服务的 **清单 **,服务注册中心需要以 **心跳30s 90s **的方式去监测清单中 的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。
服务发现: 服务调用方向服务注册中心咨询服务,并获取 **所有服务 **的实例清单,实现对具体服务实
例的访问。
通过上面的调用图会发现,除了微服务,还有一个组件是服务注册中心,它是微服务架构非常重要
的一个组件,在微服务架构里主要起到了协调者的一个作用。注册中心一般包含如下几个功能:
- 服务发现:
服务注册:保存服务提供者和服务调用者的信息
服务订阅:服务调用者订阅服务提供者的信息,注册中心向订阅者推送提供者的信息
- 服务配置:
配置订阅:服务提供者和服务调用者订阅微服务相关的配置
配置下发:主动将配置推送给服务提供者和服务调用者
- 服务健康检测
检测服务提供者的健康情况,如果发现异常,执行服务剔除
常见的注册中心组件有哪些?
nacos:—它是阿里巴巴的组件.
eureka: —它是netflix公司的组件—该组件已经停止更新
zookeeper—它是apache公司的
1.下载nacos注册中心
下载地址: github.com/alibaba/nac…
默认它启动模式为–集群模式—修改它为单机模式
启动脚本
访问:
账号和密码: nacos
微服务注册和拉取注册中心的内容
服务提供端
(1)引入nacos的依赖
<!--nacos的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
(2) 配置注册中心的地址–默认本地端口号8848
#配置nacos的地址
spring.cloud.nacos.server-addr=localhost:8848
#为微服务定义名称
spring.application.name=cjj-product
(3)进入nacos服务列表查看服务
(4)消费端
(1)引入nacos依赖
<!--nacos的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
(2)配置注册中心的地址
#为微服务定义名称
spring.application.name=cjj-order
#nacos地址,
spring.cloud.nacos.server-addr=localhost:8848
(3)修改controller层的代码
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderServer orderServer;
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient client;
@GetMapping("insertOrder")
public String insertOrder(Integer pid,Integer number){
final Order order = new Order();
order.setUid(3);
order.setUsername("aguang");
order.setNumber(number);
//从哪个微服务拉取
final List<ServiceInstance> instances = client.getInstances("cjj-product");
//访问微服务集群中的某个服务--这里是下标为0的服务
final ServiceInstance serviceInstance = instances.get(0);
final URI uri = serviceInstance.getUri();
final Product product = restTemplate.getForObject(uri+"/product/findProductById/" + pid, Product.class);
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
final Integer integer = orderServer.insertOrder(order);
return integer>0?"下单成功":"下单失败";
}
}
Ribbon实现负载均衡
1.什么是负载均衡
通俗的讲, 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。
根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。
服务端负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡
而客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求
我们在微服务调用关系中一般会选择 **客户端负载均衡 **,也就是在服务调用的一方来决定服务由哪个提供者执行.
2.演示:—手动完成负载均衡
手动开启两个提供端
controller
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderServer orderServer;
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient client;
@GetMapping("insertOrder")
public String insertOrder(Integer pid,Integer number){
final Order order = new Order();
order.setUid(3);
order.setUsername("aguang");
order.setNumber(number);
//从哪个微服务拉取
final List<ServiceInstance> instances = client.getInstances("cjj-product");
//随机下标
final int i = new Random().nextInt(instances.size());
//随机访问微服务集群中的某个服务--随机策略
final ServiceInstance serviceInstance = instances.get(i);
final URI uri = serviceInstance.getUri();
final Product product = restTemplate.getForObject(uri+"/product/findProductById/" + pid, Product.class);
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
final Integer integer = orderServer.insertOrder(order);
return integer>0?"下单成功":"下单失败";
}
}
上面通过手动完成了负载均衡的调用,存在的问题: 它采用的随机负载均衡,如果我想使用轮询负载均衡策略。只能修改源代码。这势必会造成代码的耦合。—springcloud提供了一个组件–可以灵活的完成负载均衡。–ribbon
3.基于Ribbon实现负载均衡
Ribbon利用从nacos中读取到的服务信息,在调用服务节点提供的服务时,会合理(策略)的进行负载。在Springcloud中,可以将注册中心和Rabbion配合使用,Ribbon自动的从注册中心获取服务者提供的列表信息,并基于内置的负载均衡算法,请求服务。
Rbibon的主要作用
(1)服务调用
基于Ribbon实习那服务调用,是通过拉取到的所有服务列表组成,(服务名-请求路径的)映射关系,借助ReatTemplate最终进行调用
(2)负载均衡
当有多个服务提供者的时候,Ribbon可以根据负载均衡的算法,自动选择需要调用的服务地址
1.告诉restTemplate使用ribbon完成负载均衡
/**
* 注入RestTemplate用来实现http远程调用
* @return
*/
@Bean
//告诉restTemplate将Ribbon作为负载均衡器
@LoadBalanced
public RestTemplate restTemplate(){
final RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
2.修改controller
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderServer orderServer;
@Autowired
private RestTemplate restTemplate;
@GetMapping("insertOrder")
public String insertOrder(Integer pid,Integer number){
final Order order = new Order();
order.setUid(3);
order.setUsername("aguang");
order.setNumber(number);
//Ribbon默认采用的是轮询策略
final Product product = restTemplate.getForObject("http://cjj-product/product/findProductById/" + pid, Product.class);
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
final Integer integer = orderServer.insertOrder(order);
return integer>0?"下单成功":"下单失败";
}
}
3.测试
4.Ribbon的负载均衡策略有哪些,以及如何修改
Ribbon负载均衡的七种策略 | cjj | ||
---|---|---|---|
id | 策略名称 | 策略对应的类名 | 实现原理 |
1 | 轮询策略 | RoundRobinRule | 轮询策略表示每次都顺序的选取下一个provider,比如一共有五个procider,第一次取第一个,第二次选第二个,以此类推 |
2 | 权重轮询策略 | WeightedResponseTimeRule | 1.根据每个procider的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。2.原理:一开始为轮询策略,并开启计时器,每30s手机一次每个provider的平均响应时间,当信息足够时,给每个peocider附上一个权重,并按照权重随机选择procider,高权重的procider会被高概率选中 |
3 | 随机策略 | RandomRule | 从provider列表中随机选择一个provider |
4 | 最少并发数策略 | BestAvailableRule | 选择正在请求中的并发数最小的provider,除非这个provider在熔断中 |
5 | 在”选定的负载均衡策略”基础上进行重试机制 | RetryRule | 1.“选定的负载均衡策略”这个策略是沦胥你策略RoundRobinRule 2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内,当选择provider不成功,则一直尝试采用选择“选定的负载均衡策略:轮询策略” 最后选择一个可用的provider |
6 | 可用敏感策略 | AvailabilityFiteringRule | 过滤性能差的provider有两种:第一种:过滤掉在eureka中处于一直连接失败的provider 第二种:过滤掉高并发的provider |
7 | 区域敏感性策略 | ZoneAvoidanceRule | 1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下的区域中选可用的provider 2.如果这个ip区域内有一个或多个实例不可达或相应变慢,都会降低该ip区域内其他ip被选中的权重。 |
上面是ribbon内置的负载均衡策略,我们也可以自定义负载均衡。
5.如何改变ribbon的负载均衡策略—修改配置文件
修改配置文件
#修改ribbon的负载均衡策略为随机策略
cjj-product.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
openfeign完成服务调用
什么是OpenFeign。
OpenFeign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地方法一样简单, 只需要创建一个接口并添加一个注解即可。
Nacos很好的兼容了OpenFeign, OpenFeign负载均衡默认集成了 Ribbon, 所以在Nacos下使用OpenFeign默认就实现了负载均衡的效果。
完成openfeign的调用
(1)依赖
<!-- openfeign的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
(2)创建openfeign接口
//供给端的服务名
@FeignClient(value = "cjj-product")
public interface ProductFeign {
//接口的方法,必须和被调用者的接口参数完全一致,路径也完全一致
@GetMapping("/product/findProductById/{pid}")
public Product findProductById(@PathVariable Integer pid);
}
(3)开启openfeign注解驱动
(4)修改controller
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderServer orderServer;
//注入定义的openfeign接口
@Autowired
private ProductFeign productFeign;
@GetMapping("insertOrder")
public String insertOrder(Integer pid,Integer number){
final Order order = new Order();
order.setUid(3);
order.setUsername("aguang");
order.setNumber(number);
//使用openfeign远程调用,完成服务的调用
final Product product = productFeign.findProductById(pid);
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
final Integer integer = orderServer.insertOrder(order);
return integer>0?"下单成功":"下单失败";
}
}