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

 

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

 

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:这种方式的好处是,不管是前台管理员还是后台管理员均看不到这个帖子,除非直接查数据库。

超轻量PHP框架 – Fat-Free Framework

很久没写PHP了,可能快有1年多。我竟然很奇葩的想在函数的参数上申明参数类型。也不知道是跟哪个语言的串了。

写过几个ThinkPHP的系统,感觉写一些小的东西的时候,ThinkPHP还是不是很合适,太过复杂,自定义的空间也比较小。百度后找到了个国外的超轻量框架Fat-Free Framework。百度谷歌了很久,基本没找到,关于Fat-Free Framework的中文手册。

通过开发群里的朋友才知道,国内应该只有一个叫“棒主妇商城”的开源系统采用了Fat-Free Framework框架,我下载研究了下,即使这个商城也只是局部使用了Fat-Free Framework,而且用的是3.0版本的Fat-Free Framework。而现行Fat-Free Framework的最新版本是3.5,基本没有太多的参考性。

Fat-Free Framework是一个足够轻量的框架,其压缩后只有60K,解压后Lib在330K,相对ThinkPHP几兆的框架文件,在小型项目的开发中有着天然的优势。

因我自己对Fat-Free Framework也还没研究的很透彻,具体的编程上的就不再做介绍,建议大家可以看Fat-Free Framework官方网站的用户指南和API参考。

linux之redhat as6.3 x64安装xrdp

碰到个客户要求在linuxredhat as6.3 x64能够使用windows的mstsc(远程桌面连接)直接访问linux的桌面,查了度娘,发现可以通过安装xrdp来实现。

网上有蛮多的参考说明,但是均没有从yun开始讲,而且由于源更新的缘故,一些版本都失效了。故写一篇关于xrdp的安装说明。具体操作步骤如下:

1.配置源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//删除yum
rpm -qa |grep yum
rpm -aq|grep yum|xargs rpm -e --nodeps
whereis yum
rm -rf /etc/yum
//下载yum,查询了下网易源,<span class="wp_keywordlink_affiliate"><a href="http://www.w8o.net/tag/redhat" title="View all posts in redhat" target="_blank">redhat</a></span> 6的最新版本是6.7。
wget http://mirrors.163.com/centos/6.7/os/x86_64/Packages/python-iniparse-0.3.1-2.1.el6.noarch.rpm
wget http://mirrors.163.com/centos/6.7/os/x86_64/Packages/yum-3.2.29-69.el6.centos.noarch.rpm
wget http://mirrors.163.com/centos/6.7/os/x86_64/Packages/yum-metadata-parser-1.1.2-16.el6.x86_64.rpm
wget http://mirrors.163.com/centos/6.7/os/x86_64/Packages/yum-plugin-fastestmirror-1.1.30-30.el6.noarch.rpm
//配置yum
rpm -ivh python*
rpm -ivh yum*
cd /etc/yum.repos.d/
rm -rf *
wget http://mirrors.163.com/.help/CentOS6-Base-163.repo
vi CentOS6-Base-163.repo //将所有 $releasever 修改为6.7
yum clean all
yum makecache

2.安装VNC。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//安装VNC
yum install libXfont 
yum install xorg-x11-xfs 
yum install xorg-x11-xfs-utils 
yum install xorg-x11-xinit 
yum install xorg-x11-xdm 
yum install xorg-x11-fonts*
//配置VNC帐号
vi /etc/sysconfig/<span class="wp_keywordlink_affiliate"><a href="http://www.w8o.net/tag/vnc" title="View all posts in vnc" target="_blank">vnc</a></span>servers //添加VNCSERVERS="1:root"
//设置密码
<span class="wp_keywordlink_affiliate"><a href="http://www.w8o.net/tag/vnc" title="View all posts in vnc" target="_blank">vnc</a></span>server //输入的密码需要于本机的root密码相同,否者无法登录。
//配置VNC图像化访问
cd
cd..
cd root
cd .vnc
vi xstartup //添加unset SESSION_MANAGER以及exec /etc/X11/xinit/xinitrc,并且注释掉其他所有内容,如果没此文件可以自己创建,添加以上两行代码。

3.安装xrdp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//安装xrdp
yum install gcc pam-devel openssl-devel libX11-devel libXfixes-devel tigervnc-server
yum groupinstall Desktop //完整桌面(可选安装)
cd /usr/local/src
wget http://sourceforge.net/projects/xrdp/files/xrdp/0.6.1/xrdp-v0.6.1.tar.gz
tar xzvf xrdp-v0.6.1.tar.gz
cd xrdp-v0.6.1
./bootstrap
./configure
make
make install clean //如还是缺少运行库的话,请根据提示自行yum install
vi /etc/rc.d/rc.local  //加入/etc/xrdp/xrdp.sh start使xrdp开机启动
/etc/xrdp/xrdp.sh start  //启动xrdp
/etc/init.d/iptables stop //关闭防火墙,也可添加防火墙规则。

至此,已经完成xrdp的安装了,如果后续修改密码的话,需要修改系统的root密码以及vnc密码。

1
2
passwd //系统密码修改
vncpasswd //VNC密码修改

apache配置黑名单和白名单

因为项目需要,部署之后客户需要实现白名单功能。以前一般使用iis服务器或者是网上的DZ或CP面板,而且也仅仅操作过黑名单。百度搜索,基本都是黑名单的操作方法,本文章主要是讲使用apache来规则实现黑白名单,从程序角度实现的就不在陈述,PHP程序可以直接获取客户端IP在初始化class或者login方法实现禁止或仅允许区段IP通过访问。

