基于redis的分布式锁

package com.orchard.userimpl;

import com.orchard.common.annotation.EnableRedissionConfig;
import com.orchard.common.config.ServerConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;




@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableRedissionConfig
public class PomeloUserImplApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        ServerConfig.initServerConfig();
        // 注意这里要指向原先用main方法执行的Application启动类
        return builder.sources(PomeloUserImplApplication.class);
    }

    public static void main(String[] args) {
        ServerConfig.initServerConfig();
        // 注意这里要指向原先用main方法执行的Application启动类
        SpringApplication.run(PomeloUserImplApplication.class, args);
    }

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
    package com.orchard.common.annotation;

import com.orchard.common.support.lock.RedissionLocak;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({RedissionLocak.class})
@Documented
@Inherited
public @interface EnableRedissionConfig {
}

基于redis的分布式锁_第1张图片

singleServerConfig:
  idleConnectionTimeout: 10000
  pingTimeout: 1000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  reconnectionTimeout: 3000
  failedAttempts: 3
  subscriptionsPerConnection: 5
  clientName: null
  address: "redis://127.0.0.1:6379"
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  connectionMinimumIdleSize: 10
  connectionPoolSize: 64
  database: 1
  dnsMonitoring: false
  dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
useLinuxNativeEpoll: false
package com.orchard.common.support.lock;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;

import java.io.IOException;


@Configuration
public class RedissionLocak {
    
    @Value("classpath:/redisson-conf.yml")
    Resource configFile;
    
    @Bean(destroyMethod = "shutdown")
    RedissonClient redisson()
            throws IOException {
        Config config = Config.fromYAML(configFile.getInputStream());
        return Redisson.create(config);
    }
    
    @Bean
    DistributedLockTemplate distributedLockTemplate(RedissonClient redissonClient) {
        return new SingleDistributedLockTemplate(redissonClient);
    }
}

package com.orchard.common.support.lock;

import java.util.concurrent.TimeUnit;

/**
 * 分布式锁操作模板
 */
public interface DistributedLockTemplate {

    long DEFAULT_WAIT_TIME = 30;
    long DEFAULT_TIMEOUT   = 5;
    TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;

    /**
     * 使用分布式锁,使用锁默认超时时间。
     * @param callback
     * @param fairLock 是否使用公平锁
     * @return
     */
    <T> T lock(DistributedLockCallback<T> callback, boolean fairLock);

    /**
     * 使用分布式锁。自定义锁的超时时间
     *
     * @param callback
     * @param leaseTime 锁超时时间。超时后自动释放锁。
     * @param timeUnit
     * @param fairLock 是否使用公平锁
     * @param 
     * @return
     */
    <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit, boolean fairLock);

    /**
     * 尝试分布式锁,使用锁默认等待时间、超时时间。
     * @param callback
     * @param fairLock 是否使用公平锁
     * @param 
     * @return
     */
    <T> T tryLock(DistributedLockCallback<T> callback, boolean fairLock);

    /**
     * 尝试分布式锁,自定义等待时间、超时时间。
     * @param callback
     * @param waitTime 获取锁最长等待时间
     * @param leaseTime 锁超时时间。超时后自动释放锁。
     * @param timeUnit
     * @param fairLock 是否使用公平锁
     * @param 
     * @return
     */
    <T> T tryLock(DistributedLockCallback<T> callback, long waitTime, long leaseTime, TimeUnit timeUnit, boolean fairLock);
}
package com.orchard.common.support.lock;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;

public class SingleDistributedLockTemplate implements DistributedLockTemplate {
    
    private RedissonClient redisson;

    public SingleDistributedLockTemplate(RedissonClient redisson) {
        this.redisson = redisson;
    }

    @Override
    public <T> T lock(DistributedLockCallback<T> callback, boolean fairLock) {
        return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT, fairLock);
    }
    
    @Override
    public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit, boolean fairLock) {
        RLock lock = getLock(callback.getLockName(), fairLock);
        if(!Thread.currentThread().isInterrupted()) {
            try {
                lock.lock(leaseTime, timeUnit);
                return callback.process();
            } finally {
                if (lock != null && lock.isLocked()) {
                    lock.unlock();
                }
            }
        }
        return null;
    }

    @Override
    public <T> T tryLock(DistributedLockCallback<T> callback, boolean fairLock) {
        return tryLock(callback, DEFAULT_WAIT_TIME, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT, fairLock);
    }

    @Override
    public <T> T tryLock(DistributedLockCallback<T> callback, long waitTime, long leaseTime, TimeUnit timeUnit, boolean fairLock) {
        RLock lock = getLock(callback.getLockName(), fairLock);
        try {
            if (lock.tryLock(waitTime, leaseTime, timeUnit)) {
                return callback.process();
            }
        } catch (InterruptedException e) {

        } finally {
            if (lock != null && lock.isLocked()) {
                lock.unlock();
            }
        }
        return null;
    }

    private RLock getLock(String lockName, boolean fairLock) {
        RLock lock;
        if (fairLock) {
            lock = redisson.getFairLock(lockName);
        } else {
            lock = redisson.getLock(lockName);
        }
        return lock;
    }

}
package com.orchard.common.support.lock;

