1. 相关概念
OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求,用法其实就是编写一个接口,在接口上添加注解即可。
可以简单理解它是借鉴Ribbon的基础之上,封装的一套服务接口+注解的方式的远程调用器。 不管是消费端还是服务提供方,需要书写的代码量也都很少, 比起Ribbon来说,更加的优雅和直接。
2. OpenFeign能干什么
它的宗旨是在编写Java Http客户端接口的时候变得更加容易,其底层整合了Ribbon,所以也支持负载均衡。
之前我们使用Ribbon的时候,利用RestTemplate对Http请求进行封装处理,但是在实际开发中,由于对服务依赖的调用不可能就一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以OpenFeign在此基础之上做了进一步的封装,由它来帮助我们定义和实现依赖服务接口的定义,我们只需创建一个接口并使用注解的方式来配置它,即可完成对微服务提供方的接口绑定,简化Ribbon的操作。
3. 具体使用
这里我们通过一个案例来演示,分别有3个模块
模块名 | 功能介绍 |
---|---|
commons | 通用模块,定义工具类,以及openfeign的调用接口定义,consumer和provider模块都会引用该模块 |
consumer | 消费模块,用于消费provider提供的接口 |
provider | 提供消费接口,实现来自于commons模块定义的openfeign接口 |
4. 引入公共依赖pom.xml
在父项目中引入以下3个依赖,这样3个模块都能用到
<!--公共引入依赖的地方-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
5. commons模块
定义响应类和接口,具体如下
响应类JsonResult
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonResult<T> {
private Integer code;
private T data;
}
OpenFeign调用接口
/**
* 此接口就是配合使用OpenFeign的接口,
* 在此接口中添加@FeignClient接口同时标注
* 要调用的服务端名称,同时使用与服务提供者
* 方法签名一致的抽象方法来表示远程调用的
* 具体内容
*/
@Service
//表示远程调用服务名称
@FeignClient("nacos-provider")
public interface ProviderOpenApis {
@GetMapping("info/{id}")
JsonResult<String> info(@PathVariable("id") Integer id);
}
6. provider模块
服务Controller
需要提供访问接口,并且实现ProviderOpenApis
// 提供的服务接口类
@RestController
public class ApiController implements ProviderOpenApis {
@Value("${server.port}")
private String serverPort;
@GetMapping("/getServerPort")
public String getServerPort() {
return "Hello Nacos Discovery Port:" + serverPort;
}
//模仿数据库存储数据
public static HashMap<Integer, String> hashMap = new HashMap<>();
static {
hashMap.put(1, "鼠标");
hashMap.put(2, "键盘");
hashMap.put(3, "耳机");
}
@Override
public JsonResult<String> info(Integer id) {
String data = hashMap.get(id);
if (null == data) {
throw new NullPointerException(String.format("商品#%d不存在", id));
}
return new JsonResult<>(200, "ServerPort:" + serverPort + ", 商品:" + data);
}
}
// 启动类
@SpringBootApplication
//启用服务注册发现客户端,这样才能通过nacos获取和注册服务
@EnableDiscoveryClient
public class NacosProviderApplication {
public static void main(String[] args) {
SpringApplication.run(NacosProviderApplication.class, args);
}
}
application.properties
server.port=8081
spring.application.name=nacos-provider
# 将服务注册到服务发现中
spring.cloud.nacos.discovery.server-addr=192.168.43.11:8848
management.endpoints.web.exposure.include=*
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--注入阿里巴巴nacos服务注册发现相关包-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--引入commons模块,方便implements ProviderOpenApis和返回JsonResult-->
<dependency>
<groupId>org.example</groupId>
<artifactId>commons</artifactId>
<version>0.0.1</version>
</dependency>
7. consumer模块
该模块用于消费provider通过的服务,是通过provider在commons模块提供的ProviderOpenApis暴露的方法来调用的,是基于OpenFeign来实现的,所以调用起来非常方便,看起来像调用本地的服务方法一样。
该模块需要依赖commons模块,但不依赖provider模块。 该模块需要从注册中心中获取provider提供的服务。所以需要依赖注册中心的服务发现功能。
引入依赖
<!--注入阿里巴巴nacos服务注册发现相关包-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--引入commons模块,方便使用ProviderOpenApis和接收JsonResult-->
<dependency>
<groupId>org.example</groupId>
<artifactId>hianzuo-commons</artifactId>
<version>0.0.1</version>
</dependency>
配置文件
server.port=8083
spring.application.name=nacos-consumer
spring.cloud.nacos.discovery.server-addr=192.168.43.11:8848
# 暴露所有端点,给dashboard用
management.endpoint.web.exposure.include=*
# 消费者将要去访问的微服务名称(注册成功的Nacos的微服务提供者)
service-url.nacos-user-service=http://nacos-provider
调用代码
@RestController
public class OpenFeignTestController {
//服务提供者URL
@Value("${service-url.nacos-user-service}")
private String SERVICE_URL;
@Autowired
private ProviderOpenApis providerOpenApis;
@GetMapping("/info/{id}")
public JsonResult<String> fallback(@PathVariable Integer id) {
//通过Ribbon发起远程访问
return providerOpenApis.info(id);
}
}
代码看起来简单优雅,直接通过服务调用,就实现了远程调用, 服务提供方可以提供一个单独的开放模块或者jar包的形式把要开放的接口定义在其中,像ProviderOpenApis 一样,这样消费方就可以直接使用。
测试结果
8. 调用超时控制
OpenFeign 客户端默认等待1秒钟,但是如果服务端业务超过1秒,则会报错。为了避免这样的情况,我们需要设置feign客户端的超时控制。
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ribbon.ReadTimeout=5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ribbon.ConnectTimeout=5000
9. 日志级别控制
Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。 简单理解,就是对Feign接口的调用情况进行监控和输出
日志级别
- NONE:默认的,不显示任何日志;
- BASIC:仅记录请求方法、URL、响应状态码及执行时间;
- HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;
- FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。
具体使用
首先需要配置日志级别的Bean
@Bean
Logger.Level feignLoggerLevel(){
//开启详细日志
return Logger.Level.FULL;
}
然后需要在配置文件中定义哪些类以啥级别监控并打印日志
# openfeign日志以什么级别监控哪个接口
logging.level.com.example.nacos.consumer.ProviderOpenApis=debug