Spring Cloud Feign

在Spring Cloud体系中,Feign的作用就是可以与Eureka组合在一起简化各服务之间的http调用,在Spring Cloud Netflix体系中,Feign和Ribbon都是用于调用其他服务的,它们之间的关系是,Feign是在Ribbon的基础上发展而来的,Ribbon的特性在Feign里面同样有体现,比如要修改Feign的负载均衡策略,其实就是修改Ribbon的负载均衡策略,按照Ribbon的配置进行即可(这大概是现有版本Spring Cloud Netflix默认集成了Riibon的缘由,就是要使用Feign,不要直接使用Ribbon,我瞎猜的)。Feign存在的意义就是简化http调用,让开发者可以像调用方法一样访问http接口,在非Sping Cloud体系项目中,遇到需要调用第三方接口的需求,也可以使用它去简化一些http的调用。

Feign是一个声明式的Rest客户端,它能让http调用更加简单。Feign提供了http请求的模板,通过编写简单的接口和插入注解,就可以定义好http请求的参数、格式、地址等信息,这样,就可以像调用方法一样,完成各服务之间请求处理。

加入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

脱离Spring Cloud使用Feign

1、编写一个提供接口服务的项目(这里暂时命名为cloud-server0项目,使用9000端口,服务名也定义为cloud-server0),提供的接口如下:

@Controller
public class Server0Controller {
	
	@GetMapping("/server0/api0")
	@ResponseBody
	public String methodA() {
		return "第1个服务提供者:第一个接口";
	}
	
}

2、创建另外一个项目(这里暂时命名为cloud-server3-Feign项目,使用9003端口,服务名也定义为cloud-server3-Feign),在启动类中开启对Feign的支持

@SpringBootApplication
// 开启Feign支持。注意:如果Feign相关代码不在当前类所在包或子包路径下,需要指定扫描的路径
@EnableFeignClients
public class Server3FeignApp {

	public static void main(String[] args) {
		SpringApplication.run(Server3FeignApp.class, args);
	}
}

3、在cloud-server3-Feign项目中编写Feign调用上面接口的代码

// 不在Spring Cloud中使用Feign服务
public interface FeignWithoutCloudService {

    // 指明使用GET的请求方式调用目标接口,目标接口的uri:/server0/api0
	@RequestLine("GET /server0/api0")
	String methodA();
	
}

4、在cloud-server3-Feign项目中配置Feign(否则光凭上面一个光秃秃的interface,鬼知道该怎么执行)。然后就可以像使用其他Spring Bean一样,注入FeignWithoutCloudService后,就可以像调用方法一样访问其他http接口

@Configuration
public class FeignConfig {

	@Bean
	public FeignWithoutCloudService initFeign() {
		// 简单配置Feign(Feign还有其他很多配置),指明FeignWithoutCloudService接口中所有方法的调用路径是127.0.0.1:9000
		FeignWithoutCloudService feignWithoutCloudService = Feign.builder()
				.target(FeignWithoutCloudService.class, "http://127.0.0.1:9000");
		return feignWithoutCloudService;
	}
	
}

5、启动cloud-server0项目(http接口提供者),然后在cloud-server3-Fign项目中编写下面的单元测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class FeignWithoutCloudServiceTest {

    // 像调用方法一样简单.注入被配置为Feign Client的实例
	@Autowired
	private FeignWithoutCloudService feignWithoutCloudService;
	
	@Test
	public void testA() {
		String requestResult = feignWithoutCloudService.methodA();
		System.out.println("调用结果 : " + requestResult);
	}
	
}

运行结果如下:

20210523212012590.png

可以看到,实现了像调用方法一样调用http接口。在这之前的项目中,一般调用http接口,可以使用HttpClient、Okhttp、RestTemplate,甚至是原生的网络编程Httpurlconnection,Feign确实简化了不少http的调用。

Feign的常用注解

在上面的FeignWithoutCloudService接口中,已经使用@RequestLine注解。

@RequestLine注解(包路径为:feign.RequestLine)

