定时任务几乎是每个业务系统必不可少的功能,计算到期时间、过期时间等,定时触发某项任务操作。在使用单体应用时,基本使用Spring提供的注解即可实现定时任务,而在使用微服务集群时,这种方式就要考虑添加分布式锁来防止多个微服务同时运行定时任务而导致同一个任务重复执行。

  除了使用注解,现在还有一种方式,就是搭建分布式任务平台,所有的微服务注册到分布式任务平台,由分布式任务平台统一调度,这样避免了同一任务被重复执行。这里我们选择使用XXL-JOB作为分布式任务调度平台,XXL-JOB核心设计目标是开发迅速、学习简单、轻量级、易扩展。

  使用分布式任务调度平台的优点除了避免同一任务重复执行外,还有使用简单,可以手动执行、有详细的调度日志查看任务具体执行情况等优点。

  XXL-JOB官方架构设计图:

  下面我们按照步骤来介绍,如何结合我们的微服务平台将分布式任务调度平台XXL-JOB集成进来,实现我们需要的定时任务功能。

一、微服务框架整合xxl-job-admin

1、XXL-JOB开源网站下载源码,下载地址 https://github.com/xuxueli/xxl-job/releases ,下载下来的源码如下:
xxl-job-admin:调度中心
xxl-job-core:公共依赖
xxl-job-executor-samples:执行器Sample示例(选择合适的版本执行器,可直接使用,也可以参考其并将现有项目改造成执行器)
:xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器,推荐这种方式;
:xxl-job-executor-sample-frameless:无框架版本;

  下载下来的开源包有三个目录:xxl-job-admin、xxl-job-core和xxl-job-executor-samples,顾名思义,xxl-job-admin是分布式任务平台的服务端兼管理台,我们需要部署的也是这个工程,我们可以把整个工程集成到我们的微服务中,统一打包部署;xxl-job-core是公共依赖包,我们其他需要实现定时任务的微服务需要引入这个包来实现定时任务执行器。xxl-job-executor-samples为定时任务执行器的实例代码。

2、在基础平台gitegg-platform工程gitegg-platform-bom中引入xxl-job-core核心包,统一版本管理。
......
<!--分布式任务调度平台XXL-JOB核心包-->
<xxl-job.version>2.3.1</xxl-job.version>
......
<!--分布式任务调度平台XXL-JOB核心包-->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job.version}</version>
</dependency>
3、将xxl-job-admin集成到微服务工程中,方便统一打包部署

  根据我们的微服务架构设计,gitegg-plugin作为我们系统的插件工程,里面放置我们需要的插件服务。有些插件是必须的,而有些插件可能会用不到,此时我们就可以根据自己的业务需求去选择部署业务插件。

  为和我们的微服务深度集成就不是解耦的特性,我们需要对xxl-job-admin的配置文件进行适当的修改:

  • 首先修改pom.xml,保持各依赖库版本一致,修改parent标签,使其引用GitEgg工程的基础jar包和微服务配置注册功能,同时排除logback,使用log4j2记录日志
