业务场景:
1、项目中很多场景下使用到了定时任务,一般采用job的方式
2、一些轻量级的定时操作,如定时查数据库,将数据加载到内存中,不用频繁查数据库,可以采用多线程(newSingleThreadScheduledExecutor)的方式实现显得更轻量高效
废话不多说,直接上代码
(1)、创建一个接口
package com.search.vst.search.service;
/**
* @desc 商圈
* @author zhanhao
*/
import com.search.vst.search.beans.vo.PoiBusinessAreaVo;
public interface PoiBusinessAreaService {
PoiBusinessAreaVo getPoiBusinessArea(String cityId, String keyWords);
void updatePoiBusinessAreaConfig();
}
(2)、创建一个实现类
package com.search.vst.search.service.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.PostConstruct;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.search.vst.search.ENV;
import com.search.vst.search.beans.vo.PoiBusinessAreaVo;
import com.search.vst.search.service.PoiBusinessAreaService;
import com.search.vst.search.service.WordTaggingDataService;
@Service("poiBusinessAreaService")
public class PoiBusinessAreaServiceImpl implements PoiBusinessAreaService {
private static Log log=LogFactory.getLog(PoiBusinessAreaServiceImpl.class);
@Autowired
private WordTaggingDataService wordTaggingDataService;
private ReentrantLock trieBuildLock=new ReentrantLock();
private volatile List<PoiBusinessAreaVo> poiBusinessAreaList= new ArrayList<PoiBusinessAreaVo>();
private static ScheduledExecutorService ex=Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat("PoiBusinessAreaService_trie_worker_%d").build());
@PostConstruct
protected void init() {
ex.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
updatePoiBusinessAreaConfig();
} catch (Exception e) {
log.warn("updatePoiBusinessAreaConfig init error " + e.getMessage());
}
}
}, 0, 2, TimeUnit.MINUTES);
updatePoiBusinessAreaConfigForFirstTime();
}
/**
* 因为项目在重启的过程中,如果依赖的search_calculate也在重启,那么调用dubbo会失败,导致tire数构建失败,这里启动一个线程做多次尝试
*/
private void updatePoiBusinessAreaConfigForFirstTime() {
Thread loopInitThread = new Thread(new Runnable() {
@Override
public void run() {
// 延迟一分钟执行
try {
TimeUnit.MINUTES.sleep(1);
} catch (InterruptedException e1) {
}
log.info(Thread.currentThread().getName() + " start!");
int maxTryTimes = 10;
int tryInterval = 20;
while (maxTryTimes > 0 && CollectionUtils.isEmpty(poiBusinessAreaList)) {
try {
maxTryTimes--;
updatePoiBusinessAreaConfig();
} catch (Exception e) {
if (ENV.isArk()) {
log.warn("try to update config error,times:" + maxTryTimes + "@error " + e);
} else {
log.error("try to update config error,times:" + maxTryTimes, e);
}
} finally {
try {
TimeUnit.SECONDS.sleep(tryInterval);
} catch (InterruptedException e) {
log.error("interupted ,times:" + maxTryTimes, e);
}
}
}
log.info(Thread.currentThread().getName() + " finish!");
}
});
loopInitThread.setName("poi_buildTrie_loopInitThread");
loopInitThread.start();
}
private void buildTrie() {
List<PoiBusinessAreaVo> poiBusinessAreas = wordTaggingDataService.getPoiBusinessArea();
this.poiBusinessAreaList = poiBusinessAreas;
}
@Override
public void updatePoiBusinessAreaConfig() {
boolean lockRequired = false;
try {
lockRequired = trieBuildLock.tryLock(10, TimeUnit.SECONDS);
if (lockRequired) {
buildTrie();
} else {
log.warn("lockRequired unsuccessful");
}
} catch (InterruptedException e) {
log.error("lockRequired fail", e);
} finally {
if (lockRequired) {
trieBuildLock.unlock();
}
}
}
@Override
public PoiBusinessAreaVo getPoiBusinessArea(String cityDistrictId, String keyWords) {
if (CollectionUtils.isNotEmpty(this.poiBusinessAreaList)) {
for (PoiBusinessAreaVo poiBusinessAreaVo : this.poiBusinessAreaList) {
if (Objects.equals(cityDistrictId, poiBusinessAreaVo.getCityId())
&& Objects.equals(keyWords, poiBusinessAreaVo.getPoiBusinessArea())) {
return poiBusinessAreaVo;
}
}
}
return null;
}
}
总结:该业务场景下,一方面减少job的配置与维护,另方面减少频繁查数据库,减少数据库压力,此种方式显得更轻量