该注解用于Feign接口中的方法体上,用于定义调用目标http接口的请求动作,比如GET、POST等,以及定义目标http接口的资源定位符uri。请求动作和uri中间隔一个空格。@RequestLine是支持Rest风格的,比如这种uri:xxx/{yyy}/zzz。

注:在Spring Cloud体系中使用,因为锲约配置的原因,需要使用SpringMVC中的@GetMapping、@PostMapping等去代替@RequestLine

下述示例,表示用GET请求访问一个ip:port/server0/api0 的接口:

@RequestLine("GET /server0/api0")
String methodA();

@Param注解(包路径为:feign.Param)

在方法参数中使用该注解,用于定义目标http接口所需要的参数(目标接口的参数类型限制:multipart/form-data)。

下述示例,表示用POST请求访问一个ip:port/server0/api1的接口,目标接口需要一个id参数:

@RequestLine("POST /server0/api1")
String methodB(@Param("id") String id);

说明:@Param作为一个参数绑定注解,里面的值必须和目标http接口的参数名保持一致,Feign接口的参数名可以自定义(和Mybatis的@Param类似)。比如上面可以改为:@Param("id") String userId

@Headers注解(包路径为:feign.Param)

该注解用于Feign接口中的方法体上,因为请求头都是键值对形式,所有它的值需要用英文逗号隔开。作用是为当前http请求携带请求头信息。它支持的value值是一个String数组,即除了可以定义单个请求头,还可以用花括号"{}"定义多个请求头。

下述示例,表示为当请求携带token和Content-Type请起头:

@Headers({"token:zepal123456", "Content-Type:application/json"})

@QueryMap注解(包路径为:feign.Param)

该注解用于Feign接口的方法参数上。用于构建动态请求参数,标记一个Map,使该Map中的所有键值对都成为目标http接口的请求参数。这种方式灵活性较高,假如需要调用的http接口,一会需要3个请求参数,一会需要2个请求参数,@QueryMap就可以派上用场了,当然,目标http接口是固定的请求参数也可以使用,只是可读性没那么强。

下述示例,使用@QueryMap定义请求参数,调用该方法时,传入的Map参数中,所有的键值对都会作为当前http调用的请求参数:

// 参数params中所有的键值对,都会作为请求参数
@RequestLine("POST /server0/api5")
String methodG(@QueryMap Map<String, String> params);

@HeaderMap注解(包路径为:feign.Param)

该注解用于Feign接口的方法参数上。用于构建动态请求头,标记一个Map,使该Map中的所有键值对都成为请求头。这种方式定义请求头灵活性比较高,假如需要调用的http接口,一会需要3个请求头,一会需要2个请求头,@HeaderMap就可以派上用场了,当然,目标http接口是固定的请求头也可以使用,只是可读性没那么强。

下述示例,使用@HeaderMap定义请求头,调用该方法时,传入的Map参数中,所有的键值对都会作为当前http调用的请求头:

// 参数headers中所有的键值对,都会作为请求头
@RequestLine("POST /server0/api4")
String methodF(@HeaderMap Map<String, String> headers);

@Body注解(包路径为:feign.Param)

该注解用于Feign接口中的方法体上。用于标记请求体,比如传递json、xml等格式的请求参数,需要配合@Headers注解使用。

下述示例,表示使用json格式的请求参数:

@RequestLine("POST /server0/api2")
@Headers("Content-Type:application/json")
// json以转义符'%7B'开始,以转义符'%7D'结束
@Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
String methodD(@Param("user_name") String user_name, @Param("password") String password);

下述示例,表示使用xml格式的请求参数:

@RequestLine("POST /server0/api3")
@Headers("Content-Type: application/xml")
@Body("<login \"user_name\"=\"{user_name}\" \"password\"=\"{password}\"/>")
String methodE(@Param("user_name") String user_name, @Param("password") String password);

Feign的原生配置

在上面的FeignConfig编码中,只是简单的配置了Feign,Feign支持以Feign.builder()去构建相关设置。

