在Spring Cloud中,使用Feign非常简单——只需创建接口,并在接口上添加注解即可。 Feign支持多种注解,例如Feign自带的注解或者JAXRS注解等。Spring Cloud对Feign进行了增强,使其支持Spring MVC注解,另外还整合了Ribbon和Eureka,从而使得Feign的使用更加方便
①:Ribbon+RestTemplate进行微服务调用 模式 ResponseEntity responseEntity = restTemplate.getForEntity("http://order-service/order/queryOrdersByUserId/"+userId,List.class);
Ribbon进行调用
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
缺点:
ResponseEntity responseEntity = restTemplate.getForEntity("http://orderservice/order/queryOrdersByUserId/"+userId,List.class); ①:我们不难发现,我们构建上诉的URL 是比较简单的,假如我们业务系统十分复杂,类似如下节点 https://www.baidu.com/s? wd=asf&rsv_spt=1&rsv_iqid=0xa25bbeba000047fd&issp=1&f=8&rsv_bp=0&rsv_idx=2&ie=utf8&tn=baiduhome_pg&rsv_enter=1&rsv_sug3=3&rsv_sug1=2&rsv_sug7=100&rsv_sug2=0&inputT=328&rsv_sug4=328 那么我们构建这个请求的URL是不是很复杂,若我们请求的参数有可能变动,那么是否这个URL是不是很复 杂
②: 如果系统业务非常复杂,而你是一个新人,当你看到这行代码,恐怕很难一眼看出其用途是什么!此时,你 很可能需要寻求老同事的帮助(往往是这行代码的作者,哈哈哈,可万一离职了呢?),或者查阅该目标地 址对应的文档(文档常常还和代码不匹配),才能清晰了解这行代码背后的含义!否则,你只能陷入蛋疼的 境地!
1.3.1)我们采取开发中常用的套路 定义一个03-ms-alibaba-feign-api工程
①:第一步,引入依赖
org.springframework.cloud
spring‐cloud‐starter‐openfeign
②:第二步:修改打包方式(因为该工程式一个普通的jar 不需要打可执行的jar)
org.apache.maven.plugins
maven‐jar‐plugin
第三步:编写声明式接口
@FeignClient(name = "product‐center")
public interface ProductCenterFeignApi {
/**
* 声明式接口,远程调用http://product‐center/selectProductInfoById/{productNo}
* @param productNo
* @return
*/
@RequestMapping("/selectProductInfoById/{productNo}")
ProductInfo selectProductInfoById(@PathVariable("productNo") String productNo);
}
1.3.2)调用者工程03-ms-alibaba-feign-order
第一步:引入依赖包 03-ms-alibaba-feign-api
com.tuling
03‐ms‐alibaba‐feign‐api
0.0.1‐SNAPSHOT
第二步: 开启注解加入 @EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class MsAlibabaFeignOrderApplication {
public static void main(String[] args) {
SpringApplication.run(Tulingvip03MsAlibabaFeignOrderApplication.class, args);
}
}
第三步:调用方式(像调用本地方式一样调用远程服务)
@Autowired
private ProductCenterFeignApi productCenterFeignApi;
ProductInfo productInfo = productCenterFeignApi.selectProductInfoById(orderNo);
1.3.3)我们服务提供者ms-alibaba-feign-product 的controller 需要实现我们的productCenterFeignApi接口,防止修 改(比如我们的productCenterFeignApi修改了,若没有实现该接口,服务提供者感知不到)
第一步:引入依赖 ms-alibaba-feign-api
第二步:我们的ProductInfoController实现productCenterFeignApi接口
@RestController
public class ProductInfoController implements ProductCenterFeignApi
①: 我们在03-ms-alibaba-feign-api工程中添加Feign的自定义配置
/**
* 这个类上千万不要添加@Configuration,不然会被作为全局配置文件共享
*
*/
public class ProductCenterFeignConfig {
@Bean
public Logger.Level level() {
//return Logger.Level.FULL;
return Logger.Level.BASIC;
}
}
@FeignClient(name = "product‐center",configuration = ProductCenterFeignConfig.class)
public interface ProductCenterFeignApi {
/**
* 声明式接口,远程调用http://product‐center/selectProductInfoById/{productNo}
* @param productNo
* @return
*/
@RequestMapping("/selectProductInfoById/{productNo}")
ProductInfo selectProductInfoById(@PathVariable("productNo") String productNo);
}
②:针对调用端工程03-ms-alibaba-customcfg-feign-order针对日志com.demo.feignapi 包下的日志级 别必须调整为DEBUG级别的 不然是不会打印日志的
logging:
level:
com:
demo:
feignapi: debug
ProductCenterFeignApi 不用指定configuration的选项
@FeignClient(name = "product‐center")
public interface ProductCenterFeignApi {
/**
* 声明式接口,远程调用http://product‐center/selectProductInfoById/{productNo}
* @param productNo
* @return
*/
@RequestMapping("/selectProductInfoById/{productNo}")
ProductInfo selectProductInfoById(@PathVariable("productNo") String productNo);
}
在调用方:03-ms-alibaba-customcfg-feign-order 通过feign:client:config:微服务名称:loggerLevel: 日志级别来指定
logging:
level:
com:
demo:
feignapi: debug
feign:
client:
config:
product‐center:
loggerLevel: full
根据自动装配我们FeignClients的配置中的默认锲约是springmvc(也就是说支持SpingMvc注解)
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
所以我们在FeignClient上可以使用Mvc的注解
@RequestMapping("/selectProductInfoById/{productNo}")
ProductInfo selectProductInfoById(@PathVariable("productNo") String productNo);
现在我们需要想使用Feign的原生注解来标识方法需要修改锲约
public class ProductCenterFeignConfig{
/**
* 根据SpringBoot自动装配FeignClientsConfiguration 的FeignClient的契约是SpringMvc
*
通过修改契约为默认的Feign的锲约,那么就可以使用默认的注解
* @return
*/
@Bean
public Contract feiContract() {
return new Contract.Default();
}
}
FeignClient类ProductCenterFeignApi使用Feign原生的注解
@FeignClient(name = "product‐center",configuration = ProductCenterFeignConfig.class)
public interface ProductCenterFeignApi {
/**
* 修改锲约为Feign的 那么就可以使用默认的注解
* @param productNo
* @return
*/
@RequestLine("GET /selectProductInfoById/{productNo}")
ProductInfo selectProductInfoById(@Param("productNo") String productNo);
}
public class MyRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("token", UUID.randomUUID().toString());
}
}
public class ProductCenterFeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return new MyRequestInterceptor();
}
}
@FeignClient(name = "product‐center",configuration = ProductCenterFeignConfig.class)
//@FeignClient(name = "product‐center")
public interface ProductCenterFeignApi {
@RequestLine("GET /getToken4Header")
String getToken4Header(@RequestHeader("token") String token);
}
服务提供者
@RestController
@Slf4j
public class ProductInfoController implements ProductCenterFeignApi {
@RequestMapping("/getToken4Header")
public String getToken4Header(@RequestHeader("token") String token) {
log.info("token:{}",token);
return token;
}
}
第一步:开启连接池配置
feign:
client:
config:
product-center:
loggerLevel: full
contract: feign.Contract.Default
httpclient:
#让feign底层使用HttpClient去调用
enabled: true
max‐connections: 200 #最大连接数
max‐connections‐per‐route: 50 #为每个url请求设置最大连接数
第二步:调整Feign的日志级别(强烈推荐使用Basic级别的)
2.5)Feign的生产实践 (以Feign的超时说了算) 问题
①:Feign的底层用的是Ribbon,那么我们怎么配置超时时间
服务提供方模拟耗时 睡眠3S
配置这个 ribbon肯定会超时
ribbon:
connectTimeout: 2000
readTimeout: 2000
(Feign不会超时)
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
正好调用是3S 说明以Feign的超时说了算
这种配置 Feign超时了
ribbon:
connectTimeout: 5000
readTimeout: 5000
http:
client:
enabled: true
feign:
client:
config:
default:
connectTimeout: 2000
readTimeout: 2000