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

SpringCloud学习笔记-3

声明:笔记来源于网络,如有侵权联系删除

1 openfeign

1)openfeign远程调用声明式实现

1.启动类中添加注解 @EnableFeignClients

@EnableFeignClients
@SpringBootApplication
public class OrderMainApplication {public static void main(String[] args){SpringApplication.run(OrderMainApplication.class,args);}//项目启动时执行@BeanApplicationRunner applicationRunner(NacosConfigManager nacosConfigManager){return args->{System.out.println("=========");ConfigService configService = nacosConfigManager.getConfigService();configService.addListener("service-order.properties", "DEFAULT_GROUP", new Listener() {@Overridepublic Executor getExecutor() {return Executors.newFixedThreadPool(4);}@Overridepublic void receiveConfigInfo(String configInfo) {System.out.println("变化的信息为:"+configInfo);//模拟邮件发送System.out.println("邮件发送:"+configInfo+"变量发生变化请注意");}});};}
}

2.创建feignclient接口

 输入feign.ProductFeignClient

@FeignClient(value = "service-product")
public interface ProductFeignClient {@GetMapping("/product/{id}")//标注在controller里面是接收请求,标注在FeignClient上是发送请求Product getProductById(@PathVariable("id") Long id);}

 

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {@AutowiredDiscoveryClient discoveryClient;@AutowiredRestTemplate restTemplate;@AutowiredLoadBalancerClient loadBalancerClient;@AutowiredProductFeignClient productFeignClient;@Overridepublic Order createOrder(Long productId, Long userId) {//Product product = getProductFromRemoteWithLoadBalanceAnnotation(productId);//使用openfeignProduct product = productFeignClient.getProductById(productId);Order order = new Order();order.setId(1L);order.setAddress("京海");//金额order.setTotalAmount(product.getPrice().multiply(BigDecimal.valueOf(product.getNum())));order.setUserId(userId);order.setNickName("张三");order.setProductList(Arrays.asList(product));return order;}//普通的调用private Product getProductFromRemote(Long productId){//获取所有商品微服务实例所在的ip和端口号List<ServiceInstance> instanceList = discoveryClient.getInstances("service-product");//获取第一个实例ServiceInstance instance = instanceList.get(0);//拼接远程调用地址 http://localhost:9000/product/4String url = "http://"+instance.getHost()+":"+instance.getPort()+"/product/"+productId;//打印日志log.info("请求地址{}",url);//给远程发送请求Product pro = restTemplate.getForObject(url, Product.class);return pro;}//LoadBalancerClient实现负载均衡private Product getProductFromRemoteWithLoadBalancer(Long productId){ServiceInstance instance = loadBalancerClient.choose("service-product");//拼接远程调用地址 http://localhost:9000/product/4String url = "http://"+instance.getHost()+":"+instance.getPort()+"/product/"+productId;//打印日志log.info("请求地址{}",url);//给远程发送请求Product pro = restTemplate.getForObject(url, Product.class);return pro;}//注解实现负载均衡private Product getProductFromRemoteWithLoadBalanceAnnotation(Long productId){String url = "http://service-product/product/"+productId;//给远程发送请求 此时 restTemplate会动态替换url的值Product pro = restTemplate.getForObject(url, Product.class);return pro;}}

重启服务,然后浏览器输入http://localhost:8000/create?userId=5&productId=88,然后回车,之后再刷新2次,这样相当于调用了3次接口。

可以看到返回数据成功,并且product微服务3个实例每个实例都打印了hello,自动做到了负载均衡。

 2)openfeign远程调用,第三方api

这个只是简单讲解下

如果@FeignClient里面没有url这个参数,那么就是需要在注册中心找到接口的地址进行调用。上图中有url这个参数,所以不用去注册中心找,直接拼接PostMapping里面的后缀,然后调用三方接口了。这是个获取天气的接口,可以看到里面有token,Authorization,cityId等入参。这就是调用第三方api的方法,利用feign也可以实现

 3)openfeign远程调用小技巧

1.如果是调用自己写的微服务的接口,写feign接口的时候,直接复制controller里面的方法拿到feign接口里面,基本上是完全一样的。如下图:

可以看到基本一样,直接复制controller里面的方法放到feign接口里面就可以了。

2. 如果是第三方api

如上图,这个就需要看下三方接口文档了,按照上面这种格式进行编写就行,url拼接,参数按照三方接口文档的要求来传就行。 

3.面试题 服务端负载均衡和客户端负载均衡的区别。

