今天遇到需求要统计每次执行成功的个数和失败的个数,但是项目部署的是多实例的而且每个实例还是多线程的。
只使用多线程的计数器,没有很好的方法去实现多实例的数据汇总,想到使用redis或者mysql,由于是多线程的对统计字段变化频繁所以果断放弃mysql选择redis,
之前使用mysql遇到过多线程情况下数据出现ABA问题,所以在选择redis的时候不能只是单纯的更新数据,还要考虑多线程情况下数据的同步问题。
同时参考了:https://my.oschina.net/sxwailyc/blog/1973427
在此处记录一下,以后自己使用同时方便他人查看
public class CounterUtil {
static {
redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
}
private static RedisTemplate redisTemplate;
public static void counter(String key){
List<Object> result =null;
do{
redisTemplate.setEnableTransactionSupport(true);//不可少
int count = 0;
redisTemplate.watch(key); //watch某个key,当该key被其它客户端改变时,则会中断当前的操作
String value = (String) redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(value)) {
count = Integer.parseInt(value);
}
count = count + 1;
redisTemplate.multi(); //开始事务
redisTemplate.opsForValue().set(key, String.valueOf(count));
try{
result = redisTemplate.exec();//执行事务
}catch(Exception e){
//如果key被改变,提交事务时这里会报异常
}
}while(result == null || result.size()<=0);
}
}
测试方法:使用多线程调用工具类,同时在自己的redis中查看对应key值的变化是否达到预期(我这里实在springboot项目的junit测试,其他测试方法可自己重写)
@Test
void contextLoads() {
Runnable runable = new Runnable() {
@Override
public void run() {
String key = "test:success:count";
CounterUtil.counter(key);
}
};
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(runable, "thread-" + (i + 1));
thread.start();
threads.add(thread);
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}