JAVA Quartz 任务调度框架

以往写任务调度,碰到秒级任务调度,必不可免的使用sleep来做延时。

在实际操作中非常不方便,而且因为任务本身存在执行时间问题,必不可免的造成不准确的sleep。搜索了下网上相关开源框架,发现Quartz最适合我的任务场景的应用。可惜本身JAVA语言并不是很好,又无奈英文水平有限,磕磕碰碰遇到很多问题。

在IDE部分,选用Eclipse4.5+jdk1.8。框架部分,选用Quartz2.2.3。

网上大多例子均不是基于2.2.3的导致函数方法上存在差异,无法执行。而且略微复杂的功能框架javadoc内均没详细说明,例子也没写清楚。一个简单运行例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package Test;
import java.util.Date;
 
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
 
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class QuartzManager {
private static Logger log = LoggerFactory.getLogger(QuartzManager.class);
private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
public void run() throws Exception {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
// 新建任务 任务名,任务组,任务执行类
JobDetail jobDetail = newJob(HelloJob.class).withIdentity("jobName, "jobGroupName")
.build();
 
// 触发器 cron格式每秒执行
CronTrigger trigger = newTrigger().withIdentity("triggerName", "triggerGroupName")
.withSchedule(cronSchedule("0/1 * * * * ?"))
.build();
 
// 任务绑定触发器
sched.scheduleJob(jobDetail, trigger);
// 启动
if (!sched.isShutdown()) {
sched.start();
}
// 延时10秒
Thread.sleep(10L * 1000L);
// 实际引用中大家一般不会直接getKey所以例子内用字符串转换方式
// 这个地方坑了我至少2小时,网上例子里均是直接使用字符串的
// 但是2.2.3内参数类型为JobKey和TriggerKey
// 获取 JobKey
JobKey jobKey =new JobKey("jobName","jobGroupName");
// 获取 TriggerKey
TriggerKey triggerKey =new TriggerKey("triggerName","triggerGroupName");
// 停止触发器
sched.pauseTrigger(triggerKey);
// 停止任务
sched.pauseJob(jobKey);
// 删除任务
sched.deleteJob(jobKey);
} catch (Exception e) {
throw new RuntimeException(e);
}
 
public static void main(String[] args) throws Exception {
 
QuartzManager example = new QuartzManager();
example.run();
 
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package Test;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
 
public class HelloJob implements Job {
 
    private static Logger _log = LoggerFactory.getLogger(HelloJob.class);
    public HelloJob() {
    	System.out.println("Hello World! - " + new Date());
    }
    public void execute(JobExecutionContext context)
        throws JobExecutionException {
        _log.info("Hello World! - " + new Date());
    }
 
}

因为使用了slf4j,所以一定要在bin目录放置log4j.xml,配置信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
  <appender name="default" class="org.apache.log4j.ConsoleAppender">
    <param name="target" value="System.out"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="[%p] %d{dd MMM hh:mm:ss.SSS aa} %t [%c]%n%m%n%n"/>
    </layout>
  </appender>
 
 
 <logger name="org.quartz">
   <level value="info" />
 </logger>
 <logger name="Test">
   <level value="info" />
 </logger>
  <root>
    <level value="info" />
    <appender-ref ref="default" />
  </root>
 
 
</log4j:configuration>

如果想使用数据库来持久化任务的话,可以用框架源码下的docs\dbTables目录内的sql自己创建数据库。
以mysql为例子,Quartz需要使用InnoDb请使用tables_mysql_innodb.sql而并不是tables_mysql.sql,除非你的数据库默认不是MyISAM。
然后在导入成功后,在项目内引用mysql-connector-java库,再在bin目录增加配置文件quartz.properties内容如下。

1
2
3
4
5
6
7
8
9
10
11
12
org.quartz.scheduler.instanceName = Test
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
 
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/task?characterEncoding=utf-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = 123456
org.quartz.dataSource.myDS.maxConnections = 5

 

NAS方程式入侵工具包

从工具包泄露历时一个月,在全球人民紧张的气氛中“勒索病毒”爆发了,其手段是通过Windows下SMB服务的445端口,进行主动式入侵。SMB主要用于打印机和文件共享。

在黑客领域,最常见的几个端口都和文件的共享相关。包括以往常见的138、139端口。

避免中招的方法,开启防火墙屏蔽135、137、138、139、445端口。本地安装杀毒软件,避免U盘、光盘自启动传播。

从各个渠道听说得到消息,教育、医疗、公安系统大面积中奖,分析这几个网络架构得知。

此次病毒具有U盘或光盘传播能能力、具备内网自动攻击能力。家庭用户,如果不是公网IP的,不乱使用便携式设备,不存在中毒的可能性。

一个像冬天 一个像夏天

想起范玮琪的一首歌《一个像夏天 一个像秋天》,略微修改表述下现在的天气,最合适不过了。这奇怪的天气,上周还是一个冬天,这周却变成了一个夏天,可现在应该是春天!~
上两周还在犹豫穿哪件羽绒服,这周却要考虑是不是该穿T恤。
最近在找一些高分电影看,大部分感觉都挺不错的,唯一想吐槽的就是孙艺珍主演的《假如爱有天意》,会看这部电影的主要原因是之前看过车太贤和孙艺珍主演的《疯狂初恋》。促使我点进去看的原因是豆瓣8.2分,而且看了少量的豆瓣影评,都表示故事虽然很老套,但是看了一半就很感人。
整部电影中,最让人受不了的是男一和男二的长相,实在让人有一种暴力冲动。
整个故事下来只能用两个字来表述“狗血”,其中的战争场景实在是拍的假的不能再假的感觉。实在不像是2003年产的电影,应该有的质量。
看电影,看是要看奥斯卡提名或者获奖电影,豆瓣的分数真的并不是那么靠谱。

PHP CURL获取服务器时间并计算与本地的时间差

在各类抢购场景下,精准的服务器会给我们带来非常多的方便。减少因为网络问题和时差问题带来的“慢别人一步”。

废话不多说,直接上代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
header("Content-type: text/html; charset=utf-8");
date_default_timezone_set("Asia/Shanghai");
 
// 记录本机当前毫秒级时间戳
$localTime = microtime(true);
// 启动一个curl会话
$curl = curl_init();
// 设置抓取的网站
curl_setopt($curl, CURLOPT_URL, "www.w8o.net");
// 设置超时限制防止死循环
curl_setopt($curl, CURLOPT_TIMEOUT, 8);
// 设置获取响应头
curl_setopt($curl, CURLOPT_HEADER, true); 
// 设置获取的信息以文件流的形式返回
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// 执行curl
$result = curl_exec($curl);
// 返回curl执行信息
$curlStartTransferTime = curl_getinfo($curl,CURLINFO_STARTTRANSFER_TIME);
// 关闭curl会话
curl_close($curl);
// 通过服务器响应头获取服务器秒级时间
$pregResult = preg_match("/Date: ([0-9a-zA-z\:\,\s]*)\n/s",$result,$serverHeader);
$serverTime = strtotime($serverHeader[1]);
// 计算本地与服务器时间差距
$differenceTime = $localTime + $curlStartTransferTime - $serverTime;
 
echo "本地时间与服务器时间差距:".$differenceTime."s.\r\n";
echo "从建立连接到响应需要:".$curlStartTransferTime."s.\r\n";

在现实案例中很多抢购页面为了防止抗不住流量,采用静态页面的方式存储。例如某个卖手机的某视商城。

我们也可以定时curl采集页面,并通过CURLINFO_FILETIME常量来获取服务器文件最后修改时间,已确定页面是否更改,当然这个常量针对php等动态页面是无效的。另外服务器文件最后修改时间也可以通过JS来获取,执行document.lastModified;即可。

注:因为服务器响应头并不直接返回毫秒级时间,以上程式仅仅能秒级上更加精准,并非完全完全精准。

命令行下运行PHP

之前写过一篇《PHP实现程序值守》,在实际应用中,特别是自有服务器或者是本地执行中,还是非常不方便的,特别是对于线程的控制。
在cmd下运行php可以妥善解决上类问题。

w8o.php部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 设置编码
header("Content-type: text/html; charset=utf-8");
// 设置时区
date_default_timezone_set("Asia/Shanghai");
// 设置脚本执行时间
set_time_limit(0);
 
// 死循环执行程序
do{
 // 获取参数
 echo var_dump($argv);
 // 输出
 flush();
 // 请求间隔时间
 sleep(10);
}while(true);

然后使用cmd运行,以本机安装的xammp为例子运行执行

1
2
3
4
d:
cd xampp
cd php
php.exe -f D:\xampp\htdocs\w8o.php 100

即可实现可视化的程序值守,且多线程执行上也比直接采用浏览器来运行更加方便。

几个需要注意的地方:

一、CMD下执行php是不能直接获取$_GET参数的,我们需要采用$argv来获取,仅需要在需要执行的页面后加空格直接传递参数的值即可,参数允许多个,并会以数组的方式返回。

二、一般我们写php均采用UTF-8编码,而在cmd下执行输出时会产生乱码,可以在CMD下执行chcp 65001,即可切换到UTF-8编码。

三、关于中文输出,在UTF-8编码下,我实验过网上的各种方法,其实都存在缺陷,并不完美,建议大家在输出的时候还是采用英文输出比较好。或者你有完美的解决方案,也可以给我留言。

 

老物件

老物件里面总是充满了很多的回忆。

今天在打扫房间的时候翻出很多老物件,每一个老物件代表了不同时间、不同的人、不同的故事。

每次打扫完,总会犹豫哪些该哪些该留下哪一些该丢弃。留下的可能是,那些至今还舍不得的故事,丢弃的是那些已经没有所谓的人生。剩下那些不知丢还是不丢的老物件,最终还是封存了起来。或许在下次打扫的时候,它们可能就会被丢弃,又或许这辈子它们可能都会一直被保留。

老物件里最特别的一种东西,那就是照片。这是一种,不会去丢弃的东西。可是每张照片却总会能映射出另外一个人。可能代表亲情、可能代表友情,又或许那张是爱情。

翻看照片的时候才发现,原来照片里缺失的时间。是那些真正缺失的记忆。因为人总是在快乐的时候才会去拍照,而那些伤感的片段总不会在照片里。或许真的是伤感的太多,快乐太少,所以,我的照片总是集中在那么几个时间点。

把记忆拍摄下来,翻看人生,才能找到辛福。

不懂

突然发现她不懂。从她的眼神你、她的行为,我知道她完全不知道我为什么生气又或是完全不知道我为什么而高兴。

而似乎我也不懂她,完全不知道她心里在想什么。

恋爱中的情侣似乎不应该有这样的感觉,可是这种感觉却真实的发生了。

我尝试去思考,可是每每这种时候我脑子里总是一片空白。

我开始学会沉默,不再去思考……

域名界真心动荡了

最近域名界最大的莫过于管局出台了新的规定,陆续收到各个域名注册商的邮件通知,内容均指向所有.com/.net域名开始施行新注册域名所有人实名制审核管理机制,并且与7月18日开始实行。附带部分注册商推送了关于域名转入优惠的信息。这不免让我想起3月那篇《互联网域名管理办法(修订征求意见稿)》,Web开发又进入了更加严密的管控
我估计整体实施这个意见稿对于先行成千上亿的网站是一个非常大的困难,所以可能使用逐步实施的方法,先针对新注册域名进行要求。

中国互联网又有了更加严密的管制,Web开发同仁,声泪俱下……

JS跨域大法

本篇幅主要将如何在任意互联网网站插入JS,使用JS让页面进行一些自动化操作。例如:数据抓取、数据提交、模拟按键等。

众所周知,浏览器的调试器是可以做到在任意网站指定指定JS代码的。但是本地通过嵌套iframe或者修改document.domain均无法实现,内嵌网站非完全控制下的JS跨域操作。

前几天,帮朋友写个抢单器,写个过程中第一次发现CURL无法处理的情况,最后通过JS来解决所以有了下面这篇文章。

经过研究,发现至少3种以上的解决方法,因为每个人的应用目的不同,今天主要讲2种网上没有提到的思路。不管那种方法均可使用append添加代码到指定页面。

方法一、通过页面内链入的外部JS运行库或者统计代码的域名修改Hosts,指向到本地,自架设web服务端,加载自己想执行的JS。

这种方法有两个缺陷,如果指定页面没有加载外部的JS,则无法进行JS更换。而且因为修改了Hosts所以所有嵌入这个域名的JS的页面均受影响。优点是可以用火狐浏览器来跑这个任务,稳定和持续性是非常好的。

方法二、通过浏览器插件或扩展来实现加载自己想执行的JS。

我仅研究了下谷歌浏览器的扩展方法。发现存在以下一个缺陷,JS独立于原有页面的JS以外,例如原页面已经有JQ库了,但是如果你以JQ的语法来写的话,需要自己重新加载JQ运行库。优点是直接装载扩展就可以了,对于使用的人来说降低了门槛。

PS:针对Session登录和302重定向来防盗的页面这两种方法还是有奇效的。

 

Discuz 3.2 积分BUG

最近需要在一个论坛下个文件,结果附件购买竟然需要500积分,尝试了3个帐号终于发现一个无敌BUG。ps:为什么是三个……因为前两个被管理员封了……

一般热门论坛全天基本都会有管理员在线,所以通过刷主题和回复的方式获得积分,实在是太容易被封号。如果是无人管理的,那么写个CURL脚本,尽情的刷吧。表示之前在鼠窝论坛这么玩过,当然这个只是为了纯粹的好玩。

这次尝试的某编程语言论坛设置了必须使用QQ登录才能登录,尝试写了CURL不成功,后转用网页按键精灵解决。ps:反正发帖要求间隔1分钟以上……

下面是BUG的关键部分(适用于所有回复获得积分或金币的论坛)

首先,新建主题,然后编辑好内容,点击保存草稿。

然后在“我的帖子”内找到这个草稿,开始尽情的回复,知道计算楼层得到的积分足够你购买附件了。

最后,把主题发表。

你会发现你一次性得到了包括新主题和回复在内的所有积分。

得到积分后马上去购买和下载你所需要的附件,然后坐等被封吧……

PS:这种方式的好处是,不管是前台管理员还是后台管理员均看不到这个帖子,除非直接查数据库。