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

轻量级动态可监控线程池 - DynamicTp

一、背景介绍 

使用线程池ThreadPoolExecutor的过程中你是否有以下痛点呢?

  • 代码中创建了一个 ThreadPoolExecutor,但是不知道那几个核心参数设置多少比较合适
  • 凭经验设置参数值,上线后发现需要调整,改代码重新发布服务,非常麻烦
  • 线程池相对开发人员来说是个黑盒,运行情况不能及时感知到,直到出现问题

如果有以上痛点,动态可监控线程池框架(DynamicTp)或许可以帮助到我们。 

如果看过 ThreadPoolExecutor 的源码,大概可以知道它对核心参数基本都有提供 set / get 方法以及一些扩展方法,可以在运行时动态修改、获取相应的值,这些方法有:

public void setCorePoolSize(int corePoolSize);
public void setMaximumPoolSize(int maximumPoolSize);
public void setKeepAliveTime(long time, TimeUnit unit);
public void setThreadFactory(ThreadFactory threadFactory);
public void setRejectedExecutionHandler(RejectedExecutionHandler handler);
public void allowCoreThreadTimeOut(boolean value);public int getCorePoolSize();
public int getMaximumPoolSize();
public long getKeepAliveTime(TimeUnit unit);
public BlockingQueue<Runnable> getQueue();
public RejectedExecutionHandler getRejectedExecutionHandler();
public boolean allowsCoreThreadTimeOut();protected void beforeExecute(Thread t, Runnable r);
protected void afterExecute(Runnable r, Throwable t);

现在大多数的互联网项目都会微服务化部署,有一套自己的服务治理体系,微服务组件中的分布式配置中心扮演的就是动态修改配置,实时生效的角色。

那么我们是否可以结合配置中心来做运行时线程池参数的动态调整呢?

答案是肯定的,而且配置中心相对都是高可用的,使用它也不用过于担心配置推送失败这类问题,而且也能减少研发动态线程池组件本身的难度和工作量。 

综上,可以总结出以下的背景:

  • 广泛性在 Java 开发中,想要提高系统性能,线程池已经是一个 90% 以上开发都会选择使用的基础工具
  • 不确定性:项目中可能会创建很多线程池,既有IO密集型的,也有CPU密集型的,但线程池的参数并不好确定,需要有套机制在运行过程中动态去调整参数
  • 无感知性:线程池运行过程中的各项指标一般感知不到;需要有套监控报警机制在事前、事中就能让开发人员感知到线程池的运行状况,及时处理
  • 高可用性:配置变更需要及时推送到客户端,需要有高可用的配置管理推送服务,配置中心是现在大多数互联网系统都会使用的组件,与之结合可以极大提高系统可用性

二、DynamicTp的功能特性

基于以上背景分析,DynamicTp框架对线程池 ThreadPoolExecutor 做一些扩展增强,主要实现以下目标: 

  • 实现对运行中线程池参数的动态修改,实时生效
  • 实时监控线程池的运行状态,触发设置的报警策略时报警,报警信息推送办公平台
  • 定时采集线程池指标数据,配合像 Grafana 这种可视化监控平台做大盘监控

DynamicTp框架的官网地址为:

https://dynamictp.cn/

