微服务系列二:跨微服务请求优化,注册中心+OpenFeign

news2024/11/24 10:53:18

目录

前言

一、纯 RestTemplate 方案存在的缺陷

二、注册中心模式介绍

三、注册中心技术:Nacos

3.1 Docker部署Nacos

3.2 服务注册

3.3 服务发现 

四、代码优化:OpenFeign工具

4.1 OpenFeign快速入门

4.2 连接池的必要性

4.3 抽取服务、最佳实践

4.4 日志配置

五、服务注册与调用巩固


前言

前面通过微服务基础入门,我们大致了解的微服务的拆分。并且发现了跨微服务的请求调用问题。当时我们使用RestTemplate进行请求发送。也发现了一个比较大的问题——请求的url需要开发者人为提供,这种硬编码的方式无论是在什么项目里都应该被避免。更何况如果一个微服务分布在好几台服务器上,我们又该如何做负载均衡呢?因此本篇主要针对跨微服务的优化问题提出解决方案的学习。

一、纯 RestTemplate 方案存在的缺陷

  • item-service这么多实例,cart-service如何知道每一个实例的地址

  • http请求要写url地址,cart-service服务到底该调用哪个实例呢

  • 如果在运行过程中,某一个item-service实例宕机,cart-service依然在调用该怎么办

  • 如果并发太高,item-service临时多部署了N台实例,cart-service如何知道新实例的地址

因此,对于新方案,必须要有以下几个优势:

1. 只需关注有无实例有该功能调用?无需关注调用实例的地址

2. 拥有负载均衡策略,可以在多实例间自动进行策略切换

3. 自动监控实例健康状态,及时切断异常实例的连接

4. 允许实例动态变化,何时注册何时即可投入使用。

二、注册中心模式介绍

所谓注册中心模式可以理解为 “中介模式”,拿房屋中介来举例子吧:

房东【服务提供者】只需要把自己的房屋信息告诉(注册)中介,不需要自己去找租客。

租客【服务消费者】只需要到房屋中介处寻找(调用)自己需要的房屋,不需要满大街找房东。

而中介负责整合房屋资源(注册服务列表),同时在租客寻找的时候提供对应的房源(提供调用)

注意到我上面举例的用词了吧,在微服务中也是一样的。在微服务远程调用的过程中,包括两个角色:

  • 服务提供者:提供接口供其它微服务访问,比如item-service

  • 服务消费者:调用其它微服务提供的接口,比如cart-service

注册中心模式的整体流程如下:

  • 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心

  • 调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署)

  • 调用者自己对实例列表负载均衡,挑选一个实例

  • 调用者向该实例发起远程调用

如何实现宕机实例、异常实例的检测?

  • 引入心跳检测机制【类似Redis的哨兵机制】
  • 所有注册到中心的实例,每隔一段时间必须向中心发送信号,证实自己是健康状态
  • 当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除

如何实现宕机通知、新添实例通知?

  • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表

  • 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表。

如何实现负载均衡策略?

  • 成功获取到实例列表后,调用者可以根据一定的策略(随机、轮询等)挑选任意一个实例发送请求

三、注册中心技术:Nacos

目前开源的注册中心框架有很多,国内比较常见的有:

  • Eureka:Netflix公司出品,目前被集成在SpringCloud当中,一般用于Java应用

  • Nacos:Alibaba公司出品,目前被集成在SpringCloudAlibaba中,一般用于Java应用

  • Consul:HashiCorp公司出品,目前集成在SpringCloud中,不限制微服务语言

其中Nacos上手快、配置简单、并且有详细的中文文档提供学习。因此本次实验采取Nacos进行。

Nacos 快速开始icon-default.png?t=O83Ahttps://nacos.io/zh-cn/docs/quick-start.html

3.1 Docker部署Nacos

部署步骤

  • 导入nacos数据库文件
  • 修改nacos配置文件,并上传到服务器
  • 执行容器创建命令
  • 确保启动顺序:必须先启动数据库,再启动nacos
  •  测试访问http://192.168.186.140:8848/nacos/ 账号密码均为 nacos

