文章目录
memcached分布式锁
实现原理:
memcached带有add函数,利用add函数的特性即可实现分布式锁。
add和set的区别在于:如果多线程并发set,则每个set都会成功,但最后存储的值以最后的set的线程为准。而add的话则相反,add会添加第一个到达的值,并返回true,后续的添加则都会返回false。
利用该点即可很轻松地实现分布式锁。
优缺点
优点:并发高效。
缺点:
(1)memcached采用列入LRU置换策略,所以如果内存不够,可能导致缓存中的锁信息丢失。
(2)memcached无法持久化,一旦重启,将导致信息丢失。
开发准备
安装memcached服务端
为方便起见,已经将memcached服务器端程序上传到下面的目录,使用时只需要双击运行就好!
安装jar到maven本地仓库
开发的时候有的同学可能会遇到下面jar无法下载的情况
<!-- mvn install:install-file -Dfile="java_memcached-release_2.6.6.jar" -DgroupId=com.danga -DartifactId=java-memcached -Dversion="2.6.6" -Dpackaging=jar -->
<dependency>
<groupId>com.danga</groupId>
<artifactId>java-memcached</artifactId>
<version>2.6.6</version>
</dependency>
在java-memcached-2.6.6.jar同级目录运行
mvn install:install-file -Dfile="java_memcached-release_2.6.6.jar" -DgroupId=com.danga -DartifactId=java-memcached -Dversion="2.6.6" -Dpackaging=jar
代码开发
初始化Memcached客户端
@Component
public class MemcachedLockClient
{
@Value("${memcached.serverUrl:127.0.0.1:11211}")
private String server;
/**
* 构建缓存客户端
*/
private MemCachedClient cachedClient;
@PostConstruct
public void init()
{
cachedClient = new MemCachedClient();
// 获取连接池实例
SockIOPool pool = SockIOPool.getInstance();
// 设置缓存服务器地址,可以设置多个实现分布式缓存
pool.setServers(new String[] {server});
// 设置初始连接5
pool.setInitConn(5);
// 设置最小连接5
pool.setMinConn(5);
// 设置最大连接250
pool.setMaxConn(250);
// 设置每个连接最大空闲时间3个小时
pool.setMaxIdle(1000 * 60 * 60 * 3);
// 设置连接池维护线程的睡眠时间
// 设置为0,维护线程不启动
// 维护线程主要通过log输出socket的运行状况,监测连接数目及空闲等待时间等参数以控制连接创建和关闭。
pool.setMaintSleep(30);
// 设置是否使用Nagle算法,因为我们的通讯数据量通常都比较大(相对TCP控制数据)而且要求响应及时,因此该值需要设置为false(默认是true)
pool.setNagle(false);
// 设置socket的读取等待超时值
pool.setSocketTO(3000);
// 设置socket的连接等待超时值
pool.setSocketConnectTO(0);
// 设置完pool参数后最后调用该方法,启动pool。
pool.initialize();
}
public void add(String key, Object value)
{
cachedClient.set(key, value);
}
public boolean add(String key, Object value, int milliseconds)
{
return cachedClient.add(key, value, milliseconds);
}
public boolean add(String key, Object value, Date milliseconds)
{
return cachedClient.add(key, value, milliseconds);
}
public void remove(String key)
{
cachedClient.delete(key);
}
public Object get(String key)
{
return cachedClient.get(key);
}
}
锁相关操作核心代码
String value = RandomStringUtils.randomNumeric(8);
if (memcachedLock.add("key", value, new Date(System.currentTimeMillis() + 30 * 1000)) == false)
{
log.info("报名或抢购失败,请稍后再试! 锁持有者:{}", memcachedLock.get("key"));
return;
}
StopWatch clock = new StopWatch();
try
{
// 领取成功
log.info("★★★★★★★★ {} 报名或抢购处理中★★★★★★★★", memcachedLock.get("key"));
clock.start();
// 模拟耗时业务操作
TimeUnit.MILLISECONDS.sleep(RandomUtils.nextInt(10000, 20000));
clock.stop();
log.info("{} 运行 {} ms ---------------", memcachedLock.get("key"), clock.getLastTaskTimeMillis());
}
finally
{
memcachedLock.remove("key");
log.info("释放memcachedLock锁");
}
源码传送
https://gitee.com/00fly/effict-side/tree/master/springboot-locks
本地运行效果
docker运行
下载docker文件下的这2个文件,上传至带有docker-compose以及docker环境的服务器
运行命令
sh scale.sh
docker ps
docker logs -f <containerId>
编排文件
docker-compose.yml
version: '3.0'
networks:
default:
name: devops
driver: bridge
ipam:
config:
- subnet: 172.88.88.0/24
services:
springboot-schedule:
image: registry.cn-shanghai.aliyuncs.com/00fly/springboot-schedule:1.0.0
deploy:
resources:
limits:
cpus: '0.80'
memory: 200M
reservations:
cpus: '0.05'
memory: 160M
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://172.88.88.11:3306/schedule?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true
- SPRING_DATASOURCE_USERNAME=test
- SPRING_DATASOURCE_PASSWORD=test123
- MEMCACHED_SERVERURL=172.88.88.12:11211
restart: on-failure
logging:
driver: json-file
options:
max-size: 5m
max-file: '1'
mysql:
image: mysql:5.7
container_name: mysql
deploy:
resources:
limits:
cpus: '1.0'
memory: 300M
reservations:
cpus: '0.05'
memory: 200M
volumes:
- ./mysql:/var/lib/mysql/
- ./init:/docker-entrypoint-initdb.d/
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: root123
MYSQL_USER: test
MYSQL_PASSWORD: test123
MYSQL_DATABASE: schedule
command:
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
restart: on-failure
logging:
driver: json-file
options:
max-size: 5m
max-file: '1'
networks:
default:
ipv4_address: 172.88.88.11
memcached:
image: memcached
container_name: memcached-server
deploy:
resources:
limits:
cpus: '1.0'
memory: 100M
reservations:
cpus: '0.05'
memory: 20M
restart: on-failure
logging:
driver: 'json-file'
options:
max-size: '5m'
max-file: '1'
networks:
default:
ipv4_address: 172.88.88.12
有任何问题和建议,都可以向我提问讨论,大家一起进步,谢谢!
-over-