1. 应用背景
项目中需要对GPS原始坐标进行高德坐标变形,GPS坐标一直在新增,为了保证服务的稳定性,采用了简单的消息队列进行处理。
设定定时任务,每次将待变形的坐标数据保存至待变形消息队列,然后变形服务去这个队列中取数据,将变形后的结果存入已变形消息队列,更新服务进入这个队列取数据,更新数据库。
备注:近5个月坐标转化量
2015年12月,日平均坐标量约6.16万
2015年11月,日平均坐标量约6.38万
2015年10月,日平均坐标量约3.71万
2015年09月,日平均坐标量约5.81万
2015年08月,日平均坐标量约7.40万
2. 消息队列
package com.autonavi.messagequeue;
import java.util.LinkedList;
import java.util.NoSuchElementException;
/**
* @note 消息队列FIFO
*/
public class MessageQueue<T> {
private int theMaxSizeOfQueue = 5000;
/**
* @param 1-锁定;0-可执行
*/
private int theLockStatusOfQueue = 1;
private LinkedList<T> msgList = new LinkedList<T>();
/**
* @param void
* @return t or null
*/
public T getMsg(){
T t;
try{
t = msgList.getFirst();
removeMsg();
}catch(NoSuchElementException e){
return null;
}
return t;
}
/**
* @param t
* @return true-添加成功;false-队列已满,需等候
*/
public boolean addMsg(T t){
boolean res = false;
if(lockStatusMsg() == false){
lockMsg();
int size = sizeMsg();
if(size < theMaxSizeOfQueue){
msgList.addLast(t);
res = true;
}else{
res = false;
}
}else{
res = false;
}
unlockMsg();
return res;
}
/**
* @param t
* @return true-删除成功;false-空队列
*/
public boolean removeMsg(){
boolean res = false;
if(lockStatusMsg() == false){
T t;
lockMsg();
try{
t = msgList.removeFirst();
}catch(NoSuchElementException e){
res = false;
t = null;
}
if(t != null){
res = true;
}else{
res = false;
}
}
unlockMsg();
return res;
}
public int sizeMsg(){
return msgList.size();
}
/**
* @param 锁定状态
*/
public boolean lockStatusMsg(){
if(theLockStatusOfQueue == 1){
return true;
}else{
return false;
}
}
/**
* @param 锁定
*/
public void lockMsg(){
theLockStatusOfQueue = 1;
}
/**
* @param 解锁
*/
public void unlockMsg(){
theLockStatusOfQueue = 0;
}
}
3. 定时任务
package com.autonavi.task;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.autonavi.constants.Constant;
import com.autonavi.dao.impl.mapper.CarPositionMapper;
import com.autonavi.domain.Coordinate;
import com.autonavi.messagequeue.MessageQueue;
import com.autonavi.method.DataChange;
import com.autonavi.method.DataQuery;
import com.autonavi.method.DataUpdate;
/**
* <p>Author:
* <p>Date: 15-12-24
* <p>Version: 2.0
* <p>note: 4个定时计划: 数据查询,数据变形,数据更新,日志记录
* @param <msgQueue>
*/
@Component
public class ScheduledTasks {
@SuppressWarnings("unused")
@Autowired
private CarPositionMapper carPositionMapper;
private static final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class);
// 队列1:存储待变形数据
MessageQueue<Coordinate> msgQueue_in = new MessageQueue<Coordinate>();
// 队列2:存储已变形数据
MessageQueue<Coordinate> msgQueue_out = new MessageQueue<Coordinate>();
/**
* <p>note: 1
* <p>note: 数据查询业务
* @param <msgQueue_1>
*/
@Scheduled(cron="0 0/10 0-23 * * ?")
public void executeDataQueryTask() {
long startTime = System.currentTimeMillis();
logger.info("ScheduledTasks-executeDataChangeTask: 待变形队列-入队:start");
Coordinate coor = null;
List<Coordinate> list = DataQuery.query();
int size = 0;
if(list != null){
size = list.size();
}
if(size > 0){
for(int i=0;i<size;i++){
coor = list.get(i);
msgQueue_in.addMsg(coor);
}
}
long endTime = System.currentTimeMillis();
logger.info("ScheduledTasks-executeDataQueryTask: 待变形队列-入队:"+msgQueue_in.sizeMsg());
logger.info("ScheduledTasks-executeDataChangeTask: 待变形队列-入队:end,耗时:"+(endTime-startTime)+"ms");
}
/**
* <p>note: 2
* <p>note: 数据变形业务
*/
@Scheduled(cron="0 0/3 0-23 * * ?")
public void executeDataChangeTask() {
long startTime = System.currentTimeMillis();
logger.info("ScheduledTasks-executeDataChangeTask: 已变形队列-入队:start");
List<Coordinate> list = new ArrayList<Coordinate>();
List<Coordinate> list_return = null;
int size = msgQueue_in.sizeMsg();
int current_times = 0;
if(size < Constant.DEAL_MAX_TIMES){
current_times = size;
}else{
current_times = Constant.DEAL_MAX_TIMES;
}
for(int i=0;i<current_times;i++){
Coordinate coor = msgQueue_in.getMsg();
list.add(coor);
}
if(list != null && list.size() != 0){
list_return = DataChange.change(list);
}
if(list_return != null && list_return.size() != 0){
for(int j=0;j<list_return.size();j++){
msgQueue_out.addMsg(list_return.get(j));
}
}
long endTime = System.currentTimeMillis();
// System.out.println("消息队列2:入队"+msgQueue_out.sizeMsg());
logger.info("ScheduledTasks-executeDataChangeTask: 已变形队列-入队:"+msgQueue_out.sizeMsg());
logger.info("ScheduledTasks-executeDataChangeTask: 已变形队列-入队:end,耗时:"+(endTime-startTime)+"ms");
}
/**
* <p>note: 3
* <p>note: 数据更新业务
*/
@Scheduled(cron="0 0/3 0-23 * * ?")
public void executeDataUpdateTask() {
long startTime = System.currentTimeMillis();
logger.info("ScheduledTasks-executeDataChangeTask: 已变形队列-出队:start");
List<Coordinate> list = new ArrayList<Coordinate>();
Integer counts = 0;
int size = msgQueue_out.sizeMsg();
int current_times = 0;
if(size < Constant.DEAL_MAX_TIMES){
current_times = size;
}else{
current_times = Constant.DEAL_MAX_TIMES;
}
for(int i=0;i<current_times;i++){
Coordinate coor = msgQueue_out.getMsg();
list.add(coor);
}
if(list != null && list.size() != 0){
counts = DataUpdate.update(list);
}
long endTime = System.currentTimeMillis();
// System.out.println("ScheduledTasks-executeDataUpdateTask: 已变形数量-消息出队:"+msgQueue_in.sizeMsg());
logger.info("ScheduledTasks-executeDataChangeTask: 已变形队列-出队:"+counts);
logger.info("ScheduledTasks-executeDataChangeTask: 已变形队列-出队:end,耗时:"+(endTime-startTime)+"ms");
}
/**
* <p>note: 4
* <p>note: 消息队列日志
*/
@Scheduled(cron="0 0/10 0-23 * * ?")
public void executeDataQueueTask() {
int msgQueue_in_size = msgQueue_in.sizeMsg();
int msgQueue_out_size = msgQueue_out.sizeMsg();
logger.info("ScheduledTasks-executeDataQueueTask: 待变形队列-数据量:"+msgQueue_in_size);
logger.info("ScheduledTasks-executeDataQueueTask: 已变形队列-数据量:"+msgQueue_out_size);
}
}
4. 实现说明
目前,还是一个单线程息消息队列,后续会实现多线程消息队列。仅供大伙参考。如需项目代码,请留言。
5. 最终效果
坐标变形结果都挺好。服务运行也挺稳定。