首先Nacos需要管理服务列表,必然是依赖数据库的。本次实验采取先前Docker布置好的MySQL。

第一步: 导入nacos数据库文件

第二步:修改nacos配置文件,并上传到服务器

第三步: 执行容器创建命令

8848 是用于客户端与服务通信的主要端口。

9848 是 gRPC 端口,用于与 Nacos 的 gRPC 通信(如果需要)。

9849 是 Raft 端口,用于 Nacos 集群中节点之间的通信(如果运行集群模式)。

docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim

第四步:确保启动顺序:必须先启动数据库,再启动nacos

第五步:测试访问http://192.168.186.140:8848/nacos/ 账号密码均为 nacos

3.2 服务注册

  • 导入坐标依赖
  • 配置nacos的访问地址和服务名称
  • 使用nacos

3.2.1 导入坐标依赖

在需要注册服务的模块中,导入nacos的坐标

3.2.2 配置nacos的访问地址和服务名称

3.2.3 使用nacos

将服务注册到nacos,我们拿item-service为例,启动多个实例,模拟多服务器部署:

启动后查看nacos网站--服务列表

测试服务宕机后,nacos服务列表是否会更新:

3.3 服务发现 

  • 导入坐标依赖
  • 配置nacos的访问地址和服务名称
  • 发现并调用服务

3.3.1 导入坐标依赖

我们在cart-service中的pom.xml中添加nacos的依赖:

<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

可以发现,这里Nacos的依赖于服务注册时一致,这个依赖中同时包含了服务注册和发现的功能。因为任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者。

因此,等一会儿cart-service启动,同样会注册到Nacos

3.3.2 配置nacos的访问地址和服务名称

cart-serviceapplication.yml中添加nacos地址配置:

spring:
  application:
    name: cart-service
  cloud:
    nacos:
      server-addr: 192.168.186.140:8848

3.3.3 发现并调用服务

到此为止,我们还需要准备负载均衡策略。以最简单的随机策略为例。

为了能够发现服务,获取实例列表,这里还需要使用SpringCloud提供的服务发现工具:DiscoveryClient

该工具被SpringCloud自动注入装配,我们只需要注入就可以使用,我们利用它修改我们原先的代码逻辑:

RestTemplase实现代码:

使用DiscoveryClient工具 + Nacos后代码

3.3.4 发现服务测试

3.3.5 完整代码

 // 注入服务发现工具
    @Resource
    private DiscoveryClient discoveryClient;


    /**
     * DiscoveryClient
     * @param vos
     */
    private void handleCartItems(List<CartVO> vos) {
        // 1.获取商品id
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
        // 2.查询商品
        // 2.1 发现item-service服务的请求实例
        List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
        if(CollUtils.isEmpty(instances)) {
            throw new BizIllegalException("商品服务不可用");
        }
        //2.2 负载均衡选择一个实例
        ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));

        //2.3 构建请求、发送请求
        ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
                instance.getUri() + "/items?ids={ids}", // 请求路径
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<ItemDTO>>() {},
                Map.of("ids", CollUtil.join(itemIds, ","))
        );

        //2.4 解析响应对象
        if(!response.getStatusCode().is2xxSuccessful()) {
            // 查询失败
            return;
        }
        // 2.5 获取查询商品对象
        List<ItemDTO> items = response.getBody();

        // 3.构建商品id与商品对象的映射
        Map<Long,ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));

        //4. 写入VO对象返回前端
        for(CartVO v : vos) {
            ItemDTO item = itemMap.get(v.getItemId());
            if(item == null) {
                continue;
            }
            v.setNewPrice(item.getPrice()); // 最新价格
            v.setStock(item.getStock()); // 库存
            v.setStatus(item.getStatus()); // 状态
        }
    }

四、代码优化:OpenFeign工具

到这里我们已经解决了跨微服务请求的难题了,是不是挺简单的。确实,SpringCloud给我们提供了太多好用的工具了,使用DiscoveryClient + Nacos + RestTemplate解决了这个问题。

但是回看我们写的完整代码。是不是感觉有些复杂啊,想要构建一个简单的请求。我们先是去寻找服务,接着手写负载均衡选择实例、然后才是利用RestTemplate构建请求......

