服务调用,即一个服务调用另一个服务,此过程可以分为服务调用者、服务提供者。基本上都会使用注册中心来作为中间件。
地址硬编码即将微服务的IP、端口号、请求url等具体的api地址通过代码的形式写在调用者服务中。
平常的单体应用中,客户端访问应用api即可取得数据;在微服务中,服务调用其它服务的api,就可以获取其数据,这样做的好处在于服务细分化,即将一个服务拆分为很多小服务。
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、端口就会越来越多,这将使得后期维护变得极为困难。
注册中心Eureka、Consul、甚至接下来的OpenFeign均集成了Ribbon,足以证明了Ribbon的强大。
Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。
Ribbon依赖:Eureka、Consul均集成了,因此这些依赖实际上已经被导入了,不用手动添加,依赖如下。
<dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-netflix-ribbonartifactId> <version>2.2.1.RELEASEversion> 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; }
整个调用过程就是这么简单,当然前提是你已经使用了注册中心。
不同配置的主机部署相同的服务时,可能会承受的压力是等效的,但由于主机配置不同,导致出现配置低的主机承受不了,配置高的主机绰绰有余,因此可以为配置高的主机多分摊一些压力,达到负载均衡。即对多个相同功能的微服务,实现平衡调用。
负载均衡分服务端负载均衡和客户端负载均衡:
负载均衡的开启:在注册RestTemplate到容器时,添加注解 @LoadBalanced就开启了。
Ribbon提供了负载均衡策略,如果一般情况下不建议修改,除非一样的服务,部署在不同配置的主机上。
修改服务调用者的负载均衡策略:
#修改Ribbon负载均衡策略:服务名.ribbon.NFLoadBalancerRuleClassName.策略 img-service: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
当服务调用者调用一个服务时,被调用的服务可能无法响应(宕机),此时调用者无法进行对其调用,那么可以重试请求去调用另一个相同的服务。
为调用者服务配置请求重试:
<dependency> <groupId>org.springframework.retrygroupId> <artifactId>spring-retryartifactId> dependency>
添加配置
spring: cloud: loadbalancer: retry: enabled: true #开启springCloud的重试功能,默认开启 img-service: ribbon: ConnectTimeout: 250 #ribbon连接的超时时间 ReadTimeout: 1000 #ribbon数据读取的超时时间 OkToRetryOnAllperations: true #是否对所有操作都重试 MaxAutoRetriesNextServer: 1 #切换实例的重试次数 MaxAutoRetries: 1 #当前实例的重试次数
SpringCloud对feign组件进行了增强,使其支持SpringMVC注解、整合了Ribbon和Eureka。
openfeign的调用写法和前面几种略有不同,但这种方式即简单又高效,是十分值得使用的方式。
引入依赖:
<dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-openfeignartifactId> dependency> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId> 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 对象(就算注册了,也会失效)。
Feign 中本身已经集成了Ribbon依赖和自动配置,因此我们不需要额外引入依赖.也不需要额外配置。
什么是服务降级?即服务请求已经达到上设置的上限(阀值)时,为后面的请求提供一个低级的服务来处理请求,以此来解决高并发问题。
OpenFeign内部集成了Hystrix,通过Hystrix,可以实现服务的降级操作。服务降级的解决方案不止Hystrix一种:https://blog.csdn.net/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); }
为什么说此接口实现类不止一个呢,通过接口加注解来调用服务,不就相当于内置提供了一个实现吗。
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 #设置触发压缩的大小下限
上面均为默认配置,无特殊需求,可以不写。
为调用者添加日志配置:
feign: client: config: img-service: #此处为服务名 loggerLevel: FULL #NONE:不输出日志(性能最好)。 #BASIC:适用于生产环境追踪问题 #HEADERS:在BASIC的基础上记录请求和响应头信息 #FULL:记录所有 logging: level: com.feign.ImgFeignClient: debug1 #接口全类名
从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