分布式锁实现二. memcached分布式锁

本文介绍了如何使用Memcached的add函数实现分布式锁,阐述了其并发高效的优点和内存不足、无法持久化的缺点。详细讲解了开发准备、安装步骤以及在SpringBoot项目中通过Docker和docker-compose进行部署的方法。
摘要由CSDN通过智能技术生成

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-

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值