目录
一、前言
上一篇介绍了通过RestTemplate实现微服务之间请求调用,本篇介绍通过fegin的方式如何在微服务之间发送请求。
1、关于Fegin
1、Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端。
2、Fegin内置Ribbon用来做客户端负载均衡。
3、使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务
4、Spring Cloud 在Feign的基础上支持了Spring MVC的注解,也就是OpenFeign。
2、注意事项
1、OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口。
2、@RequesMapping不能在类名上与@FeignClient同时使用。
3、POM依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 熔断降级 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
二、编码实现
1、启动类
启动类上要加 @EnableFeignClients
/**
* @ClassName: MsgApplication
* @Description msg启动类
* @author 月夜烛峰
* @date 2022/7/22 08:53
*/
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class MsgApplication {
public static void main(String[] args) {
SpringApplication.run(MsgApplication.class, args);
}
}
否则定义的fegin接口不能自动装配,也即是不会被spring接管,报错信息如下:
2、创建openfeign接口
package com.zhufeng.web.api;
import com.alibaba.fastjson.JSONObject;
import com.zhufeng.web.fallback.UserFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @ClassName: FeignUserApi
* @Description 对外提供api接口
* @author 月夜烛峰
* @date 2022/8/31 16:55
*/
@FeignClient(value = "zhufeng-web-user", fallbackFactory = UserFallbackFactory.class)
public interface ShowUserApi {
/**
* 根据id获取user信息
* @param id
* @return
*/
@GetMapping("/fegin/{id}")
JSONObject showUserById(@PathVariable("id") int id);
}
@FeignClient中,value配置为要访问的微服务名称,fallbackFactory为回调工厂。
3、Controller代码
package com.zhufeng.web.controller;
import com.alibaba.fastjson.JSONObject;
import com.zhufeng.web.api.ShowUserApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName: RestUserController
* @Description TODO
* @author 月夜烛峰
* @date 2022/8/30 09:55
*/
@Slf4j
@RestController
public class RestUserController {
@Autowired
ShowUserApi showUserApi;
@RequestMapping("/fegin/{id}")
public JSONObject showUserInfo(@PathVariable("id") int id) {
log.info("接收到feign请求,id:"+id);
JSONObject userJson = new JSONObject();
userJson.put("status", "success");
userJson.put("name","zhufeng");
return userJson;
}
@RequestMapping("/user/{id}")
public JSONObject showUserById(@PathVariable("id") int id) {
log.info("请求参数id:"+id);
return showUserApi.showUserById(id);
}
}
4、回调工厂
当访问异常时,回调工厂对返回进行进行特殊处理。
package com.zhufeng.web.fallback;
import com.alibaba.fastjson.JSONObject;
import com.zhufeng.web.api.ShowUserApi;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName: UserFallbackFactory
* @Description fegin接口回调工厂类
* @author 月夜烛峰
* @date 2022/8/31 16:58
*/
@Slf4j
@Component
public class UserFallbackFactory implements FallbackFactory<ShowUserApi> {
@Override
public ShowUserApi create(Throwable throwable) {
log.error("请求接口报错。。。");
return new ShowUserApi() {
@Override
public JSONObject showUserById(int id) {
log.info("调用工厂:id="+id);
JSONObject json = new JSONObject();
json.put("id", id);
json.put("status", "error");
json.put("type", "factory");
json.put("msg", "no user found...");
return json;
}
};
}
}
在浏览器中 127.0.0.1:8081/user/1001 访问showUserById方法,该方法调用feign接口访问showUserInfo。
可以正常获取数据,接口访问正常。
注意:
注册中心使用的nacos,需要将zhufengweb-user服务注册到nacos中。
三、文件配置
1、Feign接口日志级别
在Feign中内置了日志级别:
NONE:默认值,不记录任何日志
BASIC:仅记录请求方法、URL、响应状态码以及执行时间
HEADERS:记录 BASIC 级别的基础上,记录请求和响应的 header
FULL:记录请求和响应的 header、body 和元数据
FULL虽然记录信息较全,但实际使用时因资源存储限制,大多使用BASIC。
这里以FULL级别展示有哪些信息
配置项:
#feign接口信息打印
logging.level.com.zhufeng.web.api.ShowUserApi=debug
#全局配置
feign.client.config.default.loggerLevel = full
#也可指定微服务中打印feign信息
#feign.client.config.zhufeng-web-user.loggerLevel=full
运行效果,控制台打印了 ShowUserApi#showUserById 信息:
2022-09-02 11:07:03.811 INFO 18022 --- [nio-8081-exec-2] c.z.web.controller.RestUserController : 接收到feign请求,id:1001
2022-09-02 11:07:03.838 DEBUG 18022 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] <--- HTTP/1.1 200 (102ms)
2022-09-02 11:07:03.838 DEBUG 18022 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] connection: keep-alive
2022-09-02 11:07:03.838 DEBUG 18022 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] content-type: application/json
2022-09-02 11:07:03.838 DEBUG 18022 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] date: Fri, 02 Sep 2022 03:07:03 GMT
2022-09-02 11:07:03.838 DEBUG 18022 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] keep-alive: timeout=60
2022-09-02 11:07:03.838 DEBUG 18022 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] transfer-encoding: chunked
2022-09-02 11:07:03.838 DEBUG 18022 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById]
2022-09-02 11:07:03.838 DEBUG 18022 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] {"name":"zhufeng","status":"success"}
2022-09-02 11:07:03.838 DEBUG 18022 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] <--- END HTTP (37-byte body)
2022-09-02 11:07:04.797 INFO 18022 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: zhufeng-web-user.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2、超时时间
微服务请求通信中,极有可能出现请求超时或着网络不同的情况
# 开启Feign对Hystrix的支持,使回调函数生效
feign.hystrix.enabled=true
# 超时时间可根据不同微服务进行配置,例如仅配置 zhufeng-web-user
# 指定Feign连接提供者的超时时限,5秒
feign.client.config.zhufeng-web-user.connectTimeout=5000
# 指定Feign从请求响应的超时时限,5秒
feign.client.config.zhufeng-web-user.readTimeout=5000
#超时时间全局配置,全局配置只需要指定default即可
#feign.client.config.default.loggerLevel = full
# 指定Feign连接提供者的超时时限,5秒
#feign.client.config.default.connectTimeout=5000
# 指定Feign从请求响应的超时时限,5秒
#feign.client.config.default.readTimeout=5000
配置超时时间后,为验证是否生效,可在请求的接口中设置等待时间大于5秒
对Controller代码进行修改,增加等待时间:
@RequestMapping("/fegin/{id}")
public JSONObject showUserInfo(@PathVariable("id") int id) {
log.info("接收到feign请求,id:"+id);
JSONObject userJson = new JSONObject();
userJson.put("status", "success");
userJson.put("name","zhufeng");
try {
for(int i=9;i>0;i--){
log.info("请求等待中..."+i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
log.error("服务超时,",e);
}
return userJson;
}
因为5秒内,没有返回数据,服务超时,返回信息为回调工厂UserFallbackFactory中配置的信息。
控制台日志信息:
2022-09-02 10:49:47.964 INFO 17734 --- [nio-8081-exec-2] c.z.web.controller.RestUserController : 接收到feign请求,id:1001
2022-09-02 10:49:47.988 INFO 17734 --- [nio-8081-exec-2] c.z.web.controller.RestUserController : 请求等待中...9
2022-09-02 10:49:48.885 ERROR 17734 --- [ HystrixTimer-1] c.z.web.fallback.UserFallbackFactory : 请求接口报错。。。
2022-09-02 10:49:48.889 INFO 17734 --- [ HystrixTimer-1] c.z.web.fallback.UserFallbackFactory : 调用工厂:id=1001
2022-09-02 10:49:48.949 INFO 17734 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: zhufeng-web-user.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2022-09-02 10:49:48.994 INFO 17734 --- [nio-8081-exec-2] c.z.web.controller.RestUserController : 请求等待中...8
2022-09-02 10:49:49.999 INFO 17734 --- [nio-8081-exec-2] c.z.web.controller.RestUserController : 请求等待中...7
2022-09-02 10:49:51.006 INFO 17734 --- [nio-8081-exec-2] c.z.web.controller.RestUserController : 请求等待中...6
2022-09-02 10:49:52.011 INFO 17734 --- [nio-8081-exec-2] c.z.web.controller.RestUserController : 请求等待中...5
2022-09-02 10:49:52.972 DEBUG 17734 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] <--- ERROR SocketTimeoutException: Read timed out (5098ms)
2022-09-02 10:49:52.973 DEBUG 17734 --- [feng-web-user-1] com.zhufeng.web.api.ShowUserApi : [ShowUserApi#showUserById] java.net.SocketTimeoutException: Read timed out
at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:280)
at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:306)
at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:347)
四、性能优化
默认情况下Feign不使用线程池,在并发请求较多时,会产生性能问题,可做如下配置:
# 使用httpclient链接池
feign.httpclient.enabled=true
# 连接池中最大连接数,默认值200
feign.httpclient.max-connections=200
# 每个route最大并发连接数 默认值50
feign.httpclient.max-connections-per-route=50
重新启动项目,日志级别设置为debug,控制台打印信息:
2022-09-02 12:14:41.895 DEBUG 18667 --- [feng-web-user-1] o.a.h.client.protocol.RequestAuthCache : Auth cache not set in the context
2022-09-02 12:14:41.896 DEBUG 18667 --- [feng-web-user-1] h.i.c.PoolingHttpClientConnectionManager : Connection request: [route: {}->http://10.211.55.2:8081][total available: 0; route allocated: 0 of 50; total allocated: 0 of 200]
2022-09-02 12:14:41.898 DEBUG 18667 --- [feng-web-user-1] h.i.c.PoolingHttpClientConnectionManager : Connection leased: [id: 0][route: {}->http://10.211.55.2:8081][total available: 0; route allocated: 1 of 50; total allocated: 1 of 200]
2022-09-02 12:14:41.898 DEBUG 18667 --- [feng-web-user-1] o.a.http.impl.execchain.MainClientExec : Opening connection {}->http://10.211.55.2:8081
如果打印 PoolingHttpClientConnectionManager 相关信息,则说明配置已生效