当前位置: 首页 > news >正文

SpringCloud【OpenFeign】

 相关官方文档:Spring Cloud OpenFeign Features :: Spring Cloud Openfeign

1,远程调用

1.1,声明式实现--调用自己的服务

注解驱动:

开启远程调用:@EnableFeignClients

指定远程地址:@FeignClient

指定请求方式:@GetMapping、@PostMapping、@DeleteMapping...

指定携带数据:@RequestHeader、@RequestParam、@RequestBody...

指定结果返回:响应模式

导入依赖:services模块下导入

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

开启Feign:启动类加注解

package org.example.order;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@EnableFeignClients//开启feign远程调用功能
@EnableDiscoveryClient
@SpringBootApplication
public class OrderMainApplication {public static void main(String[] args) {SpringApplication.run(OrderMainApplication.class, args);}
}

编写远程调用客户端:新建feign接口org.example.order.feign.ProductFeignClient

package org.example.order.feign;import org.example.producet.domain.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;//需要调用的服务的名字
@FeignClient(value = "services-product")
public interface ProductFeignClient {/*** mvc注解的两个使用逻辑* @GetMapping如果是在controller注解下,代表接受对应get请求,@PathVariable代表接受对应参数,接受请求时,把{id}拿过来封装给id* @GetMapping如果是在FeignClient注解下,代表发送对应get请求,@PathVariable代表发送对应参数,谁调用这个方法谁传递id进来,放到{id}** 返回Product  把services-product返回的json字符串转Product*/@GetMapping("/getProductById/{id}")Product getProductById(@PathVariable("id") Long id);}

修改调用接口:orderServiceImlp

@Slf4j
@Service
public class OrderServiceImpl implements OrderService { @AutowiredProductFeignClient productFeignClient;@Overridepublic Order createOrder(Long userId, Long productId) {
//        Product product = getProductByRemoteWithLoadBalancerByAnnotatin(productId);Product product = productFeignClient.getProductById(productId);Order order = new Order();order.setId(1l);//todo  远程调用order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));order.setUserId(userId);order.setNickName("爱学习");order.setAddress("北京");//todo  远程调用order.setProductList(Arrays.asList(product));return order;}
}

测试发现openFeign自带负载均衡 ,如果开启了多个product服务,会负载均衡调用

 1.2,第三方API--调用第三方服务

编写远程调用

/*** 测试直接远程调用三方api*  value :此时没有注册中心,就随意命名* url:三方api地址*/
@FeignClient(value = "test",url = "https://www.baidu.com")
public interface EduTestFeignClient {//根据三方api接口文档,的地址和参数编写@PostMapping("/getInfo")String pageQueryInfo(@RequestParam("key")String key,@RequestParam("signature")String signature,@RequestBody RequestBodyVo requestBodyVo);
}

 实现调用

@RestController
@RequestMapping("/testFeign")
public class TestClient {@Autowiredprivate testFeignClient testFeignClient;@GetMapping("/getInfo")public String getInfo(){RequestBodyVo requestBodyVo = new RequestBodyVo();requestBodyVo.setOrgId("123");String str = testFeignClient.pageQueryInfo("eduZHJ17J0ki9d4rXWb","srUAnLFEw2YP+RnB3PBVNsveJr35BuncUISX5x2rxZk=",requestBodyVo);System.out.println(str);return str;}
}

 思考:(面试题)

客户端负载均衡和服务端负载均衡的区别?

客户端负载均衡:发起调用方,根据自己的负载均衡算法,选择一个对方服务,发起调用

客户端发起请求---注册中心获取所有地址---客户端选择一个--发起调用

服务端负载均衡:客户端只管发,服务端固定接口收到请求后,负载均衡分给自己的服务

客户端发起请求---服务端开启多个,但只对外暴露一个固定接口---请求过来之后,自己选择分给哪个服务--发起调用

2,进阶用法

2.1,日志

1,添加日志级别:yml添加

#日志级别
logging:level:org.example.order.feign: debug

2,添加日志全记录组件

package org.example.order.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import feign.Logger;@Configuration
public class OrderConfig {/*** 日志全记录组件* @return*/@BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}
}

3,测试日志

3.1,TestController

    @AutowiredApiFeignClient apiFeignClient;@GetMapping("/getApiInfo")public String getApiInfo(){return apiFeignClient.organization("cxxxxxxxxx");}

3.2,ApiFeignClient

