微服务调用 #
在微服务架构中,最常见的场景就是微服务之间的相互调用。比如,用户微服务调用订单微服务发起一个下单的请求,在进行保存订单之前,需要调用商品微服务查询商品的信息。我们把调用方称为服务消费者,把请求的接收者称为服务提供者
RestTemplate #
- RestTemplate有Spring框架提供的一个用于后端服务器模拟发送HTTP请求的一种技术
- 它支持所有的HTTP标准方法,包含:GET,POST,PUT,DELETE等
常见用法 #
-
getForObject
- getForObject函数实际上是对getForEntity函数的进一步封装,如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用getForObject
-
postForObject
- 如果你只关注,返回的消息体,可以直接使用postForObject。用法和getForObject一致。
-
getForEntity
- getForEntity方法的返回值是一个
ResponseEntity<T>,ResponseEntity<T>是Spring对HTTP请求响应的封装, 包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。 - 可以用一个数字做占位符,最后是一个可变长度的参数,来一一替换前面的占位符
- getForEntity方法的返回值是一个
-
postForEntity
- 方法的第一参数表示要调用的服务的地址 第二个参数表示上传的参数 第三个参数表示返回的消息体的数据类型
-
exchange
-
ResponseEntity<String> results = restTemplate.exchange(url,HttpMethod.GET, null, String.class, params); /** 说明:1)url: 请求地址; 2)method: 请求类型(如:POST,PUT,DELETE,GET); 3)requestEntity: 请求实体,封装请求头,请求内容 4)responseType: 响应类型,根据服务接口的返回类型决定 5)uriVariables: url中参数变量值 */
-
Ribbon #
解决的问题 #
-
RestTemplate可以实现微服务之间的相互调用,但是RestTemplate有一定的问题:
- 必须要确定的具体地址
- 如果具体地址出问题了,就无法正常的做自动切换
- 微服务都是以集群的方式来部署,无法去选择集群中其他地址
-
解决方案:Ribbon
-
Ribbon是NetFlix的一个负载均衡器,例外它还可以同样使用RestTemplate完成对微服务之间的调用
-
你可以认为:Ribbon就是在RestTemplate的基础之上,提供了负载均衡的能力
-
Ribbon一般是配合Eureka注册中心来使用,意味着:Ribbon可以从Eureka中去发现自己需要的微服务实例
负载均衡 #
-
Ribbon提供的这种负载均衡的原理,是一种客户端的负载均衡机制
-
负载均衡:将负载(HTTP的请求)较为平均的分配到服务器上去
-
负载均衡常见的算法:轮询,权重,IP地址黏贴,最少连接
-
负载均衡的分类
- 服务端负载均衡:服务端的这种方式,一定是有服务器(Apache/Nginx)在参与,它的原理:包工头-工人之间的关系,它是属于任务分配的一种方式
- 客户端负载均衡:客户端的负载均衡一般是发生在分布式应用程序内容,它的原理:是属于调用者与被调用者之间的关系
Ribbon使用 #
1、导入Ribbon的依赖,只要使用Eureka,那么程序已经自带了 #
2、开启负载均衡 #
-
在调用的一方,开启负载均衡
-
配置Ribbon实际上就是开启它的负载均衡
-
说Ribbon的底层就是RestTemplate,那么配置Ribbon实际上就是在配置RestTemplate
//在产生RestTemplate实例时,使用@LoadBalanced注解,那么RestTemplate就拥有了负载均衡的能力
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
3、消费端调用方式 #
public String getUrl(){
// 将之前的return "http://127.0.0.1:9091/"中
// 127.0.0.1:9091替换成 micro-base-server(被调用者的spring.application.name)
return "http://micro-base-server/";
}
Ribbon提供的负载均衡 #
com.netflix.loadbalancer.RoundRobinRule- 轮询(默认)
com.netflix.loadbalancer.RandomRule- 随机
com.netflix.loadbalancer.RetryRul- 重试,该策略会先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
com.netflix.loadbalancer.WeightedResponseTimeRule- 权重,该策略根据平均响应时间计算所有服务的权重,响应时间越快的服务权重就越大,权重越大被选中的几率就越高。刚启动时如果统计信息不足,则使用RoundRobinRule策略,等到统计信息足够多的时候,则会自动切换到WeightedResponseTimeRule策略
com.netflix.loadbalancer.BestAvailableRule- 最佳,该策略会先过滤掉由于多次出现访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
com.netflix.loadbalancer.AvailabilityFilteringRule- 请求过滤,该策略会过滤掉由于多次出现访问故障而处于断路器跳闸状态的服务,过滤掉并发的连接数超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问
配置Ribbon #
全局配置 #
在配置类中进行定义
@Configuration
public class loadbanlancerConfig {
@Bean
public IRule myRandomRule() {
return new RandomRule();
}
}
#ribbon的全局时间配置
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule
ConnectTimeout: 250 #Ribbon的连接超时时间(毫秒),默认1秒
ReadTimeout: 1000 #Ribbon的数据读取超时时间,默认1秒
OkToRetryOnAllOperations: true #是否对所有操作都进行重试
MaxAutoRetriesNextServer: 1 #切换实例的重试次数
MaxAutoRetries: 1 #对当前实例的重试次数
局部配置 #
star-orders: #微服务的名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule
ConnectTimeout: 250 #Ribbon的连接超时时间(毫秒),默认1秒
ReadTimeout: 1000 #Ribbon的数据读取超时时间,默认1秒
OkToRetryOnAllOperations: true #是否对所有操作都进行重试
MaxAutoRetriesNextServer: 1 #切换实例的重试次数
MaxAutoRetries: 1 #对当前实例的重试次数
当全局配置和局部配置,同时存在时将以 “局部配置为主”
OpenFeign #
解决的问题 #
RestTemplate ,Ribbon都存在这么一个问题:代码耦合度很高。OpenFeign,它就是一种典型的接口式编程方案,从而可以做到微服务之间相互调用时,松散耦合关系
-
Feign是Netflix开发的声明式,模板化的HTTP客户端
-
Feign可帮助我们更加便捷,优雅的调用HTTP API
-
在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。
-
Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便
-
OpenFeign实际上就是对Ribbon接口化封装,所以feign的负载均衡和时间配置,也是和ribbon一样的
Feign和OpenFeign的区别 #
OpenFeign组件的使用 #
避坑:使用Feign,如果消费端传递对象类型的参数,那么服务端必须使用@RequestBody接参
1、在消费者添加依赖 #
<!-- OpenFeign的调用插件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、消费者开启对OpenFeign的支持 #
在启动类上,添加@EnableFeignClients,开启对OpenFeign的支持
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class MicroUserServerApplication {
public static void main(String[] args) {
SpringApplication.run(MicroUserServerApplication.class, args);
}
}
3、定义feign接口 #
//@FeignClient的value属性,定义了服务生产方的spring.application.name名称
@FeignClient("star-product")
public interface ProductServerFeign {
//无参get请求
@GetMapping("/product/findAllProduct")
ResultVO findAllProduct();
//有参get请求,这种方法需要使用注解@PathVariable来声明参数位置
@GetMapping("/product/findByPid/{pid}")
ResultVO findByPid(@PathVariable("pid") Integer pid);
//有参post请求,这种方法需要在feign和提供者中使用@RequestBody来传参
@PostMapping("/product/save")
ResultVO save(@RequestBody Product product);
}
4、消费者控制层注入该接口实例,并且调用方法 #
@Autowired
private ProductServerFeign productServerFeign;
@GetMapping("/findAllProduct")
public ResultVO findAllProduct(){
ResultVO resultVO = productServerFeign.findAllProduct();
return resultVO;
}
@GetMapping("/findByPid/{pid}")
public ResultVO findByPid(@PathVariable("pid") Integer pid){
ResultVO resultVO = productServerFeign.findByPid();
return resultVO;
}
@PostMapping("/save")
public ResultVO save(Product product){
ResultVO resultVO = productServerFeign.save(product);
return resultVO;
}
日志打印 #
-
Feign提供了日志打印功能,我们可以通过配置来调整日志级别,可以让我们程序员查看Feign中Http请求的细节
-
开发环境以及测试环境,可以将Feign日志功能开启,但是生产环境下,请将日志功能关闭
-
日志级别
- NONE(性能最佳,适用于生产):不记录任何日志(默认值)
- BASIC(适用于生产环境追踪问题):仅记录请求方法、URL、响应状态代码以及执行时间
- HEADERS:记录BASIC级别的基础上,记录请求和响应的header
- FULL(比较适用于开发及测试环境定位问题):记录请求和响应的header、body和元数据
-
具体配置
- 打开调用方的application.ym并修改
feign:
client:
config:
micro-base-server: #被调用者的微服务名称
loggerLevel: FULL
logging:
level:
root: info #将日志下调至Debug
top.ygang.microuserserver.classdemo.openFeign.MicroBaseServerFeign: debug