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

目录

前言

一、纯 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接口方法的配置步骤?

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android音频进阶之PCM设备创建(九十三)

简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+…

自定义规则配置教程

大家在使用waf的时候&#xff0c;因为业务特殊性和waf的严格校验&#xff0c;有时会产生误报&#xff0c;阻拦合法流量。 这个时候&#xff0c;只能通过自定义规则进行补充&#xff0c;选择加白名单或者黑名单。 很多人会说配置黑白名单失效了&#xff0c;其实95%以上都是自己…

HTML 文档规范与解析模式:DOCTYPE、<html> 标签以及结构化页面

文章目录 `<!DOCTYPE html>` 文档类型声明标准模式与怪异模式HTML5 的简化声明`<html>` 标签`<head>` 标签`<body>` 标签小结<!DOCTYPE html> 文档类型声明 在 HTML 文档中,<!DOCTYPE html> 是一个重要的文档类型声明,主要用于告知浏览…

双分解+一区极光优化+Transformer!CEEMDAN-Kmeans-VMD-PLO-Transformer多元时序预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CEEMDAN-Kmeans-VMD-PLO-Transformer融合K均值聚类的数据双重分解极光优化Transformer多元时间序列预测&#xff08;完整源码和数据&#xff09; 2.CEEMDAN分解&#xff0c;计算样本熵&#xff0c;根据样…

MATLAB绘图基础10:MATLAB极坐标相关图形

参考书&#xff1a;《 M A T L A B {\rm MATLAB} MATLAB与学术图表绘制》(关东升)。 10.MATLAB极坐标相关图形 10.1 极坐标图 极坐标图是在极坐标系下绘制数据的图形&#xff0c;极坐标系以中心为原点&#xff0c;使用角度和半径表示数据点的位置&#xff1b; 极坐标图适用于…

IDEA加载通义灵码插件及使用指南

安装通义灵码插件 登录通义灵码IDE插件 下载登录参考教程 https://help.aliyun.com/zh/lingma/user-guide/download-the-installation-guide 本地工程和企业知识库准备 请下载本地工程和知识库压缩包&#xff0c;并在本地解压缩&#xff0c;其中包含demoProject和知识库文件…

【VScode】VScode内的ChatGPT插件——CodeMoss全解析与实用教程

在当今快速发展的编程世界中&#xff0c;开发者们面临着越来越多的挑战。如何提高编程效率&#xff0c;如何快速获取解决方案&#xff0c;成为了每位开发者心中的疑问。今天&#xff0c;我们将深入探讨一款颠覆传统编程体验的插件——CodeMoss&#xff0c;它将ChatGPT的强大功能…

Centos 网络接口打vlan标签

Centos 网络接口打vlan标签 本次使用给bond打vlan标签&#xff0c;其实其他普通接口也一样 Centos创建bond前需要关闭NetworkManager [root192 network-scripts]# systemctl disable NetworkManager --now Removed symlink /etc/systemd/system/multi-user.target.wants/Netwo…

Java语言简单了解

一.java语言的三个版本 1.javaSE&#xff1a;java语言的(标准版)&#xff0c;用户桌面应用的开发&#xff0c;是其他两个版本的基础 桌面应用&#xff1a;用户只要打开程序&#xff0c;程序的界面会让用户在最短的时间内找到他们需要的功能。 2.javaMe&#xff1a;java语言的…

华宇TAS应用中间件入围鲲鹏应用创新大赛2024全国总决赛

近日&#xff0c;鲲鹏应用创新大赛2024全国总决赛入围名单出炉。华宇TAS应用中间件经过区域赛、半决赛一路披荆斩棘&#xff0c;在众多优秀的解决方案中脱颖而出&#xff0c;成功入围全国总决赛。 这也表明华宇TAS应用中间件在方案创新性、技术领先性、商业前景、社会价值等方…

科研绘图系列:R语言多个组合堆积图(stacked plot)

