微服务系列二:跨微服务请求优化,注册中心+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.mzph.cn/news/884812.shtml

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

相关文章

国产数据库之Vastbase海量数据库 G100

海量数据库Vastbase是基于openGauss内核开发的企业级关系型数据库。其语法和Oracle数据库很像&#xff0c;基本是从Oracle数据库迁移到海量数据库&#xff0c;以下简单介绍入门的使用 1、建库操作 地址&#xff1a;x.x.x.x root/Qa2021 安装路径&#xff1a;/home/vastbase 创…

爬虫学习4

from threading import Thread#创建任务 def func(name):for i in range(100):print(name,i)if __name__ __main__:#创建线程t1 Thread(targetfunc,args("1"))t2 Thread(targetfunc, args("2"))t1.start()t2.start()print("我是诛仙剑")from …

不要只知道deepl翻译,这里有10个专业好用的翻译工具等着你。

deepl翻译的优点还是有很多的&#xff0c;比如翻译的准确性很高&#xff0c;支持翻译的语言有很多&#xff0c;并且支持翻译文件和文本。但是现在翻译工具那么多&#xff0c;大家需要翻译的场景也有很多&#xff0c;怎么能只拥有一个翻译工具呢。所以在这里我帮助大家寻找了一波…

使用Docker Compose构建多容器应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Docker Compose构建多容器应用 引言 Docker Compose 简介 安装 Docker Compose 创建基本配置 运行多容器应用 查看服务状态 …

WindowsDocker安装到D盘,C盘太占用空间了。

Windows安装 Docker Desktop的时候,默认位置是安装在C盘,使用Docker下载的镜像文件也是保存在C盘,如果对Docker使用评率比较高的小伙伴,可能C盘空间,会被耗尽,有没有一种办法可以将Docker安装到其它磁盘,同时Docker的数据文件也保存在其他磁盘呢? 答案是有的,我们可以…