如何能够优化项目代码,减少开发者的工作量呢?这一节我们使用另一个工具——OpenFegin来解决这个问题。

4.1 OpenFeign快速入门

以cart-service中的查询我的购物车为例。因此下面的操作都是在cart-service中进行。

使用步骤

  • 引入OpenFeign依赖 和 loadBalancer负载均衡依赖
  • 启动类下添加 @EnableFeignClients 依赖 启动 OpenFeign服务
  • 编写client接口,用于实现请求发送(这一步就跟你编写业务controller很像很像)
  • 实现类中注入client接口
  • 使用client接口中的方法发送请求

4.1.1 引入依赖

        <!--openFeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--负载均衡器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

4.1.2 添加启动注解

在cart-service启动类下添加@EnableFeignClients 依赖 启动 OpenFeign服务

4.1.3 编写client接口

无需编写实现类,SpringCloud帮我们动态生成

4.1.4 实现类注入client接口

4.1.5 使用client定义的接口方法

你看看,现在的代码是不是简单暴了。完全不需要再手动找服务、手动完成负载均衡、手动编写restTemplate发送请求了。

    /**
     * OpenFeign实现
     * @param vos
     */
    private void handleCartItems(List<CartVO> vos) {
        // 1.获取商品id
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
        // 2.查询商品
        List<ItemDTO> items = itemClient.queryItemByIds(itemIds); // openfeign调用

        // 3.构建商品id与商品对象的映射
        Map<Long,ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));

        //4. 写入VO对象返回前端
        for(CartVO v : vos) {
            ItemDTO item = itemMap.get(v.getItemId());
            if(item == null) {
                continue;
            }
            v.setNewPrice(item.getPrice()); // 最新价格
            v.setStock(item.getStock()); // 库存
            v.setStatus(item.getStatus()); // 状态
        }
    }

4.1.6 测试代码

4.2 连接池的必要性

4.2.1 HTTP连接 与 HTTP消息

目前我们已经将代码优化得非常好了。理论上日常开发做到这块就可以了。但如如何还要想优化的话,接下来我们需要考虑的就是开销方面的问题了。我们回忆一下,现在我们每次需要发送跨端请求时,都需要先建立服务器之间的连接,然后才会去发送http消息。但是:

两台服务器建立HTTP连接的过程复杂且耗时,特别是其中的3次握手和4次分手过程产生的开销,对于传输大量较小的HTTP消息来说,这种开销显得尤为显著。

于是乎为了减少建立HTTP连接的大开销,我们需要建立HTTP连接池。

4.2.2 HTTP客户端技术选型

主要用到的HTTP客户端技术包括以下三种,其中第一种是OpenFeign默认的底层实现:

  • HttpURLConnection:默认实现,不支持连接池

  • Apache HttpClient :支持连接池

  • OKHttp:支持连接池

由于HttpURLConnection不支持连接池,所有我们得更改其他的HTTP客户端技术,本次实验选取OKHttp。

HTTP客户端技术补充说明

HttpURLConnection

  • 概述

    HttpURLConnection是Java标准库中的一部分,用于发送HTTP请求和接收HTTP响应。它提供了一组简单的方法来发送HTTP请求和处理响应,使开发人员能够轻松地与服务器进行通信。

  • 特点

    • 简单易用:HttpURLConnection提供了直观的API,使得HTTP请求和响应的处理变得简单。
    • 线程安全:HttpURLConnection是线程安全的,可以在多线程环境下使用,而无需额外的同步措施。
    • 支持多种HTTP方法:如GET、POST、PUT、DELETE等,可以根据需要选择合适的方法进行请求。
    • 支持HTTPS:可以与HTTPS服务器建立安全连接,通过SSL/TLS协议进行数据传输,确保数据的安全性。
    • 跨平台:作为Java标准库的一部分,可以在各种Java平台上使用,具有良好的跨平台性。
  • 限制

    HttpURLConnection的默认实现不支持连接池,这意味着每次发送HTTP请求时都需要建立新的连接,这可能会导致性能下降,特别是在发送大量HTTP请求的情况下。