/**
 * 分布式锁回调接口
 */
public interface DistributedLockCallback<T> {
    /**
     * 调用者必须在此方法中实现需要加分布式锁的业务逻辑
     *
     * @return
     */
    public T process();

    /**
     * 得到分布式锁名称
     *
     * @return
     */
    public String getLockName();
}
package com.orchard.common.annotation;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
    /**
     * 锁的名称。
     * 如果lockName可以确定,直接设置该属性。
     */
    String lockName() default "";

    /**
     * lockName后缀
     */
    String lockNamePre() default "";
    /**
     * lockName后缀
     */
    String lockNamePost() default "lock";

    /**
     * 获得锁名时拼接前后缀用到的分隔符
     * @return
     */
    String separator() default ".";
    /**
     * 
     *     获取注解的方法参数列表的某个参数对象的某个属性值来作为lockName。因为有时候lockName是不固定的。
     *     当param不为空时,可以通过argNum参数来设置具体是参数列表的第几个参数,不设置则默认取第一个。
     * 
*/
String param() default ""; /** * 将方法第argNum个参数作为锁 */ int argNum() default 0; /** * 将方法第argNum个参数作为锁 */ int[] argNums() default {}; /** * 是否使用公平锁。 * 公平锁即先来先得。 */ boolean fairLock() default false; /** * 是否使用尝试锁。 */ boolean tryLock() default false; /** * 最长等待时间。 * 该字段只有当tryLock()返回true才有效。 */ long waitTime() default 30L; /** * 锁超时时间。 * 超时时间过后,锁自动释放。 * 建议: * 尽量缩简需要加锁的逻辑。 */ long leaseTime() default 5L; /** * 时间单位。默认为秒。 */ TimeUnit timeUnit() default TimeUnit.SECONDS; }
package com.orchard.common.config.aop;

