微服务之OpenFeigin

一:什么是Feign

1.1)Feign是Netflix开发的声明式、模板化的HTTP客户端,其灵感来自Retrofit、JAXRS­2.0以及WebSocket。Feign 可帮助我们更加便捷、优雅地调用HTTP API。

在Spring Cloud中,使用Feign非常简单——只需创建接口,并在接口上添加注解即可。 Feign支持多种注解,例如Feign自带的注解或者JAX­RS注解等。Spring Cloud对Feign进行了增强,使其支持Spring MVC注解,另外还整合了Ribbon和Eureka,从而使得Feign的使用更加方便

1.2)Ribbon Vs 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)在我们工程中怎么添加Feign

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

二:如何自定义Feign

2.1)默认情况下,Feign的调用不打印日志,我们需要通过自定义来打印我们的Feign的日志 (basic适 用于生产环境)

微服务之OpenFeigin_第1张图片

①: 我们在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

2.2)基于yml文件细粒度配置

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

2.3)使用Feign原生的注解配置(需要修改契约)

根据自动装配我们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);
 }

2.4)拦截器的应用配置

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;
}
 }

2.4)Feign调用优化方案

第一步:开启连接池配置

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

微服务之OpenFeigin_第2张图片

 

配置这个 ribbon肯定会超时
ribbon:
 connectTimeout: 2000
 readTimeout: 2000

(Feign不会超时)
feign:
 client:
  config:
   default:
    connectTimeout: 5000
    readTimeout: 5000

正好调用是3S 说明以Feign的超时说了算

微服务之OpenFeigin_第3张图片

这种配置 Feign超时了

ribbon:
 connectTimeout: 5000
 readTimeout: 5000
http:
 client:
 enabled: true
 feign:
 client:
  config:
   default:
    connectTimeout: 2000
    readTimeout: 2000

 微服务之OpenFeigin_第4张图片

 

你可能感兴趣的:(spring,cloud,微服务,eureka,spring,cloud)