Apache HttpClient

  • 概述

    Apache HttpClient是Apache软件基金会的一个项目,是Java标准库之外的一个广泛使用的HTTP客户端库。它提供了丰富的功能和配置选项,可以满足各种复杂的HTTP请求场景。

  • 特点

    • 稳定可靠:Apache HttpClient是一个成熟稳定的HTTP客户端库,拥有长期的开发历史和广泛的用户基础。
    • 支持连接池:通过连接池技术,可以有效地复用已经建立的连接,减少连接建立和关闭的开销,提高性能。
    • 支持HTTP/2:最新版本的Apache HttpClient支持HTTP/2协议,可以提供更高的性能和效率。
    • 丰富的配置选项:提供了多种配置选项,以满足不同的HTTP请求需求。
  • 应用

    Apache HttpClient适用于需要处理复杂HTTP请求和响应的场景,如需要设置自定义请求头、处理重定向、管理Cookies等。

OKHttp

  • 概述

    OKHttp是一个开源的Java HTTP客户端库,由Square公司开发。它被广泛用于Android开发和Java后端开发。OKHttp提供了一个简洁的API,用于发送HTTP请求和处理HTTP响应。

  • 特点

    • 高性能:OKHttp的底层实现基于Java的Socket和线程池,使用了连接池和请求重用机制,可以高效地处理大量的并发请求,并减少网络延迟。
    • 支持连接池:与Apache HttpClient类似,OKHttp也支持连接池技术,可以复用已经建立的连接。
    • 支持同步和异步请求:OKHttp支持发送同步和异步的HTTP请求,可以根据需要选择合适的请求方式。
    • 拦截器机制:提供了拦截器机制,可以在发送请求和接收响应的过程中进行干预和操作,如添加公共头部、记录日志等。
    • 支持HTTP/2和SPDY:这些协议可以提高网络性能和效率,OKHttp会自动选择支持的协议进行通信。
  • 应用

    OKHttp适用于需要高性能和灵活配置的HTTP客户端场景,如需要处理大量并发请求、需要自定义请求和响应处理等。

4.2.3 连接池的使用

  • 引入依赖
  • 开启线程池配置
  • 验证底层变化

第一步:引入依赖

cart-servicepom.xml中引入依赖:

<!--OK http 的依赖 -->
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-okhttp</artifactId>
</dependency>

第二步: 开启线程池配置

cart-service的yml文件中引入

第三步:验证底层

4.3 抽取服务、最佳实践

呼!现在应该是最佳方案了吧.......是吧,其实还是有优化的(优化是无止境的哈哈)。你观察一下。其实微服务与微服务之间往往是双向调用的,假设有1000个微服务两两互相调用。按照我们这种写法,需要给每个微服务提供 999 个 client接口。那1000个微服务就要提供 999000个接口。喔喔喔,是不是特别吓人。而且这些接口文件大都是重复的呀。

所以我们能不能把接口抽取成一个顶层模块,其他微服务模块只需要“继承”该模块,就能获得其中的方法。哇是不是很可行,那咱们马上行动。

4.3.1 抽取思路分析

  • 思路1:抽取到微服务之外的公共modul

    • 第一种的就是将所有的需要Fegin的接口都放入这个hm-api中,不同模块都可以调用这个hm-api中的接口

    • 优点:抽取更加简单,工程结构也比较清晰,不需要额外拷贝别的微服务的DTO。

    • 缺点:耦合度比较高,每个模块都可能都需要调用hm-api。

  • 思路2:每个微服务自己抽取一个module

    • 第二种是将需要调用的接口放在自己的module下,耦合度没有那么高,但是实现相对麻烦,工程结构相对更复杂、而且要额外拷贝别的微服务的DTO。

4.3.2 .抽取Feign客户端实践

  • hmall下定义一个新的module,命名为hm-api

  • 导入依赖、导入实体对象、导入client接口

hmall下定义一个新的module,命名为hm-api

导入依赖、导入实体对象、导入client接口

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>hmall</artifactId>
        <groupId>com.heima</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>hm-api</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <!--open feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- load balancer-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <!-- swagger 注解依赖 -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.6.6</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