文章目录 介绍加载R包数据数据预处理画图1画图2画图3画图4画图5画图6画图7画图8画图9组合图形系统信息介绍 堆积图(Stacked Chart),也称为堆叠图,是一种常用的数据可视化图表,主要用于展示不同类别的数据量在总体中的分布情况。堆积图可以是柱状图、条形图或面积图的形式…

net core Autofac 替换默认的服务容器 DI,微软自动的容器 不支持命名选项的

微软默认的容器&#xff0c;不支持命名选项&#xff0c;同一接口&#xff0c;多个实现。 就不支持了。 配置core 支持Autofac 容器 using Autofac; using Autofac.Extensions.DependencyInjection;namespace WebApplication13 {public interface IMyService{string GetData()…

1、Qt6 Quick 简介

一、Qt6 Quick 简介 1、Qt Quick简介 Qt Quick 是 Qt 6 中使用的用户界面技术的总称。它是在 Qt 4 中引入的&#xff0c;现在在 Qt 6 中进行了扩展。Qt Quick 本身是几种技术的集合&#xff1a; QML——用户界面标记语言JavaScript - 动态脚本语言Qt C - 高度可移植的增强型…

【HarmonyOS NEXT】在 HarmonyOS NEXT 中实现优雅的加载动画

【HarmonyOS NEXT】在 HarmonyOS NEXT 中实现优雅的加载动画 在移动应用开发中&#xff0c;加载动画是提升用户体验的重要工具。在应用程序处理数据或加载页面时&#xff0c;为用户提供视觉反馈尤为关键。在这篇博客中&#xff0c;我们将探讨如何在 HarmonyOS NEXT 中使用 Sta…

2024年10月30日(双指针算法)

一.和为s的两个数字&#xff1a; 1.题目描述&#xff1a; 这个题目就是找出两个数&#xff0c;这两个数的和是目标值&#xff0c;找到其中一对就可以返回了。 2.算法原理&#xff1a; 方法一&#xff1a; 暴力枚举的策略&#xff1a; 就是两层for循环&#xff0c;固定一个数&…

PyQt5实战——UTF-8编码器UI页面设计以及按钮连接(五)

个人博客&#xff1a;苏三有春的博客 系类往期文章&#xff1a; PyQt5实战——多脚本集合包&#xff0c;前言与环境配置&#xff08;一&#xff09; PyQt5实战——多脚本集合包&#xff0c;UI以及工程布局&#xff08;二&#xff09; PyQt5实战——多脚本集合包&#xff0c;程序…

【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0

目录 一、相关面试题 1. HTTP 与 HTTPS 有哪些区别&#xff1f; 2. HTTPS 的工作原理&#xff1f;&#xff08;https 是怎么建立连接的&#xff09; &#xff08;1&#xff09;ClientHello &#xff08;2&#xff09;SeverHello &#xff08;3&#xff09;客户端回应 &a…

【VScode】中文版ChatGPT编程工具-CodeMoss!教程+示例+快捷键

文章目录 1. 多模型选择2. 编辑快捷键3. 历史记录收藏 CodeMoss使用教程1. 安装CodeMoss插件2. 配置AI模型3. 使用快捷键4. 进行代码优化与解释5. 收藏历史记录 总结与展望 在当今快速发展的编程世界中&#xff0c;开发者们面临着越来越多的挑战。如何提高编程效率&#xff0c;…

JqGird 动态生成列使用

使用场景&#xff1a; 在工作用需要自定义动态生成列&#xff0c;通过选择下拉框&#xff0c;加载列&#xff0c;通过查询加载列对应的数据信息 当选择文件源任务显示三列 当选择数据源任务显示两列 处理方式&#xff1a; 1. 首先在刚进入界面时初始化控件 $("#pageGri…

STM32Fxx读写eeprom(AT24C16)

一.I2C 协议简介 I2C 通讯协议 (Inter &#xff0d; Integrated Circuit) 是由 Phiilps 公司开发的&#xff0c;由于它引脚少&#xff0c;硬件实现简单&#xff0c;可扩展性强&#xff0c;不需要 USART、CAN 等通讯协议的外部收发设备&#xff0c;现在被广泛地使用在系统内多个…