首先说说,为什么要写这篇文章:
v2.3.2
版本改动比较大,目前网上的资料都是旧版本,很缺乏相关资料为了避免让大家重复踩坑,综上所述就是我要写本篇文章的目的了
本文档编写时间 23.1.17,基于目前最新的稳定版 quartz 2.3.2 实现
简单介绍:
Quartz 是目前 Java 领域应用最为广泛的任务调度框架之一,目前很多流行的分布式调度框架,例如 xxl-job 都是基于它衍生出来的,所以了解和掌握 Quartz 的使用,对于平时完成一些需要定时调度的工作会更有帮助
我们通过一个最简单的示例,先快速上手 Quartz 最基本的用法,然后再逐步讲解 Quartz 每个模块的功能点
在 pom.xml
文件添加 Quart 依赖:
<!-- 引入 quartz 基础依赖:可取当前最新版本 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<!-- 引入 quartz 所需的日志依赖:可取当前最新版本 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.26</version>
<scope>compile</scope>
</dependency>
在项目的 classpath
路径下创建 Quartz 默认的 quartz.properties
配置文件,它看起来像这样:
# 调度程序的名称
org.quartz.scheduler.instanceName = MyScheduler
# 线程数量
org.quartz.threadPool.threadCount = 3
# 内存数据库(推荐刚上手时使用)
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
实现 Job
接口,然后在覆盖的 execute
函数内定义任务逻辑,如下:
package org.example.quartz.tutorial;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) {
System.out.println("hello quartz!");
}
}
我们简单的使用 main()
方法即可运行 Quartz 任务调度示例:
package org.example.quartz.tutorial;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.SimpleTrigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzTest {
public static void main(String[] args) {
try {
// 获取默认的调度器实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 打开调度器
scheduler.start();
// 定义一个简单的任务
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("job11", "group1")
.build();
// 定义一个简单的触发器: 每隔 1 秒执行 1 次,任务永不停止
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever()
).build();
// 开始调度任务
scheduler.scheduleJob(job, trigger);
// 等待任务执行一些时间
Thread.sleep(3000);
// 关闭调度器
scheduler.shutdown();
} catch (Exception se) {
se.printStackTrace();
}
}
}
最后控制台会输出任务运行的全过程,然后关闭进程,如下:
[main] INFO org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor
[main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
[main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.3.2 created.
[main] INFO org.quartz.simpl.RAMJobStore - RAMJobStore initialized.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.3.2) 'MyScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
[main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'MyScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
[main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.3.2
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED started.
hello quartz!
hello quartz!
hello quartz!
hello quartz!
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED shutting down.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED paused.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED shutdown complete.
Process finished with exit code 0
可以看到,到这里一个最基本简单的 Quartz 使用示例基本就 OK 了,接下来介绍更多核心概念和深入使用的场景
掌握 Quartz 之前,先来了解它框架的 3 个核心组件和概念,如下:
如上所示,使用 Quartz 的工作流程也很简单,大致如下:
Job
接口定义你的作业 JobDetail
实例和触发器 Trigger
实例对象scheduleJob
,开始调度执行JobDetail
实例的 execute
方法内容JobExecutionContext
对象传递到工作线程,也可以在多个工作线程中跨线程传递示意图:
关于创建 JobDetail 作业实例和 Trigger 触发器的几个注意事项:
通过示例可以看到,定义和使用 Job 都非常简单,但是如果要深入使用,你可能需要了解关于 Job 的更多细节
先看看 Quartz 对于 JobDetail 的处理策略:
JobExecutionContext
(实际上是 JobDataMap) 进行跨作业传递jobDataMap 的使用主要分 2 步:
1:在 execute()
函数内,使用 jobDataMap 获取数据
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) {
// 通过 JobDataMap 对象,可以在作业的执行逻辑中,获取参数
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
String name = jobDataMap.getString("name");
System.out.println("hello " + name);
}
}
2:jobDataMao 添加参数
// 定义作业时,通过 usingJobData 将参数放入 JobDataMap
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("job11", "group1")
.usingJobData("name", "phoenix")
.build();
最后运行效果如下:
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED started.
hello phoenix
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED shutting down.
# ....
关于 JobDataMap 的使用,需要关注以下的注意事项:
jobStore.useProperties
设置)Quartz 对于 Job 提供几个注释,合理的使用可以更好的控制 Quartz 的调度行为,具体如下:
@DisallowConcurrentExecution:添加到 Job 类中,告诉 Job 防止相同定义的任务并发执行,例如:任务 A 实例未完成任务,则任务 B 实例不会开始执行(Quartz 默认策略是不会等待,启用新线程并发调度)
@PersistJobDataAfterExecution:添加到 Job 类中,默认情况下 Job 作业运行逻辑不会影响到 JobDataMap (既每个 JobDetail 拿到的都是初始化的 JobDataMap 内容),开启该注解后,Job 的 execute()
方法完成后,对于 JobDataMap 的更新,将会被持久化到 JobDataMap 中,从而供其他的 JobDetail 使用,这对于任务 B 依赖任务 A 的运行结果的场景下,非常有用,所以强烈建议和 @DisallowConcurrentExecution
注解一起使用,会让任务运行结果更加符合预期
使用示例如下:
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class QuartzTest {
public static void main(String[] args) {
//.....
}
}
和 Job 类似,触发器的定义和使用也非常简单,但是如果想充分的利用它来工作,你还需要了解关于触发器的更多细节
在 Quartz 中 Trigger 触发器有很多种类型,但是他们都有几个共同的属性,如下:
以上共同属性的值都是 java.util.Date
对象
关于 Trigger 的其他几个概念:
SimpleTrigger 是适用于大多数场景的触发器,它可以指定特定时间,重复间隔,重复次数等简单场景,它主要设定参数如下:
具体的 API 可以参考 Quartz 的 Java Doc 文档,这里就不赘述了
misfire 处理策略:
我们上面说过 Quartz Misfire 的概念,从源码 SimpleScheduleBuilder
类中可以看到 MISFIRE_INSTRUCTION_SMART_POLICY
是默认的触发策略,但是也我们也可以在创建 Trigger 时候设置我们期望的错过触发策略,如下:
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever()
// misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW;
.withMisfireHandlingInstructionFireNow()
).build();
在 SimpleTrigger
类中的常量可以看到所有错过触发(misfire)处理逻辑:
MISFIRE_INSTRUCTION_FIRE_NOW
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
关于 misfire 的具体的行为,可以查阅 Quartz 的 Java Doc 文档
相比 SimpleTrigger 可以指定更为复杂的执行计划,CRON 是来自 UNIX
基于时间的任务管理系统,相关内容就不再展开,可以参阅 Cron - (wikipedia.org) 文档进一步了解,
Cron 也有类似 SimpleTrigger 的相同属性,设置效果如下:
看看 CronTrigger 的使用示例:
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder
.cronSchedule("0 0/2 8-17 * * ?")
.withMisfireHandlingInstructionFireAndProceed()
)
.build();
scheduler.scheduleJob(job, cronTrigger);
上述代码完成以下几件事情:
CronTrigger Misfire 策略定义在 CronTrigger
常量中,可以在 Java Doc 文档中查看其具体的行为
监听器用于监听 Quartz 任务事件执行对应的操作,大致分类如下:
在常见的 JobListener 接口中,提供以下事件监听:
public interface JobListener {
public String getName();
// 作业即将开始执行时触发
public void jobToBeExecuted(JobExecutionContext context);
// 作业即将取消时通知
public void jobExecutionVetoed(JobExecutionContext context);
// 作业执行完成后通知
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException);
}
想要实现监听,需要以下几步:
在 Schduler 中注册一个对所有任务生效的 Listener 的示例:
scheduler.getListenerManager().addJobListener(myJobListener, allJobs());
关于使用 Listener 的建议:
2.3.2
Listener 不会存储在 JobStore 中,所以在持久化模式下,每次启动都需要重新注册监听JobStore 属性在 Quartz 配置文件中声明,用于定义 Quartz 所有运行时任务的存储方式,目前主要有两种方式
RAMJobStore 是基于内存的存储模式,其特点如下:
配置方式如下:
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
JDBCJobStore 是基于数据的存储模式,其特点如下:
使用 JDBCJobStore 需要以下 3 步完成:
第一步:在项目中添加相关数据库依赖:
<!-- 添加数据库依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
第二步:在数据库执行 [Quartz 官方的 SQL DDL 脚本](quartz/tables_mysql_innodb.sql at master · quartz-scheduler/quartz (github.com)),创建数据库表结构,Quartz 核心的表结构如下:
Table Name | Description |
---|---|
QRTZ_CALENDARS | 存储Quartz的Calendar信息 |
QRTZ_CRON_TRIGGERS | 存储CronTrigger,包括Cron表达式和时区信息 |
QRTZ_FIRED_TRIGGERS | 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息 |
QRTZ_PAUSED_TRIGGER_GRPS | 存储已暂停的Trigger组的信息 |
QRTZ_SCHEDULER_STATE | 存储少量的有关Scheduler的状态信息,和别的Scheduler实例 |
QRTZ_LOCKS | 存储程序的悲观锁的信息 |
QRTZ_JOB_DETAILS | 存储每一个已配置的Job的详细信息 |
QRTZ_JOB_LISTENERS | 存储有关已配置的JobListener的信息 |
QRTZ_SIMPLE_TRIGGERS | 存储简单的Trigger,包括重复次数、间隔、以及已触的次数 |
QRTZ_BLOG_TRIGGERS | Trigger作为Blob类型存储 |
QRTZ_TRIGGERS | 存储已配置的Trigger的信息 |
第三步:配置文件修改为 JDBCJobStore 模式,配置数据源,并且将 jobStore
指定为该数据源,如下
# quartz scheduler config
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = myDS
# dataSource
org.quartz.dataSource.myDS.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://127.0.0.1:3306/quartz_demo
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = test123456
org.quartz.dataSource.myDS.maxConnections = 30
最后运行 QuartzTest 后可以看到数据库 QRTZ_JOB_DETAILS 表已经添加数据,如下:
在使用 JDBCJobStore 时,需要注意以下事项:
JobStoreCMT
QRTZ_
,可进行配置,使用多个不同的前缀有助于实现同一数据库的任务调度多组表结构StdJDBCDelegate
适用于大多数数据库,目前只针对测试 StdJDBCDelegate
时出现问题的类型进行特定的委托
org.quartz.jobStore.useProperties
设置为 True,避免将非基础类型数据存储到数据库的 BLOB 字段Quartz 整合 Springboot 非常普遍的场景,整合 Spring 可以带来好处:
可以在现有项目上添加 springboot 官方提供的 starter-quartz 依赖,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
如果是新项目,可以直接在 Spring Initializr 添加 Quartz Schduler 如下:
启动 Springboot 会发现,无需任何配置就已经整合 Quartz 模块了:
现在基于整合模式实现刚才的 Demo 示例,首先定义任务,这里不再是实现 Job 类:
public class HelloJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
String name = jobDataMap.getString("name");
System.out.println("Hello :" + name);
}
}
这里实现由 Springboot 提供的 QuartzJobBean,实现 executerInternal()
方法,这是一个经过 Spring 容器包装后的任务类,可以在任务类使用 Spring 容器的实例
在 Demo 示例里面,我们调度启动都是在 Main 方法启动,在本地测试没有问题,但在生产环境就不建议了,和 springboot 整合后关于任务执行,现在可以有 2 中选项:
我们先看看第一种实现方式,我们创建控制器,然后接收参数,创建任务,如下:
@RestController
public class HelloController {
@Autowired
private Scheduler scheduler;
@GetMapping("/hello")
public void helloJob(String name) throws SchedulerException {
// 定义一个的任务
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("job11", "group1")
.usingJobData("name", name)
.build();
// 定义一个简单的触发器: 每隔 1 秒执行 1 次,任务永不停止
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever()
).build();
// 开始调度
scheduler.scheduleJob(job, trigger);
}
}
然后启动服务器,访问接口传入参数:
$curl --location --request GET 'http://localhost:8080/hello?name=phoenix'
然后控制台会输出:
2023-01-21 22:03:03.213 INFO 23832 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-01-21 22:03:03.213 INFO 23832 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-01-21 22:03:03.214 INFO 23832 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
Hello :phoenix
Hello :phoenix
#....
将 JobDetail 注册 Bean,任务就会随 Spring 启动自动触发执行,这对于需要随程序启动执行的作业非常有效,配置如下:
先创建一个配置类:
@Configuration
public class QuartzConfig {
@Bean
public JobDetail jobDetail() {
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("job11", "group1")
.usingJobData("name", "springboot")
.storeDurably()
.build();
return job;
}
@Bean
public Trigger trigger() {
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.forJob(jobDetail())
.withIdentity("trigger1", "group1")
.withSchedule(SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever()
).build();
return trigger;
}
}
然后在 springboot 启动后,任务就自动执行:
2023-01-21 22:29:51.962 INFO 46376 --- [ main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
Hello :springboot
Hello :springboot
Hello :springboot
# ....
对于生产环境来说,高可用,负载均衡,故障恢复,这些分布式的能力是必不可少的,Quartz 天生支持基于数据库的分布式:
要启用集群模式,需要注意以下事项:
jobStore.isClustered
属性设置为 TrueinstanceId
(Quartz 提供参数让这点很容易实现)下面看看 springboot 集成的模式下如何配置 quartz 集群模式:
在 application.yml
添加 quartz 集群配置信息:
spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://127.0.0.1:3306/quartz_demo
username: root
quartz:
job-store-type: jdbc
properties:
org:
quartz:
scheduler:
instanceName: ClusteredScheduler # 集群名,若使用集群功能,则每一个实例都要使用相同的名字
instanceId: AUTO # 若是集群下,每个 instanceId 必须唯一,设置 AUTO 自动生成唯一 Id
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 25
threadPriority: 5
jobStore:
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
useProperties: true # 使用字符串参数,避免了将非 String 类序列化为 BLOB 的类版本问题
isClustered: true # 打开集群模式
clusterCheckinInterval: 5000 # 集群存活检测间隔
misfireThreshold: 60000 # 最大错过触发事件时间
使用集群模式需要添加数据库依赖,如下:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
然后创建 SchedulerConfig
配置类,将相关的配置信息加载到 SchedulerFactoryBean
中才能生效:
@Configuration
public class SchedulerConfig {
@Autowired
private DataSource dataSource;
@Autowired
private QuartzProperties quartzProperties;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
Properties properties = new Properties();
properties.putAll(quartzProperties.getProperties());
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
factory.setDataSource(dataSource);
factory.setQuartzProperties(properties);
return factory;
}
}
最后在启动日志内,可以看到 Quartz 启动集群模式运行:
使用集群模式,需要注意以下事项:
以上对于 Quartz 的总结就到这里了,有什么不当之处,欢迎交流指正。
大家好,又见面了,我是你们的朋友全栈君。本文出处:Java中使用OpenSSL生成的RSA公私钥进行数据加解密_SlashYouth–JackChai-CSDN博客_java生成rsa公私钥,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。RSA是什么:RSA公钥加密算法是1977年由RonRivest、AdiShamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。目前该加密方式广泛用于网上银行、数字签名等场合。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。OpenSSL是什么:众多的密码算法、公钥基础设施标准以及SSL协议,或许这些有趣的功能会让你产生实现所有这些算法和标准的想法。果真如此,在对你表示敬佩的同时,还是忍不住提醒你:这是一个令人望而生畏的过程。这个工作不再是简单的读懂几本密码学专
先尝尝鲜看演示不过瘾,我也玩一下(http://socket.vjscoder.com/websocket-chatroom/index.html#/)查看源码(https://github.com/ReeceCan/websocket-chatroom)Websocket介绍背景在许多场景下,用户需要得到实时的消息,比如聊天,医疗设备读数等,旧的解决方案是基于轮询的方式获取最新数据,但是并不会完全实时消息同步,并且大多数情况下请求都是没必要的,反而浪费了大量的流量和服务器资源。基于这种背景下,websocket诞生了。Websocket基本概念WebSocket是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议。WebSocket通信协议诞生于2008年,2011年成为国际标准.WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocketAPI中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。兼容性问题(主流浏览器都支持)imageWebsocket特点控制开销。在连接创
前言今天要聊的技术是序列化,这不是我第一次写序列化相关的文章了,今天动笔之前,我还特地去博客翻了下我博客早期的一篇序列化文章(如下图),竟然都过去4年了。为什么又想聊序列化了呢?因为最近的工作用到了序列化相关的内容,其次,这几年Dubbo也发生了翻天覆地的变化,其中Dubbo3.0主推的Tripple协议,更是打着下一代RPC通信协议的旗号,有取代Dubbo协议的势头。而Tripple协议使用的便是Protobuf序列化方案。另外,Dubbo社区也专门搞了一个序列化压测的项目:https://github.com/apache/dubbo-benchmark.git,本文也将围绕这个项目,从性能维度展开对Dubbo支持的各个序列化框架的讨论。当我们聊序列化的时候,我们关注什么?最近几年,各种新的高效序列化方式层出不穷,最典型的包括:专门针对Java语言的:JDK序列化、Kryo、FST跨语言的:Protostuff,ProtoBuf,Thrift,Avro,MsgPack等等为什么开源社区涌现了这么多的序列化框架,Dubbo也扩展了这么多的序列化实现呢?主要还是为了满足不同的需求。序列
#!/bin/sh ############################## ##名称:MvOtherCdrTo251.sh ##描述:/ocs/data/output目录下的25开头(251,257,258除外)对应目录下的/normal/bak下的文件全部转移到/ocs/data/output/251/normal/bak ##参数:暂无 ##作者:小工匠 ##日期:2017-06-17 ##版本:V1.0 ##备注:使用时注意修改TARGET_MENU的值,测试用,取的是bak目录 ############################## #定义退出标识符,脚本执行后,通过echo$?查看退出标识符,即上个命令或者脚本的返回结果 EXIT_FAILURE=1#Failingexitstatus EXIT_SUCCESS=0#Successfulexitstatus #开始时间 BEGIN_TIME=`date+%s` #当前执行脚本的全路径 SCRIPT_PATH=$(cd`dirname$0`;pwd) SCRIPT_NAME=`basename$0` #日志路
1.长数据是什么鬼?之前介绍了如何将多个性状的箱线图放在一个图上,比如learnasreml包中的fm数据,它有h1~h5五年的株高数据,想对它进行作图。「数据预览:」>library(learnasreml) >data(fm) >head(fm) TreeIDSpacingRepFamPlotdjdmwdh1h2h3h4h5 180001317004810.3340.4050.35829130239420630 280002317004820.3480.3930.36524107242410600 380004317004840.3540.4290.3791982180300500 480005317001710.3350.4080.36346168301510700 580008317001740.3220.3720.33233135271470670 680026317000220.3590.4500.39230132258390570 复制这里,相对h1,h2,h3,h4,h5这五个性状进行作图,我们可以将其转化为「长数据」!问题来了,什么是「长数据」,什
WeTest导读《乱世王者》是由腾讯旗下天美工作室群自主研发的一款战争策略手游,在经历了2015年-2017年的SLG品类手游的爆发之势下,于2017年11月21日正式公测。《乱世王者》继承了SLG结构的经典内核,但游戏增加了许多附带休闲社交属性的流行元素,比如AR摄像头寻宝,形象个性化的美图美颜时尚功能,以及可实时互动的专业主播驻场等,收获了大批手游玩家和粉丝。据AppAnnie数据显示,自8月15日上线起至12月31日,《乱世王者》有约八成的时间都处在畅销榜前十。即使是运营1年后,《乱世王者》也始终保持了高口碑和热度,为腾讯在SLG品类手游中占据重要的一席之地。这些成绩除了对游戏玩法的钻研和创新外,也离不开项目组在质量和安全上的高品质追求。(近期企鹅风讯采集《乱世王者》的口碑指数和评论数)(企鹅风讯采集近3个月《乱世王者》的iOS榜单排名) 基于SLG核心构建手游安全保障方案自腾讯从进入游戏行业之后,就遭遇过不少外挂的侵袭,因为不断的踩坑,腾讯在进入手游领域之初,非常的重视安全保护和防范。自2011年以来腾讯就建立了专业的手游安全团队,用来保障腾讯手游的安全打击游走在游戏周边的灰色
除了传统的CSS,你还可以使用内联样式和CSS-in-JS作为React应用程序的样式选项。对于内联样式,你可以将JavaScript对象传递给样式属性:constmyStyle={ fontSize:24, lineHeight:'1.3em', fontWeight:'bold', }; <spanstyle={myStyle}>HelloWorld!</p>复制然而,并非所有CSS特性都受支持。另一方面,CSS-in-JS是一种使用JavaScript来设置组件样式的技术。在解析此JavaScript时,会生成CSS(通常作为<style>元素)并附加到DOM中。这个功能由第三方库实现。例如,下面是使用Aphrodite实现的上一个示例:import{StyleSheet,css}from'aphrodite'; conststyles=StyleSheet.create({ myStyle:{ fontSize:24, lineHeight:'1.3em', f
题目描述请实现两个函数,分别用来序列化和反序列化二叉树解题思路对于序列化:使用前序遍历,递归的将二叉树的值转化为字符,并且在每次二叉树的结点不为空时,在转化val所得的字符之后添加一个’,’作为分割;对于空节点则以‘#,’代替。对于反序列化:将字符串按照“,”进行分割,插入到队列中,然后依次从队列中取出字符建立节点,递归创建一个二叉树。参考代码/* publicclassTreeNode{ intval=0; TreeNodeleft=null; TreeNoderight=null; publicTreeNode(intval){ this.val=val; } } */ importjava.util.Queue; importjava.util.LinkedList; publicclassSolution{ StringSerialize(TreeNoderoot){ if(root==null){ return"#,"; } StringBufferres=newStringBuffer(root.val+","); res.appe
VerilogHDL通过对reg型变量建立数组来对存储器建模,可以描述RAM型存储器,ROM存储器和reg文件。数组中的每一个单元通过一个数组索引进行寻址。在Verilog语言中没有多维数组存在。memory型数据是通过扩展reg型数据的地址范围来生成的。其格式如下:reg[n-1:0]存储器名[m-1:0];或reg[n-1:0]存储器名[m:1];在这里,reg[n-1:0]定义了存储器中每一个存储单元的大小,即该存储单元是一个n位的寄存器。存储器名后的[m-1:0]或[m:1]则定义了该存储器中有多少个这样的寄存器。最后用分号结束定义语句。下面举例说明:reg[7:0]mema[255:0];这个例子定义了一个名为mema的存储器,该存储器有256个8位的存储器。该存储器的地址范围是0到255。注意:对存储器进行地址索引的表达式必须是常数表达式。另外,在同一个数据类型声明语句里,可以同时定义存储器型数据和reg型数据。见下例:parameterwordsize=16,//定义二个参数。memsize=256;reg[wordsize-1:0]mem[memsize-1:0],w
大家好,又见面了,我是你们的朋友全栈君。一、用xftp远程根据把解压后的安装包文件上传到指定目录/opt/module/。然后,cd/opt/module/pycharm-community-linux-2018.1.4/bin/,执行以代码chmodu+xpycharm.sh赋予pycharm.sh执行权限[atguigu@hadoop101bin]$chmodu+xpycharm.sh复制最后,执行$shpycharm.sh启动pycharm[atguigu@hadoop101bin]$pycharm.sh StartupError:Unabletodetectgraphicsenvironment复制报错了,因为我是在xshell启动的,没有图形界面。所以需要直接到linux虚拟机的图形终端去启动。这里选skip,用默认设置。16.2.3版本的pycharm可能是下图,按照图片设置好快捷键,主题和字体就行了。点击ok二、设置启动图标step1:选择菜单Tools/CreateDesktopEntry…,设置任务栏启动图标设置图标时需要勾选Createtheentryforall
2022年8月8日,中国移动发布《2022年至2024年抗DDOS设备集中采购项目》招标公告。本期集中采购产品为抗DDOS设备,预估采购规模约1159台,其中标包1共计308台,标包2共计192台,标包3共计659台。本次招标为预估规模,实际采购量以采购合同为准。 注:预计本次采购需求满足期为2年。本项目采用混合招标,划分3个标包,具体标包划分及中标人数量如下:标包1:中标数量为1-2家。 标包2:中标数量为1-2家。 标包3:中标数量为2家。本项目允许投标人同时中标的最多标包数为3个。本项目设置最高投标限价,标包1不含税投标总价不超过2770.05万元人民币;标包2不含税投标总价不超过860.77万元人民币;标包3不含税投标总价不超过3793.81万元人民币,投标人投标报价高于最高投标限价的,其投标将被否决。中标候选人公示2022年10月14日发布中标候选人公示,迪普分得3126万元、华为2358万元、新华三945万元。标包1:第1:华为技术有限公司投标报价:22447138.56元(不含税)中标份额:70%第2:杭州迪普科技股份有限公司投标报价:26209666.56元(不含税)中
知识点目录==========>传送门 线程和进程的简单概括。 1.进程就是"活动中"的程序,一个。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。进程之间是相互独立的。程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。 2.线程有时候又被称为轻量级进程,是程序执行的最小单元。和上文中一样的,一个进程可对应多个线程,而一个线程只属于一个进程。进程的执行是以线程为单位进行得。 如果上面说法没明白也很正常,线程和进程本来就有点抽象,下面举个例子应该明白了,这个例子是看大牛的文章得到的。 1. 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。 2. 假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。 3. 进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。 4. 一个车间里,可以有很多工人。他们协同完成一个任务。 5.
题面 对于每个点,我们可以用一次dfs求出这个点到以这个点为字树的最远距离和次远距离; 然后用换根法再来一遍dfs求出这个点到除这个点子树之外的最远距离; 显然的,每次的询问我们可以用向上的最大值加向下的最大值得到; 具体换根法的实现可以看下面的代码~; #include<bits/stdc++.h> #defineinc(i,a,b)for(registerinti=a;i<=b;i++) usingnamespacestd; inthead[200010],cnt; classlittlestar{ public: intto;intnxt; voidadd(intu,intv){ to=v;nxt=head[u]; head[u]=cnt; } }star[200010]; template<classnT> inlinevoidread(nT&x) { charc;while(c=getchar(),!isdigit(c));x=c^48; while(c=getchar(),isdigit(c))x=x*10+c-4
原文地址:https://www.cnblogs.com/xuxiaona/p/4962727.html 近期做了一个存储过程,执行时发现非常的慢,竟然需要6、7秒! 经排查,发现时间主要都耗在了其中一段查询语句上。这个语句用于查出结构相同的两个表中,其中两个字段的任一个字段数据相同的记录。 例如,A表的结构如下所示: --会员表 CREATETableMember (MemberIDint,--会员ID MemberNamevarchar(50),--会员姓名 MemberPhonevarchar(50)--会员电话 ) go复制 B表的结构与A表完全相同,假设表名为Member_Tmep。 现在Member表中有7000条不重复的数据,Member_Tmep表中有2000条数据,需要查出这两张表中,会员姓名或会员电话相同,但会员ID不相同的记录。 按照普通的逻辑,我一开始是这样写的: selecta.MemberID,a.MemberName,a.MemberPhone fromMembera,Member_Tmepb where(a.MemberName=b.
一、什么是策划 1.策划是为了实现既定目标而进行的一系列动作 2.策划是通过合理的分析,找到实现目标的最佳途径 3.目标是综合变量;没有绝对韦祎,定位最重要 二、游戏策划的大体分工 1.系统策划 负责游戏系统玩法设计,以及针对不同类型用户的需求进行衍生玩法设计; 推动设计方案的落实,验收并优化功能点,对玩法最重品质负责; 针对游戏产品上线后的数据进行分析,持续改善游戏产品。 2.剧情策划 设定游戏的世界背景及剧情发展路线; 根据既定ip做游戏内任务的设计、玩法背景包装; 制定世界物理规则,设计并绘制游戏地图、npc造型需求、道具需求等; 对游戏的世界观代入感负责。 3.数值策划 设定游戏的战斗数值或经济数值; 负责移动游戏全局数值模型,用理论和实际测试保持数值的可研究性、平衡性、可调性; 熟练运用数值建模,调整并优化:单局体验、成长体系、经济体系等体系的数值设定,并根据实际调试优化数值; 负责调整数据、跟进版本实现,保证游戏开发质量。 4.关卡策划 设计游戏副本、日常活动的战斗体验; 关卡情景代入设计,关卡机关,怪物,事件的设计以及配置。 怪物ai,技能设计,boss战斗乐趣设计; 设
安装aria2 aria2是linux下的一个下载工具,它支持http、bt种子、磁力链接三种方式下载 sudoapt-getinstallaria2复制 配置aria2 aria2支持命令参数,也支持指定配置文件,这里我们使用指定配置文件参数的方式来启动。 mkdir.aria2 vim.aria2/aria2.config复制 添加下面内容 continue #后台运行 daemon=true #默认下载目录 dir=/home/pi/Downloads #立即分配下载所需的空间对ext4支持最好 file-allocation=falloc log-level=warn max-connection-per-server=4 max-concurrent-downloads=3 max-overall-download-limit=200K min-split-size=5M enable-http-pipelining=true #启用rpc调用接口 enable-rpc=true rpc-listen-all=true #rpc的访问密码 rpc-secret=hz
我是一个C++初学者,控制台实现了一个八皇后问题。 代码如下: //"八皇后问题"V1.0 //李国良于2017年1月11日编写完成 #include<iostream> #include<Windows.h> usingnamespacestd; constintArSize=8;//这个数等于几,就是几皇后。 intnum=0; voidsolve(boolarr[ArSize][ArSize],introw); boolcheck(boolarr[ArSize][ArSize],introw,intcolumn); voidoutPut(boolarr[ArSize][ArSize]); intmain() { SetConsoleTitle("八皇后问题"); boolchessboard[ArSize][ArSize]; for(auto&i:chessboard) { for(auto&j:i) { j=false; } } solve(chessboard,0); cout<<"八皇
在Mac上安装上Mysql后发现用不了。在这里记录一下解决方法。 确保mysql服务正常启动。 进入系统偏好设置 进入mysql,查看mysql是否启动 如果启动不了,点击InitializeDatabase进行初始化数据库,输入密码(8位),密码类型选择第二项(UseLegacyPasswordEncryption) 配置环境变量。 编辑系统配置文件,终端输入 sudovim/etc/profile 复制 在profile末尾加上mysql的安装目录,然后强制保存并退出(wq!) exportPATH=$PATH:/usr/local/mysql/bin 复制 重载配置文件:终端输入 source/etc/profile 复制 登陆mysql。 mysql-uroot-p 复制 参考博客:https://www.cnblogs.com/yanlin-10/p/9388911.html
经常收到研发支援单,需要帮客户导出某主题/某活动下某些成百上千的指定用户上传的文件(图片,视频等),本来管理后台是有导出功能的,但是达不到客户心血来潮的要求, 有时候时客户退租,需要拷贝上传数据,索性就花费了午休时间,封装了一个Winform工具, 丢给运营支持, 以后就不会再来找研发,毕竟大家都很忙. 话不多说先上思路, 其实很简单,sql查询捞出活动-用户-文件url等信息保存到excle, 或者客户会直接提供这些文件信息, 这都是前提, 那么在此前提下,只需要用程序去读取文件将里面的数据一一下载下来就好. 第一步,创建winform窗体程序, 引用NPOI库,和日志Nlog,直接掠过. 第二步,封装一个Helper类,将Excel文件读取到Table中,先行后列 ///<summary> ///读取excel文件数据到DataTable ///&l