现在,任何微服务要调用item-service中的接口,只需要引入hm-api模块依赖即可,无需自己编写Feign客户端了。

4.3.3 配置扫描包

为什么要配置扫描包?

cart-service的启动类定义在com.hmall.cart包下,扫描不到ItemClient,我们必须配置扫描包,不然会报错。

使用步骤

  • 在cart-service的pom.xml中引入hm-api模块
  • 配置扫描包路径

在cart-service的pom.xml中引入hm-api模块(模块调用模块)

  <!--feign模块-->
  <dependency>
      <groupId>com.heima</groupId>
      <artifactId>hm-api</artifactId>
      <version>1.0.0</version>
  </dependency>

配置扫描包路径方法一:声明扫描包

在cart-service的启动类上添加扫描 hm-api的声明

注意哈!由于删除了原本模块中的client、dto。导包部分要重新导

配置扫描包路径方法一:声明要用的FeignClient

在cart-service的启动类上添加声明要用的FeignClient

4.4 日志配置

OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:

  • NONE:不记录任何日志信息,这是默认值。

  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间

  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息

  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。

4.4.1 定义日志级别

package com.hmall.api.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;

public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.FULL;
    }
}

4.4.2 日志配置生效

【局部生效】在某个FeignClient中配置,只对当前FeignClient生效

@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)

全局生效】在@EnableFeignClients中配置,针对所有FeignClient生效。

@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

日志格式