经过多个版本的迭代,目前最新版本 v1.1.7 具有以下特性:

  • 代码零侵入:我们改变了线程池以往的使用姿势,所有配置均放在配置中心,服务启动时会从配置中心拉取配置生成线程池对象放到 Spring 容器中,使用时直接从 Spring 容器中获取,对业务代码零侵入

  • 通知告警:提供多种报警维度(配置变更通知、活性报警、容量阈值报警、拒绝触发报警、任务执行或等待超时报警),已支持企业微信、钉钉、飞书、邮件报警,同时提供 SPI 接口可自定义扩展实现

  • 运行监控:定时采集线程池指标数据,支持通过 MicroMeter、JsonLog 日志输出、Endpoint 三种方式,可通过 SPI 接口自定义扩展实现

  • 任务增强:提供任务包装功能,实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper,可以支持线程池上下文信息传递

  • 多配置中心支持:基于主流配置中心实现线程池参数动态调整,实时生效,已支持 Nacos、Apollo、Zookeeper、Consul、Etcd、Polaris,同时也提供 SPI 接口可自定义扩展实现

  • 中间件线程池管理:集成管理常用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars、SofaRpc、RabbitMq 等组件的线程池管理(调参、监控报警)

  • 轻量简单:基于 SpringBoot 实现,引入 starter,接入只需简单 4 步就可完成,顺利 3 分钟搞定

  • 多模式:参考 Tomcat 线程池提供了 IO 密集型场景使用的 EagerDtpExecutor 线程池

  • 兼容性:JUC 普通线程池和 Spring 中的 ThreadPoolTaskExecutor 也可以被框架监控,@Bean 定义时加 @DynamicTp 注解即可

  • 可靠性:框架提供的线程池实现 Spring 生命周期方法,可以在 Spring 容器关闭前尽可能多的处理队列中的任务

  • 高可扩展:框架核心功能都提供 SPI 接口供用户自定义个性化实现(配置中心、配置文件解析、通知告警、监控数据采集、任务包装等等)

  • 线上大规模应用:参考美团线程池实践,美团内部已经有该理论成熟的应用经验

美团线程池实践:

https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html

三、架构设计

DynamicTp框架功能大体可以分为以下几个模块:

  1. 配置变更监听模块

  2. 线程池管理模块

  3. 监控模块

  4. 通知告警模块

  5. 三方组件线程池管理模块

四、接入步骤 

4.1 引入依赖

在pom.xml中引入下述依赖:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath/></parent><groupId>com.bc</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencyManagement><dependencies><!-- 整合Spring Boot--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.3.RELEASE</version><type>pom</type><scope>import</scope></dependency><!-- 整合Spring Cloud --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Greenwich.SR3</version><type>pom</type><scope>import</scope></dependency><!-- 整合Spring Cloud Alibaba --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.1.2.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>6.0.1.Final</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>org.dromara.dynamictp</groupId><artifactId>dynamic-tp-spring-cloud-starter-nacos</artifactId><version>1.1.7</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

这里有一个可能需要关注的问题,就是曾经在引入了上述依赖后,启动项目会报下述错误:

Caused by: java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/json/JsonMapper

解决方法:就是关于jackson的版本依赖低了,将其版本设定为2.10.0版本就可以解决这个问题 

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.10.0</version>
</dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.10.0</version>
</dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>2.10.0</version>
</dependency>

4.2 配置中心配置线程池实例 

采用nacos作为配置中心,nacos服务端的版本为v2.1.2(此处我是采用Docker进行搭建服务端),nacos客户端的版本在上述依赖引入中也是2.1.2版本。

第一步: 在nacos中创建一个新的命名空间local

新创建的命名空间local其命名空间ID为:322d6d5e-16bb-4f0d-846a-bbb1012eff92 

第二步:在配置管理的local命名空间下新增配置记录

新增配置的各个选项中,其Data ID为dtp_config,GROUP为DTP_SERVER_GROUP,描述为DTP动态线程池服务的配置分组,配置格式为YAML

其中Data ID的命令规则为:应用名-环境profile名.文件后缀

  • 应用名(Data ID前缀):默认为  ${spring.appliction.name}, 也可以使用${spring.cloud.nacos.config.prefix}来配置
  • 环境profile名:既${spring.profiles.active}指定的环境。  若不区分环境,则本内容既前面的中划线都可以不用存在。
  • 文件后缀:既SpringBoot中配置文件扩展名,也是Nacos中的配置格式。

上述yaml里面的配置内容如下所示: 

