SpringCloud-OpenFeign官方文档使用大全详解
一、Feign与OpenFeign的区别
- 1)Feign是Spring Cloud组件中一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用接口,就可以调用服务注册中心的服务。
Feign的依赖
1 <dependency>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-starter-feign</artifactId>
4 </dependency>
- 2)OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中.
OpenFeign的依赖
1 <dependency>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-starter-openfeign</artifactId>
4 </dependency>
说明:
springcloud F 及F版本以上 springboot 2.0 以上基本上使用openfeign,openfeign 如果从框架结构上看就是2019年feign停更后出现版本,也可以说大多数新项目都用openfeign ,2018年以前的项目在使用feign
二、openFeign教程
Spring-Cloud-OpenFeign使用教程 - 知乎
Spring Cloud OpenFeign使用教程
Spring Cloud OpenFeign Demo
之前项目中需要在Spring Cloud中使用OpenFeign的情况,Spring Cloud的版本是目前最新的Greenwich.SR2版本,对应的Spring boot是2.1.7.RELEASE。
在网上找了很多资料,大多言之不详,并且版本也比较低,不适合我的最新版本Spring Cloud的需求。 所以决定还是自己写个教程。
本教程要解决如下几个问题:
- 怎么配置OpenFeignServer
- 怎么配置OpenFeignClient
- 多个参数传递问题
- FeignClient的日志问题
- 多个FeignClient使用同一个name的问题
怎么配置OpenFeignServer
我们知道OpenFeign是用在Spring Cloud中的声明式的web service client。
OpenFeignServer就是一个普通的Rest服务,不同的是我们需要将他注册到eureka server上面,方便后面的OpenFeignClient调用。
启动类如下:
@SpringBootApplication
@EnableDiscoveryClient
public class OpenFeignServer {
public static void main(String[] args) {
SpringApplication.run(OpenFeignServer.class, args);
}
}
我们定义了两个Rest服务:
OrderController:
@Slf4j
@RestController
@RequestMapping(path = "/order")
public class OrderController {
/**
* PostMapping with @RequestBody
* @param user
*/
@PostMapping("doOrder")
public void doOrder(@RequestBody User user){
log.info("do order !!!!");
}
}
UserController:
@RestController
@RequestMapping(path = "/user")
public class UserController {
/**
* GetMapping example with @RequestParam
* @param userId
* @return userName
*/
@GetMapping("getUserName")
public String getUserName(@RequestParam("userId") String userId){
if("100".equals(userId)) {
return "张学友";
}else{
return "刘德华";
}
}
/**
* GetMapping example with @RequestParam and @SpringQueryMap
* @param userId
* @param user
* @return userAge
*/
@GetMapping("getUserAge")
public String getUserAge(@RequestParam("userId") String userId, @SpringQueryMap User user){
if("100".equals(userId)) {
return "20";
}else{
return "18";
}
}
}
我们将其注册到eureka上面,名字为openfeign-server
spring:
application:
name: openfeign-server
怎么配置OpenFeignClient
OpenFeignClient的pom依赖如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动类上增加@EnableFeignClients注解,告诉框架扫描所有使用注解@FeignClient定义的feign客户端,并把feign客户端注册到IOC容器中。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};
}
需要使用@FeignClient来注解一个interface,如下所示:
@FeignClient(value = "openfeign-server")
@RequestMapping(path = "/user")
public interface UserClient {
@GetMapping("getUserName")
public String getUserName(@RequestParam("userId") String userId);
@GetMapping("getUserAge")
public String getUserAge(@RequestParam("userId") String userId, @SpringQueryMap User user);
}
其中@FeignClient中的value是要调用的服务的注册名,即OpenFeignServer在eureka的注册名。
FeignClient的 Request路径,方式和参数要和被调用的Rest服务保持一致。
这样我们就可以像下面一样来调用OpenFeignClient了:
@Slf4j
@RestController
public class UserController {
@Autowired
private UserClient userClient;
@GetMapping("getUserName2")
public void getUserName(){
log.info(userClient.getUserName("100"));
}
}
查看FeignClient注解代码如下:
public @interface FeignClient {
@AliasFor("name")
String value() default "";
String contextId() default "";
@AliasFor("value")
String name() default "";
String qualifier() default "";
String url() default "";
boolean decode404() default false;
Class<?>[] configuration() default {};
Class<?> fallback() default void.class;
Class<?> fallbackFactory() default void.class;
String path() default "";
boolean primary() default true;
}
下面就对各个属性进行分析:
value、name
value和name的作用一样,如果没有配置url那么配置的值将作为服务名称,用于服务发现。反之只是一个名称。value和name必须配一个
contextId
我们不想将所有的调用接口都定义在一个类中,有一种解决方案就是为每个Client手动指定不同的contextId,这样就不会冲突了。
url
url用于配置指定服务的地址,相当于直接请求这个服务,不经过Ribbon的服务选择。像调试等场景可以使用。
decode404
当调用请求发生404错误时,decode404的值为true,那么会执行decoder解码,否则抛出异常。
configuration:
configuration是配置Feign配置类,在配置类中可以自定义Feign的Encoder、Decoder、LogLevel、Contract等。
fallback:
定义容错的处理类,也就是回退逻辑,fallback的类必须实现Feign Client的接口,无法知道熔断的异常信息。(不推荐)
fallbackFactory:
也是容错的处理,可以知道熔断的异常信息。(推荐)注意!客户端要熔断降级必须在yaml文件里进行配置,客户端:
feign:
hystrix:
enabled:true
package com.ruoyi.system.api.factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.RemoteUserService;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.model.LoginUser;
/**
* 用户服务降级处理
*
* @author ruoyi
*/
@Component
public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserService>
{
private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class);
@Override
public RemoteUserService create(Throwable throwable)
{
log.error("用户服务调用失败:{}", throwable.getMessage());
return new RemoteUserService()
{
@Override
public R<LoginUser> getUserInfo(String username, String source)
{
return R.fail("获取用户失败:" + throwable.getMessage());
}
@Override
public R<Boolean> registerUserInfo(SysUser sysUser, String source)
{
return R.fail("注册用户失败:" + throwable.getMessage());
}
};
}
}
package com.ruoyi.system.api;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.factory.RemoteUserFallbackFactory;
import com.ruoyi.system.api.model.LoginUser;
/**
* 用户服务
*
* @author 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);
}
path:
path定义当前FeignClient访问接口时的统一前缀,比如接口地址是/user/get, 如果你定义了前缀是user, 那么具体方法上的路径就只需要写/get 即可。
primary:
primary对应的是@Primary注解,默认为true,官方这样设置也是有原因的。当我们的Feign实现了fallback后,也就意味着Feign Client有多个相同的Bean在Spring容器中,当我们在使用@Autowired进行注入的时候,不知道注入哪个,所以我们需要设置一个优先级高的,@Primary注解就是干这件事情的。
qualifier:
qualifier对应的是@Qualifier注解,使用场景跟上面的primary关系很淡,一般场景直接@Autowired直接注入就可以了。
第三方调用,在项目使用openFeign:(入参可以是json对象/实体/json字符串,返回值类型对应起来即可)
package com.oceansite.system.api;
import com.alibaba.fastjson.JSONObject;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @ClassName: RemoteNavigationService
* @Description:
* @Author:
* @Date: 2022/3/29 9:36
**/
@FeignClient(contextId = "remoteNavigationService",url = "https://xcx.apsatcom.com/OSN/api/prod",name = "navigation")
//上面远程调用的url是写死的,可以通过增加uri入参,实现动态url,见文末
public interface RemoteNavigationService {
/**
*@Description: 小站列表接口
*@Author: yangfengfan
*@Param: [jsonObject]
*@Return: com.oceansite.system.api.domain.Result
*@Date: 2022/3/29 10:53
**/
@PostMapping(value = "/terminal/group/v1",headers = {"accept-encoding=gzip","content-type=application/json; charset=UTF-8","accept=application/json"})
public JSONObject getTerminalGroupPage2(@RequestBody JSONObject navigationData);
/**
*@Description: 小站实时信息查询接口
*@Author: yangfengfan
*@Param: [jsonObject]
*@Return: com.oceansite.system.api.domain.Result
*@Date: 2022/3/29 10:53
**/
@PostMapping(value = "/terminal/status/v1",headers = {"accept-encoding=gzip","content-type=application/json; charset=UTF-8","accept=application/json"})
public JSONObject getListTerminalByParm(@RequestBody JSONObject navigationData);
/**
*@Description: 小站数据发送接收链路接口
*@Author: yangfengfan
*@Param: [jsonObject]
*@Return: com.oceansite.system.api.domain.Result
*@Date: 2022/3/29 10:53
**/
@PostMapping(value = "/nms/ucp/v1",headers = {"accept-encoding=gzip","content-type=application/json; charset=UTF-8","accept=application/json"})
public JSONObject qeryNmsUcpInfo(@RequestBody JSONObject navigationData);
}
多个参数传递问题
一般我们会使用@GetMapping和@PostMapping两种方式来调用Rest服务。
而接收的参数则会使用@RequestParam和@RequestBody来获取。
首先我们讲一下@RequestBody,@RequestBody只能用在Post请求,并且一个Post请求只能有一个@RequestBody。 @RequestBody的参数可以包括复杂类型。
然后我们讲一下@RequestParam,@RequestParam可以用在Post和Get请求中,但是要注意:@RequestParam 的参数只能是基本类型或者Enum,或者List和Map(List和Map里面也只能是基本类型)。所以@RequestParam可以和@RequestBody一起使用。
如果我们是Get请求,但是又有复合类型怎么办? 比如我们想传递一个User对象。User对象里面只有普通的两个String属性。 这里我们可以使用@SpringQueryMap:
@GetMapping("getUserAge")
public String getUserAge(@RequestParam("userId") String userId, @SpringQueryMap User user);
注意:@SpringQueryMap后面的参数只能是普通的POJO,不能是复合类型,否则解析不了。如果必须使用复合类型,那么使用@RequestBody吧。
FeignClient的日志问题
OpenFeign的Logger.Level有4种级别:
- NONE 没有日志
- BASIC 请求方法,请求URL,返回Code和执行时间
- HEADERS 请求和返回的头部基本信息
- FULL 请求和返回的头部,内容,元数据
要想使用这些级别,必须将OpenFeignClient的logger设置成debug级别:
#日志配置
logging:
level:
com:
flydean: debug
同时我们在代码中配置OpenFeign的日志级别:
@Configuration
public class CustFeignLogConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
这样我们在日志里面就可以看到DEBUG的所有HTTP请求信息。
多个FeignClient使用同一个name的问题
其实这里我们的Server定义了两个Rest服务,User和Order。
上面我们讲到了可以这样定义UserClient:
@FeignClient(value = "openfeign-server")
@RequestMapping(path = "/user")
public interface UserClient {
...
}
如果我们同样的这样定义OrderClient:
@FeignClient(value = "openfeign-server")
@RequestMapping(path = "/order")
public interface OrderClient {
...
}
运行时候就会报错。 原因是两个FeignClient使用了同一个value!
那怎么解决这个问题呢?
/**
* 因为@FeignClient的value不能重复,所以需要在这里以自定义的方式来创建
* @author wayne
* @version FeignClientController, 2019/9/5 7:07 下午
*/
@Data
@Component
@Import(FeignClientsConfiguration.class)
public class FeignClientController {
private OrderClient orderClient;
private UserClient userClient;
public FeignClientController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
this.orderClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract)
//默认是Logger.NoOpLogger
.logger(new Slf4jLogger(OrderClient.class))
//默认是Logger.Level.NONE
.logLevel(Logger.Level.FULL)
.target(OrderClient.class, "http://openfeign-server");
this.userClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract)
//默认是Logger.NoOpLogger
.logger(new Slf4jLogger(UserClient.class))
//默认是Logger.Level.NONE
.logLevel(Logger.Level.FULL)
.target(UserClient.class, "http://openfeign-server");
}
}
方法就是手动创建FeignClient, 上面的例子中,我们手动创建了OrderClient和UserClient两个FeignClient。
注意下面的代码片段,手动创建的FeignClient默认是没有logger和logLevel的。所以上面我们配置好的log信息对手动创建的FeignClient是无效的。 下面展示了如何手动添加:
//默认是Logger.NoOpLogger
.logger(new Slf4jLogger(OrderClient.class))
//默认是Logger.Level.NONE
.logLevel(Logger.Level.FULL)
超时控制介绍
默认Feign客户端只等待一秒钟,但是服务霭处理需要超过1秒,导致Feign客户端不想等待了,直接返回报错。为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。
解决方法:ym|文件中开启配置,application.yml
# 设置feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
# 指的是建立连接后从服务器读取到可用资源所用时间
ReadTimeout: 5000
# 指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ConnectTimeout: 5000
没配置上面的yml文件时,如果我们调用如下的方法会报错
@GetMapping("/feign/timeout")
public String paymentFeignTimeout() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
return serverPort;
}
配置了,就会出现以下结果
如何运行
本项目的模块都是以spring boot构建的,直接在编译器中运行Main方法即可启动。
- 启动openfeign-registry-server
openfeign-registry-server会启动eureka server,供后面的OpenFeignServer和OpenFeignClient注册。
- 启动openfeign-server
-
启动openfeign-client
-
测试openFeignClient
get请求url: http://localhost:8000/getUserName1
查看日志,看看输出吧。
本项目代码地址 :spring-cloud-openfeign-demo
更多教程请参考 flydean的博客
三、OpenFeign配置文件详解
SpringCloud OpenFeign 全功能配置详解(一文吃透OpenFeign)-CSDN博客(重要)
可以在应用程序中指定 application.properties / application.properties 文件,或作为命令行开关。本附录提供了通用Spring Cloud OpenFeign属性的列表,以及对使用它们的底层类的引用。
OpenFeign(3)--->OpenFeign的配置说明 - 灰信网(软件开发博客聚合)
Feign 配置文件详解_feign.httpclient-CSDN博客
配置项 | 默认值 | 描述 |
---|---|---|
feign.client.config | ||
feign.client.default-config | default | |
feign.client.default-to-properties | true | |
feign.compression.request.enabled | false | 配置请求GZIP压缩。 |
feign.compression.request.mime-types | [text/xml, application/xml, application/json] | 配置压缩支持的MIME TYPE |
feign.compression.request.min-request-size | 2048 | 配置压缩数据大小的下限 |
feign.compression.response.enabled | false | 配置响应GZIP压缩。 |
feign.compression.response.useGzipDecoder | false | 启用默认的gzip解码器。 |
feign.httpclient.connection-timeout | 2000 | 连接超时时间(单位:秒) |
feign.httpclient.connection-timer-repeat | 3000 | |
feign.httpclient.disable-ssl-validation | false | |
feign.httpclient.enabled | true | 开启(true)/关闭(false) Apache HTTP请求方式 |
feign.httpclient.follow-redirects | true | |
feign.httpclient.max-connections | 200 | 线程池最大连接数(全局) |
feign.httpclient.max-connections-per-route | 50 | 线程池最大连接数(单个HOST) |
feign.httpclient.time-to-live | 900 | 线程存活时间(单位:秒) |
feign.httpclient.time-to-live-unit | ||
feign.hystrix.enabled | false | 开启(true)/关闭(false)Hystrix功能 |
feign.okhttp.enabled | false | 开启(true)/关闭(false) OK HTTP请求方式 |
Feign提供了两大类配置属性来配置上述三种HTTP客户端,feign.client.*和feign.httpclient.*,前者支持按实例进行配置(注解-1),后者全局共享一套配置,包含线程池配置,但只影响HttpClient和OkHttp,不影响HttpURLConnection,具体关系见下表。
注解-1:所谓按实例进行配置,就是指每个FeignClient实例都可以通过feign.client.<feignClientName>.*来单独进行配置,注意首字母小写。而feign.client.default.*表示默认配置。
HTTP客户端 | 连接超时时间 | 请求超时时间 | 线程存活时间 | 线程池最大连接数(全局) | 线程池最大连接数(单个HOST) |
---|---|---|---|---|---|
HttpURLConnection | feign.client.[default|<feignClientName>].connect-timeout 默认值:10秒 | feign.client.[default|<feignClientName>].read-timeout 默认值:60秒 | N/A | N/A | N/A |
HttpClient | feign.httpclient.connection-timeout 默认值:2秒 | 默认值: -1(RequestConfig.Builder.socketTimeout) | feign.httpclient.time-to-live 默认值:900秒 | feign.httpclient.max-connections 默认值:200 | feign.httpclient.max-connections-per-route 默认值:50 |
OkHttp | feign.httpclient.connection-timeout 默认值:2秒 | feign.client.[default|<feignClientName>].read-timeout 默认值:10秒 | feign.httpclient.time-to-live 默认值:900秒 | feign.httpclient.max-connections 默认值:200 | N/A |
从上表可以看到,Feign提供了两个连接超时配置,HttpURLConnection使用feign.client.[default|<feignClientName>].connect-timeout,而HttpClient和OkHttp则使用feign.httpclient.connection-timeout,这一点要尤其注意。
Feign搭载着Spring Cloud的顺风车,Feign正以席卷之势成为使用Spring架构的大大小小互联网公司发起HTTP调用的首选框架。基于接口的声明式定义、客户端负载均衡、断路器和后备方法(fallback)是Feign相对上一代HTTP调用框架(比如Spring Template,Apache HttpClient)的四大优势。
类似于Retrofit和OkHttp的关系,Feign实际上是对普通HTTP客户端的一层封装,其目的是降低集成成本、提升可靠性。Feign支持三种HTTP客户端,包括JDK自带的HttpURLConnection、Apache HttpClient和Square OkHttp, 默认使用Apache HttpClient。
三种HTTP客户端比较:
HttpURLConnection:不支持线程池,一般不会选用。
HttpClient:相比OkHttp,HttpClient并没有明显的优势,可能是因为使用更广泛,所以被Feign选为默认实现。从5.0版本开始才支持HTTP/2。
OkHttp:开发Android应用的首选HTTP客户端,支持HTTP/2,通过设置feign.okhttp.enabled=true启用。
参考地址:
【微服务十一】SpringCloud之OpenFeign的常用配置(超时、数据压缩、日志、重试等)_openfeign 重试次数配置_秃秃爱健身的博客-CSDN博客
四,遇到的问题
遇到的问题1:动态URL问题:
OpenFeign中动态URl、动态传递接口地址_feign动态调用url-CSDN博客(直接配置!!)
例如:url = "${feign.client.url.TestUrl}",
遇到的问题2:nested exception is java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
@FeignClient注解下的类似@RequestParam注解等一定要有value属性
遇到的问题3:
OpenFeign使用拦截器:【微服务笔记06】微服务组件之OpenFeign配置信息及RequestInterceptor请求拦截器_朱友斌的博客-CSDN博客
结论:@FeignClient configuration 指定的配置类不要加 @Configuration,如果加了@Configuration注解,那这个配置类就是一个全局配置类。
@FeignClient configuration参数配置_Riven Chen的博客-CSDN博客(重要)
@FeignClient中的configuration使用拦截器,可以给微服务间的调用,增加参数或鉴权使用
@FeignClient(name = "weixinService", url = "https://api.weixin.qq.com",
configuration = WeiXinInterceptor.class)
public interface WeiXinService {
下方的拦截器如果类上加@Configuration,那么全局生效,如果不加,则@FeignClient指定生效。
package com.byd.msp.gateway.fegin.wexin;
import feign.Client;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* 微信接口统一鉴权
*/
@Component
public class WeiXinInterceptor implements RequestInterceptor {
/**
* 小程序 appId
*/
@Value("${wechat.app_id:xxxxx}")
private String appId;
/**
* 小程序 appSecret
*/
@Value("${wechat.app_secret:xxxxxx}")
private String secret;
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.query("appid", appId);
requestTemplate.query("secret", secret);
requestTemplate.query("grant_type", "authorization_code");
}
@Bean
public Client generateClient() {
try {
SSLContext ctx = SSLContext.getInstance("SSL");
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
ctx.init(null, new TrustManager[]{tm}, new SecureRandom());
return new Client.Default(ctx.getSocketFactory(), (hostname, session) -> true);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (KeyManagementException e) {
throw new RuntimeException(e);
}
}
}
遇到的问题4:feign远程调用公共api模块,一直报错null。(重要)
第一个是依赖问题,没有注入CodeDictionaryProviderFallback的依赖直接启动报错,通过该方式解决:SpringBoot启动类默认自动扫描包范围 + spring.factories理解_spring boot 包扫描范围_keep one's resolveY的博客-CSDN博客
第二个是公共api模块@FeignClient的接口没有被加载,使用@EnableFeignClients注解的属性字段basePackages解决
一般情况下,我们在启动类上面添加了@EnableFeignClients注解就是表明当前应用服务(我们称之为服务A)中有的地方想要引用其它应用服务(我们称之为服务B)中的接口。
如果服务B可以单独启动起来并且注册到注册中心,则我们仅仅在服务A的启动类中添加@EnableFeignClients注解即可;
如果服务B没有单独启动起来,而是以Jar包的形式被引入到服务A中,则服务A在启动的时候是不会主动去加载服务B中标注了@FeignClient注解的interface而去自动生成bean对象。这个时候就需要使用basePackages属性字段去指明应用程序A在启动的时候需要扫描服务B中的标注了@FeignClient注解的接口的包路径。
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.bc.product.client"})
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
如果Jar引入,那么最好这样写,然后再加入imports:
遇到的问题5:3.8版本以下,openfeign远程调用上传多个文件时报错
通过Feign 的方式 进行调用,feign 调用的本质还是http内部通信请求,实现模块之间的调用。
通过feign进行文件上传时,feign 侧示例代码如下:
@FeignClient public interface UploadClient { @PostMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String uploadFile(@RequestPart(value = "file") MultipartFile file, @RequestParam String uploadPath); }
文件属性需要使用 @RequestPart 属性,而且在请求中添加 consumes = MediaType.MULTIPART_FORM_DATA_VALUE ,
文件表单上传请求通常使用的ContentType为 multipart/form-data ,通过以上直接调用feign的方式即可实现feign 文件上传。
@RequestPart 与 @RequestParam 的区别:
1. @RequestPart这个注解用在multipart/form-data表单提交请求的方法上。
2. 支持的请求方法的方式MultipartFile,属于Spring的MultipartResolver类。这个请求是通过http协议传输的。
3. @RequestParam也同样支持multipart/form-data请求。
4. 他们最大的不同是,当请求方法的请求参数类型不再是String类型的时候。
5. @RequestParam适用于name-valueString类型的请求域,@RequestPart适用于复杂的请求域(像JSON,XML)。
Feign实现多文件上传,Open Feign多文件上传问题及解决_java_脚本之家(feignFormEncoder的bean上需要加@Primary)
@Scope注解 详细讲解及示例_扛麻袋的少年的博客-CSDN博客
Feign接口实现上传多个文件的坑_feign多文件上传_油腻的程序猿啊的博客-CSDN博客
遇到的问题6:远程调用性能优化
谈谈Spring Cloud OpenFeign远程调用性能优化_feign调用效率-CSDN博客
遇到的问题7:openfeign 设置header如何入参
【feign】OpenFeign设置header的5种方式_feign @headers-CSDN博客