22:26:25:336 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] ---> GET http://item-service/items?ids=100000006163 HTTP/1.1
22:26:25:336 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] ---> END HTTP (0-byte body)
22:26:25:518 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] <--- HTTP/1.1 200  (182ms)
22:26:25:518 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] connection: keep-alive
22:26:25:519 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] content-type: application/json
22:26:25:519 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] date: Fri, 01 Nov 2024 14:26:25 GMT
22:26:25:519 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] keep-alive: timeout=60
22:26:25:519 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] transfer-encoding: chunked
22:26:25:519 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] 
22:26:25:520 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] [{"id":"100000006163","name":"巴布豆(BOBDOG)柔薄悦动婴儿拉拉裤XXL码80片(15kg以上)","price":67100,"stock":10000,"image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t23998/350/2363990466/222391/a6e9581d/5b7cba5bN0c18fb4f.jpg!q70.jpg.webp","category":"拉拉裤","brand":"巴布豆","spec":"{}","sold":11,"commentCount":33343434,"isAD":false,"status":2}]
22:26:25:520 DEBUG 4716 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient          : [ItemClient#queryItemByIds] <--- END HTTP (371-byte body)
22:26:26:007  INFO 4716 --- [ent-executor-12] com.alibaba.nacos.common.remote.client   : [64fa38f0-9dff-4673-8014-52dbf5095a2f] Receive server push request, request = NotifySubscriberRequest, requestId = 22
22:26:26:008  INFO 4716 --- [ent-executor-12] com.alibaba.nacos.common.remote.client   : [64fa38f0-9dff-4673-8014-52dbf5095a2f] Ack server push request, request = NotifySubscriberRequest, requestId = 22

五、服务注册与调用巩固

1. 注册中心模式的角色有哪些?流程是什么?

2. 注册中心模式是如何实现宕机实例、异常实例的检测的?

3. 概述一下Nacos的使用流程?

4. 微服务中的服务发现是如何实现的?

5. 如何优化微服务远程调用的代码逻辑?(取代RestTemplate的工具)

6. 谈谈OpenFeign工具的使用流程?

7. OpenFeign底层的HTTP客户端技术是什么?有什么特点?

8. 除了HttpURLConnection,还要哪些HTTP客户端技术,有何特点?

9. 如何提高client接口代码的复用性。你有什么实现思路?

10. 跨模块调用client接口方法的配置步骤?

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

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

相关文章

SRS:构建实时免费视频服务器的全方位指南

SRS&#xff08;Simple Realtime Server&#xff09;是一个开源的、基于MIT协议的实时视频服务器&#xff0c;以其简单、高效而著称。它支持多种流媒体协议&#xff0c;包括RTMP、WebRTC、HLS、HTTP-FLV、SRT、MPEG-DASH和GB28181等&#xff0c;使其成为直播和WebRTC领域的理想…

copilot教我学408---OS之文件管理

四、文件管理 (一)文件 1.文件的基本概念 2.文件元数据和索引节点(inode) 3.文件的操作 建立,删除,打开,关闭,读,写 4.文件的保护 5.文件的逻辑结构 6.文件的物理结构 (二)目录 1.目录的基本概念 2.树形目录 3.目录的操作 4.硬链接和软链接 (三)文件系统 1.文件系统的全局结构(…

论文阅读笔记:Activating More Pixels in Image Super-Resolution Transformer

论文阅读笔记&#xff1a;Activating More Pixels in Image Super-Resolution Transformer 1 背景1.1 问题1.2 提出的方法 2 创新点3 方法4 模块4.1 混合注意力模块&#xff08;HAB&#xff09;4.2 重叠交叉注意力模块&#xff08;OCAB&#xff09;4.3 同任务预训练 5 效果5.1 …

导师双选系统开发:Spring Boot技术详解

第一章 绪论 1.1 选题背景 如今的信息时代&#xff0c;对信息的共享性&#xff0c;信息的流通性有着较高要求&#xff0c;尽管身边每时每刻都在产生大量信息&#xff0c;这些信息也都会在短时间内得到处理&#xff0c;并迅速传播。因为很多时候&#xff0c;管理层决策需要大量信…

关于wordpress instagram feed 插件 (现更名为Smash Balloon Social Photo Feed)

插件地址&#xff1a; Smash Balloon Social Photo Feed – Easy Social Feeds Plugin – WordPress 插件 | WordPress.org China 简体中文 安装后&#xff0c;配置教程&#xff1a; Setting up the Instagram Feed Pro WordPress Plugin - Smash Balloon 从这里面开始看就…

Redis学习:BitMap/HyperLogLog/GEO案例 、布隆过滤器BloomFilter、缓存预热+缓存雪崩+缓存击穿+缓存穿透

Redis学习 文章目录 Redis学习1、BitMap/HyperLogLog/GEO案例2. 布隆过滤器BloomFilter3. 缓存预热缓存雪崩缓存击穿缓存穿透 1、BitMap/HyperLogLog/GEO案例 真实需求面试题 亿级数据的收集清洗统计展现对集合中数据进行统计&#xff0c;基数统计&#xff0c;二值统计&#xf…

基于Python的智能旅游推荐系统设计与实现

一、摘要 本毕业设计的内容是设计并且实现一个基于Python技术的智能旅游推荐系统。它是在Windows下&#xff0c;以MYSQL为数据库开发平台&#xff0c;使用Python技术进行设计。智能旅游推荐系统的功能已基本实现&#xff0c;主要实现首页&#xff0c;个人中心&#xff0c;用户…

从零开始的c++之旅——多态

1. 多态的概念 通俗来说就是多种形态。 多态分为编译时多态&#xff08;静态多态&#xff09;和运行时多态&#xff08;动态多态&#xff09;。 编译时多态主要就是我们之前提过的函数重载和函数模板&#xff0c;同名提高传不同的参数就可以调 用不同的函数&#xff0c…

第二十六章 Vue之在当前组件范围内获取dom元素和组件实例

目录 一、概述 二、获取dom 2.1. 具体步骤 2.2. 完整代码 2.2.1. main.js 2.2.2. App.vue 2.3. BaseChart.vue 三、获取组件实例 3.1. 具体步骤 3.2. 完整代码 3.2.1. main.js 3.2.2. App.vue 3.2.3. BaseForm.vue 3.3. 运行效果 一、概述 我们过去在想要获取一…

plt中subplot综合实战

目录 背景介绍实战 背景介绍 下面是一份贸易数据&#xff08;Prod_Trade.xlsx&#xff09;&#xff0c;需要多角度针对2012年数据进行报表分析&#xff0c;需使用subplot分格展示。Prod_Trade的数据结构包括 Date,Order_Class,Sales Transport,Trans_Cost, Region ,Category, …

DevOps开发运维简述

DevOps平台是一套集成的解决方案&#xff0c;旨在协调软件开发&#xff08;Development&#xff09;和信息技术运维&#xff08;Operations&#xff09;。它促进跨功能团队合作&#xff0c;实现自动化流程&#xff0c;确保持续集成与持续交付&#xff08;CI/CD&#xff09;。 一…

基于java+SpringBoot+Vue的微服务在线教育系统设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven mysql5.7或8.0等等组成&#x…

粒子群优化双向深度学习!PSO-BiTCN-BiGRU-Attention多输入单输出回归预测

粒子群优化双向深度学习&#xff01;PSO-BiTCN-BiGRU-Attention多输入单输出回归预测 目录 粒子群优化双向深度学习&#xff01;PSO-BiTCN-BiGRU-Attention多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现PSO-BiTCN-BiGRU-Attention粒子…

终端文件管理神器 !!!

项目简介 nnn是一款专为命令行爱好者打造的高效终端文件管理器。它以其超小的体积、几乎零配置的要求以及卓越的速度表现而著称。nnn不仅适用于Linux、macOS、BSD等操作系统&#xff0c;还能够在诸如树莓派、Android上的Termux、WSL、Cygwin等多个平台运行。它遵循POSIX标准&am…

Uniapp 实现app自动检测更新/自动更新功能

实现步骤 配置 manifest.json 在 manifest.json 中设置应用的基本信息&#xff0c;包括 versionName 和 versionCode。 一般默认0.0.1&#xff0c;1. 服务器端接口开发 提供一个 API 接口&#xff0c;返回应用的最新版本信息&#xff0c;版本号、下载链接。客户端检测更新 使…

基础算法——排序算法(冒泡排序,选择排序,堆排序,插入排序,希尔排序,归并排序,快速排序,计数排序,桶排序,基数排序,Java排序)

1.概述 比较排序算法 算法最好最坏平均空间稳定思想注意事项冒泡O(n)O( n 2 n^2 n2)O( n 2 n^2 n2)O(1)Y比较最好情况需要额外判断选择O( n 2 n^2 n2)O( n 2 n^2 n2)O( n 2 n^2 n2)O(1)N比较交换次数一般少于冒泡堆O( n l o g n nlogn nlogn)O( n l o g n nlogn nlogn)O( n l…

多元数据库时代,云和恩墨携手鲲鹏引领数据库一体机新变革

近年来&#xff0c;随着企业数据存储结构日益多元化&#xff0c;传统架构数据库面临发展瓶颈&#xff0c;越来越多企业倾向于采用不同类型的数据库满足多样化的数据需求。这一趋势下&#xff0c;国内数据库市场呈现百花齐放的态势&#xff0c;产业加速迈入多元数据库时代。 作为…

[SWPUCTF 2022 新生赛]Cycle Again -拒绝脚本小子,成为工具糕手

1.题目 打开&#xff0c;一张图片&#xff0c;一个压缩包 2.分析 图片丢进随波逐流中 发现第一部分的flag NSSCTF{41d769db- 丢进b神的工具中 爆出第二段flag 9f5d-455e-a458-8012ba3660f3} 两段进行拼接 NSSCTF{41d769db-9f5d-455e-a458-8012ba3660f3} 直接拿下 遥遥领…

机场电子采购信息系统

摘 要 互联网的发展&#xff0c;改变了人类原来繁琐的生活和消费习惯&#xff0c;人们的时间观念也在不断加强&#xff0c;所以各种信息系统的数量越来越多&#xff0c;方便了用户&#xff0c;用户习惯也发生了改变。对于传统的企业采购模式来说由于费用高、速度慢、不透明化…

RabbitMQ设置消息过期时间

RabbitMQ设置消息过期时间 1、过期消息&#xff08;死信&#xff09;2、设置消息过期的两种方式2.1、设置单条消息的过期时间2.1.1、配置文件application.yml2.1.2、配置类RabbitConfig2.1.3、发送消息业务类service&#xff08;核心代码&#xff09;2.1.4、启动类2.1.5、依赖文…