50、zookeeper,redis 分布式锁 的实现
1、分布式锁的产生
在单个JVM 多个线程中获取共享资源:可以用 synchronized 或者用 JUC里面的Lock锁,可以保证线程安全数据一致
在多个JVM 多个线程中获取共享资源:用 synchronized 或者用 JUC里面的Lock锁 不能保证数据一致,因为在分布式系统中存在多个JVM 抢占共享资源,解决这个问题要保证同一时间内保证有且仅有一个JVM处理,这个时候要锁的是JVM,这个就是分布式锁
2、分布式锁的实现,常用的解决方案:redis 、zookeeper
使用zookeeper实现分布式锁
添加依赖
<dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> <version>0.1</version> </dependency>
编写ZkLock接口
public interface ZkLock { public void zkLock(); public void zkUnLock(); }
编写一个抽象类实现ZkLock接口
public abstract class ZkAbstractTemplateLock implements ZkLock { public final String zkServer = "192.168.28.144:2181"; public final int timeOut = 45 * 1000; public ZkClient zkClient = new ZkClient(zkServer,timeOut); protected final String path = "/zkClient0401"; protected CountDownLatch countDownLatch = null; @Override public void zkLock() { if(tryZkLock()) { System.out.println(Thread.currentThread().getName()+ "\t" + "占用锁成功!"); }else { waitZkLock(); zkLock(); } } public abstract boolean tryZkLock(); public abstract void waitZkLock(); @Override public void zkUnLock() { if(zkClient != null) { zkClient.close(); } System.out.println(Thread.currentThread().getName()+ "\t" + "锁释放成功!"); System.out.println(); System.out.println(); } }
编写一个类继承 ZkAbstractTemplateLock 并实现抽象类中没有实现的方法
public class ZkDistrbutedLock extends ZkAbstractTemplateLock { @Override public boolean tryZkLock() { try { zkClient.createEphemeral(path); return true; }catch (Exception e) { return false; } } @Override public void waitZkLock() { IZkDataListener iZkDataListener = new IZkDataListener() { @Override public void handleDataChange(String s, Object o) throws Exception { } @Override public void handleDataDeleted(String s) throws Exception { if(countDownLatch != null) { countDownLatch.countDown(); } } }; zkClient.subscribeDataChanges(path,iZkDataListener); if(zkClient.exists(path)) { //只能等待,不能往下走 countDownLatch = new CountDownLatch(1); try { countDownLatch.wait(); } catch (InterruptedException e) { e.printStackTrace(); } zkClient.unsubscribeDataChanges(path,iZkDataListener); } } }
编写一个生产订单的工具类
public class CreateNumberUtil { private static int number = 0; public int createOrderNumber() { return ++number; } }
编写service 模拟实现生成订单的类
public class OrderService { private CreateNumberUtil createNumberUtil = new CreateNumberUtil(); private ZkLock zkLock = new ZkDistrbutedLock(); public void getOrderNumber () { zkLock.zkLock(); try{ System.out.println("生成订单成功!------------>: "+ createNumberUtil.createOrderNumber()); }catch (Exception e) { e.printStackTrace(); }finally { zkLock.zkUnLock(); } } }
多线程测试类
public static void main(String[] args) { for (int i = 1; i <= 20; i++) { new Thread(() -> { new OrderService().getOrderNumber(); }, String.valueOf(i)).start(); } }
2、redis的五大数据类型的落地应用
这些数据结构式如何使用的,用在那些应用场景?
string类型: 一般用于喜欢的文章的点赞,商品点赞
常用的命令:set k1 v1 、get k1 、incr k1,incrby k1 2、decr k1
4、redis做分布式锁
安装版本式6.0.8单机版的redis
version: '3' services: redis: image: redis:6.0.8 container_name: redis restart: always ports: - 6379:6379 networks: - mynetwork volumes: - ./redis.conf:/usr/local/etc/redis/redis.conf:rw - ./data:/data:rw command: /bin/bash -c "redis-server /usr/local/etc/redis/redis.conf " networks: mynetwork: external: true
第二种
version: '3.1' services: master: restart: always image: redis:6.0.8 container_name: redis-master ports: - 6379:6379
安装nginx做负载均衡请求分发到到两个服务:boot-redis01、boot-redis02
version: '3.1' services: nginx: restart: always image: nginx container_name: nginx ports: - 81:80 volumes: - ./conf/nginx.conf:/etc/nginx/nginx.conf - ./wwwroot:/usr/share/nginx/wwwroot
创建两个一模一样的项目:boot-redis01端口号:1111 和 boot-redis02端口号:2222
添加pom.xml依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
编写application.properties 文件
server.port=2222 spring.redis.database=0 spring.redis.host=localhost spring.redis.port=6379 spring.redis.password= spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1 spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0
编写主启动类
@SpringBootApplication public class BootRedis02Application { public static void main(String[] args) { SpringApplication.run(BootRedis02Application.class, args); } }
编写redis的配置类RedisConfig类
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) { RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(connectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } @Bean public Redisson redisson () { Config config = new Config(); config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0); return (Redisson) Redisson.create(config); } }
编写controller业务类
@RestController public class GoodsController { @Resource private StringRedisTemplate stringRedisTemplate; @Value("${server.port}") private String serverPort; @Autowired private Redisson redisson; @GetMapping(value = "/buyGoods") public String buyGoods() { RLock redissonLock = redisson.getLock("atguiguLock"); redissonLock.lock(); try { String result = stringRedisTemplate.opsForValue().get("goods:001"); int goodsNumber = result==null? 0 : Integer.parseInt(result); if(goodsNumber > 0) { int redNumber = goodsNumber -1; stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(redNumber)); System.out.println("成功购买到商品 "+ "库存还剩下:" + redNumber+ "服务端口号: " + serverPort); return "成功购买到商品 "+ "库存还剩下:" + redNumber+ "服务端口号: " + serverPort; }else { System.out.println("活动已经结束!"); return "活动已经结束"; } }finally { if(redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) { redissonLock.unlock(); } } } }

浙公网安备 33010602011771号