更多详细说明参考:https://github.com/OpenFeign/feign

设置自定义编码解码器

Feign.builder()
			.encoder(Encoder encoder)
			.decoder(Encoder encoder);

该配置的作用是对http的请求信息和响应信息进行编码和解码, Feign默认提供了基于SpringMVC     HttpMessageConverters支持的编码解码方式。支持所有实现了feign.codec.Encoder和feign.codec.Decoder接口的编码解码,常见支持Feign编码解码的有JacksonEncoder/JacksonDecoder、GsonEncoder/GsonDecoder、SaxEncoder/SaxDecoder(基于XML 格式的Sax 库持久化转换协议)、JAXBEncoder/JAXBDecoder(基于XML 格式的JAXB 库持久化转换协议)、ResponseEntityEncoder/ResponseEntityDecoder、SpringEncoder/SpringDecoder,以及自定义的编码解码,只要实现Encoder和Decoder接口即可。

设置日志信息

设置日志主要是用于生成Feign的http调用日志。

Feign默认提供的日志级别有:NONE(不输出日志)/BASIC(只输出请求方法的URL和响应的状态码以及接口的执行时间)/HEADERS(将BASIC信息和请求头信息输出)/FULL(输出完整的请求信息)

示例:

Feign.builder()
			.logger((new Logger.JavaLogger()).appendToFile("D:/feign.log"))
			.logLevel(Level.FULL);

 然后会在目标文件路径下输出日志文件:feign.log,内容如下:

20210525220617787.png

设置超时时间

 示例:

Feign.builder()
			.options(new Options(1000, 1000));

 第一个参数是:连接超时时间,单位是毫秒,默认是10s;第二个参数是:读取超时时间,单位是毫秒,默认是60s。同时Options(包路径是:feign.Request.Options),提供另外一个构造方法,多一个boolean类型参数,表示告知Feign客户端是否遵循重定向,默认为true。

设置请求拦截器

作用是用于拦截Feign的所有请求,然后做一些其他逻辑,比如,为所有请求都加上一个请求头,做一些鉴权服务等。所有定义的请求拦截都需要去实现feign.RequestInterceptor接口,去重新它里面的apply(RequestTemplate template)方法。

示例:

public void test() {
		Feign.builder()
			.requestInterceptor(new ForwardedForInterceptor());
	}

	// 为当前项目中所用使用Feign的http请求添加一个请求头token
	static class ForwardedForInterceptor implements RequestInterceptor {
		@Override
		public void apply(RequestTemplate template) {
			template.header("token", "xxxx");
		}
	}

 另外,Feign还支持了请求拦截器链的形式(即多个拦截器依次组成的一层一层拦截),拦截顺序按照拦截器链的遍历顺序执行,所以这里为了保证定义的拦截器链按照自己想要的顺序执行,需要使用List等存储有序的结构去创建。配置伪代码如下:

Feign.builder()
        .requestInterceptors(Iterable<RequestInterceptor> requestInterceptors)

Feign提供了一个Basic认证组件(feign.auth.BasicAuthRequestInterceptor),用于认证Spring Security。即假如调用的目标http接口,开启了Spring Security认证,那http请求的鉴权信息就可以使用Feign提供的Basic认证组件。配置伪代码如下:

# username和password分别是目标http接口鉴权信息
Feign.builder()
    .requestInterceptor(new BasicAuthRequestInterceptor("username", "password"))

设置调用客户端组件

Feign默认使用的HttpClient对http接口进行调用。Feign支持所有实现了feign.Client接口的http组件,除了默认HttpClient,还有OkHttpClient,还可以支持Ribbon提供的智能路由和弹性功能。

示例:

Feign.builder()
			.client(Client client);

设置重试机制

该配置的作用是,当请求超时或异常后,对http请求进行重试。支持实现了feign.Retryer接口的所有重试。Feign提供了一个默认的重试类feign.Retryer.Default,提供的值:最大重试次数5次,重试间隔100ms,最大重试等待时间1s。

示例:

