当前位置: 首页 > 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.lryc.cn/news/585712.html

相关文章:

  • 学习日记-spring-day46-7.11
  • 伺服驱动控制CANopen协议
  • 网络编程(基本概念)
  • 【C++篇】二叉树进阶(上篇):二叉搜索树
  • TCP详解——流量控制、滑动窗口
  • mysql的性能优化:组提交、数据页复用、全表扫描优化、刷脏页
  • 【JMeter】调试方法
  • 论容器化 | 分析Go和Rust做医疗的后端服务
  • Express实现定时任务
  • 飞算科技正在撬动各行业数字化转型的深层变革
  • ch06 部分题目思路
  • OpenCV实现感知哈希(Perceptual Hash)算法的类cv::img_hash::PHash
  • 深入探究编程拷贝
  • 基于Java Spring Boot开发的旅游景区智能管理系统 计算机毕业设计源码32487
  • 4万亿英伟达,凭什么?
  • 【Linux应用】Ubuntu20.04 aarch64开发板一键安装ROS2(清华源)
  • PandaCoder重大产品更新-引入Jenkinsfile文件支持
  • mysql的LIMIT 用法
  • 【AI大模型】超越RAG的搜索革命!分层框架让AI像专家团队一样深度思考
  • Java教程:JavaWeb ---MySQL高级
  • 隆重介绍 Xget for Chrome:您的终极下载加速器
  • linux kernel struct regmap_config结构详解
  • 【Quest开发】快速添加可手指触摸按钮
  • 3 OneNET-调试器模拟上报数据
  • Visual Studio Code 的 settings.json 配置指南
  • HarmonyOS NEXT端云一体化开发初体验
  • 世俱杯直播数据源通过反汇编获取到
  • gradle中namespace和applicationId的区别
  • Ubuntu20.04运行openmvg和openmvs实现三维重建(未成功,仅供参考)
  • 【酶解法】小鼠脾脏单细胞悬液的制备指南