方法1:通过apache的虚拟主机配置文件httpd.conf来控制,本方法适用于自己架设的服务器,一般虚拟主机是不能接触到httpd.conf文件的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#白名单,仅允许192.168.0.1以及192.168.1.1-192.168.1.255访问
<Directory "D:/htdocs">
    Options Indexes FollowSymLinks Includes ExecCGI
    AllowOverride All
    #以下为关键代码
    Order Deny,Allow
    Allow from 192.168.0.1
    Allow from 192.168.1.0/24
    Deny from All
</Directory>
 
#黑名单,禁止192.168.0.1以及192.168.1.1-192.168.1.255访问
<Directory "D:/htdocs">
    Options Indexes FollowSymLinks Includes ExecCGI
    AllowOverride All
    #以下为关键代码
    order allow,deny 
    allow from all 
    deny from 192.168.0.1
    deny from 192.168.1.
</Directory>

方法2:通过apache的虚拟主机的伪静态文件.htaccess来控制,本方法适用于支持.htaccess的所有主机。

1
2
3
4
5
6
7
8
9
10
#白名单,仅允许192.168.0.1以及192.168.1.1-192.168.1.255访问
Order Deny,Allow
Allow from 192.168.0.1
Allow from 192.168.1.0/24
Deny from All
#黑名单,禁止192.168.0.1以及192.168.1.1-192.168.1.255访问
order allow,deny 
allow from all 
deny from 192.168.0.1
deny from 192.168.1.

PS:192.168.1.0/24与192.168.1. 等价,仅为不同书写格式。

豆瓣FM音乐批量红心办法

上次为大家讲了批量下载方法,这次为大家将下如何实现批量红心。

火狐和FireBug就不再讲了,具体参数获取方法请参考《豆瓣FM音乐累计收听下载办法》。以下是代码,建议一次设置的页数在10页以内。因为个页面相当于发起了1+15次连接,连续操作很多容易被识别为洪水攻击,而造成404访问。

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
javascript: var tpage = 10;
var a = [];
var page = 1;
var n = 0;
for (page = 1; page <= tpage; page++) {
    n = (page - 1) * 15;
    $.ajax({
        type: "GET",
        url: url,
        async: false,
        dataType: "json",
        success: function(json) {
            if(!json.songs){
                a.push("----第"+page+"页出错!");
            }else{
                for (var i = 0; i < json.songs.length; i++) {
                $.ajax({
                    type: "POST",
                    url: "http://douban.fm/j/song/"+json.songs[i].id+"/interest?action=y&ck=MFdp",
                    data:{action:'y',ck:'MFdp'},
                    dataType: "html",
                    success: function(html) {}
 
                    });
                }
            }
        }
    });
}
document.write(a.join("<b"+"r>"));

豆瓣FM音乐累计收听下载办法

最近一直苦恼与如何下载豆瓣FM上的音乐,想装入SD卡放车上听。因为数量巨大实在不想一个个去搜索选择。研究了下具体操作办法。

如果仅仅是需要下载加心歌曲话请直接移步百度搜索豆瓣匠1.3.6,直接可以帮助你下载到红心歌曲,监听http包发现他是用了安卓版的豆瓣FM的接口,这个JSON文件内直接暴露了音乐的连接地址。我特地下载了安卓版豆瓣FM发现他并没有累计收听这个记录,所以我们也无法批量获取这些MP3的原始地址。这篇文章主要讲如何帮助大家下载豆瓣FM累计收听的成千上万首歌曲。

必备工具:Firefox(火狐浏览器),FireBug(火狐的调试软件,如直接附加不成功可以手动下载后手动附加插件),E音乐盒(用来下载歌曲的软件,它有个特殊的功能,能直接批量导入歌名就帮你下载歌曲)。

1.使用火狐,打开FireBug,登录豆瓣FM,点击累计收听,查看FireBug,可以发现,当每次访问累计收听或是在累计收听内翻页会发起一个AJAX的GET去获取列表,注意看参数ck、spbid需要记录下来这个是用户相关参数,type是类型played代表的是累计收听(其他类别可以成其他的参数),start是分页参数。

1

 

2.使用jq代码批量获取歌曲名称和歌手。注意看第1行,这个是具体操作的页码我这个例子上的只获取2页。第3行则是从第几页开始获取。请将之前用FireBug获取到的用户相关参数替换成你自己的。然后复制到记事本,在重新粘帖到刚才的浏览器窗口的地址栏,回车访问,如果跳转到搜索了请检查头部的javascript:是否被浏览器删除了,如删除请手动补回再访问。

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
javascript: var tpage = 2;
javascript: var tpage = 2;
var a = [];
var page = 1;
var n = 0;
for (page = 1; page <= tpage; page++) {
    n = (page - 1) * 15;
    $.ajax({
        type: "GET",
        url: url,
        async: false,
        dataType: "json",
        success: function(json) {
            if(!json.songs){
                a.push("----第"+page+"页出错!");
            }else{
                for (var i = 0; i < json.songs.length; i++) {
                    a.push(json.songs[i].artist + " - " + json.songs[i].title);
                }
            }
        }
    });
}
document.write(a.join("<b"+"r>"));

3.将浏览器获得的内容复制到E音乐盒,并使用“批量音乐名称添加歌曲”。

2
4.在E音乐盒内可全选右键批量下载。

35.最后说明下。E音乐盒已经停止更新了,数据源只能采用谷歌的,但是部分朋友网络翻墙问题请自行解决,或可以采用本方法的逻辑去把所有累计收听加心。