Remember a distributed lock-based on database

1: Distributed locks, there are three ways I know

A: Traditional database global lock

B: Global lock based on cache, such as redis

C: distributed lock based on zookeeper

The priority of these three methods is C> B> A. Because of the company's structure, the first implementation method was finally chosen. So this article only talks about distributed locks on the database. Here are a few links to related knowledge, please check it yourself

Several implementation methods of distributed locks

The correct implementation of Redis distributed locks

Distributed lock and implementation (two)-based on ZooKeeper implementation

2: Advantages and disadvantages of database global lock

advantage:

Simple and easy to implement

Disadvantages:

A: The existence of a database is generally a single point, once the database is down. Services are not available.

B: The lock has no expiration time. Once unlocking fails, the lock will always exist, resulting in unavailability of services and blocking of threads

C: The lock can only be non-blocking, the lock cannot be reentrant

3: The solution to the database global lock

1: Single point? The database can make more database backups.

2: No expiration time? Scheduled tasks, clean up once in a while. Or every time a lock is locked, an expected effective time is inserted, and the next time the lock is locked, it is judged whether the current time is greater than the effective time to determine whether the lock is invalid.

3: Non-blocking? Open another thread to obtain loop

4: Non-reentrant, add machine information and thread information when locking, and judge these two fields first when acquiring next time

4: Code example

@Component
public class ArchiveTask {
    
    
	
	private ThreadPoolTaskScheduler scheduler  = null;
	private ArchiveTaskDAO dao;
	private ScheduledFuture<?> future;
	private String clearDataDay = "90"; //默认迁移90天前的数据
	private String wfmProcedure = null;
	public static String stype = "TASK_SCHEDULER";
	public static String pkey = "WFM_ARCHIVE_TASK";
	public static String lock = "lock";   // 上锁状态
	public static String unLock = "unLock"; // 无锁状态
	public static String errorLock = "errorLock"; // 发生了错误
	public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
	private boolean isLock = false;
	
	//private Logger logger = LoggerFactory.getLogger(ArchiveTask.class);
	
	public void init(){
    
    
		
		scheduler = new ThreadPoolTaskScheduler();
		dao = (ArchiveTaskDAO) BaseDAOFactory.getDAO(ArchiveTaskDAO.class.getName());
		wfmProcedure = Optional.ofNullable(dao.getConfigByType("ORDER", "HIS_SAVE_ORDERHIS_BY_ORDERID"))
				.orElse("HIS_SAVE_ORDERHIS_BY_ORDERID");
	}
	public void schedule(){
    
    
		
		scheduler.initialize();
		future = scheduler.schedule(()->{
    
    
			// 执行任务
			runTask();
			
		}, new Trigger(){
    
    
			// 先执行 Trigger,在执行 Runnable
			@Override
			public Date nextExecutionTime(TriggerContext triggerContext) {
    
    
				// TODO Auto-generated method stub
				Date nextExecDate = null;
                try {
    
    
                	//查询定时计划
                    Map<String,Object> taskMap = dao.getTaskSchedulerCron();
                    String cron = MapUtils.getString(taskMap, "cron", "");
                    if(cron.equals("")) {
    
    
                        return null;
                    } 
                    // 定时任务触发,可修改定时任务的执行周期。数据库修改即可,不用重启应用。
                    CronTrigger trigger = new CronTrigger(cron);
                    nextExecDate = trigger.nextExecutionTime(triggerContext);
                    //计算时间差
                    Long timeDiffSecond =  (nextExecDate.getTime() - new Date().getTime())/1000;
                    dao.updateTaskNextTimeDiff(timeDiffSecond.toString());
                }catch (Exception e) {
    
    
                    e.printStackTrace();
                }
                return nextExecDate;
			}
			
		});
	}
	public void stop(){
    
    
		future.cancel(true);
	}
	public void reStart(){
    
    
		future.cancel(true);
		schedule();
	}
	public void test(){
    
     //  不通过定时任务,测试
		scheduler.initialize();
		scheduler.execute(()->{
    
    
			runTask();
		});	
	}
	
	private  void runTask(){
    
    
		try{
    
    
			Map<String,Object> taskMap = dao.getTaskSchedulerCron();
			//0) 功能开关,如果关闭则直接跳过
			String openStatus = MapUtils.getString(taskMap, "openStatus","");
			if(!openStatus.equals("open")){
    
    
				return;
			}
			//1)查询锁的有效时间
            boolean lockValid = true; // 判断之前的加锁时间是否有效
            String timeDiffSecondStr = MapUtils.getString(taskMap, "timeDiffSecond","");
            String modifyDay = MapUtils.getString(taskMap, "modifyDay", ""); // 最近修改锁时间
            if(modifyDay.equals("")||timeDiffSecondStr.equals("")){
    
    
            	lockValid = false;
            }else{
    
    
            	Long timeDiffSecond = Long.parseLong(timeDiffSecondStr);
            	Date modifyDate = sdf.parse(modifyDay);
            	Calendar  tmpCal = Calendar.getInstance();
            	tmpCal.setTime(modifyDate);
            	tmpCal.add(Calendar.SECOND, timeDiffSecond.intValue());
	            Date curDateForValid = tmpCal.getTime(); //锁的有效时间
	            Date curDate = new Date();
	            if(curDate.after(curDateForValid)){
    
     //锁失效
	            	lockValid = false;
	            }
            }
            //2) 先判断是否已有其他线程、进程在执行数据迁移任务了,数据库锁
			synchronized (ArchiveTask.class) {
    
    
				int flag = dao.taskSchedulerLock(lockValid);
	            if(flag < 1){
    
     // 无法加锁,已被其他任务锁住了
	            	return;
	            }
	            isLock = true;
			}
			//3)查询需要迁移的数据
			Calendar  cal = Calendar.getInstance();
			String dayStr = MapUtils.getString(taskMap, "day", clearDataDay);
			Integer dayI = Integer.parseInt(dayStr);
            cal.add(Calendar.DAY_OF_YEAR, - dayI.intValue());
            Date endDate = cal.getTime();  //  截止日期
            Map<String,Object> params = new HashMap<String, Object>();
            params.put("endDate", endDate);
            params.put("iomBaseDataName", iomBaseDataName);
            //4) (逻辑处理)
            //更新锁状态
            dao.updateTaskSchedulerLock(unLock);
            isLock = false;
		}catch(Exception ex){
    
    
			ex.printStackTrace();
			//如果出错 则更新数据库锁 为 出错状态
			dao.updateTaskSchedulerLock(errorLock);
			isLock = false;
		}
	}
	public void releaseLock(){
    
    
		if(isLock==true){
    
    
			dao.updateTaskSchedulerLock(unLock);
		}
	}
}

Guess you like

Origin blog.csdn.net/u013591094/article/details/109301317
Recommended