spring:dynamic:tp:enabled: true                               # 是否启用dynamictp,默认trueenabledBanner: true                        # 是否打印启动图标,默认为true表示打印,值为false表示不打印   enabledCollect: true                        # 是否开启监控指标采集,默认truecollectorTypes: micrometer,logging          # 监控数据采集器类型(logging | micrometer | internal_logging | JMX),默认micrometerlogPath: /home/logs/dynamictp/user-center/  # 监控日志数据路径,默认 ${user.home}/logs,采集类型非logging不用配置monitorInterval: 5                          # 监控时间间隔(报警检测、指标采集),默认5splatforms:                                  # 通知报警平台配置- platform: wechatplatformId: 1                            # 平台id,自定义urlKey: 3a700-127-4bd-a798-c53d8b69c     # webhook 中的 keyreceivers: test1,test2                   # 接受人企微账号- platform: dingplatformId: 2                            # 平台id,自定义urlKey: f80dad441fcd655438f4a08dcd6a     # webhook 中的 access_tokensecret: SECb5441fa6f375d5b9d21           # 安全设置在验签模式下才的秘钥,非验签模式没有此值receivers: 18888888888                   # 钉钉账号手机号- platform: larkplatformId: 3urlKey: 0d944ae7-b24a-40                 # webhook 中的 tokensecret: 3a750012874bdac5c3d8b69c         # 安全设置在签名校验模式下才的秘钥,非验签模式没有此值receivers: test1,test2                   # 接受人username / openid- platform: emailplatformId: 4receivers: 123456789@163.com            # 收件人邮箱,多个用逗号隔开executors:                               # 动态线程池配置,都有默认值,采用默认值的可以不配置该项,减少配置量- threadPoolName: dtpExecutor          # 线程池名称,必填threadPoolAliasName: 测试线程池        # 线程池别名,可选executorType: common                 # 线程池类型 common、eager、ordered、scheduled、priority,默认 commoncorePoolSize: 6                      # 核心线程数,默认为1maximumPoolSize: 8                   # 最大线程数,默认cpu核数queueCapacity: 2000                  # 队列容量,默认1024queueType: VariableLinkedBlockingQueue         # 任务队列,查看源码QueueTypeEnum枚举类,默认VariableLinkedBlockingQueuerejectedHandlerType: CallerRunsPolicy          # 拒绝策略,查看RejectedTypeEnum枚举类,默认AbortPolicykeepAliveTime: 60                              # 空闲线程等待超时时间,默认60threadNamePrefix: test                         # 线程名前缀,默认dtpallowCoreThreadTimeOut: false                  # 是否允许核心线程池超时,默认falsewaitForTasksToCompleteOnShutdown: true         # 参考spring线程池设计,优雅关闭线程池,默认trueawaitTerminationSeconds: 5                     # 优雅关闭线程池时,阻塞等待线程池中任务执行时间,默认3,单位(s)preStartAllCoreThreads: false                  # 是否预热所有核心线程,默认falserunTimeout: 0                                  # 任务执行超时阈值,单位(ms),默认0(不统计)queueTimeout: 0                                # 任务在队列等待超时阈值,单位(ms),默认0(不统计)taskWrapperNames: ["ttl", "mdc"]               # 任务包装器名称,继承TaskWrapper接口notifyEnabled: true                            # 是否开启报警,默认trueplatformIds: [4]                             # 报警平台id,不配置默认拿上层platforms配置的所有平台notifyItems:                     # 报警项,不配置自动会按默认值(查看源码NotifyItem类)配置(变更通知、容量报警、活性报警、拒绝报警、任务超时报警)- type: changeenabled: true- type: capacity               # 队列容量使用率,报警项类型,查看源码 NotifyTypeEnum枚举类enabled: truethreshold: 80                # 报警阈值,默认70,意思是队列使用率达到70%告警platformIds: [2]             # 可选配置,本配置优先级 > 所属线程池platformIds > 全局配置platformsinterval: 120                # 报警间隔(单位:s),默认120- type: liveness               # 线程池活性enabled: truethreshold: 80                # 报警阈值,默认 70,意思是活性达到70%告警- type: reject                 # 触发任务拒绝告警enabled: truethreshold: 100               # 默认阈值10- type: run_timeout            # 任务执行超时告警enabled: truethreshold: 5                 # 默认阈值10- type: queue_timeout          # 任务排队超时告警enabled: truethreshold: 100               # 默认阈值10

