Spring Cloud分布式微服务系统中利用redssion实现分布式锁
在非分布式系统中要实现锁的机制很简单,利用java.util.concurrent.locks包下的Lock和关键字synchronized都可以实现。但是在分布式系统中,如何实现各个单独的微服务需要共享某个资源的时候进行有效的锁的保护机制呢?这边使用Redisson来实现。
一、Redisson简介
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。更多,https://github.com/redisson/redisson/wiki/1.-%E6%A6%82%E8%BF%B0
二、springboot+redission 演示
1、pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>1.5.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>1.5.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>1.5.8.RELEASE</version> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.3.2</version> </dependency> </dependencies>
2、application.properties
server.port=8070
# redisson
redisson.address=192.168.3.3:6379
redisson.password=test123
3、DistributedLocker
import java.util.concurrent.TimeUnit; /** * 定义Lock的接口类 * @author ko * */ public interface DistributedLocker { void lock(String lockKey); void unlock(String lockKey); void lock(String lockKey, int timeout); void lock(String lockKey, TimeUnit unit ,int timeout); }
4、RedissonDistributedLocker
import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import com.redlock.locker.DistributedLocker; import java.util.concurrent.TimeUnit; /** * Lock接口实现类 * @author ko * */ public class RedissonDistributedLocker implements DistributedLocker { private RedissonClient redissonClient; @Override public void lock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); lock.lock(); } @Override public void unlock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); lock.unlock(); } @Override public void lock(String lockKey, int leaseTime) { RLock lock = redissonClient.getLock(lockKey); lock.lock(leaseTime, TimeUnit.SECONDS); } @Override public void lock(String lockKey, TimeUnit unit ,int timeout) { RLock lock = redissonClient.getLock(lockKey); lock.lock(timeout, unit); } public void setRedissonClient(RedissonClient redissonClient) { this.redissonClient = redissonClient; } }
5、RedissonProperties
import org.springframework.boot.context.properties.ConfigurationProperties; /** * redisson属性装配类 * @author ko * */ @ConfigurationProperties(prefix = "redisson") public class RedissonProperties { private int timeout = 3000; private String address; private String password; private int connectionPoolSize = 64; private int connectionMinimumIdleSize=10; private int slaveConnectionPoolSize = 250; private int masterConnectionPoolSize = 250; private String[] sentinelAddresses; private String masterName; public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public int getSlaveConnectionPoolSize() { return slaveConnectionPoolSize; } public void setSlaveConnectionPoolSize(int slaveConnectionPoolSize) { this.slaveConnectionPoolSize = slaveConnectionPoolSize; } public int getMasterConnectionPoolSize() { return masterConnectionPoolSize; } public void setMasterConnectionPoolSize(int masterConnectionPoolSize) { this.masterConnectionPoolSize = masterConnectionPoolSize; } public String[] getSentinelAddresses() { return sentinelAddresses; } public void setSentinelAddresses(String sentinelAddresses) { this.sentinelAddresses = sentinelAddresses.split(","); } public String getMasterName() { return masterName; } public void setMasterName(String masterName) { this.masterName = masterName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public int getConnectionPoolSize() { return connectionPoolSize; } public void setConnectionPoolSize(int connectionPoolSize) { this.connectionPoolSize = connectionPoolSize; } public int getConnectionMinimumIdleSize() { return connectionMinimumIdleSize; } public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) { this.connectionMinimumIdleSize = connectionMinimumIdleSize; } }
6、RedissLockUtil
import java.util.concurrent.TimeUnit; import com.redlock.locker.DistributedLocker; /** * redis分布式锁工具类 * @author ko * */ public class RedissLockUtil { private static DistributedLocker redissLock; public static void setLocker(DistributedLocker locker) { redissLock = locker; } public static void lock(String lockKey) { redissLock.lock(lockKey); } public static void unlock(String lockKey) { redissLock.unlock(lockKey); } /** * 带超时的锁 * @param lockKey * @param timeout 超时时间 单位:秒 */ public static void lock(String lockKey, int timeout) { redissLock.lock(lockKey, timeout); } /** * 带超时的锁 * @param lockKey * @param unit 时间单位 * @param timeout 超时时间 */ public static void lock(String lockKey, TimeUnit unit ,int timeout) { redissLock.lock(lockKey, unit, timeout); } }
7、RedissonAutoConfiguration
import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.redisson.config.SentinelServersConfig; import org.redisson.config.SingleServerConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.redlock.bean.RedissonProperties; import com.redlock.locker.DistributedLocker; import com.redlock.locker.impl.RedissonDistributedLocker; import com.redlock.util.RedissLockUtil; /** * Redisson+springboot 自动配置类 * @author ko * */ @Configuration @ConditionalOnClass(Config.class) @EnableConfigurationProperties(RedissonProperties.class) public class RedissonAutoConfiguration { @Autowired private RedissonProperties redssionProperties; /** * 哨兵模式自动装配 * @return */ @Bean @ConditionalOnProperty(name="redisson.master-name") RedissonClient redissonSentinel() { Config config = new Config(); SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redssionProperties.getSentinelAddresses()) .setMasterName(redssionProperties.getMasterName()) .setTimeout(redssionProperties.getTimeout()) .setMasterConnectionPoolSize(redssionProperties.getMasterConnectionPoolSize()) .setSlaveConnectionPoolSize(redssionProperties.getSlaveConnectionPoolSize()); if(redssionProperties.getPassword() != null && !"".equals(redssionProperties.getPassword())) { serverConfig.setPassword(redssionProperties.getPassword()); } return Redisson.create(config); } /** * 单机模式自动装配 * @return */ @Bean @ConditionalOnProperty(name="redisson.address") RedissonClient redissonSingle() { Config config = new Config(); SingleServerConfig serverConfig = config.useSingleServer() .setAddress(redssionProperties.getAddress()) .setTimeout(redssionProperties.getTimeout()) .setConnectionPoolSize(redssionProperties.getConnectionPoolSize()) .setConnectionMinimumIdleSize(redssionProperties.getConnectionMinimumIdleSize()); if(redssionProperties.getPassword() != null && !"".equals(redssionProperties.getPassword())) { serverConfig.setPassword(redssionProperties.getPassword()); } return Redisson.create(config); } /** * 装配locker类,并将实例注入到RedissLockUtil中 * @return */ @Bean DistributedLocker distributedLocker(RedissonClient redissonClient) { RedissonDistributedLocker locker = new RedissonDistributedLocker(); locker.setRedissonClient(redissonClient); RedissLockUtil.setLocker(locker); return locker; } }
8、暴露接口 TestController
import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.redlock.util.RedissLockUtil; /** * 测试类 * @author ko * */ @RestController public class TestController { // private static final Logger LOG = Logger.getLogger(BootApplication.class.getName()); // LOG.log(Level.INFO, "All procn"); private final String _lock = "_lock"; static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String getCurrentDate(){ return sdf.format(new Date()); } @RequestMapping(value = "/testlock",method=RequestMethod.GET) public String testlock(String name) { new Thread(new Runnable() { @Override public void run() { RedissLockUtil.lock(_lock, TimeUnit.MINUTES, 10); System.out.println(getCurrentDate()+" "+name+" begin..."); for (int i = 0; i < 20; i++) { try { Thread.sleep(1000); System.out.println(getCurrentDate()+" "+name+" "+i); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(getCurrentDate()+" "+name+" end..."); RedissLockUtil.unlock(_lock); } }).start(); return "testlock"; } }
9、微服务启动类 BootApplication
@SpringBootApplication public class BootApplication { public static void main(String[] args) { SpringApplication.run(BootApplication.class, args); } }
三、启动演示
复制项目,第二个服务的端口改为8071,启动两个服务,先在浏览器里输入http://localhost:8070/testlock?name=zhang,3s后再输入http://localhost:8071/testlock?name=wang,3s后再输入http://localhost:8070/testlock?name=zhang1,3s后再输入http://localhost:8071/testlock?name=wang1,如果没有锁机制,打印的数据应该是交叉的,反之,同一时刻只会打印一次的请求,不会出现交叉打印数据的情况。
官方文档:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95