Feign 是
spring Cloud Netflix
组件中的一量级Restful
的HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了Ribbon
和RestTemplate
,实现WebService
的面向接口编程。Feign 简化了
RestTemplate
代码,是声明式服务调用组件:核心就是像调用本地方法一样调用远程方法。让开发者无需关注,远程调用过程,和交互细节。Feign 本身并不支持
spring MVC
注解,它有一套自己的注解,为了更方便使用Spring Cloud
孵化OpenFeign
。并且支持spring mvc
的注解,例如:@RequestMapping、@PathVariable。openFeign 的
@FeignClient
可以解析Spring MVC
的@RequestMapping
注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡调用服务。soringboot 2.0 以后基本使用
OpenFeign
官网
特性
- Hystrix 和它的 Fallback
- HTTP 请求响应的压缩
- Ribbon 负载均衡客户端
- 可拔插的HTTP编码器和解码器
- 拔插的注解支持,包括Feign 注解 和JAX-RS 注解
- Feign 微服务调用 (nacos 作为注册中心)
- 快速开始
- 项目
- pom 文件
- goods
- order
- 测试
- @FeignClient ‼️
- Feign 日志开启
- HTTP Client 替换
- 修改
- 如果使用 okhttp 的可以自定义配置
- 参数传递
- 特殊需求,Get方法传递了多参数
- 实现Token 传递
- 底部
项目预览
项目 pom 文件父项目
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.3.RELEASEversion>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-nacos-discoveryartifactId>
<version>2.0.1.RELEASEversion>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Finchley.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/libs-milestoneurl>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
goods 消费者
<parent>
<artifactId>springcloud-openfeigartifactId>
<groupId>org.examplegroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>openfeig-goodsartifactId>
<description>商品服务description>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
order 生产者
<parent>
<artifactId>springcloud-openfeigartifactId>
<groupId>org.examplegroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>openfeig-orderartifactId>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
dependencies>
goods
server:
port: 8081
spring:
application:
name: openfeig-goods
cloud:
nacos:
server-addr: ...:8848
@RestController
public class GoodsController {
@GetMapping("hello/{message}")
public String hello(@PathVariable String message){
return "来自goods的消息 =="+ message;
}
}
order
server:
port: 8080
spring:
application:
name: openfeig-order
cloud:
nacos:
server-addr: ....:8848
@SpringBootApplication
//开启远程调用的注解 扫描,可以扫描 @FeignClient
@EnableFeignClients
public class OpenFeigOrderApplication {
public static void main(String[] args) {
SpringApplication.run(OpenFeigOrderApplication.class,args);
}
}
// 准备远程调用的接口
@FeignClient(name = "openfeig-goods")
public interface GoodsClienService {
@GetMapping("hello/{message}")
String hello(@PathVariable String message);
}
//提供对外暴露的 api
@RestController
public class OrderController {
//nacos 客户端信息
@Autowired
private LoadBalancerClient loadBalancerClient;
//调用的 service
@Autowired
private GoodsClienService cartService;
@GetMapping("add/{message}")
public String add(@PathVariable String message){
ServiceInstance choose = loadBalancerClient.choose("openfeig-goods");
return cartService.hello(message) +" "+ choose.getHost() + "--" + choose.getPort();
}
}
测试
@FeignClient ‼️
需要
@EnableFeignClients
来开启扫描当定义的Feign接口中的方法被调用时,通过JDK的代理方式,生成具体的
RequestTemplate
。这个对象中,封装了http需要的全部信息。参数、方法名等等
属性 | 说明 |
---|---|
name | 指定FeignClient等名称,如果项目使用了Ribbon,那么name属性会作为微服务等名称,用于服务发现 |
url | 一般用于调试,可以手动指定@FeignClient 调用的服务地址 |
decode404 | 当发生404错误时,如果该字段值为true,会调用decoder进行编码,否则抛出FeignException |
configuration | Feign 配置列,可以自定义Feign 的 Encoder、Decoder、LogLevel、Contract 也可以在配置文件中配置 |
fallback | 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现 @FeignClient 标记的接口 |
fallbackFactory | 工厂类,用于生成fallback类实例,通过这个属性可以实现每个接口通用的容错逻辑,减少重复的代码 |
path | 定义当前FeignClient 的统一前缀 |
需要在配置文件中 logback.xml 把日志级别改为debug
# open feign 日志设置
logging:
level:
com.springcloud.study.service.ProviderClientService: debug
//全局的配置 日志配置类
@Configuration
public class FeignServiceConfig {
/*
Logger.Level 设置级别
NONE : 不记录任何信息
BASIC: 仅记录请求方法,URL以及响应状态码
HEADERS: 除了记录 BASIC级别信息外,还会记录请求和响应的头信息
FULL: 记录所有的请求于响应的明细,包括头信息,请求体,元数据
*/
@Bean
Logger.Level feignLogger(){
return Logger.Level.FULL;
}
}
//指定单独的配置 MyFeignConfig 是一个 日志的配置类
@FeignClient(configurtion = MyFeignConfig.class)
HTTP Client 替换
修改Feign 默认使用的是JDK原生的
URLConnection
发送HTTP请求,没有用链接池。对每个地址都会建立一个长链接。feign 的
HTTP
客户端支持3中框架HttpURLConnection、HttpClient、OkHttp
默认是 HttpURLConnection
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-httpclientartifactId>
dependency>
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-okhttpartifactId>
dependency>
feign:
httpclient:
# 开启httpclient
enabled: true
如果使用 okhttp 的可以自定义配置
okHttp 优势
- 支持SPDY,合并多个请求到同一个主机
- 使用链接池
- 使用GZIP压缩减少传输数据体积
- 缓存响应结果,减少重复请求
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {
@Bean
public okhttp3.OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS) //链接超时时间
.readTimeout(60,TimeUnit.SECONDS) //读超时
.writeTimeout(60,TimeUnit.SECONDS) //写超时
.retryOnConnectionFailure(true) //自动重试
.connectionPool(new ConnectionPool()) //创建链接池 okhttp包的
.build();
}
}
参数传递
特殊需求,Get方法传递了多参数get 方式,
@PathVariable、@RequestParm
注解来接收post 方式 ,
@RequestBody
接收请求参数
需要时实现Feign 的
RequestInterceptor
中的pally
进行统一处理
TODO 还是用 post 方法发送省事
实现Token 传递认证鉴权的时候,使用JWT,或spring security 都需要拿到token
RequestInterceptor
拦截器,在feign 调用的时候,向请求头里添加需要传递的token基于上面的代码改动 注意token 大小写的问题
//生产端
@Component
public class FeignTokenAddInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
if(null==getHttpServletRequest()){
//此处省略日志记录
return;
}
//将获取Token对应的值往下面传
requestTemplate.header("oauthToken", getHeaders(getHttpServletRequest()).get("oauthtoken"));
}
private HttpServletRequest getHttpServletRequest() {
try {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
} catch (Exception e) {
return null;
}
}
/**
* Feign拦截器拦截请求获取Token对应的值
* @param request
* @return
*/
private Map<String, String> getHeaders(HttpServletRequest request) {
Map<String, String> map = new LinkedHashMap<>();
Enumeration<String> enumeration = request.getHeaderNames();
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
System.out.println("模拟token " + map.get("oauthtoken"));
return map;
}
}
@GetMapping("hello/{message}")
public String hello(@PathVariable("message") String message, HttpServletRequest req){
String oauthToken = req.getHeader("oauthToken");
System.out.println( "token =" + oauthToken);
return "来自goods的消息 =="+ message + oauthToken;
}
底部
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)