import com.orchard.common.annotation.DistributedLock;
import com.orchard.common.support.lock.DistributedLockCallback;
import com.orchard.common.support.lock.DistributedLockTemplate;
import com.orchard.common.support.redis.RedisUtil;
import com.orchard.common.util.ValidUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class LogAspectServiceApi {
    
    @Autowired
    private DistributedLockTemplate lockTemplate;
    
    @Autowired
    private RedisUtil redisUtil;
    
    /**
     * @return
     * @author Simon
     * @Description 申明一个切点 里面是 execution表达式
     * @date 2018/11/18 19:20
     */
    @Pointcut("execution(* com.orchard..*.*(..))")
    private void controllerAspect() {
    }
    
    @Around(value = "controllerAspect()")
    public Object around(ProceedingJoinPoint pjd) throws Throwable{
        
        methodBefore(pjd);
        Object result = annotatedBus(pjd);
        Method method = ((MethodSignature) pjd.getSignature()).getMethod();
        //此处有bug,当使用事务时,事务失败会导致接口被重复请求了一次。
        //注释掉try catch事务生效(当事务异常被catch捕获,则不会发生事务回滚)
        try {
            if(ValidUtil.isEmpty(result)) {
                //返回通知
                result = pjd.proceed();
            }
        } catch (Exception e) {
            log.error("###LogAspectServiceApi.class afterThrowing() ### ERROR-method:{}, exception:{}", method, e);
        } finally {
            methodAfter(result, pjd);
            //必须返回结果,否则controller无法获取service返回的结果(正常情况下)
            return result;
        }
        
    }
    
    /**
     * 扩展使用
     * @param pjd
     */
    private Object annotatedBus(ProceedingJoinPoint pjd) {
        Signature signature = pjd.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations) {
            
            log.info("@Around 获取方法注解: {}", annotation.toString());
            if(annotation instanceof DistributedLock) {
                
                Object[] arguments = pjd.getArgs();
                
                final String lockName = getLockName(method, arguments);
                
                return lock(pjd, method, lockName);
                
            }
        }
        return null;
    }
    
    
    public String getLockName(Method method, Object[] args) {
        Objects.requireNonNull(method);
        DistributedLock annotation = method.getAnnotation(DistributedLock.class);
        
        String lockName = annotation.lockName(),
                param = annotation.param();
        
        if (isEmpty(lockName)) {
            if (args.length > 0) {
                if (isNotEmpty(param)) {
                    Object arg;
                    if (annotation.argNum() > 0) {
                        arg = args[annotation.argNum() - 1];
                    } else {
                        arg = args[0];
                    }
                    lockName = String.valueOf(getParam(arg, param));
                } else if (annotation.argNum() > 0) {
                    lockName = args[annotation.argNum() - 1].toString();
                } else if (annotation.argNums().length > 0) {
                    int length = annotation.argNums().length;
                    for(int i = 0; i < length; i++) {
                        lockName += args[annotation.argNums()[i]].toString();
                    }
                }
            }
        }
        
        if (isNotEmpty(lockName)) {
            String preLockName = annotation.lockNamePre(),
                    postLockName = annotation.lockNamePost(),
                    separator = annotation.separator();
            
            StringBuilder lName = new StringBuilder();
            if (isNotEmpty(preLockName)) {
                lName.append(preLockName).append(separator);
            }
            lName.append(lockName);
            if (isNotEmpty(postLockName)) {
                lName.append(separator).append(postLockName);
            }
            
            lockName = lName.toString();
            
            return lockName;
        }
        
        throw new IllegalArgumentException("Can't get or generate lockName accurately!");
    }
    
    /**
     * 从方法参数获取数据
     *
     * @param param
     * @param arg 方法的参数数组
     * @return
     */
    public Object getParam(Object arg, String param) {
        if (isNotEmpty(param) && arg != null) {
            try {
                Object result = PropertyUtils.getProperty(arg, param);
                return result;
            } catch (NoSuchMethodException e) {
                throw new IllegalArgumentException(arg + "没有属性" + param + "或未实现get方法。", e);
            } catch (Exception e) {
                throw new RuntimeException("", e);
            }
        }
        return null;
    }
    
    public Object lock(ProceedingJoinPoint pjp, Method method, final String lockName) {
        
        DistributedLock annotation = method.getAnnotation(DistributedLock.class);
        
        boolean fairLock = annotation.fairLock();
        
        boolean tryLock = annotation.tryLock();
        
        if (tryLock) {
            return tryLock(pjp, annotation, lockName, fairLock);
        } else {
            return lock(pjp,lockName, fairLock);
        }
    }
    
    public Object lock(ProceedingJoinPoint pjp, final String lockName, boolean fairLock) {
        return lockTemplate.lock(new DistributedLockCallback<Object>() {
            @Override
            public Object process() {
                return proceed(pjp);
            }
            
            @Override
            public String getLockName() {
                return lockName;
            }
        }, fairLock);
    }
    
    public Object tryLock(ProceedingJoinPoint pjp, DistributedLock annotation, final String lockName, boolean fairLock) {
        
        long waitTime = annotation.waitTime(),
                leaseTime = annotation.leaseTime();
        TimeUnit timeUnit = annotation.timeUnit();
        
        return lockTemplate.tryLock(new DistributedLockCallback<Object>() {
            @Override
            public Object process() {
                return proceed(pjp);
            }
            
            @Override
            public String getLockName() {
                return lockName;
            }
        }, waitTime, leaseTime, timeUnit, fairLock);
    }
    
    public Object proceed(ProceedingJoinPoint pjp) {
        try {
            return pjp.proceed();
        } catch (Throwable throwable) {
        	//(猜测)如果此处异常,会导致lock方法返回null,从而又执行pjp.proceed();
            throw new RuntimeException(throwable);
        }
    }
    
    private boolean isEmpty(Object str) {
        return str == null || "".equals(str);
    }
    
    private boolean isNotEmpty(Object str) {
        return !isEmpty(str);
    }
    
    /**
     * 方法调用结束记录日志
     * @param result
     */
    private void methodAfter(Object result, ProceedingJoinPoint pjd) {
        
        Signature signature = pjd.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (result == null) {
            return;
        }
        if(!(result instanceof ModelMap)) {
            log.info("### method:{}, result:{}", method, result.toString());
            return;
        }
        
        ModelMap map = (ModelMap) result;
        if(!map.containsKey("SUCCESS")) {
            return;
        }
        
    }
    
    /**
     * 方法调用前记录日志
     * @param pjd
     */
    private void methodBefore(ProceedingJoinPoint pjd) {
        //获取方法名称
        String methodName = "class:" + pjd.getTarget().getClass().getName() + " method:" + pjd.getSignature().getName();
        
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        
        if (requestAttributes == null) {
            return;
        }
        javax.servlet.http.HttpServletRequest request = requestAttributes.getRequest();
        
      /*  //获取请求的URL//执行目标方法
        HttpServletRequest request = (HttpServletRequest)*/
        
        // 打印请求内容
        log.info("请求地址:" + request.getRequestURL().toString()
                +"请求方式:" + request.getMethod()
                +"请求类方法:" + methodName
                +"请求类方法参数:" + Arrays.toString(pjd.getArgs())
        );
    }
    
}

你可能感兴趣的:(Spring,Spring,Boot)