编程的小道上,我们都在不断的探索和学习,这将是一条曲折的小道,毕竟天才属于少数. 写一段优雅的代码,献给可爱的猿们.
序:一直都很喜欢优雅的代码,也见过一些糟糕的代码,把编程当作一种享受,一种极致的追求,便能写出现阶段属于自己的优雅的代码.
从提一个简单的需求开始:现阶段需要实现一个同步Redis的小程序,当然有点经验的你自然不在话下,待我们一一道来.
- 一个优雅的配置文件,如你所见,下列YML配置项目定义了ZK,Oracle数据源,Redis数据源,已经一个工作描述List<Map<String,String>> 格式的 works.
spring:
application:
name: ZXBK2REDIS
#ZK
cloud:
zookeeper:
enabled: true
connect-string: host1:2181,host2:2181,host3:2181
discovery:
root: /services
register: true
#REDIS
redis:
database: 1
host: host211
port: 6379
timeout: 1800000
pool:
max-active: 20
min-idle: 5
#DB
datasource:
url: jdbc:oracle:thin:@localhsot:1521/ORCL
username: root
password: root
driver-class-name: oracle.jdbc.driver.OracleDriver
#WORKS
tvc.sync.redis:
works:
- id: 1
name: name1
sql: select * from t1
enabled: true
- id: 2
name: name2
sql: select * from t2
enabled: false
- id: 3
name: name3
sql: select * from t3
enabled: true
- id: 4
name: name4
sql: select * from t4
enabled: true
- id: 5
name: name5
sql: name5
enabled: true
- 我们需要做的是基于上述配置文件定义一个数据源,一个Redis的连接池,一个List<Map<String,String>>的集合对象
一个DataSource 数据源的定义
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
log.debug("DataSource INIT ");
return DataSourceBuilder.create().build();
}
一个直接拿来使用的RedisTemplate对象
@Autowired
private RedisTemplate<String, String> redisTemplate;
一个直接绑定到Java集合的文本数据模型
@ConfigurationProperties(prefix = "tvc.sync.redis")
public class JobConfig {
private List<Map<String, String>> works;
- 经验法则, 代码的分布,并发的潜能,留一条后路. 看一段代码 关键代码加粗
@Component
public class TimerTask extends Thread implements ApplicationContextAware {
public static final Log log = LogFactory.getLog(TimerTask.class);
public static final Lock lock = new ReentrantLock();
@Autowired
private JobConfig jobConfig;
@Autowired
private RedisTemplate<String, String> redisTemplate;
private ExecutorService workThreadPool;
@Scheduled(cron = "${tvc.sync.redis.cron:0 0 2 * * ?}")
public void syncRedisJob() {
if (workThreadPool == null) {
workThreadPool = Executors.newFixedThreadPool(jobConfig.getConcurrency());
}
if (!lock.tryLock()) {
log.info("实例已经在同步!!!");
return;
}
//
List<Map<String, String>> works = new ArrayList<Map<String, String>>();
for (Map<String, String> work : jobConfig.getWorks()) {
if ("true".equals(work.get("enabled"))) {
works.add(work);
}
}
// 同步计数器
CountDownLatch countDownLatch = new CountDownLatch(works.size());
try {
printSysStatus();
// 同步标记
redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
connection.set("sync".getBytes(), "0".getBytes());
log.info("sync:" + 0);
return true;
}
});
// 清库
{
log.info("开始清理数据");
Set<String> keys = redisTemplate.keys("v:*");
redisTemplate.delete(keys);
/*
* for (String key : keys) { redisTemplate.delete(key); }
*/
log.info("清理结束,共:" + keys.size() + "条");
}
// 准备同步
for (Map<String, String> work : works) {
TaskRun taskRun = applicationContext.getBean(TaskRun.class);
taskRun.setCountDownLatch(countDownLatch);
taskRun.setWork(work);
workThreadPool.execute(taskRun);
}
///
try {
countDownLatch.await(jobConfig.getTimeout(), TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
log.error("同步超时", e);
}
} finally {
// 同步标记
try {
redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
connection.set("sync".getBytes(), "1".getBytes());
log.info("sync:" + 1);
return true;
}
});
} finally {
lock.unlock();
}
if ("true".equals(jobConfig.getEndautogc())) {
System.gc();
}
}
}
@Override
public void run() {
syncRedisJob();
}
// IOC初始化时调用一次 见IOCInit.java
public void init() {
this.start();
}
// 状态日志
public void printSysStatus() {
log.info(this);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
private ApplicationContext applicationContext;
@Override
public String toString() {
return "TimerTask [jobConfig=" + jobConfig + ", redisTemplate=" + redisTemplate + ", workThreadPool="
+ workThreadPool + ", applicationContext=" + applicationContext + "]";
}
}
- 允许并发无疑是意见好事,是允许而不是必须,我们当然可以使用一个大小为1的固定线程池来实现.
- 总要谨慎潜在的危险. 合理是用finally 块无疑是一种保障. ,让等待付出代价是必要的,有限的超时.
- 避免潜在的危险,真正属于自己的东西用起来才更放心.遍历删除一定要小心. 输出一个看的明白的的怪物总是有必要的.
- 意外是有发生,也许就在明天,你只能启动一个.
最后多写几行代码,浏览器中可以调度一个作业那是在好不过了.
@RestController
public class RestHandler {
@Autowired
private TimerTask timerTask;
@RequestMapping("/execute")
public String immediatelyExecute() {
new Thread() {
public void run() {
timerTask.run();
};
}.start();
return timerTask.toString();
}
}