<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>gitegg-plugin</artifactId>
<groupId>com.gitegg.cloud</groupId>
<version>1.0.1.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion> <artifactId>gitegg-job</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.test.skip>true</maven.test.skip> <netty-all.version>4.1.63.Final</netty-all.version>
<gson.version>2.9.0</gson.version> <spring.version>5.3.20</spring.version>
<spring-boot.version>2.6.7</spring-boot.version> <mybatis-spring-boot-starter.version>2.2.2</mybatis-spring-boot-starter.version>
<mysql-connector-java.version>8.0.29</mysql-connector-java.version> <slf4j-api.version>1.7.36</slf4j-api.version>
<junit-jupiter.version>5.8.2</junit-jupiter.version>
<javax.annotation-api.version>1.3.2</javax.annotation-api.version> <groovy.version>3.0.10</groovy.version> <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.4.0</maven-javadoc-plugin.version>
<maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version>
</properties> <dependencies>
<!-- gitegg Spring Boot自定义及扩展 -->
<dependency>
<groupId>com.gitegg.platform</groupId>
<artifactId>gitegg-platform-boot</artifactId>
</dependency>
<!-- gitegg Spring Cloud自定义及扩展 -->
<dependency>
<groupId>com.gitegg.platform</groupId>
<artifactId>gitegg-platform-cloud</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.version}</version>
<!-- 去除springboot默认的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- 去除springboot默认的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- freemarker-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<!-- 去除springboot默认的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- mail-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<!-- 去除springboot默认的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- starter-actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<!-- 去除springboot默认的logback配置-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<!--分布式任务调度平台XXL-JOB核心包-->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<!-- 去除冲突的slf4j配置-->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
  • 修改application.properties ,根据我们系统的规范,新增bootstrap.yml、bootstrap-dev.yml、bootstrap-prod.yml、bootstrap-test.yml文件。将application.properties部分配置,移到bootstrap.yml配置中。因xxl-job-admin单独数据库,且其默认使用的是Hikari数据库连接池,这里我们不打算改动,仍然使其保持原有的数据库配置,我们将可配置的内容放置在Nacos微服务配置中心上,同时在bootstrap.yml中添加多yaml文件配置(请注意,在我们本地使用的是yml结尾的文件,Nacos服务注册中心上使用的是yaml结尾的文件,两者是一样的,只是扩展名的不同)。

bootstrap.yml配置:

