redis分布式锁

下载编译

github下载最新的:git clone https://github.com/redis/hiredis.git

也可下载历史稳定版本(当前v1.0.2):https://github.com/redis/hiredis/tags, wget https://github.com/redis/hiredis/archive/refs/tags/v1.0.2.tar.gz

#解压并进入hiredis目录
$ mkdir build
$ cd build
$ cmake -DCMAKE_INSTALL_PREFIX=<install dir> ..
$ make
$ make install
实现分布式锁

封装部分功能:

#include <string>
#include <vector>
#include <hiredis/hiredis.h>

class RedisClient {
public:
    RedisClient() : m_client(NULL) {}
    ~RedisClient() {
        if (m_client) {
            redisFree(m_client);
        }
    }

    bool Init(const std::string& hostname, int port) {
        struct timeval timeout = { 1, 500000 }; // 1.5 seconds
        m_client = redisConnectWithTimeout(hostname.c_str(), port, timeout);
        if (m_client == NULL || m_client->err) {
            if (m_client) {
                printf("Connection error: %s\n", m_client->errstr);
                redisFree(m_client);
            } else {
                printf("Connection error: can't allocate redis context\n");
            }
            return false;
        }
        printf("Connection success!\n");
        return true;
    }

    bool Ping() {
        if (!m_client) {
            return false;
        }

        redisReply *reply;
        reply = (redisReply*)redisCommand(m_client, "PING");
        if (reply) {
            // printf("reply->type: %d, reply->str: %s, reply->integer: %d\n", reply->type, reply->str, reply->integer);
            if (reply->type == REDIS_REPLY_STATUS && strncmp(reply->str, "PONG", 4) == 0) {
                freeReplyObject(reply);
                return true;
            }
            freeReplyObject(reply);
        }
        return false;
    }

    bool Del(const std::string& key) {
        redisReply *reply = (redisReply*)redisCommand(m_client, "DEL %s", key.c_str());
        
        if (reply) {
            // printf("reply->type: %d, reply->str: %s, reply->integer: %d\n", reply->type, reply->str, reply->integer);
            if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 1) {
                freeReplyObject(reply);
                return true;
            }
            freeReplyObject(reply);
            return false;
        }
        return false;
    }
    
    // 使用lua脚本确保删除的是之前设置的,避免误删
    bool Del(const std::string& key, const std::string& value) {
        redisReply *reply;
        std::string luaScript = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end";
        reply = (redisReply*)redisCommand(m_client, "EVAL %s 1 %s %s", luaScript.c_str(), key.c_str(), value.c_str());
        //reply = (redisReply*)redisCommand(m_client, "DEL %s", key.c_str());
        
        if (reply) {
            // printf("reply->type: %d, reply->str: %s, reply->integer: %d\n", reply->type, reply->str, reply->integer);
            if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 1) {
                freeReplyObject(reply);
                return true;
            }
            freeReplyObject(reply);
            return false;
        }
        return false;
    }

    // count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
    // count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
    // count = 0 : 移除表中所有与 value 相等的值。
    bool LRem(const std::string& key, const std::string& value, int count = 0) {
        if (!m_client) {
            return false;
        }

        redisReply *reply;
        reply = (redisReply*)redisCommand(m_client, "LREM %s %d %s", key.c_str(), count, value.c_str());
        if (reply) {
            freeReplyObject(reply);
            return true;
        }
        return false;
    }

    //设置key
    // EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
    // PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
    // NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
    // XX :只在键已经存在时,才对键进行设置操作。
    // 目前只设置 PX、NX, 实现带锁的分布式
    bool Set(const std::string& key, const std::string& value, int timeout) {
        if (!m_client) {
            return false;
        }

        redisReply *reply;
        reply = (redisReply*)redisCommand(m_client, "SET %s %s PX %d NX", key.c_str(), value.c_str(), timeout);
        if (reply) {
            // printf("reply->type: %d, reply->str: %s, reply->integer: %d\n", reply->type, reply->str, reply->integer);
            if (reply->type == REDIS_REPLY_NIL) {
                freeReplyObject(reply);
                return false;
            }
            if (reply->type == REDIS_REPLY_STATUS && strncmp(reply->str, "OK", 2) == 0) {
                freeReplyObject(reply);
                return true;
            }
            freeReplyObject(reply);
            return false;
        }
        return false;
    }

private:
    redisContext *m_client;
};

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <thread>
#include <sstream>

#include "RedisClient.h"

void TestDistributed(RedisClient* client) {
    std::ostringstream ss;
    ss << std::this_thread::get_id();
    std::string value = ss.str();
    while (true) {
        if (client->Ping()) {
            //timeout 3000
            if (!client->Set("job", value, 3000)) {
                printf("%s Set failed!\n", value.c_str());
                std::this_thread::sleep_for(std::chrono::milliseconds(500));
                continue;
            } else {
                //do something
                printf("%s Acquire the lock!\n", value.c_str());
                std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                //finished, unlock
                if (!client->Del("job", value)) {
                    printf("Del job1 error\n");
                }
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
}

int main() {
    for (int i = 0; i != 3; ++i) {
        std::thread([&](){
            RedisClient* client = new RedisClient();
            if (!client->Init("192.168.11.245", 63790)) {
                return 0;
            }
            TestDistributed(client);
        }).detach();
    }
    
    while(true) {
        std::this_thread::sleep_for(std::chrono::seconds(100));
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值