一、概述
Zookeeper是一个分布式的(多台机器同时干一件事情)、具有开源框架、分布式应用程序的协调服务。Google公司Chubby产品,是Hadoop和HBase重要的组件。
Zookeeper目标封装了大量的复杂关键的技术,将简单的接口暴露,高效的使用Zookeeper稳定性非常高。
二、搭建
Zookeeper是使用java语言开发的,Zookeeper看作是一个java的应用程序。依赖于JDK的环境,前提条件是需要在Linux系统上安装JDK。如何在linux安装JDK。在此不作介绍
zookeeper镜像库下载地址:http://archive.apache.org/dist/zookeeper
下载完成之后。利用ssh连接工具Finalshell来连接linux服务器上传到指定目录/usr/local/目录下
下一步需要解压缩这个包。使用如下命令
cd /usr/local # 切换到Zookeeper压缩包目录下 tar zxvf apache-zookeeper-3.8.0-bin.tar.gz -C /usr/local # 使用tar命令将该目录下的apache-zookeeper-3.8.0-bin.tar.gz文件解压缩到/usr/local目录下
这里博主就不截图显示命令的执行情况了。因为我这里已经执行过。只要命令没有粘贴错误,就不会有错误情况发生
执行完成之后,点击打开解压缩完成的目录
下一步我们需要配置一下zookeeper。打开conf目录
使用linux提供的复制命令来完成复制动作
cd /usr/local/zookeeper/apache-zookeeper-3.8.0-bin/conf # 切换到zookeeper配置文件目录conf cp zoo_sample.cfg zoo.cfg # 复制zoo_sample.cfg,保存成zoo.cfg文件
这里不再截图,因为博主已经执行过
双击打开zoo.cfg文件
/usr/local/zookeeper/apache-zookeeper-3..-bin/data /usr/local/zookeeper/apache-zookeeper-3..-bin/log
完成之后,需要切换到zookeeper运行目录bin下
cd /usr/local/zookeeper/apache-zookeeper-3.8.0-bin/bin #切换到bin目录
使用以下命令启动zookeeper服务
./zkServer start # 启动zookeeper服务
关于是否已经成功启动了zookeeper服务,可以使用以下命令
./zkServer.sh status # 查看zookeeper服务状态
至此,zookeeper已经运行起来了。下面讲解利用基于zookeeper集群技术创建的InterProcessMutex分布式锁来解决商品秒杀等高并发带来的问题
以下博主的代码是基于SSM框架搭建的项目。而InterProcessMutex运行在SSM框架下
导入InterProcessMutex类的相关依赖
pom.xml
<!-- 基于Zookeeper原生的客户端类实现的分布式并发问题的解决方法
包含有Zookeeper相关技术方案
-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<!-- 网友投票该版本最好 -->
<version>4.2.0</version>
</dependency>
在我的代码当中还有一些SSM框架运行所需要的依赖,你们只作为看看,不做复制粘贴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cy</groupId>
<artifactId>zookeeper_lock_product</artifactId>
<version>1.0-SNAPSHOT</version>
<!--声明项目打包方式:jar,war-->
<packaging>war</packaging>
<dependencies>
<!--spring与springMVC相关依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- MyBatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<!-- mysql连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- 数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 基于Zookeeper原生的客户端类实现的分布式并发问题的解决方法
包含有Zookeeper相关技术方案
-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<!-- 网友投票该版本最好 -->
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<!--定义变量spring.version 内嵌TOMCAT,支持最高springMVC版本是5.3.0以下版本-->
<spring.version>5.2.7.RELEASE</spring.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--构建项目环境-->
<build>
<!-- 插件配置-->
<plugins>
<!-- Maven内嵌的Tomcat插件-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<!-- 目前Apache只提供Tomcat6和Tomcat7两个插件-->
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>9001</port>
<path>/</path>
</configuration>
<executions>
<execution>
<!-- 点击Maven的pachage打包完成后,运行服务-->
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
以下讲解的内容一部分对你们代码有帮助,一部分代码只做了解
一、只做了解
(1)数据准备
博主这里创建了两张测试表
CREATE TABLE `product` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `product_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '商品名称', `stock` int(11) DEFAULT NULL COMMENT '库存', `version` int(11) DEFAULT '0' COMMENT '版本号', PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='商品' CREATE TABLE `order` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `pid` int(11) DEFAULT NULL COMMENT '商品编号', `user_id` int(11) DEFAULT NULL COMMENT '用户编号', PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单编号';
分别为这两张表写了pojo实体类
并且为这两张表定义了mapper层接口
构建了service层接口并实现了该层方法。在service层开发了商品秒杀的逻辑
二。你们要复制粘贴的代码在控制层Controller
zookeeper服务器的连接是依靠服务器IP+固定交互信息的端口号2181来连接的。如果你们不知道自己的服务器IP地址,按以下指示去确定
打开liunx服务器终端。输入
ifconfig # 此命令是linux提供的查看服务器配置的信息
=
接着控制层往下看代码:
贴上完整的controller层代码
package com.DurkBlue.product.controller;
import com.durkblue.product.service.IProductService;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 商品秒杀控制层
*/
@RestController
public class ProductController {
@Autowired
private IProductService productService;
// 声明Zookeeper集群环境的IP
private String connectionStr = "192.168.99.10:2181,192.168.99.11:2181,192.168.99.12:2181";
@GetMapping("/product/reduce")
public String reduceStock(Integer id) throws Exception {
// 1.添加锁执行以下步骤
/**
* a.创建一个重试策略,防止连接zookeeper集群没有及时连接出现的问题,尝试多次连接.每次重试间隔时间设置为1s
* 参数1:表示每一次重试的时间间隔(毫秒)
* 参数2:表示重试多少次
*/
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
// b.创建Curator对象(表示Zookeeper连接的工具对象)
CuratorFramework client = CuratorFrameworkFactory.newClient(connectionStr, retryPolicy);
// c.开启客户端,表示连接上集群
client.start();
/**
* d.根据工具类对象创建内部互斥锁对象
* 参数1:表示目标客户端(client对象)
* 参数2:表示这个锁在集群中的节点名称(要求唯一)
*/
InterProcessMutex lock = new InterProcessMutex(client, "/product_" + id);
try {
// e.开启锁(在目标方法执行之前)
lock.acquire();
productService.reduceStock(id);
} catch (Exception e) {
e.printStackTrace();
}finally {
// 2.释放锁(目标方法被调用,无论是否产生异常,都应该将锁释放)
lock.release();
}
return "SUCCESS";
}
}
好。接下来咱们利用apipost测试工具来制造高并发的情况来测试接口执行情况,看看是不是完美解决了高并发带来的问题
执行完毕
打开navicat,观察product数据表情况
再来观察以下java控制台输出