server:
port: 8007
spring:
profiles:
active: '@spring.profiles.active@'
application:
name: '@artifactId@'
cloud:
inetutils:
ignored-interfaces: docker0
nacos:
discovery:
server-addr: ${spring.nacos.addr}
config:
server-addr: ${spring.nacos.addr}
file-extension: yaml
extension-configs:
# 必须带文件扩展名,此时 file-extension 的配置对自定义扩展配置的 Data Id 文件扩展名没有影响
- data-id: ${spring.nacos.config.prefix}.yaml
group: ${spring.nacos.config.group}
refresh: true
- data-id: ${spring.nacos.config.prefix}-xxl-job.yaml
group: ${spring.nacos.config.group}
refresh: true
### xxl-job-admin config
mvc:
servlet:
load-on-startup: 0
static-path-pattern: /static/**
resources:
static-locations: classpath:/static/
### freemarker
freemarker:
templateLoaderPath: classpath:/templates/
suffix: .ftl
charset: UTF-8
request-context-attribute: request
settings.number_format: 0.##########
### actuator
management:
server:
servlet:
context-path: /actuator
health:
mail:
enabled: false
### mybatis
mybatis:
mapper-locations: classpath:/mybatis-mapper/*Mapper.xml

Nacos上gitegg-cloud-config-xxl-job.yaml配置:

server:
servlet:
context-path: /xxl-job-admin
spring:
datasource:
url: jdbc:mysql://127.0.0.1/xxl_job?useSSL=false&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
### datasource-pool
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 10
maximum-pool-size: 30
auto-commit: true
idle-timeout: 30000
pool-name: HikariCP
max-lifetime: 900000
connection-timeout: 10000
connection-test-query: SELECT 1
validation-timeout: 1000
### email
mail:
host: smtp.qq.com
port: 25
username: xxx@qq.com
from: xxx@qq.com
password: xxx
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
socketFactory:
class: javax.net.ssl.SSLSocketFactory
### xxl-job, access token
xxl:
job:
accessToken: default_token
### xxl-job, i18n (default is zh_CN, and you can choose "zh_CN", "zh_TC" and "en")
i18n: zh_CN
## xxl-job, triggerpool max size
triggerpool:
fast:
max: 200
slow:
max: 100
### xxl-job, log retention days
logretentiondays: 30
4、初始化xxl-job-admin需要的数据库脚本

  初始化脚本存放在下载的包目录的\xxl-job-2.3.1\doc\db\tables_xxl_job.sql中,一共需要8张表。我们将xxl-job-admin的数据库和业务数据库分开,配置不同的数据源,在Nacos配置单独的xxl-job-admin配置文件。

  • 新建xxl_job数据库

  • 打开数据库执行建表语句

5、在GitEgg工程的父级pom.xml下添加静态文件过滤

  xxl-job-admin是SpringMVC项目,其前端页面由ftl文件和静态文件组成,默认情况下maven启用分环境读取配置时,会对resource目录下的@进行替换,导致静态文件下的字体文件不能用,所以,这里需要进行和jks文件一样的过滤配置:

        <resources>
<!-- 增加分环境读取配置 -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.jks</exclude>
<exclude>static/**</exclude>
</excludes>
</resource>
<!-- 解决jks被过滤掉的问题 -->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.jks</include>
<include>static/**</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
6、在Gateway添加xxl-job-admin路由转发

  xxl-job-admin路由转发需要添加两方面内容,一个是xxl-job-admin注册到Nacos注册中心上的gitegg-job服务,一个是xxl-job-admin前端页面请求的静态文件转发。第一个是为了和我们整体微服务保持一致,第二个是为了解决xxl-job-admin前端ftl页面在请求静态文件时,请求的是/xxl-job-admin根路径。新增Gateway路由转发配置如下:

        - id: gitegg-job
uri: lb://gitegg-job
predicates:
- Path=/gitegg-job/**
filters:
- StripPrefix=1
- id: xxl-job-admin
uri: lb://gitegg-job
predicates:
- Path=/xxl-job-admin/**
filters:
- StripPrefix=0
7、增加xxl-job-admin访问白名单

  xxl-job-admin有自己的权限访问控制,我们不在网关对其进行鉴权,所以在Nacos配置中,增加白名单配置:

# 网关放行设置 1、whiteUrls不需要鉴权的公共url,白名单,配置白名单路径 2、authUrls需要鉴权的公共url
oauth-list:
......
whiteUrls:
......
- "/gitegg-job/**"
- "/xxl-job-admin/**"
......
8、启动xxl-job-admin微服务,查看是否启动成功,默认用户名密码: admin/123456

二、测试XXL-JOB定时任务功能

  我们在上面的第一步中,完成了xxl-job-admin的整合和启动,xxl-job-admin可以看做是分布式任务的服务注册中心和管理台,如果我们需要实现定时任务,还需要具体实现执行器让xxl-job-admin调用执行。

  XXL-JOB支持多种方式的定时任务调用,可以将定时任务执行器写在业务代码中,也可以写在xxl-job-admin服务端:

  • BEAN模式(类形式): Bean模式任务,支持基于类的开发方式,每个任务对应一个Java类。
  • BEAN模式(方法形式): Bean模式任务,支持基于方法的开发方式,每个任务对应一个方法。
  • GLUE模式(Java/Shell/Python/NodeJS/PHP/PowerShell) :任务以源码方式维护在调度中心,支持通过Web IDE在线更新,实时编译和生效,因此不需要指定JobHandler。
1、增加xxl-job通用配置

  新增gitegg-platform-xxl-job工程,增加通用配置XxlJobConfig.java通用配置,这样在需要使用定时任务的微服务中,只需要引入一次即可,不需要重复配置。

XxlJobConfig.java:

package com.gitegg.platform.xxl.job.config;

import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* xxl-job config
*
* @author xuxueli 2017-04-28
*/
@Slf4j
@Configuration
public class XxlJobConfig { @Value("${xxl.job.admin.addresses}")
private String adminAddresses; @Value("${xxl.job.accessToken}")
private String accessToken; @Value("${xxl.job.executor.appname}")
private String appname; @Value("${xxl.job.executor.address}")
private String address; @Value("${xxl.job.executor.ip}")
private String ip; @Value("${xxl.job.executor.port}")
private int port; @Value("${xxl.job.executor.logpath}")
private String logPath; @Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays; @Bean
public XxlJobSpringExecutor xxlJobExecutor() {
log.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); return xxlJobSpringExecutor;
} /**
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
*
* 1、引入依赖:
* <dependency>
* <groupId>org.springframework.cloud</groupId>
* <artifactId>spring-cloud-commons</artifactId>
* <version>${version}</version>
* </dependency>
*
* 2、配置文件,或者容器启动变量
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
*
* 3、获取IP
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
*/
}

Nacos配置中心:

