当前位置: 首页 > news >正文

RestTemplate使用 | RestTemplate设置http连接池参数

场景:接口从httpClient调用改成RestTemplate调用之后频报以下异常,开始排查是否连接泄漏或存在连接池问题。
排查发现配置类没设置线程池参数,导致走了默认的配置:因默认单个路由最大连接数为2加上第三方接口响应时间较长,造成http连接长时间获取不到导致的超时问题。另外,使用RestTemplate不用手动释放或关闭连接,不用httpclient.close(),spring已经托管了、不会存在连接泄漏的问题。
注:后文贴出的是已修复好的、加上httpClient连接池设置的配置,可放心食用
新发现:原来http连接池还可以设置单个路由最大并发数,这是为了防止单一服务抢占所有资源

异常日志:

I/O error on POST request for “https://xxxx”: Timeout waiting for connection from pool; nested exception is org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool org.springframework.web.client.ResourceAccessException: I/O error on POST request for “https://xxxx”: Timeout waiting for connection from pool; nested exception is org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool

问题排查思路:
在这里插入图片描述

文章目录

  • 零、环境说明
  • 一、配置类和maven依赖
    • (一)配置类
    • (二)依赖
  • 二、http连接池参数监控类(非必须,主要是为了后续观察http连接池参数)
  • 三、用Controller测试
    • (一)多线程并发测试,观察线程池参数值
    • (二)用postman模拟并发,观察线程池参数值变化(未观察到并发数变化,可能这种测试方法不适用)
    • (三)注释掉http连接池,观察默认连接设置
  • 四、结论
  • 五、其他问题
    • (一). RestTemplate 和 HttpClient的关系
      • 1.RestTemplate (Spring Framework)
      • 2.HttpClient (Apache)
      • 3.实际调用流程:
      • 4.为什么需要这种分层设计
      • 5.多序列化模板
    • (二)线程池、http连接池、durid数据库连接池、Tomcat连接池,这些池的连接数有没有重叠的地方(AI回答)
    • 1. 各“池”的作用与层级
    • 2. 连接数的重叠场景(串联关系)
    • 3. 配置建议(如何设置连接数)
    • 4.总结

零、环境说明

  • java
  • jdk8
  • maven
  • spring-boot-starter-parent 2.7.5

一、配置类和maven依赖

(一)配置类

双template,能同时应对响应时间较长的接口、响应时间正常的接口。性能方面,设置了http连接池参数,资源可高效复用


@Slf4j
@Configuration
public class RestTemplateConfig {// http连接池@Beanpublic PoolingHttpClientConnectionManager connectionManager() {PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();// 设置整个连接池最大连接数connectionManager.setMaxTotal(200);// 设置每个路由(目标主机)的最大连接数connectionManager.setDefaultMaxPerRoute(50);// 空闲连接验证间隔(毫秒)connectionManager.setValidateAfterInactivity(5000);return connectionManager;}@Beanpublic RequestConfig requestConfig() {return RequestConfig.custom().setSocketTimeout(10000)           // Socket超时 10: 根据接口响应时长评估。不可设置过长。在连接泄漏或服务端卡顿时,会长时间占用线程资源。容易导致客户端线程池被占满,引发级联故障.setConnectTimeout(2000)           // 连接超时 2: 建立TCP连接通常在毫秒级.setConnectionRequestTimeout(2000) // 从连接池获取连接超时 2秒 :从连接池获取连接应该是瞬间的,除非连接池已满.build();}@Beanpublic CloseableHttpClient httpClient() {return HttpClients.custom().setConnectionManager(connectionManager()) // 关键:添加连接池管理器.setDefaultRequestConfig(requestConfig()).evictExpiredConnections() // 驱逐过期连接.evictIdleConnections(30, TimeUnit.SECONDS) // 驱逐空闲30秒以上的连接.build();}@Beanpublic ClientHttpRequestFactory clientHttpRequestFactory() {return new HttpComponentsClientHttpRequestFactory(httpClient());}// 正常版的RestTemplate@Beanpublic RestTemplate restTemplate() {return new RestTemplate(clientHttpRequestFactory());}// 长响应时长版本的RestTemplate@Bean("longTimeoutRestTemplate")public RestTemplate longTimeoutRestTemplate() {RequestConfig longTimeoutConfig = RequestConfig.custom().setSocketTimeout(60000).setConnectTimeout(2000).setConnectionRequestTimeout(2000).build();// http连接池PoolingHttpClientConnectionManager longTimeoutConnectionManager =new PoolingHttpClientConnectionManager();longTimeoutConnectionManager.setMaxTotal(200);longTimeoutConnectionManager.setDefaultMaxPerRoute(50);CloseableHttpClient longTimeoutClient = HttpClients.custom().setConnectionManager(longTimeoutConnectionManager).setDefaultRequestConfig(longTimeoutConfig).evictExpiredConnections().evictIdleConnections(30, TimeUnit.SECONDS).build();return new RestTemplate(new HttpComponentsClientHttpRequestFactory(longTimeoutClient));}}

(二)依赖