点击发布以后,在其配置管理列表中就会新增一条记录:

4.3 客户端的配置文件

在resources目录下新建一个名为application.yml的文件:

server:port: 10086spring:application:name: dtp-server

接着还需在resources目录下新建一个名为bootstrap.yml的文件(bootstrap.yml文件加载顺序先于application.yml): 

spring:cloud:nacos:discovery:server-addr:  127.0.0.1:28999namespace: 322d6d5e-16bb-4f0d-846a-bbb1012eff92config:server-addr: 127.0.0.1:28999namespace: 322d6d5e-16bb-4f0d-846a-bbb1012eff92group: DTP_SERVER_GROUP                           # 配置组(如果不指定,则默认为DEFAULT_GROUP)prefix: dtp_config                                # Data ID的前缀(如果不指定,则默认取 ${spring.appliction.name})file-extension: yaml                             # 指定文件后缀(如果不指定,则默认为properties),此处指定为yaml格式extension-configs:- dataId: dtp_config.yamlgroup: DTP_SERVER_GROUPrefresh: true                 # 必须配置,负责自动刷新不生效refresh-enabled: true                           # 如果在Nacos控制台界面中人工调整配置项的值,SpringBoot会立即自动取得最新值。因为Nacos客户端带自动刷新功能,可以通过配置 spring.cloud.nacos.config.refresh.enabled=false 来关闭自动刷新

至此关于这一步的操作算是已经完成了,接下来将说一下nacos的共享和常规配置文件相关的扩展内容。

spring.cloud.nacod.config.shared-configs:这是一个用于读取共享的配置文件的配置项,里面可以有多组配置。 在源码中有三个属性:data-id、group、refresh。

public static class Config {private String dataId;private String group;private boolean refresh;...
}

spring.cloud.nacod.config.extension-configs:在shared-configs之后加载,但是优先级大于shared-configs,一般用于单模块配置。shared-configs可以配置为项目共有配置,如redis配置,数据库链接等等。

spring:cloud:nacos:discovery:server-addr:  127.0.0.1:28999namespace: 322d6d5e-16bb-4f0d-846a-bbb1012eff92config:server-addr: 127.0.0.1:28999namespace: 322d6d5e-16bb-4f0d-846a-bbb1012eff92group: DTP_SERVER_GROUP                           # 配置组(如果不指定,则默认为DEFAULT_GROUP)prefix: dtp_config                                # Data ID的前缀(如果不指定,则默认取 ${spring.appliction.name})file-extension: yaml                              # 指定文件后缀(如果不指定,则默认为properties),此处指定为yaml格式# 用于共享的配置文件shared-configs:- data-id: common-mysql.yamlgroup: SPRING_CLOUD_EXAMPLE_GROUP- data-id: common-redis.yamlgroup: SPRING_CLOUD_EXAMPLE_GROUP- data-id: common-base.yamlgroup: SPRING_CLOUD_EXAMPLE_GROUP# 常规配置文件,优先级大于 shared-configs,在 shared-configs 之后加载extension-configs:- data-id: nacos-config-advanced.yamlgroup: SPRING_CLOUD_EXAMPLE_GROUPrefresh: true- data-id: nacos-config-base.yamlgroup: SPRING_CLOUD_EXAMPLE_GROUPrefresh: true		refresh-enabled: true

4.4  启动类添加@EnableDynamicTp注解

package com.example.demo;import org.dromara.dynamictp.core.spring.EnableDynamicTp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableDynamicTp
@EnableDiscoveryClient
@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

4.5 使用DtpExecutor

第一种方式:使用@Resource 或 @Autowired 进行依赖注入 