xxl:
job:
admin:
addresses: http://127.0.0.1/xxl-job-admin
accessToken: 'default_token'
executor:
appname: ${spring.application.name}
address:
ip:
port: 9999
logpath: D:\\log4j2_nacos\\xxl-job\\jobhandler
logretentiondays: 30
2、实现定时任务测试代码

  我们在gitegg-service-system中测试定时任务执行器,先在pom.xml中添加gitegg-platform-xxl-job依赖,然后新增SystemJobHandler.java测试类

SystemJobHandler.java:

package com.gitegg.service.system.jobhandler;

import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /**
* 定时任务示例代码,其他更多示例请查看
* https://www.xuxueli.com/xxl-job
* @author GitEgg
*/
@Slf4j
@Component
public class SystemJobHandler { /**
* 1、简单任务示例(Bean模式)不带返回值
*/
@XxlJob("systemJobHandler")
public void systemJobHandler() throws Exception { XxlJobHelper.log("不带返回值:XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobHelper.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
} /**
* 2、简单任务示例(Bean模式)带成功或失败返回值
*/
@XxlJob("userJobHandler")
public ReturnT<String> userJobHandler() throws Exception { XxlJobHelper.log("带返回值:XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobHelper.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return ReturnT.SUCCESS;
} }
3、配置xxl-job-admin新增执行器
  • 新增时:

  • gitegg-service-system服务启动后,自动注册:

4、新增xxl-job-admin任务

  执行器可以看做是一组微服务,而任务是微服务具体执行的方法。任务新增后,默认是STOP状态,需要手动启动,当列表显示RUNNING时,表示该任务是运行状态,会根据配置的时间执行。

5、查看执行器是否执行

  在本地开发环境查看任务执行的方式有多种,直接Debug也可以,生产环境我们可以查看xxl-job日志,在测试代码中记录的log,在xxl-job-admin管理台都可以详细查看。

  通过以上操作步骤,我们将xxl-job和xxl-job-admin整合到了我们的微服务架构中,只需要在有任务调度需求的微服务中实现执行器就可以满足我们的需求了。

源码地址:

Gitee: https://gitee.com/wmz1930/GitEgg

GitHub: https://github.com/wmz1930/GitEgg

SpringCloud微服务实战——搭建企业级开发框架(四十二):集成分布式任务调度平台XXL-JOB,实现定时任务功能的更多相关文章

  1. SpringCloud微服务实战——搭建企业级开发框架(十二):OpenFeign+Ribbon实现负载均衡

      Ribbon是Netflix下的负载均衡项目,它主要实现中间层应用程序的负载均衡.为Ribbon配置服务提供者地址列表后,Ribbon就会基于某种负载均衡算法,自动帮助服务调用者去请求.Ribbo ...

  2. SpringCloud微服务实战——搭建企业级开发框架(十四):集成Sentinel高可用流量管理框架【限流】

      Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流.流量整形.熔断降级.系统负载保护.热点防护等多个维度来帮助开发者保障微服务的稳定性. Sentinel 具有 ...

  3. SpringCloud微服务实战——搭建企业级开发框架(十):使用Nacos分布式配置中心

    随着业务的发展.微服务架构的升级,服务的数量.程序的配置日益增多(各种微服务.各种服务器地址.各种参数),传统的配置文件方式和数据库的方式已无法满足开发人员对配置管理的要求: 安全性:配置跟随源代码保 ...

  4. SpringCloud微服务实战——搭建企业级开发框架(十五):集成Sentinel高可用流量管理框架【熔断降级】

      Sentinel除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一.由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积.Sentinel ...

  5. SpringCloud微服务实战——搭建企业级开发框架(十六):集成Sentinel高可用流量管理框架【自定义返回消息】

    Sentinel限流之后,默认的响应消息为Blocked by Sentinel (flow limiting),对于系统整体功能提示来说并不统一,参考我们前面设置的统一响应及异常处理方式,返回相同的 ...

  6. SpringCloud微服务实战——搭建企业级开发框架(十九):Gateway使用knife4j聚合微服务文档

      本章介绍Spring Cloud Gateway网关如何集成knife4j,通过网关聚合所有的Swagger微服务文档 1.gitegg-gateway中引入knife4j依赖,如果没有后端代码编 ...

  7. SpringCloud微服务实战——搭建企业级开发框架(四十):使用Spring Security OAuth2实现单点登录(SSO)系统

    一.单点登录SSO介绍   目前每家企业或者平台都存在不止一套系统,由于历史原因每套系统采购于不同厂商,所以系统间都是相互独立的,都有自己的用户鉴权认证体系,当用户进行登录系统时,不得不记住每套系统的 ...

  8. SpringCloud微服务实战——搭建企业级开发框架(三十六):使用Spring Cloud Stream实现可灵活配置消息中间件的功能

      在以往消息队列的使用中,我们通常使用集成消息中间件开源包来实现对应功能,而消息中间件的实现又有多种,比如目前比较主流的ActiveMQ.RocketMQ.RabbitMQ.Kafka,Stream ...

  9. SpringCloud微服务实战——搭建企业级开发框架(三十四):SpringCloud + Docker + k8s实现微服务集群打包部署-Maven打包配置

      SpringCloud微服务包含多个SpringBoot可运行的应用程序,在单应用程序下,版本发布时的打包部署还相对简单,当有多个应用程序的微服务发布部署时,原先的单应用程序部署方式就会显得复杂且 ...

随机推荐

  1. Python入门-深入了解数据类型以及方法

    写在开始:每一种数据类型,有对应一种功能,面对不同的问题,使用不同类型. 1.全部数据类型 1.2数值型:解决数字的计算问题 #基础的计算,求除结果,求商,求余数 print(10 / 3) prin ...

  2. PCI总线基本概念与历史

    PCI总线历史 这里必须说下 PCI-SIG,1991 年下半年,Intel 公司,并联合IBM.Compaq.AST.HP.DEC 等100 多家公司成立了PCI 集团 并且Intel公司首先提出了 ...

  3. Rb(redis blaster),一个为 redis 实现 non-replicated 分片的 python 库

    Rb,redis blaster,是一个为 redis 实现非复制分片(non-replicated sharding)的库.它在 python redis 之上实现了一个自定义路由系统,允许您自动定 ...

  4. Java语言学习day12--7月11日

    ###16随机点名器代码实现 * A: 随机点名器案例代码 /* 随机点名器,集合改进 (学生的姓名和年龄) 现实中有学生这个事物,使用定义类的形式,描述学生事物 属性: 姓名,年龄 姓名存储了数组, ...

  5. 2022.02.27 CF811E Vladik and Entertaining Flags

    2022.02.27 CF811E Vladik and Entertaining Flags https://www.luogu.com.cn/problem/CF811E Step 1 题意 在一 ...

  6. python基础练习题(题目 计算两个矩阵相加)

    day30 --------------------------------------------------------------- 实例044:矩阵相加 题目 计算两个矩阵相加. 分析:矩阵可 ...

  7. Infrastructure 知识: DNS 命令: dig, host

    dig 基本用法: dig @server name type 或者用-t type来指定(更常见) dig @server -t type name 例子详解 # 最简单的使用 $ dig www. ...

  8. AQS源码阅读

    简介 AQS 全程为 AbstractQueuedSynchronizer , 在 java.util.concurrent.locks包下的一个抽象类. 类的具体作用以及设计在开始类描述信息里面就有 ...

  9. 【前端干货】别再羡慕别人的Excel啦,教你点击按钮直接打开侧边栏!

    负责技术支持的葡萄又来啦. 三日不见,我们的客户又为我们发来新的问题. 这次我们需要实现的场景是在前端表格环境中,像模板按钮那样,点击之后弹出一个侧边栏,然后通过点击不同的单元格显示不同的内容. 挤接 ...

  10. 【Istio实际操作篇】Istio入门,10分钟快速安装

    @ 目录 前言 本文说明 请大家务必查看 环境准备 详细版 入门:搭建步骤 Istio软件包下载 下载Istio 卸载 简洁版 安装 卸载 学习不走弯路,gz号「yeTechLog」 前言 上一篇讲了 ...