 如上图第一个,发起调用的时候,需要通过注册中心获取地址,然后自己通过负载均衡选择具体调用地址调用,这就是客户端负载均衡。

第二个,直接调用固定的地址访问墨迹天气,墨迹天气对外暴漏一个固定地址,接收请求时,自己根据负载均衡选择调用自己的具体微服务,这就是被调用方(服务端)负载均衡

 4)openfeign日志配置

1.yml文件配置日志级别

logging:level:com.atguigu.order.feign: debug

注意包名要换成自己的feign所在的包哈

2. 创建logger的bean类,注意引入的包是feign下的

@Configuration
public class ProductServiceConfig {@BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}
}

 

重启服务,然后浏览器输入http://localhost:8000/create?userId=5&productId=88

可以看到打印了如下日志:

 5)openfeign超时控制

服务调用时,如果被调用方宕机,而调用方还在一直请求和等待响应时,如果一直有新的请求进来也是等待,那么就会造成资源耗尽,服务雪崩。因此需要有超时控制。

 如果未超时,返回正确结果。如果超时了,1.返回错误信息 2.返回兜底数据

这里超时就需要做出配置。

超时有两种情况:

1connectTimeout 连接超时 就是A向B请求建立连接,从开始请求到连接成功的时间,如上图1.建立连接的耗时。生活中的例子就是A给B打电话,从拨通号码到B接听,这段时间的耗时就是连接时间。

2readTimeout 读取超时 就是A发送请求给B后,B处理自己的业务,处理后把结果返回给A的耗时,也就是上图中B处理业务的耗时。生活中的例子就是A给B打电话,B接听后,A问:吃了没。B过了5S回答:吃了。那么这5S就是读取耗时。

 

 openFeign默认的连接超时时间是10秒,读取超时时间是60秒。

下面我们测试下这个时间。

我们改下product服务的接口处理时间是100秒

@Service
public class ProductServiceImpl implements ProductService {@Overridepublic Product getById(Long productId) {Product product = new Product();product.setId(productId);product.setPrice(new BigDecimal("99"));product.setProductName("苹果-"+productId);product.setNum(2);//设置休眠100秒try {TimeUnit.SECONDS.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}return product;}
}

重启服务,然后浏览器输入http://localhost:8000/create?userId=5&productId=88 

 可以看到浏览器一直转圈,60s左右后报错(因为我们设置的是处理返回结果需要100S,但是feign之间调用的默认超时设置的是60S)

下面我们来修改这个超时时间

 

输入 application-feign.yml 回车

里面配置如下代码:

spring:cloud:openfeign:client:config:default:logger-level: fullconnect-timeout: 3000read-timeout: 5000service-product:logger-level: fullconnect-timeout: 3000read-timeout: 5000

 default表示默认配置。service-product表示 product这个微服务特定的配置,如果找不到微服务特定配置,就按默认配置

application.yml中,添加包含feign.yml的配置 include:feign表示导入application-feign.yml这个配置

server:port: 8000
spring:profiles:active: devinclude: feignapplication:name: service-ordercloud:nacos:server-addr: 127.0.0.1:8848config:import-check:enabled: falsenamespace: ${spring.profiles.active:dev}
logging:level:com.atguigu.order.feign: debug
---
spring:config:import:- nacos:common.properties?group=order- nacos:database.properties?group=orderactivate:on-profile: dev
---
spring:config:import:- nacos:common.properties?group=order- nacos:database.properties?group=orderactivate:on-profile: test
---
spring:config:import:- nacos:common.properties?group=order- nacos:database.properties?group=orderactivate:on-profile: prod

