Feign服务调用
是客户端组件
ruoyi系统中Log\Auth\User用了远程服务调用,用工厂模式给他的报错加了层工厂类,return错误的时候重写了以下方法。
在ruoyi-common-core模块中引入依赖
<!-- SpringCloud Openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
会调用同一url中的其他服务中的接口方法(对应其他服务中的controller),在这里只是映射下路径换个名字。所以不需要实现接口。
- http调用?
- 因为是http调用,所有要调用别人负责处理网络请求的controller层。同时在自己这里属于是业务层。
为了方便统一管理,就都在api模块了。其实依赖了api模块,相当于自己有api模块。
被调用方不需要依赖api模块
被调用的几乎什么都不用做
调用匹配机制:url+
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.FORUM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
public interface RemoteUserService
注解上的value是模块名字
请求传参
Get
方式传参,使用@PathVariable
、@RequestParam
注解接收请求参数
@GetMapping(value = "/user/info/{username}")
public R<LoginUser> getUserInfo(@PathVariable("username") String username);
@RequestParam(“参数名”)
Post
方式传参,使用@RequestBody
注解接收请求参数。
@PostMapping("/operlog")
public R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog);
反客为主了属于是,原来是参数从url中匹配,现在是调用时传给url,url给远程服务,远程服务再从url取
在openfeign传递参数必须加入注解(在接口声明处),不然会报参数传入过多错误。
跨模块公用的类。多个模块都要用,所以最好声明在专门的模块中,如ruoyi中的api
数组:
/test?ids=21&ids=22
@GetMapping("/test3")
String tests(@RequestParam("ids") String[] ids)
可以调用方传集合,被调用方用vo中的list接受,类似前端传数据,名字对应就行,会自动映射到vo中的list
ruoyi中接口访问分为两种,一种是由Gateway
网关访问进来,一种是内网通过Feign
进行内部服务调用。
OpenFeign
Feign
本身并不支持Spring MVC
的注解,它有一套自己的注解,为了更方便的使用Spring Cloud
孵化了OpenFeign
。并且支持了Spring MVC
的注解,如@RequestMapping
,@PathVariable
等等。
OpenFeign
的@FeignClient
可以解析Spring MVC
的@RequestMapping
注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡调用服务。
Feign(NetFlix)---->维护---->SpringCloud OpenFeign
是一个声明式的伪http客户端
简单:写一个接口,加一个注解,调用服务代码。自动完成数据转换
具有可插拔的注解特性(支持SpringMVC注解),可使用Feign注解和JAX-RS注解.
支持可插拔的编码器和解码器,默认集成了Ribbon。
使用
调用方写对应的接口,被调用的干自己原来事情就好,啥也不用做(本来被调用方就是顺便帮忙的,代码复用的,被调用是兼职)
返回值和形参一样就行,方法名不需要一样(因为是靠uri一一对应的,不是靠方法名对应的)
入口类加入注解@EnableDiscoveryClient,consul的注解,此处注册中心用Consul
服务调用方引入OpenFeign的依赖
<!-- SpringCloud Openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在服务调用方入口类上加注解@EnableFeignClients
被调用方入口类不用加这个注解,除非它同时也是个调用方
一般远程调用的专门写个包,如feignClient
//例子1
@FeignClient("被调用的服务id") //value
public interface ProductClient{
}
//例子2 ruoyi
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
public interface RemoteUserService
{
/**
* 通过用户名查询用户信息
*
* @param username 用户名
* @param source 请求来源
* @return 结果
*/
@GetMapping("/user/info/{username}")
public R<LoginUser> getUserInfo(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 注册用户信息
*
* @param sysUser 用户信息
* @param source 请求来源
* @return 结果
*/
@PostMapping("/user/register")
public R<Boolean> registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}
调用方直接用这个接口声明成员变量,然后调用接口中的方法。
例, 当auth模块的SysLoginService调用remoteUserService.getUserInfo(username, SecurityConstants.INNER);方法时,调用的其实是system模块里同url的controller里的方法。
api模块相当于一个配置的地方,将可以被远程调用的接口根据url在api模块下映射一下,然后调用api模块中的方法会找到对应的接口
因为服务调用是跨端口的,调用方(auth)要知道被调用方(system)的url
默认轮询负载均衡
响应处理
超时处理
在调用方设置请求被调用方时的超时时间
要求服务一秒内响应,否则报错
修改默认超时设置
feign.client.config.服务id.connectTimeout=5000 #配置指定服务连接超时
feign.client.config.服务id.readTimeout=5000 #配置指定服务等待超时
feign.client.config.default.connectTimeout=5000 #配置所有服务连接超时
feign.client.config.default.readTimeout=5000 #配置所有服务等待超时
ribbon超时设置
Feign
的负载均衡底层用的就是Ribbon
,所以请求超时其实就只需要配置Ribbon
参数。
全局配置
# 请求处理的超时时间
ribbon:
ReadTimeout: 10000
ConnectTimeout: 10000
局部配置
# ruoyi-xxxx 为需要调用的服务名称
ruoyi-xxxx:
ribbon:
ReadTimeout: 10000
ConnectTimeout: 10000
日志
在调试时开启详细日志
logging.level.com.包名=debug
feign为每个客户端提供了日志对象
feign.clientl.config.服务id.loggerLevel=full
代码设置
全局配置
@Bean
public Logger.Level getLog()
{
return Logger.Level.FULL;
}
局部配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import feign.Logger;
/**
* Feign 客户端配置
*
* @author ruoyi
*/
@Configuration
public class FeignConfiguration
{
@Bean
Logger.Level feignLoggerLevel()
{
return Logger.Level.FULL;
}
}
// ====== 在客户端接口指定此配置 ======
/**
* 用户服务
*
* @author ruoyi
*/
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class, configuration = FeignConfiguration.class)
public interface RemoteUserService
{
}
Http连接池
两台服务器建立HTTP
连接的过程涉及到多个数据包的交换,很消耗时间。采用HTTP
连接池可以节约大量的时间提示吞吐量。
Feign
的HTTP
客户端支持3种框架:HttpURLConnection
、HttpClient
、OkHttp
。
默认是采用java.net.HttpURLConnection
,每次请求都会建立、关闭连接,为了性能考虑,可以引入httpclient
、okhttp
作为底层的通信框架。
例如将Feign
的HTTP
客户端工具修改为HttpClient
。
1、添加依赖
<!-- feign httpclient -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
2、全局配置
feign:
httpclient:
# 开启httpclient
enabled: true
请求拦截器
在可以通过实现feign.RequestInterceptor
接口在feign执行后
进行拦截,对请求头等信息进行修改。
例如项目中利用feign
拦截器将本服务的userId
、userName
、authentication
传递给下游服务
import feign.RequestInterceptor;
import feign.RequestTemplate;
/**
* feign 请求拦截器
*
*/
@Component
public class FeignRequestInterceptor implements RequestInterceptor
{
@Override
public void apply(RequestTemplate requestTemplate)
{
}
}