微服务系列二:跨微服务请求优化,注册中心+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-servicecloud:nacos:server-addr: 192.168.186.140:8848

3.3.3 发现并调用服务

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

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

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

RestTemplase实现代码:

使用DiscoveryClient工具 + Nacos后代码

3.3.4 发现服务测试

3.3.5 完整代码

 // 注入服务发现工具@Resourceprivate DiscoveryClient discoveryClient;/*** DiscoveryClient* @param vos*/private void handleCartItems(List<CartVO> vos) {// 1.获取商品idSet<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.获取商品idSet<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 {@Beanpublic 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.rhkb.cn/news/464267.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

andrular输入框input监听值传递

效果图&#xff1a; step1: E:\projectgood\ajnine\untitled4\src\app\apple\apple.component.html <button mat-button (click)“openDialog()”>Open dialog step2: E:\projectgood\ajnine\untitled4\src\app\apple\apple.component.ts import {Component, inject}…

像`npm i`作为`npm install`的简写一样,使用`pdm i`作为`pdm install`的简写

只需安装插件pdm-plugin-i即可&#xff1a; pdm plugin add pdm-plugin-i 然后就可以愉快地pdm i了&#xff0c;例如&#xff1a; git clone https://github.com/waketzheng/fast-dev-cli cd fast-dev-cli python -m pip install --user pipx pipx install pdm pdm plugin a…

qt QTabWidget详解

1、概述 QTabWidget是Qt框架中的一个控件&#xff0c;它提供了一个标签页式的界面&#xff0c;允许用户在不同的页面&#xff08;或称为标签&#xff09;之间切换。每个页面都可以包含不同的内容&#xff0c;如文本、图像、按钮或其他小部件。QTabWidget非常适合用于创建具有多…

关于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 从这里面开始看就…

初始JavaEE篇——多线程(5):生产者-消费者模型、阻塞队列

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 文章目录 阻塞队列生产者—消费者模型生产者—消费者模型的优势&#xff1a;生产者—消费者模型的劣势&#xff1a; Java标准库中的阻…

用ChatGPT提升工作效率:从理论到实际应用

伴人工智能技术的迅速演进&#xff0c;像ChatGPT这类语言模型已成为提升工作效率的关键工具。这类模型不仅具备处理海量数据的能力&#xff0c;还能自动化许多日常任务&#xff0c;从而提高决策的准确性。本文将深入探讨如何在工作中利用ChatGPT等AI工具提升效率&#xff0c;涵…

如何修改网络ip地址:一步步指南‌

在当今这个数字化时代&#xff0c;网络已成为我们日常生活与工作中不可或缺的一部分。无论是浏览网页、在线办公还是享受流媒体服务&#xff0c;稳定的网络连接和适当的IP地址管理都是确保良好体验的关键。然而&#xff0c;出于隐私保护、绕过地理限制或测试网络环境等需要&…

ENSP (虚拟路由冗余协议)VRRP配置

VRRP&#xff08;Virtual Router Redundancy Protocol&#xff0c;虚拟路由冗余协议&#xff09;是一种用于提高网络可用性和可靠性的协议。它通过在多个路由器之间共享一个虚拟IP地址&#xff0c;确保即使一台路由器发生故障&#xff0c;网络依然能够正常运行&#xff0c;防止…

SpringCloud Alibaba-05 Seata分布式事务处理

一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用&#xff0c;就会产生分布式事务问题。但是关系型数据库提供的能力是基于单机事务的&#xff0c;一旦遇到分布式事务场景&#xff0c;就需要通过更多其他技术手段来解决问题。 1.四大模式&#xff1a; Seata AT模式(主…

非线性数据结构之图

一、有向图&#xff08;Directed Graph&#xff09; 1. 定义 有向图是一个由顶点&#xff08;节点&#xff09;和有方向的边&#xff08;弧&#xff09;组成的图。在有向图中&#xff0c;每条边都有一个起点和一个终点&#xff0c;表示从一个顶点到另一个顶点的关系。 2. 特…

大数据之Hadoop集群