 重启服务器后,再次刷新页面,http://localhost:8000/create?userId=5&productId=88

可以看到,只过了5S左右就返回错误信息了,表示超时时间配置成功。 

注意以上都是测试的readTimeout的配置。

 6)openfeign重试机制

上面超时失败了后,需要重新调用接口(总不能不返回正确的数据吧,需要再次尝试下),这就引入了重试的机制。

如上图,默认配置是NEVER_RETRY,就是从不重试。但是可以改下配置。上图中的100,指的是第一次重试间隔100毫秒,后面的1指的是重试的最大间隔是1秒,后面的5是指的重试5次。当第一次重试时是过了100毫秒,如果还是失败,第二次重试就是100乘以1.5,也是150毫秒,如果耗时失败,那第三次重试是100乘以1.5再乘以1.5,第四次就是100*1.5*1.5*1.5,依次类推,最多尝试5次,且如果这个乘机大于1秒(1000毫秒),那就按照1S算,也就是最大间隔是1秒。 

下面我们配置下这个参数

创建bean类

@Configuration
public class ProductServiceConfig {@LoadBalanced//注解式负载均衡@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}@BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}//重试机制@BeanRetryer retryer(){return new Retryer.Default(100,1,5);}
}

 修改下product,让其打印hello

@RestController
public class ProductController {@AutowiredProductService productService;//查询商品@GetMapping("/product/{id}")public Product getProduct(@PathVariable("id")Long productId){System.out.println("hello");Product product = productService.getById(productId);return product;}
}

重启2个服务,注意product只启动一个实例哈。然后浏览器发送http://localhost:8000/create?userId=5&productId=88

 可以看到浏览器一共等待了大概25秒(5秒*5次),控制台打印了5次hello

而请求端的错误日志也是5次(5次请求日志+1次抛出异常日志)

总结:最大请求次数,包含了最初的第一次。也就是一共最多请求5次。 

 7)openfeign拦截器

openfeign在请求之前和获取结果后都可以进行业务处理。响应拦截器用的较少,我们看下请求拦截器。

 创建拦截器类

 输入interceptor.XTokenRequestInterceptor

@Component
public class XTokenRequestInterceptor implements RequestInterceptor {/***请求拦截器* @param requestTemplate 请求模版*/@Overridepublic void apply(RequestTemplate requestTemplate) {System.out.println("XTokenRequestInterceptor   ... = " + requestTemplate);requestTemplate.header("X-Token", UUID.randomUUID().toString());}
}

上图中代码是生成一个随机的字符作为请求头X-Token 

 把商品的休眠100秒逻辑注释掉

@Service
public class ProductServiceImpl implements ProductService {@Overridepublic Product getById(Long productId) {Product product = new Product();product.setId(productId);product.setPrice(new BigDecimal("99"));product.setProductName("苹果-"+productId);product.setNum(2);//设置休眠100秒
/*        try {TimeUnit.SECONDS.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}*/return product;}
}

 修改商品的controller如下:打印接收到的token

@RestController
public class ProductController {@AutowiredProductService productService;//查询商品@GetMapping("/product/{id}")public Product getProduct(@PathVariable("id")Long productId, HttpServletRequest httpServletRequest){String header = httpServletRequest.getHeader("X-Token");System.out.println("hello token=【"+header+"】");Product product = productService.getById(productId);return product;}
}

重启两个微服务,然后调用http://localhost:8000/create?userId=5&productId=88

可以看到两个微服务都打印了token的值,且完全相等。说明拦截器生效了。

 8)openfeign Fallback:兜底返回

