1. Zookeeper 非公平锁/公平锁/共享锁
2. Spring Cloud Zookeeper注册中心实战
Zookeeper 分布式锁加锁原理
上面的图示加锁有个弊端:并发量大的话,性能会下降的比较厉害,主要原因是,所有的连接
都在对同一个节点进行监听,当服务器检测到删除事件时,要通知所有的连接,所有的连接同时
收到事件,再次并发竞争,这就是羊群效应。这种加锁是非公平锁的实现。
借助于临时顺序节点,可以避免同时多个节点的并发竞争锁,缓解了服务端压力。这种实
现方式所有加锁请求都进行排队加锁,是公平锁的具体实现
分布式锁的核心代码:
引入依赖
<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.0.0</version> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.5.8</version> </dependency>
减库存操作:
@Transactional public void reduceStock(Integer id){ Product pro= proMapper.getProduct(id); if (pro.getSto() <=0 ) { throw new BusiessException("库存不足"); } int i = proMapper.decStock(id); if (i==1){ Order order = new Order(); order.setUserId(UUID.randomUUID().toString()); orderMapper.insert(order); }else{ throw new RuntimeException("新增订单失败"); } }
上面代码有个问题:当并发量较大就会导致扣减库存变为负数
用zk分布式锁curator来解决
初始化curator:注意这个start方法一定要
@Bean(initMethod = "start") public CuratorFramework curatorFramework(){ RetryPolicy retryPolicy = new ExponentialBackoffRetry(10000, 5); CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.25.111:2181", retryPolicy); return client; }
加锁操作:
@Autowired CuratorFramework curatorFramework; @PostMapping("/deduct") public Object reduceStock(Integer id) throws Exception { InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, "/product_" + id); try { interProcessMutex.acquire(); orderService.reduceStock(id); } catch (Exception e) { throw e; }finally { //注意一定要释放锁 interProcessMutex.release(); } return "ok"; }
二:注册中心
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> </dependency>
添加注册中心地址
#zookeeper 连接地址 spring.cloud.zookeeper.connect-string=192.168.25.111:2181 #将本服务注册到zookeeper spring.cloud.zookeeper.discovery.register=true spring.cloud.zookeeper.session-timeout=30000
利用ApplicationRunner的特性:应用已启动就会回调run方法,往zk注册中心注册节点
public void run(ApplicationArguments args) throws Exception { //应用启动的时候往zk注册中心注册/first/second的节点 ZookeeperRegistration registration = ServiceInstanceRegistration.builder() .defaultUriSpec() .address("all") .port(10000) .name("/first/second") .build(); this.serviceRegistry.register(registration); }
客户端的操作:
注册负载均衡器
@Bean @LoadBalanced public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); return restTemplate; }
@Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/zk") public String zk() { //直接调用 return this.restTemplate.getForObject("http://product/Info", String.class); }
这就是注册中心的用法,非常简单