SpringCloud学习笔记-3

news2025/6/7 20:45:42

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

1 openfeign

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

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

@EnableFeignClients
@SpringBootApplication
public class OrderMainApplication {
    public static void main(String[] args){
        SpringApplication.run(OrderMainApplication.class,args);
    }
    //项目启动时执行
    @Bean
    ApplicationRunner applicationRunner(NacosConfigManager nacosConfigManager){
        return args->{
            System.out.println("=========");
            ConfigService configService = nacosConfigManager.getConfigService();
            configService.addListener("service-order.properties", "DEFAULT_GROUP", new Listener() {
                @Override
                public Executor getExecutor() {
                    return Executors.newFixedThreadPool(4);
                }

                @Override
                public 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 {
    @Autowired
    DiscoveryClient discoveryClient;
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    LoadBalancerClient loadBalancerClient;
    @Autowired
    ProductFeignClient productFeignClient;
    @Override
    public Order createOrder(Long productId, Long userId) {
        //Product product = getProductFromRemoteWithLoadBalanceAnnotation(productId);
        //使用openfeign
        Product 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/4
        String 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/4
        String 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 {


    @Bean
    Logger.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 {
    @Override
    public 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: full
            connect-timeout: 3000
            read-timeout: 5000
          service-product:
            logger-level: full
            connect-timeout: 3000
            read-timeout: 5000

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

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

server:
  port: 8000
spring:
  profiles:
    active: dev
    include: feign
  application:
    name: service-order
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      config:
        import-check:
          enabled: false
        namespace: ${spring.profiles.active:dev}
logging:
  level:
    com.atguigu.order.feign: debug
---
spring:
  config:
    import:
      - nacos:common.properties?group=order
      - nacos:database.properties?group=order
    activate:
      on-profile: dev
---
spring:
  config:
    import:
      - nacos:common.properties?group=order
      - nacos:database.properties?group=order
    activate:
      on-profile: test
---
spring:
  config:
    import:
      - nacos:common.properties?group=order
      - nacos:database.properties?group=order
    activate:
      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//注解式负载均衡
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
    //重试机制
    @Bean
    Retryer retryer(){
        return new Retryer.Default(100,1,5);
    }
}

 修改下product,让其打印hello

@RestController
public class ProductController {
    @Autowired
    ProductService 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 请求模版
     */
    @Override
    public 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 {
    @Override
    public 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 {
    @Autowired
    ProductService 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 {
    @Override
    public 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//注解式负载均衡
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
    //重试机制
/*    @Bean
    Retryer 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: full
            connect-timeout: 3000
            read-timeout: 5000
          service-product:
            logger-level: full
            connect-timeout: 3000
            read-timeout: 5000
feign:
  sentinel:
    enabled: true

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2403317.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BugKu Web渗透之eval

启动场景&#xff0c;打开网页&#xff0c;显示的是一段代码。 步骤一&#xff1a; 分析代码。 代码大概意思是&#xff1a; <?php//包含"flag.php"的文件include "flag.php"; //获取网页请求的hello数据$a $_REQUEST[hello]; //显示变量a的详…

DAY45 可视化

DAY 45 Tensorborad 之前的内容中&#xff0c;我们在神经网络训练中&#xff0c;为了帮助自己理解&#xff0c;借用了很多的组件&#xff0c;比如训练进度条、可视化的loss下降曲线、权重分布图&#xff0c;运行结束后还可以查看单张图的推理效果。 如果现在有一个交互工具可…

11.RV1126-ROCKX项目 API和人脸检测画框

一.ROCKX的API 1.ROCKX的作用 ROCKX的AI组件可以快速搭建 AI的应用&#xff0c;这些应用可以是车牌识别、人脸识别、目标识别&#xff0c;人体骨骼识别等等。主要用于各种检测识别。例如下图&#xff1a; 2.ROCKX人脸识别的API rockx_ret_t rockx_create(rockx_handle_t *han…

超构光学与 AR 的深度融合 | 攻克 VAC 与眼动范围难题

原文信息 原文标题&#xff1a;“Three-dimensional varifocal meta-device for augmented reality display” 第一作者&#xff1a;宋昱舟&#xff0c;袁家琪&#xff0c;陳欽杪&#xff0c;刘小源 &#xff0c;周寅&#xff0c;程家洛&#xff0c;肖淑敏*&#xff0c;陈沐…

[ Qt ] | 与系统相关的操作(三):QFile介绍和使用

目录 之前的操作文件的方式 Qt中的文件操作简介 QFile 打开 读 写 关闭 一个例子来说明 QFileInfo 之前的操作文件的方式 C语言中&#xff0c;fopen 打开文件&#xff0c;fread fwrite 读写文件&#xff0c;fclose 关闭文件。 C中&#xff0c;fstream 打开文件&…

软件工程:如何做好软件产品

1、什么是产品 从项目到产品 产品&#xff1a;满足行业共性需求的标准产品。即要能够做到配置化的开发&#xff0c;用同一款产品最大限度地满足不同客户的需求&#xff0c;同时让产品具有可以快速响应客户需求变化的能力。 好的产品一定吸收了多个项目的共性&#xff0c;一定是…

蓝桥杯 省赛 2025python(B组)题目(分析)

目录 第一题 为什么答案是103而不是104&#xff1f; 第二题 为什么必须按长度排序&#xff1f; 第三题 易错点总结 第四题 逻辑问题&#xff1a; 可能超过时间复杂度的代码示例 1. 暴力枚举所有可能的子串 2. 递归回溯 第五题 1. 暴力枚举法 2. 优化枚举 3.数…

React - 组件通信

组件通信 概念&#xff1a;组件通信就是组件之间数据传递&#xff0c;根据组件嵌套关系不同&#xff0c;有不同的通信方法 父传子 —— 基础实现 实现步骤 父组件传递数据 - 在子组件标签上绑定属性子组件接收数据 - 子组件通过props参数接收数据 声明子组件并使用 //声明子…

飞牛使用Docker部署Tailscale 内网穿透教程

之前发过使用docker部署Tailscale的教程&#xff0c;不过是一年前的事情了&#xff0c;今天再重新发表一遍&#xff0c;这次使用compose部署更加方便&#xff0c;教程也会更加详细一点&#xff0c;希望对有需要的朋友有所帮助&#xff01; 对于大部分用户来说&#xff0c;白嫖 …

《数据挖掘》- 房价数据分析

这里写目录标题 采用的技术1. Python编程语言2. 网络爬虫库技术点对比与区别项目技术栈的协同工作流程 代码解析1. 导入头文件2. 读取原始数据3. 清洗数据4. 数据分割4.1 统计房屋信息的分段数量4.2 将房屋信息拆分为独立列4.3 处理面积字段4.4 删除原始房屋信息列 5. 可视化分…

C++之动态数组vector

Vector 一、什么是 std::vector&#xff1f;二、std::vector 的基本特性&#xff08;一&#xff09;动态扩展&#xff08;二&#xff09;随机访问&#xff08;三&#xff09;内存管理 三、std::vector 的基本操作&#xff08;一&#xff09;定义和初始化&#xff08;二&#xf…

【Zephyr 系列 11】使用 NVS 实现 BLE 参数持久化:掉电不丢配置,开机自动加载

🧠关键词:Zephyr、NVS、非易失存储、掉电保持、Flash、AT命令保存、配置管理 📌目标读者:希望在 BLE 模块中实现掉电不丢配置、支持产测参数注入与自动加载功能的开发者 📊文章长度:约 5200 字 🔍 为什么要使用 NVS? 在实际产品中,我们经常面临以下场景: 用户或…

【Android】Android Studio项目代码异常错乱问题处理(2020.3版本)

问题 项目打开之后&#xff0c;发现项目文件直接乱码&#xff0c; 这样子的 这本来是个Java文件&#xff0c;结果一打开变成了这种情况&#xff0c;跟见鬼一样&#xff0c;而且还不是这一个文件这样&#xff0c;基本上一个项目里面一大半都是这样的问题。 处理方法 此时遇到…

一些免费的大A数据接口库

文章目录 一、Python开源库&#xff08;适合开发者&#xff09;1. AkShare2. Tushare3. Baostock 二、公开API接口&#xff08;适合快速调用&#xff09;1. 新浪财经API2. 腾讯证券接口3. 雅虎财经API 三、第三方数据平台&#xff08;含免费额度&#xff09;1. 必盈数据2. 聚合…

机器学习算法时间复杂度解析:为什么它如此重要?

时间复杂度的重要性 虽然scikit-learn等库让机器学习算法的实现变得异常简单&#xff08;通常只需2-3行代码&#xff09;&#xff0c;但这种便利性往往导致使用者忽视两个关键方面&#xff1a; 算法核心原理的理解缺失 忽视算法的数据适用条件 典型算法的时间复杂度陷阱 SV…

SSIM、PSNR、LPIPS、MUSIQ、NRQM、NIQE 六个图像质量评估指标

评价指标 1. SSIM&#xff08;Structural Similarity Index&#xff09; &#x1f4cc; 定义 结构相似性指数&#xff08;Structural Similarality Index&#xff09;是一种衡量两幅图像相似性的指标&#xff0c;考虑了亮度、对比度和结构信息的相似性&#xff0c;比传统的 P…

【笔记】旧版MSYS2 环境中 Rust 升级问题及解决过程

下面是一份针对在旧版 MSYS2&#xff08;安装在 D 盘&#xff09;中&#xff0c;基于 Python 3.11 的 Poetry 虚拟环境下升级 Rust 的处理过程笔记&#xff08;适用于 WIN 系统 SUNA 人工智能代理开源项目部署要求&#xff09;的记录。 MSYS2 旧版环境中 Rust 升级问题及解决过…

矩阵QR分解

1 orthonormal 向量与 Orthogonal 矩阵 orthonormal 向量定义为 &#xff0c;任意向量 相互垂直&#xff0c;且模长为1&#xff1b; 如果将 orthonormal 向量按列组织成矩阵&#xff0c;矩阵为 Orthogonal 矩阵&#xff0c;满足如下性质&#xff1a; &#xff1b; 当为方阵时&…

【MATLAB去噪算法】基于CEEMDAN联合小波阈值去噪算法(第四期)

CEEMDAN联合小波阈值去噪算法相关文献 一、EMD 与 EEMD 的局限性 &#xff08;1&#xff09;EMD (经验模态分解) 旨在自适应地将非线性、非平稳信号分解成一系列 本征模态函数 (IMFs)&#xff0c;这些 IMFs 从高频到低频排列。 核心问题&#xff1a;模态混合 (Mode Mixing) 同…

从理论崩塌到新路径:捷克科学院APL Photonics论文重构涡旋光技术边界

理论预言 vs 实验挑战 光子轨道角动量&#xff08;Orbital Angular Momentum, OAM&#xff09;作为光场调控的新维度&#xff0c;曾被理论预言可突破传统拉曼散射的对称性限制——尤其是通过涡旋光&#xff08;如拉盖尔高斯光束&#xff09;激发晶体中常规手段无法探测的"…