Hadoop集群介绍&#xff1f;Hadoop集群的优缺点及应用场景&#xff1f;Hadoop集群搭建&#xff1f;Hadoop架构&#xff1f; Hadoop集群介绍 Hadoop集群是由多台计算机&#xff08;节点&#xff09;组成的一个分布式计算系统&#xff0c;主要用于处理大规模的数据集。以下是对Ha…

云原生+AI核心技术&最佳实践

以下内容是我在陕西理工大学2023级人工智能专业和网络专业的演讲内容&#xff0c;分享给大家。 各位老师、同学们&#xff0c;大家好啊&#xff01;能在这里跟大家一起聊聊咱们计算机专业那些事儿&#xff0c;我真的觉得超级兴奋&#xff01; 首先&#xff0c;自我介绍一下&am…

数字信号处理Python示例(5)使用实指数函数仿真PN结二极管的正向特性

文章目录 前言一、二极管的电流-电压关系——Shockley方程二、PN结二极管正向特性的Python仿真三、仿真结果分析写在后面的话 前言 使用Python代码仿真了描述二极管的电流-电压关系的Shockley方程&#xff0c;对仿真结果进行了分析&#xff0c;说明在正向偏置区域&#xff0c;…

真·香!深度体验 zCloud 数据库云管平台 -- DBA日常管理篇

点击蓝字 关注我们 zCloud 作为一款业界领先的数据库云管平台&#xff0c;通过云化自治的部署能力、智能巡检和诊断能力、知识即代码的沉淀能力&#xff0c;为DBA的日常管理工作带来了革新式的简化与优化。经过一周的深度体验&#xff0c;今天笔者与您深入探讨 zCloud 在数据库…

ICPC区域赛成都站【赛后回顾+总结】

传送门 前言赛后总结赛后回顾赛后感悟 前言 首先&#xff0c;这是本人本赛季第一场XCPC区域赛&#xff0c;也是本人算竞生涯中第一场XCPC区域赛&#xff08;之前只打过邀请赛和省赛&#xff09;。 赛后总结 然后赛后总结一下&#xff1a;我队天崩开局&#xff0c;我队出师不利…

Linux和,FreeRTOS 任务调度原理,r0-r15寄存器,以及移植freertos(一)

目录、 1、r0-r15寄存器&#xff0c;保护现场&#xff0c;任务切换的原理 2、freertos移植 3、freertos的任务管理。 一、前言 写这篇文章的目的&#xff0c;是之前面试官&#xff0c;刚好问到我&#xff0c;移植FreeRTOS 到mcu&#xff0c;需要做哪些步骤&#xff0c;当时回…

如果 MySQL 主库出现了问题,从库该何去何从呢?

🚀 博主介绍:大家好,我是无休居士!一枚任职于一线Top3互联网大厂的Java开发工程师! 🚀 🌟 在这里,你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人,我不仅热衷于探索一些框架源码和算法技巧奥秘,还乐于分享这些宝贵的知识和经验。 💡 无论你是刚刚踏…

基于Matlab 模拟停车位管理系统【源码 GUI】

系统对进入停车位的车辆进行车牌识别&#xff0c;将识别出来的车牌号显示出来&#xff1b;然后对车主进行人脸识别&#xff0c;框出车主照片的人脸部分作为车主信息的标记&#xff0c;记录在系统库中。车辆在库期间&#xff0c;系统使用者可以随意查看车辆与车主信息的获取过程…

【docker】docker 环境配置及安装

本文介绍基于 官方存储库 docker 的环境配置、安装、代理配置、卸载等相关内容。 官方安装文档说明&#xff1a;https://docs.docker.com/engine/install/ubuntu/ 主机环境 宿主机环境 Ubuntu 20.04.6 LTS 安装步骤 添加相关依赖 sudo apt-get update sudo apt-get install…

【论文阅读笔记】Wavelet Convolutions for Large Receptive Fields

1.论文介绍 Wavelet Convolutions for Large Receptive Fields 大感受野的小波卷积 2024 EECV Paper Code 2.摘要 近年来&#xff0c;人们试图通过增加卷积神经网络&#xff08;ConvolutionalNeuralNets&#xff0c;CNNs&#xff09;的核尺寸来模拟视觉变换器&#xff08;V…