  <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.5</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>commons-httpclient</groupId><artifactId>commons-httpclient</artifactId><version>3.1</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpmime</artifactId><version>4.5.13</version></dependency>
</dependencies>

二、http连接池参数监控类(非必须,主要是为了后续观察http连接池参数)

通过springboot的定时任务监控,主启动类要加上@EnableScheduling
启动后,服务将定时打印如下信息:
在这里插入图片描述

@Component
@Slf4j
public class HttpClientMonitor {private final RestTemplate restTemplate;private PoolingHttpClientConnectionManager connectionManager;public HttpClientMonitor(@Lazy @Qualifier("longTimeoutRestTemplate") RestTemplate restTemplate) {this.restTemplate = restTemplate;}@PostConstructpublic void init() {initializeConnectionManager();}private void initializeConnectionManager() {try {if (restTemplate.getRequestFactory() instanceof HttpComponentsClientHttpRequestFactory) {HttpComponentsClientHttpRequestFactory factory =(HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();HttpClient httpClient = factory.getHttpClient();// 使用反射获取真实的连接管理器this.connectionManager = getRealConnectionManager(httpClient);if (connectionManager != null) {log.info("HTTP连接池监控器初始化成功");} else {log.warn("无法获取PoolingHttpClientConnectionManager");}}} catch (Exception e) {log.warn("初始化连接管理器失败: {}", e.getMessage());}}/*** 使用反射获取真实的连接管理器*/private PoolingHttpClientConnectionManager getRealConnectionManager(HttpClient httpClient) {try {// 获取connectionManager字段Field connManagerField = httpClient.getClass().getDeclaredField("connManager");connManagerField.setAccessible(true);Object connectionManager = connManagerField.get(httpClient);// 如果是PoolingHttpClientConnectionManager,直接返回if (connectionManager instanceof PoolingHttpClientConnectionManager) {return (PoolingHttpClientConnectionManager) connectionManager;}// 如果不是,尝试查找内部的PoolingManagerreturn findPoolingManagerInFields(connectionManager);} catch (Exception e) {log.warn("通过反射获取连接管理器失败: {}", e.getMessage());return null;}}/*** 递归查找字段中的PoolingHttpClientConnectionManager*/private PoolingHttpClientConnectionManager findPoolingManagerInFields(Object obj) {if (obj == null) {return null;}// 如果已经是目标类型,直接返回if (obj instanceof PoolingHttpClientConnectionManager) {return (PoolingHttpClientConnectionManager) obj;}try {// 遍历所有字段,查找PoolingHttpClientConnectionManagerfor (Field field : obj.getClass().getDeclaredFields()) {field.setAccessible(true);Object fieldValue = field.get(obj);if (fieldValue instanceof PoolingHttpClientConnectionManager) {return (PoolingHttpClientConnectionManager) fieldValue;}// 递归查找if (fieldValue != null && !fieldValue.getClass().isPrimitive()) {PoolingHttpClientConnectionManager result = findPoolingManagerInFields(fieldValue);if (result != null) {return result;}}}} catch (Exception e) {log.warn("http连接池监控,反射异常,忽略: {}", e.getMessage());}return null;}@Scheduled(initialDelay = 10000, fixedRate = 500)public void monitorConnectionPool() {if (connectionManager == null) {initializeConnectionManager(); // 重新尝试初始化if (connectionManager == null) {log.warn("连接池监控器尚未初始化完成");return;}}try {int defaultMaxPerRoute = connectionManager.getDefaultMaxPerRoute();int validateAfterInactivity = connectionManager.getValidateAfterInactivity();PoolStats totalStats = connectionManager.getTotalStats();log.info("HTTP连接池状态 - 最大连接数: {}, 已用: {}, 可用: {}, 等待: {}, 每路由最大并发数: {}, 验证时间间隔: {}ms",totalStats.getMax(),totalStats.getLeased(),totalStats.getAvailable(),totalStats.getPending(),defaultMaxPerRoute, validateAfterInactivity); // 记录详细的连接池信息logDetailedStats();} catch (Exception e) {log.warn("监控连接池时发生异常: {}", e.getMessage());}}/*** 记录更详细的连接池统计信息*/private void logDetailedStats() {try {Set<HttpRoute> routes = connectionManager.getRoutes();if (routes != null && !routes.isEmpty()) {for (HttpRoute route : routes) {PoolStats routeStats = connectionManager.getStats(route);if (routeStats.getLeased() > 0 || routeStats.getAvailable() > 0) {log.debug("路由 {} - 最大: {}, 已用: {}, 可用: {}, 等待: {}",route.getTargetHost(),routeStats.getMax(),routeStats.getLeased(),routeStats.getAvailable(),routeStats.getPending());}}}} catch (Exception e) {log.debug("记录详细统计信息时发生异常: {}", e.getMessage());}}/*** 获取连接池统计信息*/public Map<String, Object> getConnectionPoolStats() {Map<String, Object> stats = new HashMap<>();if (connectionManager != null) {try {PoolStats totalStats = connectionManager.getTotalStats();stats.put("maxTotal", totalStats.getMax());stats.put("leased", totalStats.getLeased());stats.put("available", totalStats.getAvailable());stats.put("pending", totalStats.getPending());} catch (Exception e) {stats.put("error", e.getMessage());}} else {stats.put("error", "Connection manager not initialized");}return stats;}
}

三、用Controller测试

(一)多线程并发测试,观察线程池参数值

@Autowired@Qualifier("longTimeoutRestTemplate")private RestTemplate restTemplate;@GetMapping("/testCall")public void testCall() {ModelScoreResult request = new ModelScoreResult();// 发送POST请求ResponseEntity<ModelScoreResult> response = restTemplate.postForEntity("http://localhost:8080/testCall",request,ModelScoreResult.class);log.info("响应= {}", JSON.toJSONString(response));}@GetMapping("/testCall2")public void testCall2() {testSlowRequests();}public void testSlowRequests() {ExecutorService executor = Executors.newFixedThreadPool(100);CountDownLatch latch = new CountDownLatch(80); log.info("开始慢速接口并发测试...");ModelScoreResult request = new ModelScoreResult();for (int i = 0; i < 80; i++) {final int index = i;executor.submit(() -> {try {long start = System.currentTimeMillis();ResponseEntity<ModelScoreResult> response = restTemplate.postForEntity("http://localhost:8080/testCall",request,ModelScoreResult.class);long end = System.currentTimeMillis();log.info("慢请求 {} - 状态: {}, 耗时: {}ms",index, response.getStatusCode(), end - start);} catch (Exception e) {log.error("慢请求 {} 失败: {}", index, e.getMessage());} finally {latch.countDown();}});}try {latch.await();log.info("所有慢速请求完成");} catch (InterruptedException e) {Thread.currentThread().interrupt();}executor.shutdown();}

(二)用postman模拟并发,观察线程池参数值变化(未观察到并发数变化,可能这种测试方法不适用)

在这里插入图片描述

(三)注释掉http连接池,观察默认连接设置

在这里插入图片描述
不设置http线程池参数时,默认:最大: 20, 路由最大并发连接数: 2

四、结论

1.不设置http线程池参数时,默认:最大: 20, 路由最大并发连接数: 2

  • 本实例最多可以同时建立20个TCP连接;
  • 每个路由最大并发连接数是2,路由并发限制是为了防止单个服务耗尽所有资源,比如A服务要调BCD服务,如果不设置路由并发数限制,B服务有可能会抢CD服务的连接数导致CD服务接口无法被调用;
    路由:由三要素构成:协议(http 或 https)+目标主机(ip或ym)+ 端口

举个例子,服务Z 要分别同时调用 服务ABCDEFGHIJK 的接口(同个字母视作同个路由),假设服务被调用接口都要调三次,如A1 A2 A3;B1 B2 B3
那么此时:A1 A2 B1 B2 …. J1 J2 都可成功被调用,
A3,B3 …J3,K1 K2 K3 则无法立即被调用,需要等待http线程池的资源。
A3,B3 …J3 不能立即被调的原因是:受路由最大并发连接数限制(超过2了);
K1 K2 K3 无法立即被调用的原因是:受最大连接数限制(超过20了)

五、其他问题

(一). RestTemplate 和 HttpClient的关系

1.RestTemplate (Spring Framework)

角色:高级HTTP客户端模板

功能:

  • 提供简洁的API(getForObject, postForEntity等)

  • 处理消息转换(JSON/XML/Protobuf序列化反序列化)

  • 异常转换(将HTTP错误转换为Spring异常)

  • 请求/响应拦截

2.HttpClient (Apache)

角色:底层HTTP协议实现

功能:

  • 实际的网络通信

  • 连接池管理

  • 超时控制

  • 重定向处理

  • SSL/TLS配置

3.实际调用流程:

// 1. RestTemplate 解析URL和参数
// 2. RestTemplate 选择合适的MessageConverter
// 3. RestTemplate 通过ClientHttpRequestFactory创建请求
// 4. HttpComponentsClientHttpRequestFactory 将请求转换为HttpClient格式
// 5. HttpClient 执行实际的HTTP请求
// 6. HttpClient 返回响应给HttpComponentsClientHttpRequestFactory
// 7. RestTemplate 使用MessageConverter解析响应
// 8. RestTemplate 返回最终结果

4.为什么需要这种分层设计

优点:

  • 解耦:应用层不依赖具体的HTTP实现

  • 可替换性:可以轻松切换底层HTTP客户端

  • 功能分离:RestTemplate处理业务逻辑,HttpClient处理网络通信

  • 易于测试:可以mock ClientHttpRequestFactory进行单元测试

5.多序列化模板

@Bean(“jsonHttpClient”)@Bean(“protobufHttpClient”) @Bean(“xmlHttpClient”)
配置多个序列化方式的RestTemplate Bean

@Configuration
public class MultiRestTemplateConfig {// JSON协议的HttpClient (5秒超时)@Bean("jsonHttpClient")public CloseableHttpClient jsonHttpClient() {RequestConfig config = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).setConnectionRequestTimeout(5000).build();return HttpClients.custom().setDefaultRequestConfig(config).build();}// Protobuf协议的HttpClient (10秒超时)@Bean("protobufHttpClient")public CloseableHttpClient protobufHttpClient() {RequestConfig config = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).setConnectionRequestTimeout(5000).build();return HttpClients.custom().setDefaultRequestConfig(config).build();}// XML协议的HttpClient (30秒超时)@Bean("xmlHttpClient")public CloseableHttpClient xmlHttpClient() {RequestConfig config = RequestConfig.custom().setSocketTimeout(30000).setConnectTimeout(30000).setConnectionRequestTimeout(5000).build();return HttpClients.custom().setDefaultRequestConfig(config).build();}// 对应的RestTemplate配置@Bean("jsonRestTemplate")public RestTemplate jsonRestTemplate(@Qualifier("jsonHttpClient") CloseableHttpClient httpClient) {ClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(factory);restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());return restTemplate;}@Bean("protobufRestTemplate")public RestTemplate protobufRestTemplate(@Qualifier("protobufHttpClient") CloseableHttpClient httpClient) {ClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(factory);// 假设有Protobuf消息转换器// restTemplate.getMessageConverters().add(new ProtobufHttpMessageConverter());return restTemplate;}@Bean("xmlRestTemplate")public RestTemplate xmlRestTemplate(@Qualifier("xmlHttpClient") CloseableHttpClient httpClient) {ClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(factory);restTemplate.getMessageConverters().add(new Jaxb2RootElementHttpMessageConverter());return restTemplate;}
}

使用时按bean名注入

    @Autowired@Qualifier("jsonRestTemplate")private RestTemplate jsonRestTemplate;@Autowired@Qualifier("protobufRestTemplate")private RestTemplate protobufRestTemplate;@Autowired@Qualifier("xmlRestTemplate")private RestTemplate xmlRestTemplate;

(二)线程池、http连接池、durid数据库连接池、Tomcat连接池,这些池的连接数有没有重叠的地方(AI回答)

是的,这些“池”的连接数存在重叠,但它们管理的资源对象和层级完全不同。 它们像一套俄罗斯套娃或者一个生产线上的不同工位,各自负责特定环节的资源复用,最终共同完成一个对外请求。

核心结论是:一个外部HTTP请求,可能会顺序地、一对一地使用到这些池中的每一个连接。因此,它们的连接数不是加算关系,而是串联关系。

下面我们来详细分解每一个“池”的作用和它们之间的关系。

1. 各“池”的作用与层级

我们可以将这些连接池从外到内划分为几个层级:

连接池类型管理的资源所属层级目的
Tomcat连接池Socket连接(通常是TCP)Web服务层复用处理HTTP请求的线程,避免频繁创建销毁线程的开销。
HTTP连接池HTTP连接应用客户端层复用到其他服务的HTTP连接(包括TCP连接和SSL上下文等),避免三次握手等开销。
数据库连接池(Druid等)数据库连接(如MySQL Connector)数据持久层复用到数据库的TCP连接,避免频繁建立数据库连接、认证的开销。
线程池工作者线程(Worker Thread)基础架构层复用线程资源,避免频繁创建和销毁线程的开销,提高系统性能。

2. 连接数的重叠场景(串联关系)

想象一个最常见的业务场景:你的Java Web应用(使用Tomcat)接收一个用户请求,这个请求需要调用另一个第三方API,然后查询自己的数据库返回结果。

一个用户请求的生命周期如下:

Tomcat线程池分配连接(线程):

用户发送HTTP请求到你的服务器。

Tomcat的Acceptor线程接收到这个Socket连接。

Tomcat的线程池(即你问题中的Tomcat连接池)分配一个空闲的工作者线程(Thread-1)来处理这个请求。

此时,占用1个Tomcat线程池的连接(即一个工作线程)。


HTTP连接池分配连接:

在Thread-1的执行过程中,代码需要调用一个外部服务的REST API。

你的应用使用HttpClient(配置了连接池)来发起调用。

HttpClient从它的HTTP连接池中取出一个空闲的、到目标服务的HTTP连接(这个连接背后是一个已经建立好的TCP Socket)。

通过这个连接发送HTTP请求并接收响应。

此时,占用1个HTTP连接池中的连接。


数据库连接池分配连接:

拿到外部API的响应后,Thread-1需要将结果保存到数据库。

你的应用(通过ORM框架如MyBatis)向Druid(或其他数据库连接池)请求一个数据库连接。

Druid从它的数据库连接池中分配一个空闲的、到MySQL数据库的连接(这背后也是一个TCP Socket)。

执行SQL语句,然后释放连接(归还给池)。

此时,占用1个数据库连接池中的连接。


请求完成:

Thread-1处理完毕,生成HTTP响应返回给用户。

Thread-1变为空闲状态,归还给Tomcat线程池。

HttpClient使用的HTTP连接也归还给其连接池。

由此可见,在处理这单个用户请求的某个瞬间,它可能同时占用了:

1个 Tomcat线程池中的线程

1个 HTTP客户端连接池中的连接

1个 数据库连接池中的连接

它们是一种串联的、一对一的依赖关系,而不是重叠的竞争关系。

3. 配置建议(如何设置连接数)

正因为这种串联关系,在配置这些池的大小时,必须考虑它们之间的依赖和系统的瓶颈所在。

Tomcat最大线程数 (maxThreads):这决定了你的应用同时能处理多少个用户请求。这是整个系统的入口并发度。

HTTP客户端最大连接数:这决定了你的应用同时能向外发起多少个HTTP调用。这个数通常应该小于或等于Tomcat的maxThreads。如果Tomcat有200个线程,但HTTP连接池只有20个连接,那么当有50个请求都需要调用外部API时,最多只有20个能同时进行,剩下的30个请求会被阻塞,等待HTTP连接空闲。

数据库连接池最大连接数 (maxActive):这决定了你的应用同时能打开多少个数据库连接。这个数通常应该小于数据库服务器本身允许的最大连接数,并且也要参考Tomcat的线程数。如果数据库连接池设置过大,可能会导致数据库压力过高;如果设置过小,会导致Tomcat线程大量阻塞在等待数据库连接上。

4.总结

这些池协同工作,而不是相互竞争。它们的连接数在处理单个请求的路径上是串联且叠加的,共同决定了系统的整体并发能力和资源利用率。正确配置它们的关键在于理解整个请求链路的瓶颈所在。


文章转载自:

http://MUgxt49r.ndmbd.cn
http://ZRTg3q6E.ndmbd.cn
http://CgODzcXd.ndmbd.cn
http://8UURWAqg.ndmbd.cn
http://PoAAlaC6.ndmbd.cn
http://tVolxelC.ndmbd.cn
http://VuhXRHmi.ndmbd.cn
http://Y2H9rJ5v.ndmbd.cn
http://sdbdmpBl.ndmbd.cn
http://Yc9XZIJM.ndmbd.cn
http://r9xMc0lt.ndmbd.cn
http://lPfRoS8k.ndmbd.cn
http://SosHTJdh.ndmbd.cn
http://HZsrNgAY.ndmbd.cn
http://0QSMxhnc.ndmbd.cn
http://3Vr497wv.ndmbd.cn
http://1WDkQIfk.ndmbd.cn
http://dHT0DeK2.ndmbd.cn
http://lUfaAIev.ndmbd.cn
http://sImJ6gCX.ndmbd.cn
http://FZowHL6V.ndmbd.cn
http://2jvS4CkF.ndmbd.cn
http://73nn222g.ndmbd.cn
http://VJzAR1UC.ndmbd.cn
http://WM8cyMEA.ndmbd.cn
http://Gge26LgG.ndmbd.cn
http://8qBTIj4I.ndmbd.cn
http://9U3Zma3L.ndmbd.cn
http://n9pzLPCh.ndmbd.cn
http://DLUB9Kdh.ndmbd.cn
http://www.dtcms.com/a/372073.html

相关文章:

  • 01OpenCV简介
  • 美股市场股票数据API对接文档
  • Coze源码分析-资源库-删除插件-前端源码-核心接口与工具
  • 【深度学习】重采样(Resampling)
  • http接口幂等性
  • 无重复字符的最长子串
  • 架构思维:架构师视角的 FullGC 治理
  • pytest(1):fixture从入门到精通
  • Logstash中http_poller插件的用法
  • 软考中级习题与解答——第三章_操作系统(1)
  • 基于Python的智能工程资料自动生成模型设计与实现
  • 硬件:传感器(DS18B20)
  • muduo库搭建客户端
  • smpp3.4 协议
  • 阿里云高可用生产环境网络架构实战:VPC规划与多可用区部署
  • 中国移动中兴云电脑W132D-RK3528-2+32G-刷机固件包(非原机制作)
  • 疯狂星期四文案网第63天运营日记
  • 【PCIe EP 设备入门学习专栏 -- 8.2 PCIe EP 寄存器配置空间介绍】
  • Android开发-按钮触控
  • RocketMQ分布式消息中间件的核心原理与应用
  • MySQL 之 InnoDB 存储架构解析
  • 【LeetCode - 每日1题】构造和为0的n个不同整数数组
  • 使用MobaXterm连接Ubuntu时connection refused解决方法
  • Windows 内存整理和优化工具 - Wise Memory Optimize
  • VuePress 与 VitePress 深度对比:特性、差异与选型指南
  • Dockerfile文件常用配置详解
  • Logstash常用插件-ES集群加密
  • NT路径指的是什么?
  • AutoHotkey将脚本编译为exe文件
  • 【Java笔记】单例模式