package com.example.demo.controller;import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dromara.dynamictp.core.executor.DtpExecutor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;import java.net.InetAddress;@Slf4j
@RefreshScope
@RestController
@RequestMapping("/demo")
public class DemoController {@Value("${spring.dynamic.tp.executors[0].corePoolSize}")String springDynamicTpExecutor0CorePoolSize;@Resourceprivate DtpExecutor dtpExecutor;@GetMapping("/select")public String select() throws Exception {log.info("executors[0]的核心线程数:{}",springDynamicTpExecutor0CorePoolSize);dtpExecutor.execute(() -> {log.info("开始执行任务");try {Thread.sleep(5000);} catch (Exception e) {e.printStackTrace();}log.info("结束执行任务");});JSONObject jsonObject = new JSONObject(true);jsonObject.put("IP", InetAddress.getLocalHost().getHostAddress());jsonObject.put("可用处理器数量", Runtime.getRuntime().availableProcessors());jsonObject.put("核心线程数", dtpExecutor.getCorePoolSize());jsonObject.put("最大线程数", dtpExecutor.getMaximumPoolSize());jsonObject.put("正在工作的线程数", dtpExecutor.getActiveCount());jsonObject.put("队列中的任务数", dtpExecutor.getQueue().size());jsonObject.put("已提交的任务总数", dtpExecutor.getTaskCount());return jsonObject.toString();}
}

第二种方式:通过 DtpRegistry.getDtpExecutor("name") 获取 

package com.example.demo.controller;import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dromara.dynamictp.core.DtpRegistry;
import org.dromara.dynamictp.core.executor.DtpExecutor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.net.InetAddress;@Slf4j
@RefreshScope
@RestController
@RequestMapping("/demo")
public class DemoController {@Value("${spring.dynamic.tp.executors[0].corePoolSize}")String springDynamicTpExecutor0CorePoolSize;@GetMapping("/select")public String select() throws Exception {log.info("executors[0]的核心线程数:{}",springDynamicTpExecutor0CorePoolSize);DtpExecutor dtpExecutor = DtpRegistry.getDtpExecutor("dtpExecutor");dtpExecutor.execute(() -> {log.info("开始执行任务");try {Thread.sleep(5000);} catch (Exception e) {e.printStackTrace();}log.info("结束执行任务");});JSONObject jsonObject = new JSONObject(true);jsonObject.put("IP", InetAddress.getLocalHost().getHostAddress());jsonObject.put("可用处理器数量", Runtime.getRuntime().availableProcessors());jsonObject.put("核心线程数", dtpExecutor.getCorePoolSize());jsonObject.put("最大线程数", dtpExecutor.getMaximumPoolSize());jsonObject.put("正在工作的线程数", dtpExecutor.getActiveCount());jsonObject.put("队列中的任务数", dtpExecutor.getQueue().size());jsonObject.put("已提交的任务总数", dtpExecutor.getTaskCount());return jsonObject.toString();}
}

经过测试,不管是才有那种获取方式都可以实现线程池的动态修改和实时更新! 

五、通知报警

触发报警阈值会推送相应报警消息(活性、容量、拒绝、任务等待超时、任务执行超时),且会高亮显示相应字段,如下图所示:

配置变更会推送通知消息,且会高亮变更的字段,如下图所示:

特别说明:在引入邮件推送时,这个还需要引入额外的依赖包,具体怎么做可以参考官方技术文档。 

六、监控 

目前框架提供了四种监控数据采集方式,通过 collectorTypes 属性配置监控指标采集类型,默认 Micrometer:

  • Logging:线程池指标数据会以 Json 格式输出到指定的日志文件里

  • Internal_logging:线程池指标数据会以 Json 格式输出到项目日志文件里

  • Micrometer:采用监控门面,通过引入相关 Micrometer 依赖采集到相应的存储平台里(如 Prometheus,InfluxDb...)

  • Endpoint:暴露 Endpoint 端点,可以通过 http 方式实时获取指标数据

相关文章:

轻量级动态可监控线程池 - DynamicTp