如上图,前面我们说过,如果被调用服务超时或者错误,那么2个情况,1:返回错误信息,2返回兜底数据。如果返回错误信息,那么可能就影响用户体验或者整个流程的继续往下流转,这时候我们更希望返回一个兜底数据,拿到数据后,我们进行兜底逻辑的处理,使得系统更加健壮。 

兜底返回的目的就是为了在远程调用失败的时候拿到一个默认数据,使得业务继续往下推进。

下面开始编写代码

在order微服务的feign包,鼠标右键,创建一个类,输入 fallbak.ProductFeignClientFallback,回车

@Component
public class ProductFeignClientFallback implements ProductFeignClient {@Overridepublic Product getProductById(Long id) {System.out.println("兜底回调。。。");Product product = new Product();product.setId(id);product.setPrice(new BigDecimal("0"));product.setProductName("未知商品");product.setNum(0);return product;}
}

同时在feignclient里面指定这个类

@FeignClient(value = "service-product",fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {@GetMapping("/product/{id}")//标注在controller里面是接收请求,标注在FeignClient上是发送请求Product getProductById(@PathVariable("id") Long id);}

 

这样就可以了。同时我们把重试机制去掉,便于观察兜底回调生效。

@Configuration
public class ProductServiceConfig {@LoadBalanced//注解式负载均衡@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}@BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}//重试机制
/*    @BeanRetryer retryer(){return new Retryer.Default(100,1,5);}*/
}

 此时还没完。如果需要兜底回调,还需要Sentinel。我们引入这个依赖

在order的pom中引入依赖,右上角刷新应用

 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency></dependencies>

 

 我们在feign.yml中把sentinel开启

spring:cloud:openfeign:client:config:default:logger-level: fullconnect-timeout: 3000read-timeout: 5000service-product:logger-level: fullconnect-timeout: 3000read-timeout: 5000
feign:sentinel:enabled: true

这时候准备就绪,重启order服务,同时停掉product服务。

页面输入http://localhost:8000/create?userId=5&productId=88回车

可以看到返回了配置的兜底商品数据(未知商品)。 

当我们把product服务启动时,再次刷新页面,可以看到如下图,又返回了正确数据:

http://www.xdnf.cn/news/897247.html

相关文章:

  • Linux命令基础(2)
  • 软件功能测试目的是啥?如何通过测试用例确保产品达标?
  • <2>-MySQL库的操作
  • Python 字典(dict)的高级用法与技巧
  • 跨平台游戏引擎 Axmol-2.6.1 发布
  • [论文阅读] 人工智能 | 利用负信号蒸馏:用REDI框架提升LLM推理能力
  • 使用vsftpd搭建FTP服务器(TLS/SSL显式加密)
  • 大模型与 NLP、Transformer 架构
  • vue3子组件获取并修改父组件的值
  • TTT讲师认证题目学习记录
  • C++算法训练营 Day10 栈与队列(1)
  • Java学习——正则表达式
  • PHP语言核心技术全景解析
  • 双碳时代,能源调度的难题正从“发电侧”转向“企业侧”
  • MySQL体系架构解析(二):MySQL目录与启动配置全解析
  • React从基础入门到高级实战:React 实战项目 - 项目三:实时聊天应用
  • Linux容器篇、第二章_01Ubuntu22 环境下 KubeSphere 容器平台高可用搭建全流程
  • 悲观锁和乐观锁
  • 数据库SQLite基础
  • 《完全背包》题集
  • 天机学堂(学习计划和进度)
  • TDengine 开发指南——无模式写入
  • vue-20(Vuex 状态管理的最佳实践)
  • 如何配置nginx解决前端跨域请求问题
  • Nuxt.js 中的路由配置详解
  • (转)什么是DockerCompose?它有什么作用?
  • Ubuntu 基于sdl 音频学习的基础代码
  • 市面上哪款AI开源软件做ppt最好?
  • wordpress+woocommerce电商平台搭建方案的优势分析
  • 1.3 古典概型和几何概型