//ICBC中账户card001转出300元
@Test
public void tranfer(){
EventLog eventLog = new EventLog();
eventLog.setAmount(new BigDecimal(300));
eventLog.setFromcard("card001");
eventLog.setTocard("card002");
eventLog.setEventstate(EventState.NEW);
eventLog.setTransferDate(new Date());
eventLogService.transfer(eventLog,new BigDecimal(300));
}
/**
* 在排它锁场景下数据更新,保证数据的可靠性
*/
@Override
public void updateEventstateById(String id, EventState eventState) {
EventLog eventLog=findEventLog4Update(id);
eventLog.setEventstate(eventState);
emJ1.merge(eventLog);
}
/**
* 实现排它锁查询
*/
@Override
public EventLog findEventLog4Update(String id){
EventLog eventLog=emJ1.find(EventLog.class, id, LockModeType.PESSIMISTIC_WRITE);
return eventLog;
}
@Service
public class EventLogService {
@Autowired
private EventLogRepository eventLogRepository;
@Resource(name="jmsQueueMessagingTemplate")
private JmsMessagingTemplate jmsQueueMessagingTemplate;
@Autowired
@Qualifier("icbc2boc")
private Queue icbc2boc;
....
/**
* 根据eventstate获取EventLog数据集
* @param eventstate
* @return
*/
@Transactional(transactionManager="transactionManager1",propagation=Propagation.SUPPORTS,readOnly=true)
public List<EventLog> findByEventState(EventState eventstate){
return eventLogRepository.findByEventstate(eventstate);
}
/**
* XA事务
* @param id
* @param eventstate
*/
@Transactional(transactionManager="transactionManagerJTA",propagation=Propagation.REQUIRES_NEW)
public void transferToMQ(EventLog eventLog,EventState eventstate,CountDownLatch countDownLatch){
try {
System.out.println(Thread.currentThread().getName()+"本次处理数据:"+eventLog.getFromcard()+"、"+eventLog.getEventstate());
//再次数据库查询判断,此时用到排它锁--在两个定时任务连续执行,一旦出现程序提交事务命令至数据库,
//但数据库还未执行,此时我们全表查询的结果中当前数据行仍为修改前数据,故会造成重复消费
eventLog=eventLogRepository.findEventLog4Update(eventLog.getId());
if(EventState.Publish.equals(eventLog.getEventstate())){
System.out.println(Thread.currentThread().getName()+"数据:"+eventLog.getFromcard()+"无需处理");
return;
}
//payload
jmsQueueMessagingTemplate.convertAndSend(icbc2boc,eventLog);
eventLogRepository.updateEventstateById(eventLog.getId(), eventstate);
//构造异常场景验证XA事务
if(eventLog.getFromcard()==null){
System.out.println(Thread.currentThread().getName()+"数据异常,不处理");
System.out.println(1/0);
}else{
System.out.println(Thread.currentThread().getName()+":"+eventLog.getFromcard()+"数据处理成功");
}
} finally {
countDownLatch.countDown();
}
}
}
/**
* 转出任务
* @author song
*/
@PersistJobDataAfterExecution
@DisallowConcurrentExecution //保证每次任务执行完毕,设置为串行执行
public class TransferJob extends QuartzJobBean {
private Logger logger=LoggerFactory.getLogger(TransferJob.class);
@Autowired
@Qualifier("quartzThreadPool")
private ThreadPoolTaskExecutor quartzThreadPool;
@Autowired
private EventLogService eventLogService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("本次批处理开始");
//获取所有未发送状态的Event
List<EventLog> list=eventLogService.findByEventState(EventState.NEW);
//
final CountDownLatch countDownLatch=new CountDownLatch(list.size());
//遍历发送
for(final EventLog eventLog:list){
//通过线程池提交任务执行,大大提高处理集合效率
quartzThreadPool.submit(new Runnable() {
@Override
public void run() {
eventLogService.transferToMQ(eventLog,EventState.Publish,countDownLatch);
}
});
}
//保证所有线程执行完成后退出
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("本次批处理完成");
}
}
@Bean(name="tranferJob")
public JobDetailFactoryBean tranferJob(){
JobDetailFactoryBean factoryBean=new JobDetailFactoryBean();
//定义任务类
factoryBean.setJobClass(TransferJob.class);
//表示任务完成之后是否依然保留到数据库,默认false
factoryBean.setDurability(true);
//为Ture时当Quartz服务被中止后,再次启动或集群中其他机器接手任务时会尝试恢复执行之前未完成的所有任务,默认false
factoryBean.setRequestsRecovery(true);
return factoryBean;
}
/**
* 注册job1的触发器
* @return
*/
@Bean(name="transferJobTrigger")
public CronTriggerFactoryBean transferJobTrigger(){
//触发器
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setCronExpression("*/5 * * * * ?");
cronTriggerFactoryBean.setJobDetail(tranferJob().getObject());
//调度工厂实例化后,经过5秒开始执行调度
cronTriggerFactoryBean.setStartDelay(30000);
cronTriggerFactoryBean.setGroup("tranfer");
cronTriggerFactoryBean.setName("tranfer");
return cronTriggerFactoryBean;
}
/**
* 调度工厂,加载触发器,并设置自动启动、启动时延
* @return
*/
@Bean(name="transferSchedulerFactoryBean")
public SchedulerFactoryBean transferSchedulerFactoryBean(){
//调度工厂
SchedulerFactoryBean schedulerFactoryBean= new SchedulerFactoryBean();
schedulerFactoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));
schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContextKey");
//集群Cluster下设置dataSource
// schedulerFactoryBean.setDataSource(dataSource);
//QuartzScheduler启动时更新己存在的Job,不用每次修改targetObject后删除qrtz_job_details表对应记录了
schedulerFactoryBean.setOverwriteExistingJobs(true);
//QuartzScheduler延时启动20S,应用启动完后 QuartzScheduler 再启动
schedulerFactoryBean.setStartupDelay(20);
//自动启动
schedulerFactoryBean.setAutoStartup(true);
schedulerFactoryBean.setTriggers(transferJobTrigger().getObject());
//自定义的JobFactory解决job中service的bean注入
schedulerFactoryBean.setJobFactory(jobFactory);
return schedulerFactoryBean;
}
/**
* 用于处理待转账数据发至消息队列的线程池
* @return
*/
@Bean(name="quartzThreadPool")
public ThreadPoolTaskExecutor getThreadPoolTaskExecutor(){
ThreadPoolTaskExecutor pool=new ThreadPoolTaskExecutor();
pool.setCorePoolSize(10);
pool.setQueueCapacity(100);
pool.setMaxPoolSize(10);
pool.setKeepAliveSeconds(10);
//避免应用关闭,任务没有执行完成,起到shutdownhook钩子的作用
pool.setWaitForTasksToCompleteOnShutdown(true);
//空闲时核心线程也不退出
pool.setAllowCoreThreadTimeOut(false);
//设置拒绝策略,不可执行的任务将被抛弃
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
return pool;
}
@Configuration
public class JmsMessageConfiguration {
@Autowired
@Qualifier(value="jmsQueueTemplate")
private JmsTemplate jmsQueueTemplate;
/**
* 定义点对点队列
* @return
*/
@Bean(name="icbc2boc")
public Queue queue() {
return new ActiveMQQueue("icbc2boc");
}
/**
* 创建处理队列消息模板
* @return
*/
@Bean(name="jmsQueueMessagingTemplate")
public JmsMessagingTemplate jmsQueueMessagingTemplate() {
JmsMessagingTemplate jmsMessagingTemplate =new JmsMessagingTemplate(jmsQueueTemplate);
//通过MappingJackson2MessageConverter实现Object转换
jmsMessagingTemplate.setMessageConverter(new MappingJackson2MessageConverter());
return new JmsMessagingTemplate(jmsQueueTemplate);
}
}
@Component
public class TraferIn {
@Autowired
@Qualifier("icbc2boc")
private Queue queue;
@Autowired
@Qualifier("jmsQueueMessagingTemplate")
private JmsMessagingTemplate jmsQueueMessagingTemplate;
@Autowired
private EventLogService eventLogService;
/**
* 定义监听转账事件监听
* @param text
* @throws Exception
*/
@JmsListener(destination = "icbc2boc",containerFactory="jmsListenerContainerFactory4Queue")//ActiveMQ.DLQ
public void receiveQueue(EventLog eventLog) throws Exception {
System.out.println("接受到的事件数据:"+eventLog.toString());
eventLogService.mq2transfer(eventLog, new BigDecimal(300));
}
}
/**
* XA事务
* @param eventLog
* @param amount
*/
@Transactional(transactionManager="transactionManagerJTA",propagation=Propagation.REQUIRED)
public void mq2transfer(EventLog eventLog,BigDecimal amount){
//保存事件日志
eventLogRepository.saveEvetLog(eventLog);
// System.out.println(1/0);
}
/**
* 采用分布式事务数据源保存事件
*/
@Override
public EventLog saveEvetLog(EventLog eventLog) {
return emJ1.merge(eventLog);
}
/**
* 实现排它锁
* @param id
* @return
*/
@Query(value="select t.id from t_card t where t.id=:id for update",nativeQuery=true)
void findCard4UpdateById(@Param("id")String id);
/**
* 更新EventLog状态
* @param id
* @param eventstate
* @return
*/
@Modifying
@Query(value = "update EventLog e set e.eventstate=:eventstate where e.id = :id ")
int updateEventstateById(@Param("id")String id,@Param("eventstate")EventState eventstate);
/**
* 根据EventState查询所有EventLog
* @param EventState
* @return
*/
List<EventLog> findByEventstate(EventState eventState);
/**
* 执行原生语句实现更新
*/
@Override
public int executeUpdateNativeSQL(String strSQL) {
return em1.createNativeQuery(strSQL, Integer.class).executeUpdate();
}
/**
* 本地事务
* @param id
* @param eventstate
*/
@Transactional(transactionManager="transactionManager1",propagation=Propagation.REQUIRES_NEW)
public void transfer(EventLog eventLog,EventState eventstate,CountDownLatch countDownLatch){
try {
System.out.println(Thread.currentThread().getName()+"本次处理数据转入账号:"+eventLog.getTocard()+"、"+eventLog.getEventstate());
//通过乐观锁方式再次判断,保证事件的可靠消息,仅在极端情况下会出现重复消费,故采用乐观锁
int updateCount=eventLogRepository.updateEventstateById(eventLog.getId(),eventstate);
//如果等於则表明已经处理过
if(updateCount==0){
System.out.println(Thread.currentThread().getName()+"数据收款卡号:"+eventLog.getTocard()+"无需处理");
return;
}
//没有被处理过需要继续更新账户金额
//更新查询,采用排它锁方式,避免在多线程任务下,出现多个线程修改同一个卡号,从而事务幂等性
eventLogRepository.findCard4UpdateById(eventLog.getTocard());
//更新账户信息,转入累加,属于非幂等操作
eventLogRepository.executeUpdateNativeSQL("update t_card set amount=amount+"+eventLog.getAmount()+" where id='"+eventLog.getTocard()+"'");
// System.out.println(1/0);
System.out.println(Thread.currentThread().getName()+":"+eventLog.getFromcard()+"数据处理成功");
} finally {
countDownLatch.countDown();
}
}
@PersistJobDataAfterExecution
@DisallowConcurrentExecution //保证每次任务执行完毕,设置为串行执行
public class TransferJob extends QuartzJobBean {
private Logger logger=LoggerFactory.getLogger(TransferJob.class);
@Autowired
@Qualifier("quartzThreadPool")
private ThreadPoolTaskExecutor quartzThreadPool;
@Autowired
private EventLogService eventLogService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("本次批处理开始");
//获取所有未发送状态的Event
List<EventLog> list=eventLogService.findByEventState(EventState.NEW);
//
final CountDownLatch countDownLatch=new CountDownLatch(list.size());
//遍历发送
for(final EventLog eventLog:list){
//通过线程池提交任务执行,大大提高处理集合效率
quartzThreadPool.submit(new Runnable() {
@Override
public void run() {
eventLogService.transfer(eventLog, EventState.Publish, countDownLatch);
}
});
}
//保证所有线程执行完成后退出
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("本次批处理完成");
}
}