[MySQL#11] 索引底层(2) | B+树 | 索引的CURD | 全文索引

目录 1.B树的特点 索引结构 复盘 其他数据结构的对比 B树与B树总结 聚簇索引与非聚簇索引 辅助索引 2. 索引操作 主键索引 1. 创建主键索引 第一种方式 第二种方式 第三种方式 2. 查询索引 第一种方法 第二种方法 第三种方法 3. 删除索引 删除主键索引 删除…

【小白学机器学习31】 大数定律,中心极限定理,标准正态分布与概率的使用

目录 1 正态分布相关的2个相关定理 1.1 大数定律&#xff1a;(证明了)分布的稳定性 1.2 中心极限定理&#xff1a;(证明了)分布的收敛性 2 使用标准差和概率的2种思路 2.1 标准正态分布的曲线 2.2 两种使用方式 2.3 第1种&#xff1a;按整数倍标准差δ 作为标准使用 2.…

springcloud通过MDC实现分布式链路追踪

在DDD领域驱动设计中&#xff0c;我们使用SpringCloud来去实现&#xff0c;但排查错误的时候&#xff0c;通常会想到Skywalking&#xff0c;但是引入一个新的服务&#xff0c;增加了系统消耗和管理学习成本&#xff0c;对于大型项目比较适合&#xff0c;但是小的项目显得太过臃…

R语言结构方程模型(SEM)

原文链接&#xff1a;R语言结构方程模型&#xff08;SEM&#xff09;https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247624956&idx4&sn295580a016a86cfee8ee2277c93e32d5&chksmfa8da91bcdfa200da897f1f267492039865bdfe5d75a1c6e6df92ff5005e0eb5cc33a…

国标GB28181视频平台EasyCVR私有化视频平台工地防盗视频监控系统方案

一、方案背景 在当代建筑施工领域&#xff0c;安全监管和防盗监控是保障工程顺利进行和资产安全的关键措施。随着科技进步&#xff0c;传统的监控系统已不足以应对现代工地的安全挑战。因此&#xff0c;基于国标GB28181视频平台EasyCVR的工地防盗视频监控系统应运而生&#xf…

labview学习总结

labview学习总结 安装labview的特点一、图形化编程范式二、并行执行机制三、硬件集成能力四、应用领域优势五、开发效率六、系统集成能力**labview基本组成示意图****常用程序结构图解**结语 基础知识介绍界面前后面板的概念平铺式和层叠式 帧的概念结构类型顺序结构for循环whi…

《YOLO 目标检测》—— YOLO v4 详细介绍

文章目录 一、整体网络结构1. YOLO v4 网络结构图2.对之前版本改进创新的概括 二、对改进创新部分的具体介绍1. 输入端创新2. Backbone主干网络创新CSPDarknet53Mish激活函数Dropblock正则化 3. 特征融合创新SPP模块PAN结构 4. Prediction输出层创新CIOU LossDIoU_NMS&#xff…

动态规划 —— dp问题-按摩师

1. 按摩师 题目链接&#xff1a; 面试题 17.16. 按摩师 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/the-masseuse-lcci/description/ 2. 算法原理 状态表示&#xff1a;以某一个位置为结尾或者以某一个位置为起点 dp[i]表示&#xff1a;选择到i位置…

【react】基础知识点学习

1. 创建项目 npm install -g create-react-app npx create-react-app my-app cd my-app npm startindex.js为入口文件&#xff0c;App.js为根组件。 如何将react应用挂载在页面上&#xff1f; 将App组件渲染到id为root的DOM元素中 2. JSX JSX是|avaScript和XML(HTML)的缩写…

word及Excel常见功能使用

最近一直在整理需规文档及表格&#xff0c;Word及Excel需要熟练使用。 Word文档 清除复制过来的样式 当复制文字时&#xff0c;一般会带着字体样式&#xff0c;此时可选中该文字 并使用 ctrlshiftN 快捷键进行清除。 批注 插入->批注&#xff0c;选中文本 点击“批注”…

在Microsoft Outlook日历中添加多个时区

在Microsoft Outlook日历中添加多个时区 1.单击Outlook中的文件选项卡&#xff0c;单击选项 2.左侧菜单中选择日历 3.向下滚动到时区部分&#xff0c;并标记当前时区&#xff0c;比如China 4.选中“显示第二个时区”框 5.选择第二个时区并给它一个标签&#xff0c;比如Germa…

vue组件在项目中的常用业务逻辑(3)

获取完后台接口数据后&#xff0c;需将数据在页面中进行动态展示。 一、在getters中简化数据&#xff1a; 二、在search>index.vue中写计算属性&#xff0c;实现将接口的goodsList模块数据展示在vue的search上&#xff1a; 三、1.用v-for循环数据&#xff0c;一共十个&…

从传感器到清洁力提升,灵途科技推动家电智能化发展

双十一第一波预售刚结束&#xff0c;第二波预售活动又火热开启。得益于政府补贴和平台优惠的双重带动&#xff0c;扫地机等高端智能家电需求暴增。 今年&#xff0c;各大电商平台不再单纯追求“全网最低价”&#xff0c;而是在低价基础上注重提升用户的综合体验。家电机器人品…

基于STM32的智能温室环境监测与控制系统设计(代码示例)

一、项目概述 在现代农业中&#xff0c;智能大棚能够通过环境监测、数据分析和自动控制等技术手段&#xff0c;实现对作物生长环境的精细化管理。本项目旨在设计一个基于STM32单片机的智能大棚系统&#xff0c;能够实时监测光照强度、空气温湿度及土壤湿度&#xff0c;并根据设…

详解CRC校验原理以及FPGA实现

文章目录 一、什么是CRC校验&#xff1f;二、实现CRC校验原理以及步骤2.1 用多项式表示二元码数据2.2 选择一个生成多项式作为校验2.3 计算CRC校验码 三、CRC判断数据是否错误的原理以及步骤3.1 将收到的数据与生成多项式求余3.2 数据发生错误再进行CRC校验判断 四、FPGA实现CR…