Feign.builder()
			.retryer(Retryer retryer);

在Spring Cloud体系中使用Feign

这里就不演示负载均衡,如果需要进行负载均衡测试,多创建一个服务提供者项目,按照Ribbon的配置进行即可。

FBI WARNING:注释掉前面所有脱离Spring Cloud体系和Feign相关的代码及配置,否则可能会出现一些莫名其妙的异常。

1、创建一个Eureka项目(这里暂时命名为cloud-eureka项目,使用9999端口,服务名也定义为cloud-eureka),将上述的两个项目cloud-server0和cloud-server3都注册到Eureka

2、重新编写Feign的调用接口,如下:

// @FeignClient会将当前接口注册为Spring Bean,类似于前面定义的FeignConfig
// value:指明需要调用的服务名,也就是需要调用哪个服务的接口
// path:目标http接口中的统一uri前缀
@FeignClient(value = "cloud-server0", path = "/server0")
public interface FeignWithCloudService {

	// 默认的情况下,这里将不再支持@RequestLine("GET /api0")的方式
	// 这是Feign的契约组件提供的功能,可以更改,锲约配置仅支持在SPring Cloud体系中使用,即在非Spring Cloud体系中,还是要老老实实使用原生注解@RequestLine
	@GetMapping("/api0")
	String methodA();
	
}

3、运行测试,可以得到同样的响应信息

锲约配置

在Feign中默认提供的锲约配置是SpringMvcContract,即支持使用SpringMVC中@GetMapping、@PostMapping等去代替@RequestLine注解。

注意:在非Spring Cloud体系中使用Feign,只能使用原生的@RequestLine,而在Spring Cloud体系中使用Feign,默认配置下,需要强制使用锲约注解去代替@RequestLine。

配置示例:

// 当前版本中提供的默认契约配置类是:Contract.Default
Feign.builder()
			.contract(new Contract.Default());

说明:这种配置Feign的方式,仅存在于非Spring Cloud体系中,在非Spring Cloud体系中本身又不支持SpringMVC的契约,所以这里仅仅只是为了说明它的原生契约配置在什么地方,没什么实际意义。

在Spring Cloud体系中配置Feign

上面展示了通过Feign.builder()去进行原生的配置Feign。那么在Spring Cloud体系中,这种方式不太好使。

1、配置文件(application.properties)形式

Feign提供了一个配置属性feign.client.config,配置的值是Map<String, FeignClientConfiguration>。

示例:

# 设置Feign连接超时和读超时都为5s
feign.client.config.FeignClientConfiguration.connect-timeout=5000
feign.client.config.FeignClientConfiguration.read-timeout=5000
# 将默认的锲约配置换成SpringMvcContract:以前旧版本中SpringMvcContract是默认的契约配置.注:这个契约同样强制使用@GetMapping等注解去替代@RequestLine
feign.client.config.FeignClientConfiguration.contract=org.springframework.cloud.openfeign.support.SpringMvcContract
# 像上面原生Feign配置一样,其他更多配置以FeignClientConfiguration进行即可

2、在@FeignClient(configuration = Class<?>[])指明相应的配置(configuration注解属性是一个Class数组,表示支持多个配置类)

创建一个或多个配置类

@Configuration
public class FeignConfig {

	// 初始化重试机制(这里用了Feign默认的重试机制做示例)
	@Bean
	public Retryer initRetryer() {
		return new Retryer.Default();
	}
	
	// 初始化Feign的连接超时和读超时
	@Bean
	public Options initOptions() {
		return new Options(5000, 5000);
	}

}

在@FeignClient注解中指向该配置类

@FeignClient(value = "", path = "", configuration = FeignConfig.class)

或者以数组的方式

// 如果有写了多个配置类,就用数组的表现形式
@FeignClient(value = "", path = "", configuration = {FeignConfig.class})

GZIP压缩配置

# 开启请求压缩
feign.compression.request.enabled=true
# 开启响应压缩
feign.compression.response.enabled=true
# 还可以配置压缩的类型、最小压缩值的标准
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值