为了方便使用,添加一个注解,可以放于方法上控制方法在分布式环境中的同步执行。
/** * 标注在方法上的分布式锁注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DistributedLockable { String key(); String prefix() default "disLock:"; long expire() default 10L; // 默认10s过期 }添加一个切面来解析注解的处理,
/** * 分布式锁注解处理切面 */ @Aspect @Slf4j public class DistributedLockAspect { private DistributedLock lock; public DistributedLockAspect(DistributedLock lock) { this.lock = lock; } /** * 在方法上执行同步锁 */ @Around(value = "@annotation(lockable)") public Object distLock(ProceedingJoinPoint point, DistributedLockable lockable) throws Throwable { boolean locked = false; String key = lockable.prefix() + lockable.key(); try { locked = lock.lock(key, WebUtil.getRequestId(), lockable.expire()); if(locked) { return point.proceed(); } else { log.info("Did not get a lock for key {}", key); return null; } } catch (Exception e) { throw e; } finally { if(locked) { if(!lock.unLock(key, WebUtil.getRequestId())){ log.warn("Unlock {} failed, maybe locked by another client already. ", lockable.key()); } } } } }RequestId 的实现如下,通过注册一个Filter,在请求开始时生成一个uuid存于ThreadLocal中,在请求返回时清除。
public class WebUtil { public static final String REQ_ID_HEADER = "Req-Id"; private static final ThreadLocal<String> reqIdThreadLocal = new ThreadLocal<>(); public static void setRequestId(String requestId) { reqIdThreadLocal.set(requestId); } public static String getRequestId(){ String requestId = reqIdThreadLocal.get(); if(requestId == null) { requestId = ObjectId.next(); reqIdThreadLocal.set(requestId); } return requestId; } public static void removeRequestId() { reqIdThreadLocal.remove(); } } public class RequestIdFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String reqId = httpServletRequest.getHeader(WebUtil.REQ_ID_HEADER); //没有则生成一个 if (StringUtils.isEmpty(reqId)) { reqId = ObjectId.next(); } WebUtil.setRequestId(reqId); try { filterChain.doFilter(servletRequest, servletResponse); } finally { WebUtil.removeRequestId(); } } } //在配置类中注册Filter /** * 添加RequestId * @return */ @Bean public FilterRegistrationBean requestIdFilter() { RequestIdFilter reqestIdFilter = new RequestIdFilter(); FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(reqestIdFilter); List<String> urlPatterns = Collections.singletonList("/*"); registrationBean.setUrlPatterns(urlPatterns); registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); return registrationBean; } 4. 使用注解 @DistributedLockable(key = "test", expire = 10) public void test(){ System.out.println("线程-"+Thread.currentThread().getName()+"开始执行..." + LocalDateTime.now()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程-"+Thread.currentThread().getName()+"结束执行..." + LocalDateTime.now()); } 总结本文给出了基于Redis的分布式锁的实现方案与常见的错误示例。要保障分布式锁的正确运行,需满足本文所提的四个要求,尤其注意保证加锁解锁操作的原子性,设置过期时间,及对同一个锁的加锁解锁线程一致。原文地址:
[转载请注明出处]
作者:雨歌
欢迎关注作者公众号:半路雨歌,查看更多技术干货文章