基于spring boot 框架搭建 quartz做定时发送指令
pom依赖
netty服务端代码:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class TCPServerNetty {
private int port;
private static Map
private static Map
public TCPServerNetty(int port){
this.port = port;
}
public TCPServerNetty(){}
public void start() throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer
@Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(new MyDecoder());
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 4, 4, -8, 0));
ch.pipeline().addLast(new OutBoundHandler());
ch.pipeline().addLast( new InBoundHandler());
}
});
// b.bind(port);
// Start the server.
ChannelFuture f = b.bind(port).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
// Shut down all event loops to terminate all threads.
//bossGroup.shutdownGracefully();
//workerGroup.shutdownGracefully();
}
}
/**
* @param args
* @throws Exception
*/
public static void main(String args[]) throws Exception{
new TCPServerNetty(4456).start();
}
public static Map
return map;
}
public static void setMap(Map
TCPServerNetty.map = map;
}
public static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder();
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
stringBuilder.append(' ');
}
return stringBuilder.toString();
}
/**
* @return the messageMap
*/
public static Map
return messageMap;
}
/**
* @param messageMap the messageMap to set
*/
public static void setMessageMap(Map
TCPServerNetty.messageMap = messageMap;
}
}
解码器:
package com.example.demo.netty2;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
/**
*/
public class MyDecoder extends ByteToMessageDecoder{
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
List
}
工具类:
package com.example.demo.netty2;
public class BytesAndHex {
/**
* byte[]转十六进制
* @param bytes
* @return
*/
public static String bytesTohex(byte[] bytes) {
StringBuilder hex = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
boolean flag = false;
if (b < 0) flag = true;
int absB = Math.abs(b);
if (flag) absB = absB | 0x80;
System.out.println(absB & 0xFF);
String tmp = Integer.toHexString(absB & 0xFF);
if (tmp.length() == 1) { //转化的十六进制不足两位,需要补0
hex.append("0");
}
hex.append(tmp.toLowerCase());
}
return hex.toString();
}
/**
* 十六进制转byte[]
* @param hex
* @return
*/
public static byte[] hexTobytes(String hex) {
hex = hex.replaceAll(" ", "");
byte[] bytes = new byte[hex.length() / 2];
int index = 0;
while(index < hex.length()){
String sub = hex.substring(index,index+2);
bytes[index/2] = (byte)Integer.parseInt(sub,16);
index += 2;
}
return bytes;
}
}
读和写两个服务类:
package com.example.demo.netty2;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
public class InBoundHandler extends ChannelInboundHandlerAdapter{
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
System.out.println("CLIENT"+getRemoteAddress(ctx)+" 接入连接");
//往channel map中添加channel信息
TCPServerNetty.getMap().put(getIPString(ctx), ctx.channel());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//删除Channel Map中的失效Client
TCPServerNetty.getMap().remove(getIPString(ctx));
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx,Object msg)
throws Exception {
System.out.println("来自设备的信息:"+msg);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
String socketString = ctx.channel().remoteAddress().toString();
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.READER_IDLE) {
System.out.println("Client: "+socketString+" READER_IDLE 读超时");
ctx.disconnect();
} else if (event.state() == IdleState.WRITER_IDLE) {
System.out.println("Client: "+socketString+" WRITER_IDLE 写超时");
ctx.disconnect();
} else if (event.state() == IdleState.ALL_IDLE) {
System.out.println("Client: "+socketString+" ALL_IDLE 总超时");
ctx.disconnect();
}
}
}
public static String getIPString(ChannelHandlerContext ctx){
String ipString = "";
String socketString = ctx.channel().remoteAddress().toString();
int colonAt = socketString.indexOf(":");
ipString = socketString.substring(1, colonAt);
return ipString;
}
public static String getRemoteAddress(ChannelHandlerContext ctx){
String socketString = "";
socketString = ctx.channel().remoteAddress().toString();
return socketString;
}
private String getKeyFromArray(byte[] addressDomain) {
StringBuffer sBuffer = new StringBuffer();
for(int i=0;i<5;i++){
sBuffer.append(addressDomain[i]);
}
return sBuffer.toString();
}
protected String to8BitString(String binaryString) {
int len = binaryString.length();
for (int i = 0; i < 8-len; i++) {
binaryString = "0"+binaryString;
}
return binaryString;
}
protected static byte[] combine2Byte(byte[] bt1, byte[] bt2){
byte[] byteResult = new byte[bt1.length+bt2.length];
System.arraycopy(bt1, 0, byteResult, 0, bt1.length);
System.arraycopy(bt2, 0, byteResult, bt1.length, bt2.length);
return byteResult;
}
}
package com.example.demo.netty2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
public class OutBoundHandler extends ChannelOutboundHandlerAdapter{
private static Logger logger = LoggerFactory.getLogger(OutBoundHandler.class);
@Override
public void write(ChannelHandlerContext ctx, Object msg,
ChannelPromise promise) throws Exception {
if (msg instanceof byte[]) {
byte[] bytesWrite = (byte[])msg;
ByteBuf buf = ctx.alloc().buffer(bytesWrite.length);
logger.info("向设备下发的信息为:"+TCPServerNetty.bytesToHexString(bytesWrite));
buf.writeBytes(bytesWrite);
ctx.writeAndFlush(buf).addListener(new ChannelFutureListener(){
@Override
public void operationComplete(ChannelFuture future)
throws Exception {
logger.info("下发成功!");
}
});
}
}
}
quartz 定时任务代码:
监听类:
@Slf4j
@Configuration
public class ApplicationStartQuartzJobListener implements ApplicationListener
@Autowired
private QuartzManager quartzManager;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
try {
quartzManager.startJob();
System.out.println("任务已经启动......");
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 初始注入scheduler
*/
@Bean
public Scheduler scheduler() throws SchedulerException{
SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
return schedulerFactoryBean.getScheduler();
}
}
定时任务管理类
@Configuration
public class QuartzManager {
public static final String JOB1="job1";
public static final String GROUP1="group1";
public static final String DEFAULT_CRON="*/5 * * * * ?";
/**
* 任务调度
*/
@Autowired
private Scheduler scheduler;
/**
* 开始执行定时任务
*/
public void startJob() throws SchedulerException {
startJobTask(scheduler);
scheduler.start();
}
/**
* 启动定时任务
* @param scheduler
*/
private void startJobTask(Scheduler scheduler) throws SchedulerException {
JobDetail jobDetail= JobBuilder.newJob(MyJob.class).withIdentity(JOB1,GROUP1).build();
CronScheduleBuilder cronScheduleBuilder=CronScheduleBuilder.cronSchedule(DEFAULT_CRON);
CronTrigger cronTrigger=TriggerBuilder.newTrigger().withIdentity(JOB1,GROUP1)
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail,cronTrigger);
}
/**
* 获取Job信息
* @param name
* @param group
*/
public String getjobInfo(String name,String group) throws SchedulerException {
TriggerKey triggerKey=new TriggerKey(name,group);
CronTrigger cronTrigger= (CronTrigger) scheduler.getTrigger(triggerKey);
return String.format("time:%s,state:%s",cronTrigger.getCronExpression(),
scheduler.getTriggerState(triggerKey).name());
}
/**
* 修改任务的执行时间
* @param name
* @param group
* @param cron cron表达式
* @return
* @throws SchedulerException
*/
public boolean modifyJob(String name,String group,String cron) throws SchedulerException{
Date date=null;
TriggerKey triggerKey=new TriggerKey(name, group);
CronTrigger cronTrigger= (CronTrigger) scheduler.getTrigger(triggerKey);
String oldTime=cronTrigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)){
CronScheduleBuilder cronScheduleBuilder=CronScheduleBuilder.cronSchedule(cron);
CronTrigger trigger=TriggerBuilder.newTrigger().withIdentity(name,group)
.withSchedule(cronScheduleBuilder).build();
date=scheduler.rescheduleJob(triggerKey,trigger);
}
return date !=null;
}
/**
* 暂停所有任务
* @throws SchedulerException
*/
public void pauseAllJob()throws SchedulerException{
scheduler.pauseAll();
}
/**
* 暂停某个任务
* @param name
* @param group
* @throws SchedulerException
*/
public void pauseJob(String name,String group)throws SchedulerException{
JobKey jobKey=new JobKey(name,group);
JobDetail jobDetail=scheduler.getJobDetail(jobKey);
if (jobDetail==null)
return;
scheduler.pauseJob(jobKey);
}
/**
* 恢复所有任务
* @throws SchedulerException
*/
public void resumeAllJob()throws SchedulerException{
scheduler.resumeAll();
}
/**
* 恢复某个任务
*/
public void resumeJob(String name,String group)throws SchedulerException{
JobKey jobKey=new JobKey(name,group);
JobDetail jobDetail=scheduler.getJobDetail(jobKey);
if (jobDetail==null)
return;
scheduler.resumeJob(jobKey);
}
/**
* 删除某个任务
* @param name
* @param group
* @throws SchedulerException
*/
public void deleteJob(String name,String group)throws SchedulerException{
JobKey jobKey=new JobKey(name, group);
JobDetail jobDetail=scheduler.getJobDetail(jobKey);
if (jobDetail==null)
return;
scheduler.deleteJob(jobKey);
}
}
任务工厂
@Component
public class MyJobFactory extends AdaptableJobFactory{
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
// 进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
具体任务类:此处是拿到接入设备的通道,,发送指令
public class MyJob implements Job{
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
executeTask();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
private void executeTask() throws SchedulerException {
//String ip = "169.254.249.123";
String ip = "169.254.21.39";
Map
if(map.get(ip) != null){
Channel ch = map.get(ip);
String content = "01 03 00 00 00 02 C4 0B";
byte[] bs = BytesAndHex.hexTobytes(content);
ch.writeAndFlush(bs);
}
}
}
spring boot启动
@SpringBootApplication
@EntityScan(basePackages={"com.example.*"})
public class DemoApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
new TCPServerNetty(4455).start();
}
}
最终的效果为:
2019-08-26 13:33:10.772 INFO 11712 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
2019-08-26 13:33:10.772 INFO 11712 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.1
2019-08-26 13:33:10.772 INFO 11712 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@42f9c19a
2019-08-26 13:33:11.086 INFO 11712 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
2019-08-26 13:33:11.149 INFO 11712 --- [ main] o.s.i.endpoint.EventDrivenConsumer : Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2019-08-26 13:33:11.165 INFO 11712 --- [ main] o.s.i.channel.PublishSubscribeChannel : Channel 'application.errorChannel' has 1 subscriber(s).
2019-08-26 13:33:11.165 INFO 11712 --- [ main] o.s.i.endpoint.EventDrivenConsumer : started bean '_org.springframework.integration.errorLogger'
2019-08-26 13:33:11.165 INFO 11712 --- [ main] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now
2019-08-26 13:33:11.165 INFO 11712 --- [ main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
2019-08-26 13:33:11.180 INFO 11712 --- [ main] org.quartz.core.QuartzScheduler : Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
任务已经启动......
2019-08-26 13:33:11.180 INFO 11712 --- [ main] com.example.DemoApplication : Started DemoApplication in 1.567 seconds (JVM running for 2.115)
2019-08-26 13:33:11.666 INFO 11712 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler : [id: 0x8339289a] REGISTERED
2019-08-26 13:33:11.675 INFO 11712 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler : [id: 0x8339289a] BIND: 0.0.0.0/0.0.0.0:4455
2019-08-26 13:33:11.676 INFO 11712 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler : [id: 0x8339289a, L:/0:0:0:0:0:0:0:0:4455] ACTIVE
2019-08-26 13:33:11.922 INFO 11712 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler : [id: 0x8339289a, L:/0:0:0:0:0:0:0:0:4455] RECEIVED: [id: 0x03de23cd, L:/169.254.249.123:4455 - R:/169.254.21.39:23]
CLIENT/169.254.21.39:23 接入连接
2019-08-26 13:33:15.065 INFO 11712 --- [ntLoopGroup-5-1] com.example.demo.netty2.OutBoundHandler : 向设备下发的信息为:01 03 00 00 00 02 c4 0b
2019-08-26 13:33:15.073 INFO 11712 --- [ntLoopGroup-5-1] com.example.demo.netty2.OutBoundHandler : 下发成功!
来自设备的信息:0103040192010d5bd1
2019-08-26 13:33:20.006 INFO 11712 --- [ntLoopGroup-5-1] com.example.demo.netty2.OutBoundHandler : 向设备下发的信息为:01 03 00 00 00 02 c4 0b
2019-08-26 13:33:20.010 INFO 11712 --- [ntLoopGroup-5-1] com.example.demo.netty2.OutBoundHandler : 下发成功!
来自设备的信息:0103040191010d0a6f
文章中定时任务与netty代码融合其他文章的。。我这边只是把它实现和硬件设备实时通讯。。留个纪念