OpenFeign使用笔记
是什么
Feign是一个声明式Web Service客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
怎么用
官方github项目(readme里写得很详细,代码里还有示例)
我遇到的一些特殊请求:
(1)POST请求方式,但是请求参数放在查询字符串里:
一般的查询字符串添加可以用@QueryMap Map<String, Object>(为什么要用这个,因为不是用的spring cloud分支下封装过的feign,不支持直接用spring的注解)
@RequestLine("POST /example/foo/token")
String getAccessToken(@QueryMap Map<String, Object> queryMap);
(2)查询字符串里带特殊拼接符号如加号(实现形如GET /Groups?filter=displayName+Eq+{roleName}的查询): https://stackoverflow.com/questions/43868680/feign-client-does-not-resolve-query-parameter
@RequestLine("GET /Groups?filter={roleName}") String isValidRole(@Param(value = "roleName", expander = PrefixExpander.class) String roleName); static final class PrefixExpander implements Param.Expander { @Override public String expand(Object value) { return "displayName+Eq+" + value; } }
(3)feign添加自定义httpheader:
@RequestLine("POST /add") @Headers("TOKEN: {userToken}") Response addRecord(RecordVO recordVO, @Param("TOKEN") String userToken);
普通项目示例(<-这是一个原文链接,稍微修改了下)
maven依赖
<!-- open-feign -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>10.0.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>10.0.1</version>
</dependency>
自定义接口
import feign.Param; import feign.RequestLine; public interface RemoteService { @RequestLine("GET /users/list?name={name}") String getOwner(@Param(value = "name") String name); }
通过@RequestLine指定HTTP协议及URL地址
配置类
RemoteService service = Feign.builder() .options(new Options(1000, 3500)) .retryer(new Retryer.Default(5000, 5000, 3)).encoder(new GsonEncoder()).target(RemoteService.class, "http://127.0.0.1:8085");
options方法指定连接超时时长及响应超时时长,retryer方法指定重试策略,target方法绑定接口与服务端地址。返回类型为绑定的接口类型。
(ps.还可以指定其他的:比如
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.logger(new Logger.ErrorLogger())
.logLevel(Logger.Level.BASIC))
调用:
String result = service.getOwner("scott");
与调用本地方法相同的方式调用feign包装的接口,直接获取远程服务提供的返回值。
spring cloud 项目示例(<-这是一个原文链接)
maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
在启动类上加
注解,如果你的Feign接口定义跟你的启动类不在一个包名下,还需要制定扫描的包名@ EnableFeignClients(basePackages = "com.fangjia.api.client")
配置类
@Configuration public class FeignConfiguration { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
自定义接口
@FeignClient(value = "fangjia-fsh-house-service", path = "/house", configuration = FeignConfiguration.class, fallback = HouseRemoteClientHystrix.class) public interface HouseRemoteClient { /** * 获取企业下某用户的有效房产信息 * @param eid 企业编号 * @param uid 用户编号 * @return */ @GetMapping("/list/{eid}/{uid}") public HouseListDto hosueList(@PathVariable("eid")Long eid, @PathVariable("uid")String uid); /** * 获取房产详细信息 * @param houseId 房产编号 * @return */ @GetMapping("/{houseId}") public HouseInfoDto hosueInfo(@PathVariable("houseId")Long houseId); }
熔断回调处理
@Component public class HouseRemoteClientHystrix implements HouseRemoteClient { @Override public HouseListDto hosueList(Long eid, String uid) { return new HouseListDto(); } @Override public HouseInfoDto hosueInfo(Long houseId) { return new HouseInfoDto(); } }
原理(<-这是一个原文链接)
- 首先通过@EnableFeignCleints注解开启FeignCleint
- 根据Feign的规则实现接口,并加@FeignCleint注解
- 程序启动后,会进行包扫描,扫描所有的@ FeignCleint的注解的类,并将这些信息注入到ioc容器中。
- 当接口的方法被调用,通过jdk的代理,来生成具体的RequesTemplate
- RequesTemplate在生成Request
- Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
- 最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。