一、背景介绍 使用线程池ThreadPoolExecutor的过程中你是否有以下痛点呢&#xff1f; 代码中创建了一个 ThreadPoolExecutor&#xff0c;但是不知道那几个核心参数设置多少比较合适凭经验设置参数值&#xff0c;上线后发现需要调整&#xff0c;改代码重新发布服务&#xff0c…...

对于vsc中的vue命令 vue.json

打开vsc 然后在左下角有一个设置 2.点击用户代码片段 3.输入 vue.json回车 将此代码粘贴 &#xff08;我的不一定都适合&#xff09; { "vue2 template": { "prefix": "v2", "body": [ "<template>", " <…...

Spring Boot 官方不再支持 Spring Boot 的 2.x 版本!新idea如何创建java8项目

idea现在只能创建最少jdk17 使用 IDEA 内置的 Spring Initializr 创建 Spring Boot 新项目时&#xff0c;没有 Java 8 的选项了&#xff0c;只剩下了 > 17 的版本 是因为 Spring Boot 官方不再支持 Spring Boot 的 2.x 版本了&#xff0c;之后全力维护 3.x&#xff1b;而 …...

分享一个 ASP.NET Web Api 上传和读取 Excel的方案

前言 许多业务场景下需要处理和分析大量的数据&#xff0c;而 Excel 是业务人员常用的数据表格工具&#xff0c;因此&#xff0c;将 Excel 表格中内容上传并读取到网站&#xff0c;是一个很常见的功能&#xff0c;目前有许多成熟的开源或者商业的第三方库&#xff0c;比如 NPO…...

【算法实战】每日一题:将某个序列中内的每个元素都设为相同的值的最短次数(差分数组解法,附概念理解以及实战操作)

题目 将某个序列中内的每个元素都设为相同的值的最短次数 1.差分数组&#xff08;后面的减去前面的值存储的位置可以理解为中间&#xff09; 差分数组用于处理序列中的区间更新和查询问题。它存储序列中相邻元素之间的差值&#xff0c;而不是直接存储每个元素的值 怎么对某…...

EXCEL数据透视图中的日期字段,怎样自动分出年、季度、月的功能?

在excel里&#xff0c;这个果然是有个设置的地方&#xff0c;修改后就好了。 点击文件选项卡&#xff0c;选项&#xff0c;在高级里&#xff0c;将图示选项的勾选给取消&#xff0c;然后再创建数据透视表或透视图&#xff0c;日期就不会自动组合了&#xff1a; 这个选项只对新…...

【设计模式深度剖析】【1】【行为型】【模板方法模式】| 以烹饪过程为例加深理解

&#x1f448;️上一篇:结构型设计模式对比 文章目录 模板方法模式定义英文原话直译如何理解呢&#xff1f; 2个角色类图代码示例 应用优点缺点使用场景 示例解析&#xff1a;以烹饪过程为例类图代码示例 模板方法模式 模板方法模式&#xff08;Template Method Pattern&…...

JAVA:异步任务处理类CompletableFuture让性能提升一倍

一、前言 CompletableFuture 是 Java 8 引入的一个功能强大的类&#xff0c;用于异步编程。它表示一个可能尚未完成的计算的结果&#xff0c;你可以对其添加回调函数来在计算完成时执行某些操作。在 Spring Boot 应用中&#xff0c;CompletableFuture 可以用于提高应用的响应性…...

10Linux 进程管理学习笔记

Linux 进程管理 目录 文章目录 Linux 进程管理一.进程1.显示当前进程状态(ps)进程树(pstree)1.1实时显示进程信息(top)顶部概览信息&#xff1a;CPU 状态&#xff1a;内存状态&#xff1a;进程信息表头&#xff1a;进程列表&#xff1a;1.2(htop) 2.终止进程(kill)2.1通过名称…...

一些关于深度聚类以及部分对比学习的论文阅读笔记

