文章目录
微服务-服务调用
地址硬编码
- 1.服务调用
Ribbon
- 1.服务调用
- 2.负载均衡
- 3.请求重试
OpenFeign
- 1.服务调用
- 2.负载均衡
- 3.服务降级
- 4.请求压缩
- 5.日志配置
- 6.自定义Feign
微服务-服务调用
服务调用,即一个服务调用另一个服务,此过程可以分为服务调用者、服务提供者。基本上都会使用注册中心来作为中间件。
地址硬编码
地址硬编码即将微服务的IP、端口号、请求url等具体的api地址通过代码的形式写在调用者服务中。
平常的单体应用中,客户端访问应用api即可取得数据;在微服务中,服务调用其它服务的api,就可以获取其数据,这样做的好处在于服务细分化,即将一个服务拆分为很多小服务。
1.服务调用
1.创建两个服务,一个为服务调用者,一个为服务提供者(均可为SpringBoot项目)。
假设服务提供者(即被调用者)的配置如下:
server:
port: 9091 #服务端口号
spring:
application:
name: img-service #服务名
2.硬编码调用
服务调用者,直接使用http协议进行服务调用,需要用到RestTemplate对象。
@SpringBootApplication
public class MainApplication {
@Bean//将RestTemplate注册到容器
public RestTemplate RestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
一般情况下,控制层方法来处理用户请求,因此在这里进行服务调用:
@Autowired//注入RestTemplate
private RestTemplate rt;
@RequestMapping("/img/{id}")
public Img findimg(@PathVariable long id){//地址硬编码,不方便 ,因此需要服务注册中心
//调用图片服务(需启动图片服务)
Img img=rt.getForObject("http://localhost:9092/img/findimg/"+id,Img.class);
return img;
}
可以发现,硬编码的调用方式简单快捷。但这种调用方式将服务的ip、端口都精准地写在代码中,而且ip和端口往往是最容易变动的。当服务越来越多时,写入的ip、端口就会越来越多,这将使得后期维护变得极为困难。
Ribbon
注册中心Eureka、Consul、甚至接下来的OpenFeign均集成了Ribbon,足以证明了Ribbon的强大。
Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。
1.服务调用
Ribbon依赖:Eureka、Consul均集成了,因此这些依赖实际上已经被导入了,不用手动添加,依赖如下。
<!--Ribbon依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
调用者启动类将RestTemplate对象注册到容器,并使用注解 @LoadBalanced来实现负载均衡
@LoadBalanced
@Bean//将RestTemplate注册到容器
public RestTemplate RestTemplate(){
return new RestTemplate();
}
控制层来调用目标服务:
@Autowired//注入RestTemplate
private RestTemplate rt;
//Ribbon获取服务
@RequestMapping("/img3/{id}")
public Img ribbonimg(@PathVariable long id){
Img img=rt.getForObject("http://img-service/img/findimg/"+id,Img.class);//服务名
return img;
}
整个调用过程就是这么简单,当然前提是你已经使用了注册中心。
2.负载均衡
不同配置的主机部署相同的服务时,可能会承受的压力是等效的,但由于主机配置不同,导致出现配置低的主机承受不了,配置高的主机绰绰有余,因此可以为配置高的主机多分摊一些压力,达到负载均衡。即对多个相同功能的微服务,实现平衡调用。
负载均衡分服务端负载均衡和客户端负载均衡:
服务端:nginx、F5等
客户端:Ribbon
负载均衡的开启:在注册RestTemplate到容器时,添加注解 @LoadBalanced就开启了。
Ribbon提供了负载均衡策略,如果一般情况下不建议修改,除非一样的服务,部署在不同配置的主机上。
修改服务调用者的负载均衡策略:
#修改Ribbon负载均衡策略:服务名.ribbon.NFLoadBalancerRuleClassName.策略
img-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
3.请求重试
当服务调用者调用一个服务时,被调用的服务可能无法响应(宕机),此时调用者无法进行对其调用,那么可以重试请求去调用另一个相同的服务。
为调用者服务配置请求重试:
<!--spring 重试组件-->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
添加配置
spring:
cloud:
loadbalancer:
retry:
enabled: true #开启springCloud的重试功能,默认开启
img-service:
ribbon:
ConnectTimeout: 250 #ribbon连接的超时时间
ReadTimeout: 1000 #ribbon数据读取的超时时间
OkToRetryOnAllperations: true #是否对所有操作都重试
MaxAutoRetriesNextServer: 1 #切换实例的重试次数
MaxAutoRetries: 1 #当前实例的重试次数
OpenFeign
SpringCloud对feign组件进行了增强,使其支持SpringMVC注解、整合了Ribbon和Eureka。
1.服务调用
openfeign的调用写法和前面几种略有不同,但这种方式即简单又高效,是十分值得使用的方式。
引入依赖:
<!--SpringCloud整合的openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--引入EurekaClient-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在调用者的启动类使用注解 @EnableOpenFeign注解激活OpenFeign
@SpringBootApplication
@EnableFeignClients//激活Fegin,注意写在配置类上会启动出错
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
为调用者创建接口,并在接口上通过注解调用服务:
@Component
@FeignClient(name="img-service") //声明要调用的微服务名,name值为服务名
public interface ImgFeignClient {
// 配置需要调用的微服务接口
@RequestMapping(value = "/img/findimg/{id}",method = RequestMethod.GET)
public Img findid( @PathVariable long id);
}
调用者控制层调用接口,实现对数据的获取:
@Autowired
private ImgFeignClient ifc; //此处报红线,不用担心,是加载过慢
//feign获取服务
@RequestMapping("/img4/{id}")
public Img fignimg(@PathVariable long id){
Img img=ifc.findid(id);
return img;
}
可以看出,这种方式是最舒服的,使得调用和控制层耦合度变低。而且不需要再注册 RestTemplate 对象(就算注册了,也会失效)。
2.负载均衡
Feign 中本身已经集成了Ribbon依赖和自动配置,因此我们不需要额外引入依赖.也不需要额外配置。
3.服务降级
什么是服务降级?即服务请求已经达到上设置的上限(阀值)时,为后面的请求提供一个低级的服务来处理请求,以此来解决高并发问题。
OpenFeign内部集成了Hystrix,通过Hystrix,可以实现服务的降级操作。服务降级的解决方案不止Hystrix一种:https://blog.youkuaiyun.com/qq_52681418/article/details/113351447
Feign的调用方式为在调用者添加接口,对接口使用注解来实现服务调用,然后在控制层调用接口。这和一般的调用相比多了一层接口,因此在feign里,可以对调用的服务接口设置降级。
Feign已经集成了Hystrix,因此无需再导入依赖,只需要添加配置:
feign:
hystrix: #开启对hystrix的支持
enabled: true
实现要保护的接口,并将实现类注册到容器:假定调用服务创建的接口为ImgFeignClient 。
@Component
public class ImgFeignClientCallBack implements ImgFeignClient {
@Override // 熔断降级的方法
public Img findid(long id) {
Img img=new Img();
img.setName("触发降级方法");
return img;
}
}
调用服务的接口上添加注解,指定用来降级的实现类(因为此接口实现不止一个):
// fallback:指定服务降级方法,即实现类
@FeignClient(name="img-service",fallback= ImgFeignClientCallBack.class)
public interface ImgFeignClient {
// 配置需要调用的微服务接口
@RequestMapping(value = "/img/findimg/{id}",method = RequestMethod.GET)
public Img findid( @PathVariable long id);
}
为什么说此接口实现类不止一个呢,通过接口加注解来调用服务,不就相当于内置提供了一个实现吗。
4.请求压缩
SpringCloud Feign支持对请求、响应进行GZIP压缩,减少性能损耗。
为调用者添加如下配置:
feign:
compression:
request:
enabled: true #开启请求压缩
response:
enabled: true #开启响应压缩
同时,可以对请求的数据类型、触发压缩的大小下限。
feign:
compression:
request:
enabled: true #开启请求压缩
mime-types: text/html,application/xml,application/json #设置压缩的数据类型
min-request-size: 2048 #设置触发压缩的大小下限
上面均为默认配置,无特殊需求,可以不写。
5.日志配置
为调用者添加日志配置:
feign:
client:
config:
img-service: #此处为服务名
loggerLevel: FULL
#NONE:不输出日志(性能最好)。
#BASIC:适用于生产环境追踪问题
#HEADERS:在BASIC的基础上记录请求和响应头信息
#FULL:记录所有
logging:
level:
com.feign.ImgFeignClient: debug1 #接口全类名
6.自定义Feign
从Spring Cloud Edgware开始,Feign支持使用属性自定义Feign.
feign:
client:
config:
feignName: ##定义FeginClient的名称
connectTimeout: 5000 # 相当于Request.Options
readTimeout: 5000 # 相当于Request.Options
# 配置Feign的日志级别,相当于代码配置方式中的Logger
loggerLevel: full
# Feign的错误解码器,相当于代码配置方式中的ErrorDecoder
errorDecoder: com.example.SimpleErrorDecoder
# 配置重试,相当于代码配置方式中的Retryer
retryer: com.example.SimpleRetryer
# 配置拦截器,相当于代码配置方式中的RequestInterceptor
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false