Spring Cloud(三) Spring Cloud OpenFeign (服务调用组件,熔断降级,负载均衡)
上一章学习了Eureka client 是如何注册了 Eureka Server 中去的,在本文中将讲解 服务间如何进行调用,一、OpenFeign(声明式服务调用组件,服务消费者)作为Spring Cloud的子项目之一,Spring Cloud OpenFeign以将OpenFeign集成到Spring Boot应用中的方式,为微服务架构下服务之间的调用提供了解决方案Feign是...
上一章学习了Eureka client 是如何注册了 Eureka Server 中去的,在本文中将讲解 服务间如何进行调用,
一、OpenFeign(声明式服务调用组件,服务消费者)
作为Spring Cloud的子项目之一,Spring Cloud OpenFeign以将OpenFeign集成到Spring Boot应用中的方式,为微服务架构下服务之间的调用提供了解决方案
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果
总结:
- Feign 采用的是基于接口的注解
- Feign 整合了Hystrix,具有熔断降级的能力
- Feign 整合了Ribbon,具有负载均衡的能力
二、OpenFeign注解介绍
OpenFeign提供了两个重要标注@FeignClient和@EnableFeignClients
- @EnableFeignClients:标注用于修饰Spring Boot应用的入口类,以通知Spring Boot启动应用时,扫描应用中声明的Feign客户端可访问的Web服务。
- @FeignClient:标注用于声明Feign客户端可访问的Web服务。
1、@EnableFeignClients标注的参数
- value, basePackages (默认{})
- basePackageClasses (默认{})
- defaultConfiguration (默认{})
- clients (默认{})
2、@FeignClient标注的参数
- name, value (默认""),两者等价
- qualifier (默认"")
- url (默认"")
- decode404 (默认false)
- configuration (默认FeignClientsConfiguration.class)
- fallback (默认void.class)
- fallbackFactory (默认void.class)
- path (默认"")
- primary (默认true)
3、 @FeignClient标注的configuration参数
@FeignClient标注的configuration参数,默认是通过FeignClientsConfiguration类定义的,可以配置Client,Contract,Encoder/Decoder等。
FeignClientsConfiguration类中的配置方法及默认值如下:
- feignContract: SpringMvcContract
- feignDecoder: ResponseEntityDecoder
- feignEncoder: SpringEncoder
- feignLogger: Slf4jLogger
- feignBuilder: Feign.Builder
- feignClient: LoadBalancerFeignClient(开启Ribbon时)或默认的HttpURLConnection
4、定制@FeignClient标注的configuration类
@FeignClient标注的默认配置类为FeignClientsConfiguration,我们可以定义自己的配置类如下:
@Configuration
public class MyConfiguration {
@Bean
public Contract feignContract(...) {...}
@Bean
public Encoder feignEncoder() {...}
@Bean
public Decoder feignDecoder() {...}
...
}
然后在使用@FeignClient标注时,给出参数如下:
@FeignClient(name = "myServiceName", configuration = MyConfiguration.class, ...)
public interface MyService {
@RequestMapping("/")
public String getName();
...
}
当然,定制@FeignClient标注的configuration类还可以有另一个方法,直接配置application.yaml文件即可,示例如下:
feign:
client:
config:
feignName: myServiceName
connectTimeout: 5000
readTimeout: 5000
loggerLevel: full
encoder: com.example.MyEncoder
decoder: com.example.MyDecoder
contract: com.example.MyContract
三、OpenFeign 调用其他服务介绍
想要使用Feign也比较简单,定义一个通过注解@FeignClient()指定需要调用的服务的接口,启动类加上@EnableFeignClients开启Feign功能即可
我们先看一下官方的文档,了解一下具体操作
1、添加依赖和声明启动类注解
- 我们需要添加spring-cloud-starter-openfeign依赖
- 我们需要为启动类添加@EnableFeignClients注解,让他变为一个Feign客户端
2、创建@FeignClient接口
@FeignClient 的value值指的是任意的客户端的名称,还有url 用来指定一个主机地址
value和name属性是等价的
官网给的这段介绍中有段非常重要的描述在下文红色区域进行描述,在这段描述中我们看到这样的一段描述,如果你使用的是 Eureka Client 呢么将自动从 Eureka Server 注册表中获取服务,呢么这段描述我们可以理解为 Feign 默认是集成了 Eureka
四、OpenFeign 调用服务案例
1、建立服务注册中心Eureka Server
服务注册中心简略记录,详细的可以看之前的博文
(1)新建一个Eureka Server工程,导入eureka-server依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
(2)新建启动类:
package com.imooc.homepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* 1、pom文件中对应到spring-cloud-starter-netflix-eureka-server
* 2、只需要使用@EnableEurekaServer 注解就可以让应用变为Eureka服务端
*/
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
(3)配置文件:
spring:
application:
#这个spring应用的名字(之后调用会用到)
name: homepage-eureka
server:
#服务注册中心端口号
port: 8000
eureka:
instance:
#服务注册中心实例的主机名
hostname: localhost
client:
# 表示是否从 eureka server 中获取注册信息(检索服务),默认是true
fetch-registry: false
# 表示是否将自己注册到 eureka server(向服务注册中心注册自己),默认是true
register-with-eureka: false
service-url:
#服务注册中心的配置内容,指定服务注册中心的位置,eureka 服务器的地址(注意:地址最后面的 /eureka/ 这个是固定值)
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
2、创建服务提供者客户端Eureka Client
(1)导入依赖:
<!--spring cloud的eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--
添加web是为了解决client启动后自动关闭的问题
由于microserver-user 服务是是web项目 所以还需要添加对应的web包
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
同时要导入Spring Boot插件,使jar打包能正常运行(避免执行时报错XXX.jar中没有主清单属性):
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
(2)新建启动类:
package com.imooc.homepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* 1、pom文件中对应到spring-cloud-starter-netflix-eureka-client
* 2、使用@EnableEurekaClient 注解就可以让应用变为Eureka客户端端
* 3、@SpringBootApplication是启动器
*/
@EnableEurekaClient
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class,args);
}
}
(3)创建真实的执行类,自己执行 或 为外部提供服务(消费者调用)
package com.imooc.homepage.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ServiceController {
// 这里是为了拿到配置文件中的port
@Value("${server.port}")
private String port;
// @GetMapping("/hello/{name}")
@RequestMapping(value = "/hello/{name}",method = RequestMethod.GET)
public String getHello(@PathVariable("name") String name){
return "hello "+name+",被调用的服务提供者(客户端):homepage-eurekaClient,被调用的服务端口 port:"+port;
}
public void setPort(String port) {
this.port = port;
}
}
(4)配置文件:
这里是将自己注册进服务注册中心
spring:
application:
name: homepage-eureka-client
server:
port: 8100
eureka:
client:
service-url:
#将自己注册进下面这个地址的服务注册中心
defaultZone: http://localhost:8000/eureka/
3、创建消费者客户端
(1)添加pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>imooc-homepage</artifactId>
<groupId>com.imooc.homepage</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>homepage-feign</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>homepage-feign</name>
<dependencies>
<!--spring cloud的eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--
添加web是为了解决client启动后自动关闭的问题
由于microserver-user 服务是是web项目 所以还需要添加对应的web包
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!--配置boot运行插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(2)创建启动类
package com.imooc.homepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* 1、pom文件中对应到spring-cloud-starter-netflix-eureka-client
* 2、@SpringBootApplication:是启动器
* 3、@EnableEurekaClient:使用该注解就可以让应用变为Eureka客户端端
* 4、@EnableFeignClients:开启OpenFeign支持
*/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients//(basePackages = {"com.imooc.homepage.controllerFeign"})
public class OpenFeignApplication {
public static void main(String[] args) {
SpringApplication.run(OpenFeignApplication.class,args);
}
}
(3)新建一个接口,用于调用服务:
注意 :该接口可以理解为是 被调用服务类(服务提供者) 方法的一个映射
- 需要在 @FeignClient 中指定要调用的服务信息(如:服务名、URL等等)
- 该接口的方法必须要和 被调用的服务(服务提供者) 调用方式保持一致 (也就是说服务提供者的方法是什么样的,这里就得是什么样,包括方法上的注解。需要保证接口请求方式是一致的;需要保证接口传参是一致的)
- 需要使用@RequestMapping这种方式(避免使用GetMapping/PostMapping)
- 额外提醒,只要是传参,请都加上@RequestParam("XXXX");如果传对象就加上@RequestBody; 这样你就不用踩Feign的传参的坑了,不管是使用Feign的服务还是提供接口的client服务,请都加上这些传参注解保持一致;
package com.imooc.homepage.controllerFeign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @FeignClient(name = "")指定这是feign客户端; name属性指明要调用的服务名
* 注意:这里一定要加 @Component 注解,因为 @FeignClient 并没有将类托管给spring的功能
* 如果不加 @Component 在使用 @Autowired 注入的时候会出现问题,找不到Bean对象
*/
@FeignClient(name = "homepage-eureka-client")
@Component
public interface IServiceFeign {
// 指定要调用的服务,要和调用的服务调用方式保持一致
// @GetMapping("/hello/{name}")
@RequestMapping(value = "/hello/{name}",method = RequestMethod.GET)
public String getHello(@PathVariable("name") String name);
}
(4)调用服务:
package com.imooc.homepage.controller;
import com.imooc.homepage.controllerFeign.IServiceFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
public class ServiceController {
/**
* 1、注入IServiceFeign(feign的客户端,来调用服务)
* 注意:这里我使用 @Autowired 时编译器会报红 Could not autowire. No beans of 'IServiceFeign' type found
* 原因是如果没有在 IServiceFeign 上声明 @Component 就不会将它托管给spring容器
*/
@Autowired
// @Resource
private IServiceFeign iServiceFeign;
// @GetMapping("/consumer/{name}")
@RequestMapping(value = "/consumer/{name}",method = RequestMethod.GET)
public String consumer(@PathVariable("name") String name) {
// 调用具体的服务
return iServiceFeign.getHello(name);
}
public void setiServiceFeign(IServiceFeign iServiceFeign) {
this.iServiceFeign = iServiceFeign;
}
}
遇到的问题:
使用@Autowired时会说找不到,但改为@Resource时就可以了
解决方案:
原因是我们没有在 IServiceFeign 接口上定义 @Component
因为 @FeignClient 并没有将类托管给 spring 的功能
如果不加 @Component 在使用 @Autowired 注入的时候会出现问题,找不到Bean对象
(5) 配置文件:
这里只是将自己注册进服务注册中心,与之前一样
spring:
application:
name: homepage-feign
server:
port: 8200
eureka:
client:
service-url:
#将自己注册进下面这个地址的服务注册中心
defaultZone: http://localhost:8000/eureka/
4、启动服务进行验证(启动服务注册中心、服务提供客户端、消费端)
下图可以看出,已经将 服务提供客户端、消费端 注册进了服务注册中心
(1)服务注册中心界面
(2)服务提供客户端:自己调用自己的执行类方法
(3)消费者客户端:调用服务提供者客户端的执行类方法
五、Feign 整合 Hystrix(熔断降级的能力)
我们之前说过 openFeign 整合了 Hystrix 熔断降级的能力
我们都知道微服务的目的是功能模块的解耦,将每一个模块划分为一个单独的服务,达到其中任何一个服务出了问题而不会影响其他服务的使用。
问:什么是熔断降级?
答:
微服务间调用的前提是必须保证被调用的服务是正常启动的状态,但是我们开发过程中一定会出现以下场景:
我们的某一个微服务因为出现了问题或者需要更新的情况下,我们会暂时停止这个服务。
那么问题来了,被调用的微服务停止了,我们的消费端访问就会报错了,我们该怎么办呢?
Hystrix 就是为我们解决这一问题的,在微服务中一旦被调用的微服务停止后,Hystrix 就会熔断这个访问请求,降级去自己里面调用预制好的方法。
官方文档
如果Hystrix在类路径上,默认情况下,Feign会用一个断路器来包装所有的方法。返回一个com.hystrix.hystrixcommand也可用。这让您可以使用响应式模式(对.to可视()或。观察()或异步使用(对.queue()的调用)。
下面我们在 homepage-feign 的基础上来做一些改动
1、添加 Hystrix 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2、在 yml 配置文件中添加 Hystrix 配置开启
fegin: hystrix : enabled : true 这个配置项是指,开启熔断机制,也许在yml里显示没有相关依赖,但是不用慌,也许是因为版本问题,这个设置项加上就行,是起作用的)
默认是禁用Hystrix支持Feign,需在 yml 中加入以下配置启用Hystrix对Feign的支持
feign:
#默认是不支持的,所以这里要开启,设置为true
hystrix:
enabled: true
3、在启动类上添加 @EnableHystrix 注解,开启熔断支持
4、创建一个 Hystrix 熔断降级的类
这个类实现 @FeignClient 接口的方法,用作降级后的调用类
方法返回: return "sorry! 网络异常,服务暂时无法访问。 请求的name为:"+name;
这里可以根据使用场景,扩展做数据存储、降级调用等等。
5、 在 @FeingClient 接口注解中设定降级的类
通过注解中的 fallback 属性设定降级类
6、关闭被调用的服务进行验证
六、Feign 整合 Ribbon(负载均衡的能力)
openFeign默认支持了负载均衡的能力
问:什么是负载均衡?
答:在服务调用中,如果只有一个服务提供者,那么所有的消费者都会请求这一个服务。消费者少了还好说,如果消费者很多那么会加大服务提供者的压力,使效率低下或者服务崩溃。
我们现在建立多个相同服务提供者为之提供服务,中间由负载均衡器来根据情况为消费者提供最合适的服务对象,这样不但减小了单个服务器的压力,同时也加大了调用效率。
官方文档
根据官网的描述Feign 默认是使用 Ribbon的(不引入新的jar包也可以使用,feign默认的负载均衡策略是交替调用的),如果我们要对负载均衡的策略进行改变,可以直接将对应的jar包引入来实现负载均衡,并进行配置。
既然是负载均衡,那么也先得有东西均衡,所以我们需要再创建一个微服务,这个微服务与之前的服务提供者 homepage-eurekaClient 微服务一模一样,依赖、配置全都保持一样, 唯一的改变是 端口!
1、我是直接拷贝了 homepage-eurekaClient 然后只修改了配置文件里面的端口号
注意:服务名保持一样,因为负载均衡是通过获取注册中心的服务注册信息,根据服务名去匹配的
2、我们启动两个服务提供者和feign消费者,注册进服务注册中心
下图我们可以看到负载均衡的准备已经完成
3、 调用测试
我们重复刷新这个请求,可以看到是端口8100、8101交替调用的,这就是openFeign的负载均衡机制。
更多推荐
所有评论(0)