package org.example.order.feign;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;//需要调用的服务的名字
@FeignClient(value = "test", url = "https://devops.aliyun.com")
public interface ApiFeignClient {@GetMapping("/organization/{OrganizationId}")String organization(@PathVariable("OrganizationId") String OrganizationId);}

3.3,日志显示

2.2,超时配置

场景:A通过OpenFeign调用B,B宕机或者响应非常非常慢,则会导致A一直连接不上B或者连接上了一个读取不到数据,如果A一直等待B 的返回就会出现服务雪崩问题,(如果有服务调用A也会一直无法得到返回,相当于整条链路都卡死)

解决:限时等待,如果为超时,就正常返回,如果超时,就中断调用,此时有两种方式,一种就是直接返回错误信息,另一种就是返回兜底数据,兜底数据设置见后续笔记

超时时间

连接超时:connectTimeout(默认10s)

读取超时:readTimeout(默认60s)

此时我们只需要在商品服务加一个休眠时间,订单服务超过默认时间就会报错返回

@Service
public class ProduecServiceImpl implements ProductService {@Overridepublic Product getProductById(Long productId) {Product product = new Product();product.setId(productId);product.setPrice(new BigDecimal(10));product.setProducetName("苹果"+productId);product.setNum(3);try {TimeUnit.SECONDS.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}return product;}
}

如何设置超时时间

可以在原application,yml中添加配置,也可以新建yml,引入原application.yml,此处新建

 application-feign.yml

spring:cloud:openfeign:client:config:#默认配置,如果有多个服务的时候,没有特别配置都使用默认配置default:#日志记录logger-level: full#连接超时时间 1sconnect-timeout: 1000#读取时间 2sread-timeout: 2000#客户端名字 ,没有像这样单独指定的,都用上面默认配置services-product:#日志记录logger-level: full#连接超时时间 3sconnect-timeout: 3000#读取时间 5sread-timeout: 5000

application,yml中引入  application-feign.yml

spring:profiles:include: feign

2.3,重试机制

场景:远程调用失败,还可以尝试多次调用,如果某次成功则返回成功,如果多次调用依然失败,则结束调用,返回错误

重试策略

间隔时间,最大间隔时间,最大尝试次数

比如间隔时间100ms,最大间隔时间1s,最大尝试次数5

第一次重复:100ms之后再次调用

第二次重复:100 x 1.5  ms之后调用

第三次重复:100 x 1.5 x 1.5 ms之后调用

以此类推..........要么达到最大间隔时间1s之后,就不再x1.5,而是直接按最大间隔时间 ,每秒重复一次,要么达到最大次数5次之后不再调用

此时可在orderConfig类里面添加配置

@Configuration
public class OrderConfig {/*** 重试器*/@BeanRetryer retryer(){return new Retryer.Default();}/*** Retryer.Default()默认参数如下:间隔100ms,最大间隔1s,最大重复5次* public Default() {*             this(100L, TimeUnit.SECONDS.toMillis(1L), 5);*         }* 如果需要自定义参数值:可以传参*     @Bean*     Retryer retryer(){*         return new Retryer.Default(200,2,5);*     }*/}

2.4,拦截器

请求拦截器:给远程服务发送请求之前,请求拦截器做最后一次拦截,可以对请求做定制化修改(比如给请求投添加业务字段),然后把请求发给远程请求

响应拦截器:远程服务处理完请求之后,把响应数据交给openFeign,openFeign使用响应拦截器,对响应数据进行预处理,处理完之后,把结果返回给调用服务

请求拦截器测试用例:

编写拦截器interceptor.XTokenRequestInterceptor.java

package org.example.order.interceptor;import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;import java.util.UUID;//方式2,在拦截器类上加注解
@Component
public class XTokenRequestInterceptor implements RequestInterceptor {/*** 请求拦截器* @param requestTemplate   请求模板*/@Overridepublic void apply(RequestTemplate requestTemplate) {System.out.println("XTokenRequestInterceptor");//给请求头上加一个XTokenrequestTemplate.header("X-Token", UUID.randomUUID().toString());}
}