目录 资料SwAV问题方法方法的创新点为什么有效有什么可以借鉴的地方聚类Multi-crop 代码 PCL代码 Feature Alignment and Uniformity for Test Time Adaptation代码 SimSiam 资料 深度聚类算法研究综述(很赞&#xff0c;从聚类方法和深度学习方法两个方面进行了总结&#xff0…...

【ARM-Linux篇】u-boot编译

一、u-boot简介 uboot是一种通用的引导加载程序&#xff0c;它可以用于多种嵌入式系统&#xff0c;支持多种操作系统&#xff0c;如Linux, Android,NetBSD等。uboot的主要作用是将操作系统内核从存储设备&#xff08;如Flash, SD卡等&#xff09;加载到内存中&#xff0c;并执…...

Lombok一文通

1、Lombok简介 作为java的忠实粉丝&#xff0c;但也不得不承认&#xff0c;java是一门比较啰嗦的语言&#xff0c;很多代码的编写远不如其他静态语言方便&#xff0c;更别说跟脚本语言比较了。 因此&#xff0c;lombok应运而生。 Lombok是一种工具库&#xff0c;它提供了一组…...

Seq2Seq模型:详述其发展历程、深远影响与结构深度剖析

Seq2Seq&#xff08;Sequence-to-Sequence&#xff09;模型是一种深度学习架构&#xff0c;专为处理从一个输入序列到一个输出序列的映射任务设计。这种模型最初应用于机器翻译任务&#xff0c;但因其灵活性和有效性&#xff0c;现已被广泛应用于自然语言处理&#xff08;NLP&a…...

公网如何访问内网?

公网和内网已经成为我们生活中不可或缺的存在。由于内网的安全性考虑&#xff0c;公网无法直接访问内网资源。如何实现公网访问内网呢&#xff1f;本文将介绍一种名为【天联】的私有通道技术&#xff0c;通过安全加密&#xff0c;保障数据传输的安全性。 【天联】私有通道技术 …...

手机定制开发_基于天玑900的5G安卓手机定制方案

手机定制方案基于联发科天玑900强劲旗舰八核2.4GHz处理器。这款处理器采用了6nm先进制程工艺&#xff0c;为用户带来了痛快淋漓的性能体验。不论是进行游戏还是日常娱乐&#xff0c;用户都能轻松驾驭。手机搭载了最新的Android 13操作系统&#xff0c;提高了数据读取的准确性&a…...

免费,C++蓝桥杯等级考试真题--第2级

C蓝桥杯等级考试真题–第2级...

panic 、asset、crash 的含义和区别

在编程中&#xff0c;“panic” 和 “assert” 都是用于处理错误和异常情况的机制&#xff0c;但在不同的编程语言和框架中有一些区别。 panic&#xff1a; 含义&#xff1a;通常表示程序发生了无法恢复的错误或异常情况&#xff0c;需要立即终止程序的执行。 用法&#xff1…...

解决Windows 10通过SSH连接Ubuntu 20.04时的“Permission Denied”错误

在使用SSH连接远程服务器时&#xff0c;我们经常可能遇到各种连接错误&#xff0c;其中“Permission denied, please try again”是较为常见的一种。本文将分享一次实际案例的解决过程&#xff0c;帮助你理解如何排查并解决这类问题。 问题描述 在尝试从Windows 10系统通过SS…...

Windows 下 PostgreSQL 图形化界面安装、配置详解

相信大家对PostgreSQL都不陌生吧&#xff0c;自从MySQL被Oracle所控制后&#xff0c;PostgreSQL就成为了国内去O的首选数据库了&#xff0c;并且PostgreSQL目前不受任何商业公司控制&#xff0c;所以国内很多厂商都是基于PostgreSQL做二次开发来实现数据库自主可控的目标(国内很…...

曾巩,散文的艺术与哲思

曾巩&#xff0c;字子固&#xff0c;世称南丰先生&#xff0c;南丰&#xff08;今江西&#xff09;人&#xff0c;生于北宋真宗天禧三年&#xff08;公元1019年&#xff09;&#xff0c;卒于北宋元丰六年&#xff08;公元1083年&#xff09;&#xff0c;享年64岁。他是中国北宋…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...