深入Feign讲解例子
本文最后更新于:2020年9月10日 下午
在我的前一篇文章中已经讲解了什么是Feign,他的作用有什么,还有一个简单的Feign例子。 声明式服务调用Feign
这篇文章主要讲解Feign的复杂多参数情况、GZIP、连接池、日志情况和负载均衡。
我的api和product与上一篇文章中一样,没有什么变化。大家可以参考上一篇文章中的api和product,我们在这里为了减少重复的代码和页面,我只讲consumer中的需要修改的地方。
1、处理复杂参数
目录结构:
加入依赖:
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>e-book-product-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
123456789
ProductController中的代码:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value="list",method=RequestMethod.GET)
public List<Product> listProduct(){
List<Product> list= this.productService.listProduct();
return list;
}
//一个参数
@RequestMapping(value="get",method=RequestMethod.GET)
public Product getProduct(@RequestParam("id") Integer id) {
return this.productService.getProduct(id);
}
//------------------------多参数----------------------
//不能这么写,因为feign会自动转换为post
@RequestMapping(value="get1",method=RequestMethod.GET)
public Product getProduct1(Product obj) {
return this.productService.getProduct1(obj);
}
@RequestMapping(value="get2",method=RequestMethod.GET)
public Product getProduct2(@RequestParam("id") Integer id,@RequestParam("name") String name) {
return this.productService.getProduct2(id,name);
}
//------------------------POST-------------------------
@RequestMapping(value="add",method=RequestMethod.GET)
public Product addProduct() {
Product obj = new Product(2,"test5");
return this.productService.addProduct(obj);
}
}
12345678910111213141516171819202122232425262728293031
ProductService代码和上一篇一样。
在上面的接口中,我们定义了单个参数,多个参数,POST请求的这几种情况,在实际开发中差不多也是这几种情况。然后我们需要注意的一个地方就是多参数部分中,第一个是错误的,因为feign会自动转换为post。我们只能在参数中接收2个参数才正确,但是后面有一种方法可以解决这种问题。
第一种方法测试,报错信息:
第二种方法测试:
然后其他的都很简单,需要注意的就只有这个地方。
2、Feign基于Gzip压缩提升通信速度
关于gzip的介绍:
加入依赖:
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>e-book-product-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
123456789
目录结构:
ProductController代码:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value="list",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
@RequestMapping(value="list",method=RequestMethod.GET)
public List<Product> listProduct(){
List<Product> list= this.productService.listProduct();
System.out.println(list+"dddddddddd");
return list;
}
}
123456789101112
ProductService代码和上一篇一样
配置文件加入代码:
##默认是hostname 注册,改成IP 注册
#eureka.instance.perferIpAddress=true
#
##-----------------------------gzip
## 配置请求GZIP压缩
#feign.compression.request.enabled=true
## 配置响应GZIP压缩
#feign.compression.response.enabled=true
#
## 配置压缩支持的MIME TYPE
#feign.compression.request.mime-types=text/xml,application/xml,application/json
## 配置压缩数据大小的下限
#feign.compression.request.min-request-size=512
#-----------------------------spring boot gzip
#是否启用压缩
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
1234567891011121314151617
这里注释的代码可以不要,只要最后的两行代码,因为启用压缩过后就包含了上面的步骤。
这里需要的注意的是,如果你的页面请求接口返回在页面上的数据是xml的时候,你需要将它变为json格式才行,不然就不会成功
这里有两个解决的方法:
1. com.fasterxml.jackson.dataformat jackson-dataformat-xml
2.@RequestMapping(value="list",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
加入produces=MediaType.APPLICATION_JSON_UTF8_VALUE
当你访问接口时,看见有这两行说明就成功了。
小结一下gzip主要就是加入一个依赖,然后返回的时候不要是xml,而是json才可以。gzip可以很好的减少网页打开的速度。
3.基于连接池,提升吞吐量
介绍:
加入依赖,原来的那连个还是要加,而且还添加了另外的两个httpclient的依赖:
<!-- 使用Apache HttpClient替换Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>8.18.0</version>
</dependency>
1234567891011
pom中的代码:
#启动httpclient
feign.httpclient.enabled=true
12
目录结构:
‘
ProductController代码:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value="list",method=RequestMethod.GET)
public List<Product> listProduct(){
List<Product> list= this.productService.listProduct();
return list;
}
//一个参数
@RequestMapping(value="get",method=RequestMethod.GET)
public Product getProduct(@RequestParam("id") Integer id) {
return this.productService.getProduct(id);
}
//------------------------多参数----------------------
//不能这么写,因为feign会自动转换为post,采用httpclient解决了feign自动转post的问题
@RequestMapping(value="get1",method=RequestMethod.GET)
public Product getProduct1(Product obj) {
return this.productService.getProduct1(obj);
}
@RequestMapping(value="get2",method=RequestMethod.GET)
public Product getProduct2(@RequestParam("id") Integer id,@RequestParam("name") String name) {
return this.productService.getProduct2(id,name);
}
//------------------------POST-------------------------
@RequestMapping(value="add",method=RequestMethod.GET)
public Product addProduct() {
Product obj = new Product(2,"test5");
return this.productService.addProduct(obj);
}
}
12345678910111213141516171819202122232425262728293031
加入httpclient过后我们就可以解决上面遗留的问题了,解决了feign自动转换为post的问题。
测试:
现在测试接口就成功了。
4.日志记录每个接口URL,状态码和耗时信息
目录结构:
’
依赖包还是加入api的那连个依赖。
主要的就是怎么加入logs这个包。
ProductController代码:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value="list",method=RequestMethod.GET)
public List<Product> listProduct(){
List<Product> list= this.productService.listProduct();
return list;
}
}
12345678910
LogConfiguration代码:
@Configuration
public class LogConfiguration {
@Bean
public Logger.Level feignLoggerLevel() {
// NONE, 不记录任何信息,默认值。
// BASIC, 记录请求方法、请求URL、状态码和用时。
// HEADERS, 在BASIC的基础上再记录一些常用信息。
// FULL: 记录请求和响应报文的全部内容。
return Logger.Level.FULL;
}
}
1234567891011
ProductService代码,这里与上面的那些有点不一样:
@FeignClient(name="e-book-product",configuration = LogConfiguration.class)
public interface ProductService extends ProductFacade{
}
123
这里加入了configuration = LogConfiguration.class其实就是上面的那个类。
pom中加入一行代码:
logging.level.com.me.book.consumer.service.ProductService: DEBUG
1
其中com.me.book.consumer.service.ProductService是我新创建的那个logs那个类的全路径。现在就添加完了。
测试结果:
5.Feign负载均衡
这个例子与简单例子相似,只不过在pom中加入了这几行代码,然后在product中改变了一下代码。
pom中的加入的代码:
#全局配置
## 请求连接的超时时间
#ribbon.ConnectTimeout=5000
## 请求处理的超时时间
#ribbon.ReadTimeout=5000
#局部配置
# 对所有操作请求都进行重试
e-book-product.ribbon.OkToRetryOnAllOperations=true
# 对当前实例的重试次数
e-book-product.ribbon.MaxAutoRetries=2
# 切换实例的重试次数
e-book-product.ribbon.MaxAutoRetriesNextServer=0
# 请求连接的超时时间
e-book-product.ribbon.ConnectTimeout=3000
# 请求处理的超时时间
e-book-product.ribbon.ReadTimeout=3000
# 指定具体的服务实例清单
#e-book-product.ribbon.listOfServers=
123456789101112131415161718
在product中的ProductFacadeImpl的listProduct方法改变了一下:
@RequestMapping(value="list",method=RequestMethod.GET)
public List<Product> listProduct(){
System.out.println("########################");
// try {
// Thread.sleep(6*1000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
List<Product> list= new ArrayList<Product>();
list.add(new Product(1,"test1无的放矢付撒发付过过过过过过过过"));
list.add(new Product(2,"test2无的放矢付撒奥过过过过过过过过"));
list.add(new Product(3,"test3无的放矢付付撒奥过过过过过过过过"));
return list;
}
123456789101112131415
加入了try catch代码。我们设置的时间是连接3秒,而listProduct中延迟6秒,所以就会请求超时。而且我们可以设置重新连接的次数,为2次。
测试结果,连接超时:
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!