springboot中使用Feign整合nacos,gateway进行微服务之间的调用方法
作者:jsyandxys 发布时间:2021-07-12 07:00:28
1、什么是Feign
Feign 是 Spring Cloud Netflix 组件中的一个轻量级 RESTful 的 HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了 Ribbon 和 RestTemplate,实现了 WebService 的面向接口编程,进一步降低了项目的耦合度。
Feign 内置了 Ribbon(其主要功能是提供客户端实现负载均衡算法),用来做客户端负载均衡调用服务注册中心的服务。
Feign 本身并不支持 Spring MVC 的注解,它有一套自己的注解,为了更方便的使用,Spring Cloud 孵化了 OpenFeign。
Feign 是一种声明式、模板化的 HTTP 客户端(仅在 Consumer 中使用)。
Feign 支持的注解和用法请参考官方文档:https://github.com/OpenFeign/feign 或 spring.io 官网文档
Feign 的使用方式是:使用 Feign 的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。
2、为什么使用Feign?
1.微服务之间调用变得简单
它让微服务之间的调用变得更简单了,类似controller调用service
2.使用简单
只需要创建一个接口,然后添加注解即可使用Feign
3.简化代码
Feign默认集成了Ribbon,简化了使用Spring Cloud Ribbon负载均衡时,自动封装服务调用客户端的开发量
3、如何使用Feign?
目录及项目结构
子工程
<modules>
<module>service-provider</module>
<module>service-provider02</module>
<module>service-consumer</module>
<module>service-gateway</module>
</modules>
注:
注册中心实例 nacos
service-provider | service-provider02 服务提供者实例
service-consumer 服务消费者
service-gateway 网关服务
3.1注册中心Nacos部署
部署nacos(略)
3.2部署生产者
1.新建maven项目,过程省略…
2.关键配置
server:
port: 7070 # 端口
spring:
application:
name: service-provider # 应用名称(集群下相同)
#nacos配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
3.逻辑部分
为了检测Feign的负载均衡,所以在provide01中,将返回结果增加01后缀
/**
* 查询商品列表
*
* @return
*/
@Override
public List<Product> selectProductList() {
return Arrays.asList(
new Product(1, "华为手机01", 1, 5800D),
new Product(2, "联想笔记本01", 1, 6888D),
new Product(3, "小米平板01", 5, 2020D)
);
}
4.部署双节点provide02
重复步骤 【1~3】,修改serverImpl逻辑,改为后缀加02
/**
* 查询商品列表
*
* @return
*/
@Override
public List<Product> selectProductList() {
return Arrays.asList(
new Product(1, "华为手机02", 1, 5800D),
new Product(2, "联想笔记本02", 1, 6888D),
new Product(3, "小米平板02", 5, 2020D)
);
}
5.启动双节点,监控Nacos
3.3 部署消费者,使用Feign调用provider方法
1.新建maven工程,过程省略…
2.引入nacos和feign的pom依赖
<!-- nacos 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.9.0.RELEASE</version>
</dependency><!-- spring cloud openfeign 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.添加配置文件
server:
port: 9090 # 端口
spring:
application:
name: service-consumer # 应用名称
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
4.在服务接口层声明使用Feign
使用FeignClient注解,声明要调用的服务
// 声明需要调用的服务
@FeignClient("service-provider")
public interface ProductService
对方法使用GetMapping注解,配置需要调用的服务路由地址
// 配置需要调用的服务地址及参数
@GetMapping("/product/list")
List<Product> selectProductList();
5.方法调用
在另一个impl里调用ProductService的方法,而不用关注ProductServiceImpl里面的实现逻辑,它会远程请求生产者中controller里的方法.
/**
* 根据主键查询订单
*
* @param id
* @return
*/
@Override
public Order selectOrderById(Integer id) {
return new Order(id, "order-001", "中国", 22788D,
productService.selectProductList());
}
消费者controller中的方法
/**
* 根据主键查询订单
*
* @param id
* @return
*/
@GetMapping("/{id}")
public Order selectOrderById(@PathVariable("id") Integer id) {
return orderService.selectOrderById(id);
}
过postman调用 localhost:9090/order/2 结果,可以看到已经成功使用Feign进行了远程调用,
并且同一个请求,返回两个provider的不同结果,由此可知Feign也实现了负载均衡
4.ribbon负载均衡策略
在消费者配置文件中修改ribbon的负载均衡策略
service-provider:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置规则 随机
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置规则 轮询
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置规则 重试
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置规则 响应时间权重
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置规则 最空闲连接策略
5.Feign传参
1.Get方法
使用 @PathVariable 注解或 @RequestParam 注解接收请求参数。
provider接口层ProductService.java
ProductService.java方法
/**
* 根据主键查询商品
*
* @param id
* @return
*/
Product selectProductById(Integer id);
provider实现类ProductServiceImpl.java
ProductServiceImpl.java方法
/**
* 根据主键查询商品
*
* @param id
* @return
*/
@Override
public Product selectProductById(Integer id) {
return new Product(id, "冰箱", 1, 2666D);
}
provider的控制类ProductController.java
ProductController.java方法
/**
* 根据主键查询商品
*
* @param id
* @return
*/
@GetMapping("/{id}")
public Product selectProductById(@PathVariable("id") Integer id) {
return productService.selectProductById(id);
}
provider服务中,controller使用了(@PathVariable(“id”) Integer id) 进行参数接收
consumer的接口层ProductService.java
@GetMapping("/product/{id}")
Product selectProductById(@PathVariable("id") Integer id);
consumer服务中,使用了(@PathVariable(“id”) Integer id) 进行参数接收,写法类似于在controller中定义请求方法和定义参数
consumer的接口层OrderService.java
Order getOrderById(Integer id);
consumer的接口实现类OrderServiceImpl.java
@Override
public Order getOrderById(Integer id) {
return new Order(id, "order-003", "中国", 2666D,
Arrays.asList(productService.selectProductById(id)));
}
consumer的控制类OrderController.java
@GetMapping("selectProductById/{id}")
public Order selectProductById(@PathVariable("id") Integer id) {
return orderService.getOrderById(id);
}
访问localhost:9090/order/selectProductById/888得到结果
Post方法
使用 @RequestBody 注解接收请求参数。
provider的ProductService.java
/**
* 新增商品
*
* @param product
* @return
*/
Map<Object, Object> createProduct(Product product);
provider的ProductServiceImpl.java
/**
* 新增商品
*
* @param product
* @return
*/
@Override
public Map<Object, Object> createProduct(Product product) {
System.out.println(product.getProductName());
return new HashMap<Object, Object>() {{
put("code", 200);
put("message", "新增成功");
}};
}
provider的ProductController.java
/**
* 新增商品
*
* @param product
* @return
*/
@PostMapping("/save")
public Map<Object, Object> createProduct(@RequestBody Product product) {
return productService.createProduct(product);
}
consumer的ProductService.java
/**
* 新增商品
*
* @param product
* @return
*/
@PostMapping("/product/save")
Map<Object, Object> createProduct(Product product);
consumer的OrderService.java
Map<Object, Object> createProduct(Product product);
consumer的OrderServiceImpl.java
@Override
public Map<Object, Object> createProduct(Product product) {
return productService.createProduct(product);
}
consumer的OrderController.java
/**
* 新增商品
*
* @param product
* @return
*/
@PostMapping("/save")
public Map<Object, Object> createProduct(Product product) {
return orderService.createProduct(product);
}
访问http://localhost:9090/order/save 参数
id:6
productName:华为
productNum:1
productPrice:6666666
执行结果
6.Feign 性能优化
1.Gzip 压缩
gzip 是一种数据格式,采用 deflate 算法压缩数据;gzip 是一种流行的文件压缩算法,应用十分广泛,尤其是在 Linux 平台。
2.gzip 能力
当 Gzip 压缩一个纯文本文件时,效果是非常明显的,大约可以减少 70% 以上的文件大小。
3.gzip 作用
网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是 Gzip 与搜索引擎的抓取工具有着更好的关系。例如 Google 就可以通过直接读取 gzip 文件来比普通手工抓取更快地检索网页。
4.配置HTTP 连接池
4.1HTTP背景
两台服务器建立 HTTP 连接的过程是很复杂的一个过程,涉及到多个数据包的交换,很耗时间。
HTTP 连接需要的 3 次握手 4 次挥手开销很大,这一开销对于大量的比较小的 HTTP 消息来说更大。
4.2解决方案
采用 HTTP 连接池,可以节约大量的 3 次握手 4 次挥手,这样能大大提升吞吐量。
Feign 的 HTTP 客户端支持 3 种框架:HttpURLConnection、HttpClient、OkHttp;默认是 HttpURLConnection。可以通过查看源码 org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration.java 得知。
传统的 HttpURLConnection 是 JDK 自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,如果有可用的其他方案,没有必要自己去管理连接对象。
HttpClient 相比传统 JDK 自带的 HttpURLConnection,它封装了访问 HTTP 的请求头,参数,内容体,响应等等;它不仅使客户端发送 HTTP 请求变得容易,而且也方便了开发人员测试接口(基于 HTTP 协议的),既提高了开发的效率,又提高了代码的健壮性;另外高并发大量的请求网络的时候,也是用“连接池”提升吞吐量。
添加依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.7.4</version>
</dependency>
配置开启连接池
feign:
httpclient:
enabled: true # 开启 httpclient
使用压测工具检测单线程 10000次请求网络响应情况
图1.未配置httpclient连接池.新建连接和关闭连接占用大量时间,端口被占用无法释放.导致后来的连接无法被创建,增加程序异常几率
图2.配置httpClient连接池开启,第一次交互会打开连接,交互结束后连接并不关闭,下次交互就省去了建立连接的过程
7.整合gateway网关服务
1.搭建gateway网关服务(略)
2.增加配置
server:
port: 8001 #网关端口
spring:
application:
name: nacos-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 #注册中心地址
config:
server-addr: localhost:8848 #配置中心地址 TODO配置中心读取不到 需要调查原因
file-extension: yaml
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: nacos-gateway-provider
uri: lb://service-consumer #目标服务地址
predicates: #断言
- Path=/**
3.测试8001端口转发是否正常
来源:https://blog.csdn.net/jsyandxys/article/details/114655432
猜你喜欢
- 图片的复制无非有两种方法,一种是图片直接上传到服务器,另外一种转换成二进制流的base64码目前限chrome浏览器使用首先以um-edit
- 分页问题是一个非常普遍的问题,开发者几乎都会遇到,这里不讨论具体如何分页,说明一下Web方式下分页的原理。首先是查询获得一个结果集(表现为查
- XmlTextWriter类允许你将XML写到一个文件中去。这个类包含了很多方法和属性,使用这些属性和方法可以使你更容易地处理XML。为了使
- String类型小数值转为Long类型数值分为小数和整数,当传入的类型为String,需要获取的类型为Long,这时候直接通过Long.va
- APP生命周期wpf项目目录中有一个App.xaml.cs文件,该文件中App是一个partical类,与之对应的另一partical部分在
- 最近经常在机房看同学在玩一个走迷宫的游戏,比较有趣,自己也用java写一个实现随机生成迷宫的算法,其实就是一个图的深度优先遍历算法.基本思想
- 前言异步调用几乎是处理高并发,解决性能问题常用的手段,如何开启异步调用?SpringBoot中提供了非常简单的方式,就是一个注解@Async
- 一、在pom.xml中配置jetty插件: <build> <plugins> <p
- 项目开发中,经常会遇到定时任务的场景,Spring提供了@Scheduled注解,方便进行定时任务的开发概述要使用@Scheduled注解,
- 引言备忘录模式经常可以遇到,譬如下面这些场景:浏览器回退:浏览器一般有浏览记录,当我们在一个网页上点击几次链接之后,可在左上角点击左箭头回退
- 一、连接数据库的配置单独放在一个properties文件中之前,我们是直接将数据库的连接配置信息写在了MyBatis的conf.xml文件中
- Mybatis与JPA的优缺点JPA java持久层API可理解为一种规范,Hibernate就是其具体一个实现。它的实现应用是Spring
- 本文实例讲述了C#设计模式之Facade外观模式解决天河城购物问题。分享给大家供大家参考,具体如下:一、理论定义外观模式 &nbs
- 很久之前也写过一篇使用Jitpack发布Android开源库的文章,详见Android发布项目到jitpack的完整步骤近来因为工作原因,又
- 这篇文章主要介绍了java实现上传文件类型检测过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 父类空间优先于子类对象产生在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含
- 1.背景Java语言相比于C和C++,一个最大的特点就是不需要程序员自己手动去申请和释放内存,这一切交由JVM来完成。在Java中,运行时的
- 概述从今天开始, 小白我将带大家开启 Java 数据结构 & 算法的新篇章.冒泡排序冒泡排序 (Bubble Sort) 是一种简单
- session对象用于在会话范围内,记录每个客户端的访问状态,以便于跟踪每个客户端的操作状态,在会话存储的信息,在浏览器发出后续请求时可以获
- (注意:本文基于JDK1.8)前言任何一个容器类对象用于持有元素后,总是需要遍历元素的,即挨个去访问每个元素1次,而遍历元素,除了常规的依赖