 方式1,在application-feign.yml里面加配置,这种配置,仅对这个客户端有效

spring:cloud:openfeign:client:config:services-product:######添加拦截器配置,request-interceptors:- org.example.order.interceptor.XTokenRequestInterceptor

方法2,在拦截器类上加注解,openfeign官网有详细,会在容器中找对应一些组件,其中就有请求拦截器,就会自动应用

//方式2,在拦截器类上加注解
@Component
public class XTokenRequestInterceptor implements RequestInterceptor {/*** 请求拦截器* @param requestTemplate   请求模板*/@Overridepublic void apply(RequestTemplate requestTemplate) {System.out.println("XTokenRequestInterceptor");//给请求头上加一个XTokenrequestTemplate.header("X-Token", UUID.randomUUID().toString());}
}

拦截器写好之后,也生效之后,测试获取请求头内容

在商品的controller中添加获取

package org.example.product.controller;@RestController
public class ProductController {@AutowiredProductService productService;@GetMapping("/getProductById/{productId}")public Product getProductById(@PathVariable("productId") Long productId, HttpServletRequest request) {String header = request.getHeader("X-Token");System.out.println("product==="+header);Product product = productService.getProductById(productId);return product;}
}

请求调用之后,即可在商品的控制台查看header

2.4,兜底返回(fallback)

场景:远程调用时,调用超时或者调用错误,会返回错误信息,当不需要返回错误时,加一个返回兜底数据(默认数据,或者假数据,不影响后续业务),不报错,增加用户体验

首先编写一个兜底返回org.example.order.feign.fallback.ProductFeignClientFallback 

实现ProductFeignClient 接口

package org.example.order.feign.fallback;
import java.math.BigDecimal;import org.example.order.feign.ProductFeignClient;
import org.example.producet.domain.Product;
import org.springframework.stereotype.Component;@Component
public class ProductFeignClientFallback implements ProductFeignClient {@Overridepublic Product getProductById(Long id) {Product product = new Product();product.setId(id);product.setPrice(new BigDecimal("0"));product.setProducetName("兜底回调的数据");product.setNum(0);return product;}
}

在ProductFeignClient 接口中添加fallback = ProductFeignClientFallback.class,这时,在远程调用成功则不管,如果调用失败,则会调用fallback,返回ProductFeignClientFallback里面编写的默认数据

package org.example.order.feign;import org.example.order.feign.fallback.ProductFeignClientFallback;
import org.example.producet.domain.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;//需要调用的服务的名字
@FeignClient(value = "services-product",fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {@GetMapping("/getProductById/{id}")Product getProductById(@PathVariable("id") Long id);}

此功能需要配合sentinel使用,所以此时需要导入sentinel的包,order--pom.xml

     <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>

开启配置,在application-feign.yml配置文件添加,开启sentinel熔断功能

feign:sentinel:enabled: true

然后重启服务器,先测试连通,再关闭商品服务,测试无法连通商品服务的情况下,就会返回默认兜底数据

http://www.dtcms.com/a/274328.html

相关文章:

  • 数据治理(管理)能力评估——解读2024数据治理与数据管理能力成熟度评估模型【附全文阅读】
  • 10款主流报销管理平台对比及推荐
  • Linux操作系统之进程间通信:命名管道
  • Linux编程练习题1:打印图形
  • python学习DataFrame数据结构
  • 制作一款打飞机游戏79:道具拾取系统
  • c++设计模式:简单工厂模式
  • C++STL-list
  • 游戏的程序员会不会偷偷改自己账号的数据?
  • 线性回归的从零开始实现(详解部分疑问)
  • 【三】ObservableCollection 与 List 的区别
  • RK3566/RK3568 Android11 CAN开发(内核配置+测试验证+安卓app开发)
  • 2025 年第十五届 APMCM 亚太地区大学生数学建模竞赛C题 基于Quantum Boosting的二分类模型问题
  • 5G标准学习笔记15 --CSI-RS测量
  • 【龙泽科技】新能源汽车维护与动力蓄电池检测仿真教学软件【吉利几何G6】
  • 深入理解C语言内存空间、函数指针(三)(重点是函数指针)
  • Redis 主从复制及哨兵模式模拟部署
  • 3.检查函数 if (!CheckStart()) return 的妙用 C#例子
  • PBR渲染
  • 【网络安全】理解安全事件的“三分法”流程:应对警报的第一道防线
  • leaflet【十二】自定义图层——海量数据加载
  • 安全监测预警平台的应用场景
  • 机器学习数据集加载全攻略:从本地到网络
  • Git Submodule 介绍和使用指南
  • FS820R08A6P2LB——英飞凌高性能IGBT模块,驱动高效能源未来!
  • Vscode 下载远程服务器失败解决方法
  • Jenkins 版本升级与插件问题深度复盘:从 2.443 到 2.504.3 及功能恢复全解析
  • 和鲸社区深度学习基础训练营2025年关卡2(3)pytorch
  • 限流算法
  • GT IP核仿真测试