├── BUILD ├── LICENSE ├── Makefile ├── README.md ├── build.sh ├── src ├── command.cc ├── command.h ├── config.cc ├── config.h ├── fake_raft.h ├── id.proto ├── idserver.cc ├── idserver.h ├── idserver_test.cc ├── keyitem.h ├── main.cc ├── request.h ├── session.cc ├── session.h └── tunnel.h ├── support-files ├── id.conf ├── idgen_mon ├── run.sh ├── start.sh └── stop.sh ├── test ├── bench.c ├── testCommand.cc └── testProtocol.cc └── tools ├── check.py └── idplugin.cc /BUILD: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = 'idgenlib', 3 | srcs = [ 4 | 'src/command.cc', 5 | 'src/config.cc', 6 | 'src/session.cc', 7 | 'src/idserver.cc', 8 | ], 9 | deps = [ 10 | ':idproto', 11 | '//thirdparty/muduo:muduo_net', 12 | '//raft:raft', 13 | '//thirdparty/gflags:gflags', 14 | '//thirdparty/glog:glog', 15 | '//thirdparty/perftools:tcmalloc' 16 | ] 17 | ) 18 | 19 | cc_binary( 20 | name = 'idgen', 21 | srcs = [ 22 | 'src/main.cc' 23 | ], 24 | deps = [ 25 | ':idgenlib', 26 | '//thirdparty/muduo:muduo_inspect', 27 | '//raft:sofa_transporter', 28 | "//toft/base:binary_version" 29 | ] 30 | ) 31 | 32 | cc_binary( 33 | name = 'testproto', 34 | srcs = [ 35 | 'test/testProtocol.cc' 36 | ], 37 | deps = [ 38 | ':idgenlib' 39 | ] 40 | ) 41 | cc_binary( 42 | name = 'idplugin', 43 | srcs = [ 44 | 'tools/idplugin.cc' 45 | ], 46 | deps = [ 47 | ':idproto' 48 | ] 49 | ) 50 | 51 | cc_test( 52 | name = 'testIdgen', 53 | srcs = [ 54 | 'test/testCommand.cc', 55 | 'src/idserver_test.cc' 56 | ], 57 | deps = [ 58 | ':idgenlib' 59 | ] 60 | ) 61 | 62 | proto_library( 63 | name = 'idproto', 64 | srcs = 'src/id.proto' 65 | ) 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 eLong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:all debug release test 2 | 3 | all: debug 4 | 5 | debug: 6 | blade build ... -p debug --verbose 7 | 8 | release: 9 | blade build ... --verbose 10 | 11 | test: 12 | blade test -p debug --verbose 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # idgen介绍 2 | 3 | idgen是一个可以生成全局唯一自增id的分布式高可用服务。 4 | 5 | # 特性 6 | 7 | - **强一致** 使用raft一致性算法,多节点自动切换master-follower,任何一个节点都可以提供服务。 8 | - **接口简单** redis兼容协议,使用任何一门语言的redis客户端即可使用,没有专门的客户端。 9 | - **高性能** 使用redis-benchmark来测试,qps可达5w+/s。 10 | - **易于部署** 采用c++开发,全静态编译,零依赖,直接二进制部署。 11 | 12 | # idgen的编译安装 13 | 14 | ## 依赖 15 | 16 | - centos6.4 17 | - zlib 18 | - boost1.53.0 19 | 20 | ## 安装 21 | 22 | 1. 执行idgen包内的build.sh 23 | 2. 编译过程需要花费一些时间,编译完成后执行 alt 即能切换至编译的结果存放目录,看到最终的二进制文件。如果alt命令找不到,二进制的目录为blade-bin/idgen/ 24 | 25 | ## 本地快速搭建集群 26 | 27 | 1.源代码编译(如果下载release包,直接从第二步开始) 28 | 29 | ``` bash 30 | 31 | mkdir /tmp/blade_root 32 | 33 | export BLADE_ROOT=/tmp/blade_root 34 | 35 | git clone https://github.com/eLong-INF/idgen.git 36 | 37 | cd idgen && ./build.sh 38 | 39 | mkdir /tmp/idgen 40 | 41 | cd /tmp/idgen 42 | 43 | cp -r $BLADE_ROOT/idgen/support-files node1 44 | cp -r $BLADE_ROOT/build64_release/idgen/idgen node1/idgen 45 | 46 | ``` 47 | 48 | 2.服务启动(直接拷贝release包为node1,node2,node3) 49 | 50 | ``` 51 | cp -r node1 node2 52 | cp -r node1 node3 53 | 54 | node1/start.sh 1 55 | node2/start.sh 2 56 | node3/start.sh 3 57 | 58 | ``` 59 | 60 | ## id生成器的使用 61 | 62 | idgen使用redis通信协议,运行后,可以使用redis客户端连接测试。 63 | 64 | ``` 65 | 66 | //连接任意一个实例,发送ping,回复Pong,即该实例服务启动正常。 67 | idgen]# redis-cli -h 127.0.0.1 -p 6001 ping 68 | PONG 69 | 70 | //设置id初始值,回复OK,设置成功 71 | idgen]# redis-cli -h 127.0.0.1 -p 6001 set id 23 72 | OK 73 | 74 | //通过incr命令从任一实例中取出全局自增的id 75 | idgen]# redis-cli -h 127.0.0.1 -p 6001 incr id 76 | (integer) 24 77 | idgen]# redis-cli -h 127.0.0.1 -p 6002 incr id 78 | (integer) 25 79 | idgen]# redis-cli -h 127.0.0.1 -p 6003 incr id 80 | (integer) 26 81 | 82 | //通过incyby命令设置每次自增的增加量 83 | idgen]# redis-cli -h 127.0.0.1 -p 6001 incrby id 5 84 | (integer) 31 85 | idgen]# redis-cli -h 127.0.0.1 -p 6002 incrby id 5 86 | (integer) 36 87 | idgen]# redis-cli -h 127.0.0.1 -p 6003 incrby id 5 88 | (integer) 41 89 | 90 | ``` 91 | 92 | # 性能测试 93 | Intel(R) Core(TM) i5-3470 CPU @ 3.20GHz 4核6G内存 94 | 95 | ``` 96 | # redis-benchmark -n 100000 -h 127.0.0.1 -p 6001 -t incr 97 | 98 | ====== INCR ====== 99 | 100000 requests completed in 1.47 seconds 100 | 50 parallel clients 101 | 3 bytes payload 102 | keep alive: 1 103 | 104 | 94.31% <= 1 milliseconds 105 | 99.91% <= 2 milliseconds 106 | 100.00% <= 2 milliseconds 107 | 67888.66 requests per second 108 | ``` 109 | 110 | # 支持的命令列表 111 | 112 | - `PING` 可以用作测试服务是否正常 113 | - `SET key value` 用于设置初始id的值 114 | - `GET key` 用于查看当前的id而不会增加id的值 115 | - `INCR key` 用于把`key`的值加一,返回的值是加一后的结果 116 | - `INCRBY key value`,用于把`key`加`value`的步长后返回,一般应用于批量申请id 117 | 118 | # 运行参数 119 | 120 | idgen运行需要设置运行参数,通过--help查看参数说明。其中需要配置id.conf,如下: 121 | 122 | ``` 123 | # server{$id}=rpc address & idmachine address 124 | server1=127.0.0.1:8001 & 127.0.0.1:6001 125 | server2=127.0.0.1:8002 & 127.0.0.1:6002 126 | server3=127.0.0.1:8003 & 127.0.0.1:6003 127 | 128 | # Raft message log dir 129 | raftlog=raftlog 130 | 131 | # snapshot dir 132 | snapshot=snapshot 133 | 134 | # Election timeout in ms 135 | electionTimeout=1000 136 | 137 | # Heart beat timeout in ms 138 | heartBeatTimeout=80 139 | 140 | # If false raft logs will be replicated in batch mode 141 | forceFlush=true 142 | 143 | # 单次最大提交日志的数量,太多会影响Leader和Follower之间的稳定性 144 | maxCommitSize=10000 145 | 146 | # 提交多少个日志之后做快照, 设置为0之后按照时间来做快照 147 | snapshotLogSize = 0 148 | 149 | # 隔多久做快照,默认一天(3600*24) 150 | snapshotInterval = 86400 151 | 152 | # acccess log file 153 | accesslog = log/access.log 154 | 155 | # 一次批量申请的步长 156 | advanceStep = 10000 157 | 158 | ``` 159 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ##blade install path 6 | export PATH=$PATH:$HOME/bin 7 | 8 | this=`pwd`/.. 9 | 10 | cd $this 11 | 12 | ## pyxis ROOTPATH 13 | root=`pwd` 14 | 15 | echo "download dependent libraries" 16 | 17 | if [ ! -d "$root/typhoon-blade" ];then 18 | echo "--- Blade ---" 19 | git clone https://github.com/eLong-INF/typhoon-blade.git 20 | fi 21 | 22 | if [ ! -d "$root/toft" ];then 23 | echo "--- toft ---" 24 | git clone https://github.com/eLong-INF/toft.git 25 | fi 26 | 27 | if [ ! -d "$root/raft" ];then 28 | echo "--- raft ---" 29 | git clone https://github.com/eLong-INF/raft.git 30 | fi 31 | 32 | if [ ! -d "$root/thirdparty" ];then 33 | echo "--- thirdparty ---" 34 | git clone https://github.com/eLong-INF/thirdparty.git 35 | fi 36 | 37 | cd "$root/thirdparty" 38 | 39 | git submodule init 40 | git submodule update 41 | 42 | cd $root 43 | 44 | touch BLADE_ROOT 45 | 46 | pwd 47 | 48 | ./typhoon-blade/install 49 | 50 | cd $root/idgen/ 51 | 52 | blade build 53 | 54 | -------------------------------------------------------------------------------- /src/command.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * command.cc 3 | * 4 | * Created on: Jan 22, 2014 5 | * Author: fan 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "command.h" 15 | 16 | namespace idgen { 17 | 18 | Command::Command() 19 | : state_(kNewCommand), 20 | ncmd_(0), 21 | args_() 22 | { 23 | } 24 | 25 | bool Command::Parse(muduo::net::Buffer* buff, std::string* error) 26 | { 27 | if (state_ == kNewCommand) { 28 | const char* crlf = buff->findCRLF(); 29 | if (crlf == NULL) { 30 | return true; 31 | } 32 | std::string line(buff->peek(), crlf); 33 | size_t n = 0; 34 | if (sscanf(line.c_str(), "*%zu", &n) != 1) { 35 | error->assign("Protocol error: get command size"); 36 | return false; 37 | } 38 | ncmd_ = n; 39 | buff->retrieveUntil(crlf + 2); 40 | if (n != 0) { 41 | state_ = kParseArgs; 42 | } 43 | } 44 | 45 | while (state_ == kParseArgs) { 46 | const char* crlf1 = buff->findCRLF(); 47 | const char* crlf2 = NULL; 48 | if (crlf1 == NULL) { 49 | return true; 50 | } else { 51 | crlf2 = buff->findCRLF(crlf1 + 1); 52 | if (crlf2 == NULL) { 53 | return true; 54 | } 55 | } 56 | std::string line(buff->peek(), crlf1); 57 | std::string arg(crlf1 + 2, crlf2); 58 | size_t n; 59 | if (sscanf(line.c_str(), "$%zu", &n) != 1) { 60 | error->assign("Protocol error: get arg size"); 61 | return false; 62 | } 63 | if (arg.size() != n) { 64 | error->assign("Protocol error: arg size not matched"); 65 | return false; 66 | } 67 | buff->retrieveUntil(crlf2 + 2); 68 | args_.push_back(arg); 69 | if (args_.size() == ncmd_) { 70 | state_ = kDone; 71 | } 72 | } 73 | CHECK(state_ == kDone); 74 | std::string& name = args_[0]; 75 | std::transform(name.begin(), name.end(), name.begin(), ::toupper); 76 | return true; 77 | } 78 | 79 | const std::string& Command::Name() const 80 | { 81 | CHECK(IsDone()); 82 | return args_[0]; 83 | } 84 | 85 | const std::string& Command::Argv(size_t i) const 86 | { 87 | CHECK(IsDone()); 88 | CHECK(i < ncmd_); 89 | return args_[i]; 90 | } 91 | 92 | size_t Command::Argc() const 93 | { 94 | return ncmd_; 95 | } 96 | 97 | void Command::Reset() 98 | { 99 | state_ = kNewCommand; 100 | ncmd_ = 0; 101 | args_.clear(); 102 | } 103 | 104 | bool Command::IsDone() const 105 | { 106 | return state_ == kDone; 107 | } 108 | 109 | bool Command::IsSupportCommand() 110 | { 111 | CHECK(IsDone()); 112 | const std::string& name = Name(); 113 | const char* cmds[] = {"GET", "SET", "INCR", "INCRBY", "DEL", "PING", NULL}; 114 | const char** p = cmds; 115 | while (*p) { 116 | if (name == *p) { 117 | break; 118 | } 119 | p++; 120 | } 121 | return *p != NULL; 122 | } 123 | 124 | std::string Command::DebugString() const 125 | { 126 | CHECK(IsDone()); 127 | std::stringstream ss; 128 | ss << args_[0]; 129 | for (size_t i=1; i args_; 40 | }; 41 | 42 | } /* namespace idgen */ 43 | #endif /* IDGEN_COMMAND_H_ */ 44 | -------------------------------------------------------------------------------- /src/config.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * config.cc 3 | * 4 | * Created on: Jan 21, 2014 5 | * Author: fan 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "config.h" 18 | 19 | DEFINE_string(conf, "id.conf", "config file"); 20 | DEFINE_int32(myid, 1, "id of mine"); 21 | DEFINE_int32(prof, 0, "port of prof"); 22 | 23 | namespace idgen { 24 | 25 | Config::Config() 26 | : Myid(0), 27 | ElectionTimeout(0), 28 | HeartBeatTimeout(0), 29 | MaxCommitSize(10000), 30 | SnapshotLogSize(1000000), 31 | SnapshotInterval(3600 * 24), 32 | ForceFlush(true), 33 | AdvanceStep(10000), 34 | RaftLogDir(""), 35 | SnapshotDir(""), 36 | ProfPort(0), 37 | AccessLog("log/access.log"), 38 | RpcAddress(), 39 | AppAddress() 40 | { 41 | } 42 | 43 | void Config::Init() 44 | { 45 | Myid = FLAGS_myid; 46 | ProfPort = FLAGS_prof; 47 | 48 | raft::KvConfig config; 49 | if (config.Parse(FLAGS_conf) < 0) { 50 | LOG(FATAL) << "parse config file error."; 51 | } 52 | RaftLogDir = config.String("raftlog"); 53 | SnapshotDir = config.String("snapshot"); 54 | ElectionTimeout = config.Int("electionTimeout"); 55 | HeartBeatTimeout = config.Int("heartBeatTimeout"); 56 | MaxCommitSize = config.Int("maxCommitSize"); 57 | SnapshotLogSize = config.Int("snapshotLogSize"); 58 | SnapshotInterval = config.Int("snapshotInterval"); 59 | ForceFlush = config.Bool("forceFlush"); 60 | AccessLog = config.String("accesslog"); 61 | AdvanceStep = config.Int("advanceStep"); 62 | 63 | RpcAddress.push_back(muduo::net::InetAddress("0.0.0.0", 0)); 64 | AppAddress.push_back(muduo::net::InetAddress("0.0.0.0", 0)); 65 | 66 | char buf[64]; 67 | for (int i=1; ;i++) { 68 | sprintf(buf, "server%d", i); 69 | if (config.Exists(buf)) { 70 | std::stringstream ss(config.String(buf)); 71 | std::string rpcAddr, appAddr; 72 | std::getline(ss, rpcAddr, '&'); 73 | std::getline(ss, appAddr, '&'); 74 | 75 | std::string ip; 76 | uint16_t port; 77 | raft::Address(rpcAddr, &ip, &port); 78 | RpcAddress.push_back(muduo::net::InetAddress(ip, port)); 79 | 80 | raft::Address(appAddr, &ip, &port); 81 | AppAddress.push_back(muduo::net::InetAddress(ip, port)); 82 | 83 | LOG(INFO) << "add rpc address:" << rpcAddr << " app address:" << appAddr; 84 | } else { 85 | break; 86 | } 87 | } 88 | } 89 | 90 | 91 | } /* namespace idgen */ 92 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * config.h 3 | * 4 | * Created on: Jan 21, 2014 5 | * Author: fan 6 | */ 7 | 8 | #ifndef IDGEN_CONFIG_H_ 9 | #define IDGEN_CONFIG_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace idgen { 16 | 17 | class Config { 18 | public: 19 | Config(); 20 | void Init(); 21 | int Myid; 22 | int ElectionTimeout; 23 | int HeartBeatTimeout; 24 | int MaxCommitSize; 25 | int SnapshotLogSize; 26 | int SnapshotInterval; 27 | bool ForceFlush; 28 | int AdvanceStep; 29 | std::string RaftLogDir; 30 | std::string SnapshotDir; 31 | int ProfPort; 32 | std::string AccessLog; 33 | std::vector RpcAddress; 34 | std::vector AppAddress; 35 | }; 36 | 37 | } /* namespace idgen */ 38 | #endif /* IDGEN_CONFIG_H_ */ 39 | -------------------------------------------------------------------------------- /src/fake_raft.h: -------------------------------------------------------------------------------- 1 | #ifndef IDGEN_FAKE_RAFT_H 2 | #define IDGEN_FAKE_RAFT_H 3 | 4 | #include 5 | 6 | class FakeRaft : public raft::Raft 7 | { 8 | public: 9 | FakeRaft() :logid_(0){} 10 | virtual ~FakeRaft() {} 11 | virtual int LeaderId() { return 0; } 12 | virtual int Broadcast(const std::string& data) { 13 | syncedCallback_(logid_, data); 14 | logid_++; 15 | return 0; 16 | } 17 | virtual void SetSyncedCallback(const raft::SyncedCallback& cb) { syncedCallback_ = cb; } 18 | virtual void SetStateChangedCallback(const raft::StateChangedCallback& cb) { stateChangedCallback_ = cb; } 19 | virtual void SetTakeSnapshotCallback(const raft::TakeSnapshotCallback& cb) { takeSnapshotCallback_ = cb; } 20 | virtual void SetLoadSnapshotCallback(const raft::LoadSnapshotCallback& cb) { loadSnapshotCallback_ = cb; } 21 | 22 | void Start() { 23 | loadSnapshotCallback_(logid_); 24 | stateChangedCallback_(raft::LEADER); 25 | } 26 | 27 | private: 28 | raft::SyncedCallback syncedCallback_; 29 | raft::StateChangedCallback stateChangedCallback_; 30 | raft::TakeSnapshotCallback takeSnapshotCallback_; 31 | raft::LoadSnapshotCallback loadSnapshotCallback_; 32 | uint64_t logid_; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/id.proto: -------------------------------------------------------------------------------- 1 | package idgen.proto; 2 | 3 | enum RequestType { 4 | SET = 0; 5 | DEL = 1; 6 | GET = 2; 7 | INCR = 3; 8 | NIL = 4; 9 | ADVANCE = 5; 10 | } 11 | 12 | message Request { 13 | required RequestType type = 2; 14 | required string key = 3; 15 | required uint64 value = 4; 16 | optional uint64 xid = 5; 17 | } -------------------------------------------------------------------------------- /src/idserver.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * idgen.cc 3 | * 4 | * Created on: Jan 21, 2014 5 | * Author: fan 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "idserver.h" 15 | #include "config.h" 16 | 17 | namespace idgen { 18 | 19 | using namespace muduo; 20 | using namespace muduo::net; 21 | 22 | const std::string kLogFlushKey = "idgenFlushKey"; 23 | const std::string kIdgenLeaderKey = "idgenLeader"; 24 | 25 | IdServer::IdServer(EventLoop* loop, raft::Raft* raft, const muduo::net::InetAddress& addr, const std::string& snapshotDir) 26 | : loop_(loop), 27 | server_(loop_, addr, "idserver"), 28 | raft_(raft), 29 | sessions_(), 30 | nextSessionId_(0), 31 | snapshotdir_(snapshotDir), 32 | ready_(false), 33 | synctag_(), 34 | xid_(0), 35 | pendingReqs_(), 36 | accesslog_(NULL), 37 | lastFlush_(muduo::Timestamp::now()), 38 | keyitems_(), 39 | advanceStep_(muduo::Singleton::instance().AdvanceStep) 40 | { 41 | initSessionId(); 42 | initXid(); 43 | logFilePath_ = muduo::Singleton::instance().AccessLog; 44 | reloadlog(); 45 | 46 | raft_->SetSyncedCallback(boost::bind(&IdServer::onSynced, this, _1, _2)); 47 | raft_->SetStateChangedCallback(boost::bind(&IdServer::onStateChanged, this, _1)); 48 | raft_->SetTakeSnapshotCallback(boost::bind(&IdServer::onTakeSnapshot, this, _1, _2)); 49 | raft_->SetLoadSnapshotCallback(boost::bind(&IdServer::onLoadSnapshot, this, _1)); 50 | server_.setConnectionCallback(boost::bind(&IdServer::onConnection, this, _1)); 51 | } 52 | 53 | void IdServer::Start() 54 | { 55 | server_.start(); 56 | } 57 | 58 | void IdServer::Request(const SessionPtr& s, proto::Request* data) 59 | { 60 | requestPtr req(new request); 61 | req->Session = s; 62 | req->Start = muduo::Timestamp::now(); 63 | req->Req.CopyFrom(*data); 64 | req->Req.set_xid(xid_++); 65 | 66 | if (data->type() == proto::INCR) { 67 | std::map::iterator it; 68 | it = keyitems_.find(data->key()); 69 | 70 | // not found, error 71 | if (it == keyitems_.end()) { 72 | s->ReplyError("not exists"); 73 | VLOG(3) << "not exists"; 74 | logaccess(req, 0, "not exists"); 75 | return; 76 | } 77 | 78 | keyItemPtr k = it->second; 79 | uint64_t res = k->Current + data->value(); 80 | VLOG(10) << "current " << k->Current << " limit " << k->Limit; 81 | // 没有到limit,直接返回 82 | if (res <= k->Limit) { 83 | k->Current = res; 84 | s->ReplyInt(res); 85 | logaccess(req, res, "i"); 86 | return; 87 | } 88 | 89 | // 需要预申请key, 如果队列长度为空,表明是第一个申请的 90 | bool needAdvance = k->Waitq.size() == 0; 91 | k->Waitq.push_back(req); 92 | // 防止队列长度大于步长 93 | if (k->Waitq.size() >= size_t(advanceStep_)) { 94 | s->ReplyError("server busy"); 95 | VLOG(2) << data->key() << " waitq full"; 96 | logaccess(req, 0, "wait queue full"); 97 | return; 98 | } 99 | 100 | if (needAdvance) { 101 | advanceKey(s, data->key(), k->Current); 102 | } 103 | return; 104 | } 105 | // 其他请求 106 | replicate(req); 107 | } 108 | 109 | // advanceKey发送ADVANCE请求 110 | void IdServer::advanceKey(const SessionPtr& s, const std::string& key, 111 | uint64_t value) 112 | { 113 | requestPtr req(new request); 114 | req->Session = s; 115 | req->Start = muduo::Timestamp::now(); 116 | req->Req.set_type(proto::ADVANCE); 117 | req->Req.set_key(key); 118 | req->Req.set_value(value); 119 | req->Req.set_xid(xid_++); 120 | replicate(req); 121 | } 122 | 123 | void IdServer::replicate(const requestPtr& r) 124 | { 125 | uint64_t xid = r->Req.xid(); 126 | pendingReqs_[xid] = r; 127 | raft_->Broadcast(r->Req.SerializeAsString()); 128 | } 129 | 130 | 131 | bool IdServer::IsReady() 132 | { 133 | // leader 134 | if (IsLeader() && ready_) { 135 | return true; 136 | } 137 | 138 | // follower 139 | if (raft_->LeaderId() !=0 && !IsLeader()) { 140 | return true; 141 | } 142 | 143 | return false; 144 | } 145 | 146 | bool IdServer::IsLeader() 147 | { 148 | return raft_->LeaderId() == muduo::Singleton::instance().Myid; 149 | } 150 | 151 | InetAddress IdServer::LeaderAddress() 152 | { 153 | int leaderid = raft_->LeaderId(); 154 | if (leaderid == 0) { 155 | return InetAddress(); 156 | } else { 157 | return muduo::Singleton::instance().AppAddress[leaderid]; 158 | } 159 | } 160 | 161 | void IdServer::onConnection(const muduo::net::TcpConnectionPtr& conn) 162 | { 163 | if (conn->connected()) { 164 | VLOG(1) << conn->name() << " on, sid:" << nextSessionId_; 165 | conn->setContext(nextSessionId_); 166 | SessionPtr session(new Session(this, conn, nextSessionId_)); 167 | session->Setup(); 168 | sessions_[nextSessionId_] = session; 169 | nextSessionId_++; 170 | } else { 171 | uint64_t sid = boost::any_cast(conn->getContext()); 172 | VLOG(1) << conn->name() << " off, sid:" << sid; 173 | std::map::iterator it = sessions_.find(sid); 174 | if (it == sessions_.end()) { 175 | return; 176 | } 177 | SessionPtr& ss = it->second; 178 | ss->Teardown(); 179 | sessions_.erase(sid); 180 | } 181 | } 182 | 183 | void IdServer::onSynced(uint64_t index, const std::string& data) 184 | { 185 | loop_->runInLoop(boost::bind(&IdServer::handleSynced, this, data)); 186 | } 187 | 188 | void IdServer::onStateChanged(raft::ServerState state) 189 | { 190 | loop_->runInLoop(boost::bind(&IdServer::handleStateChanged, this, state)); 191 | } 192 | 193 | void IdServer::onTakeSnapshot(uint64_t index, 194 | const raft::TakeSnapshotDoneCallback& cb) 195 | { 196 | loop_->runInLoop(boost::bind(&IdServer::handleTakeSnapshot, this, index, cb)); 197 | } 198 | 199 | uint64_t IdServer::onLoadSnapshot(uint64_t index) 200 | { 201 | if (index == 0) { 202 | return 0; 203 | } 204 | std::string name = snapshotFile(index); 205 | std::ifstream f(name.c_str()); 206 | PCHECK(f) << "Load snapshot " << name; 207 | std::string key; 208 | uint64_t value; 209 | while (f >> key >> value) { 210 | keyItemPtr k(new keyItem); 211 | k->Current = value; 212 | k->Limit = value; 213 | keyitems_[key] = k; 214 | } 215 | return 0; 216 | } 217 | 218 | // handleSynced处理raft的日志回调 219 | // 220 | // leader广播的请求经过raft进行集群广播,如果得到大多数的同意则会调用这个 221 | // 回调函数来处理请求。 222 | void IdServer::handleSynced(const std::string& data) 223 | { 224 | LOG_EVERY_N(INFO, 10000) << "on command " << google::COUNTER; 225 | proto::Request req; 226 | req.ParseFromString(data); 227 | 228 | const std::string& name = req.key(); 229 | std::string reply; 230 | 231 | uint64_t xid = req.xid(); 232 | 233 | requestPtr r; 234 | SessionPtr s; 235 | std::map::iterator it; 236 | it = pendingReqs_.find(xid); 237 | if (it != pendingReqs_.end()) { 238 | r = it->second; 239 | pendingReqs_.erase(it); 240 | s = r->Session.lock(); 241 | logaccess(r, req.value(), "l"); 242 | } 243 | 244 | // idgen启动后,raft就开始回放日志,然而在把所有日志回放完之前是不能提供服务的 245 | // 因此leader在成为leader之后发送一个type为NIL的请求,同时附带一个唯一的tag 246 | // 如果这个请求被大家广播接受,就表明之前的日志都已经处理完毕 247 | if (req.type() == proto::NIL) { 248 | LOG(INFO) << "receive nil type, key: " << req.key(); 249 | if (synctag_ == req.key()) { 250 | LOG(INFO) << "sync tag matched. server is ready"; 251 | ready_ = true; 252 | // leader的incr的请求在没有到达limit之前是不会进行广播,因此新的leader 253 | // 需要重新设置current值以防止id回退 254 | resetKeyItems(); 255 | } 256 | return; 257 | } 258 | 259 | if (req.type() == proto::GET) { 260 | if (name == kIdgenLeaderKey) { 261 | if (s) { s->ReplyString(LeaderAddress().toIpPort().c_str()); } 262 | return; 263 | } 264 | std::map::iterator it; 265 | it = keyitems_.find(name); 266 | if (it == keyitems_.end()) { 267 | if (s) { s->ReplyError("not found");} 268 | return; 269 | } 270 | if (s) { s->ReplyInt(it->second->Current); } 271 | return; 272 | } 273 | 274 | if (req.type() == proto::SET) { 275 | if (name == kLogFlushKey) { 276 | reloadlog(); 277 | if (s) { s->ReplyString("OK"); } 278 | return; 279 | } 280 | keyItemPtr k(new keyItem); 281 | k->Current = req.value(); 282 | k->Limit = k->Current; 283 | keyitems_[name] = k; 284 | if (s) { s->ReplyString("OK"); } 285 | return; 286 | } 287 | 288 | if (req.type() == proto::DEL) { 289 | if (keyitems_.find(name) == keyitems_.end()) { 290 | if (s) { s->ReplyString("OK"); } 291 | return; 292 | } 293 | keyitems_.erase(name); 294 | if (s) { s->ReplyString("OK"); } 295 | return; 296 | } 297 | 298 | // 废弃的指令,兼容老的日志格式 299 | if (req.type() == proto::INCR) { 300 | std::map::iterator it; 301 | it = keyitems_.find(name); 302 | if (it == keyitems_.end()) { 303 | if (s) { s->ReplyError("not exists"); } 304 | return; 305 | } 306 | keyItemPtr k = it->second; 307 | k->Current = req.value(); 308 | k->Limit = k->Current; 309 | if (s) { s->ReplyString("OK"); } 310 | return; 311 | } 312 | 313 | // ADVANCE请求重置current和limit的值,同时会回复阻塞在调用incr的请求 314 | if (req.type() == proto::ADVANCE) { 315 | std::map::iterator it0; 316 | it0 = keyitems_.find(name); 317 | if (it0 == keyitems_.end()) { 318 | if (s) { s->ReplyError("not exists"); } 319 | return; 320 | } 321 | keyItemPtr k = it0->second; 322 | k->Current = req.value(); 323 | k->Limit = k->Current + advanceStep_; 324 | VLOG(10) << "ADVANCE " << name << " current " 325 | << k->Current << " limit " << k->Limit; 326 | 327 | // 回复之前阻塞的请求 328 | std::list::iterator it; 329 | for (it=k->Waitq.begin(); it!=k->Waitq.end(); it++) { 330 | requestPtr rr = *it; 331 | SessionPtr ss = rr->Session.lock(); 332 | if (ss) { 333 | k->Current += rr->Req.value(); 334 | ss->ReplyInt(k->Current); 335 | logaccess(rr, k->Current, "a"); 336 | } 337 | } 338 | k->Waitq.clear(); 339 | return; 340 | } 341 | } 342 | 343 | // resetKeyItems设置所有key的current到limit 344 | // 同时清空等待队列 345 | void IdServer::resetKeyItems() 346 | { 347 | std::map::iterator it; 348 | for (it=keyitems_.begin(); it!=keyitems_.end(); it++) { 349 | keyItemPtr k = it->second; 350 | // 考虑到新的Leader可能是之前的follower,而新的leader不知道之前的 351 | // leader的current值,但知道一定不会超过limit,因此设置自己的current 352 | // 为limit来避免id回退 353 | // 这个时候current == limit,在下次客户端请求的时候会强制进行一次advance 354 | k->Current = k->Limit; 355 | k->Waitq.clear(); 356 | } 357 | } 358 | 359 | void IdServer::handleStateChanged(raft::ServerState state) 360 | { 361 | LOG(INFO) << "state changed, clear pending requests and sessions"; 362 | pendingReqs_.clear(); 363 | std::map::iterator it = sessions_.begin(); 364 | for(; it != sessions_.end(); it++) { 365 | SessionPtr& ss = it->second; 366 | ss->Teardown(); 367 | } 368 | sessions_.clear(); 369 | 370 | ready_ = false; 371 | 372 | if (state == raft::LEADER) { 373 | // 发送一个NIL包 这个NIL包的key字段使用当前时间戳 然后等待这个包的广播 374 | // 当这个包被成功广播 证明之前的日志都被提交了 可以正常提供服务 375 | synctag_ = muduo::Timestamp::now().toString().c_str(); 376 | LOG(INFO) << "send sync tag " << synctag_; 377 | proto::Request req; 378 | req.set_type(proto::NIL); 379 | req.set_key(synctag_); 380 | req.set_value(0); 381 | raft_->Broadcast(req.SerializeAsString()); 382 | } 383 | } 384 | 385 | void IdServer::handleTakeSnapshot(uint64_t index, 386 | const raft::TakeSnapshotDoneCallback& cb) 387 | { 388 | std::string name = snapshotFile(index); 389 | FILE* fp = fopen(name.c_str(), "wb"); 390 | PCHECK(fp != NULL) << "Take snapshot " << name; 391 | 392 | std::map::iterator it; 393 | for (it=keyitems_.begin(); it!=keyitems_.end(); it++) { 394 | fprintf(fp, "%s %"PRIu64"\n", it->first.c_str(), it->second->Current); 395 | } 396 | fflush(fp); 397 | cb(); 398 | } 399 | 400 | // myid(1) + timestamp_ms(5) + zero(2) 401 | void IdServer::initSessionId() 402 | { 403 | uint64_t myid = muduo::Singleton::instance().Myid; 404 | struct timeval tv; 405 | ::gettimeofday(&tv, NULL); 406 | nextSessionId_ = static_cast(tv.tv_sec) * 1000 + tv.tv_usec / 1000; 407 | nextSessionId_ = (nextSessionId_ << 24 >> 8) | myid << 56; 408 | LOG(INFO) << "Init session id " << nextSessionId_; 409 | } 410 | 411 | void IdServer::initXid() 412 | { 413 | uint64_t myid = muduo::Singleton::instance().Myid; 414 | struct timeval tv; 415 | ::gettimeofday(&tv, NULL); 416 | xid_ = static_cast(tv.tv_sec) * 1000 + tv.tv_usec / 1000; 417 | xid_ = (nextSessionId_ << 24 >> 8) | myid << 56; 418 | LOG(INFO) << "Init xid " << nextSessionId_; 419 | } 420 | 421 | void IdServer::logaccess(const requestPtr& r, uint64_t ret, const std::string& err) 422 | { 423 | // 必须和id.proto同步 424 | static const char* methodTable[] = {"SET", "DEL", "GET", "INCR", "NIL", "ADVANCE"}; 425 | const char* addr = NULL; 426 | SessionPtr ss = r->Session.lock(); 427 | if (ss) { 428 | addr = ss->Conn()->peerAddress().toIp().c_str(); 429 | } 430 | muduo::Timestamp now = muduo::Timestamp::now(); 431 | double used = muduo::timeDifference(now, r->Start); 432 | 433 | const char* methodName = "???"; 434 | if (size_t(r->Req.type()) < sizeof(methodTable) / sizeof(methodTable[0])) { 435 | methodName = methodTable[r->Req.type()]; 436 | } 437 | 438 | fprintf(accesslog_, "%s %s %s %s %ld %ld \"%s\" %f\n", 439 | now.toFormattedString().c_str(), addr, methodName, 440 | r->Req.key().c_str(), r->Req.value(), ret, err.c_str(), used); 441 | if (muduo::timeDifference(now, lastFlush_) > 5) { 442 | fflush(accesslog_); 443 | lastFlush_ = now; 444 | } 445 | } 446 | 447 | void IdServer::reloadlog() 448 | { 449 | if (accesslog_ != NULL) { 450 | fclose(accesslog_); 451 | } 452 | accesslog_ = fopen(logFilePath_.c_str(), "ae"); 453 | PCHECK(accesslog_ != NULL); 454 | } 455 | 456 | std::string IdServer::snapshotFile(uint64_t idx) 457 | { 458 | char name[64]; 459 | sprintf(name, "%s/snapshot.%020"PRIu64, snapshotdir_.c_str(), idx); 460 | return name; 461 | } 462 | 463 | } /* namespace idgen */ 464 | -------------------------------------------------------------------------------- /src/idserver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * idserver.h 3 | * 4 | * Created on: Jan 21, 2014 5 | * Author: fan 6 | */ 7 | 8 | #ifndef IDGEN_IDSERVER_H_ 9 | #define IDGEN_IDSERVER_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | 30 | namespace muduo { 31 | namespace net { 32 | class EventLoop; 33 | } 34 | } 35 | 36 | namespace idgen { 37 | 38 | class IdServer { 39 | public: 40 | IdServer(muduo::net::EventLoop* loop, raft::Raft* raft, const muduo::net::InetAddress& addr, const std::string& snapshotDir); 41 | void Start(); 42 | void Request(const SessionPtr& s, proto::Request* data); 43 | bool IsReady(); 44 | bool IsLeader(); 45 | muduo::net::InetAddress LeaderAddress(); 46 | 47 | private: 48 | void onConnection(const muduo::net::TcpConnectionPtr& conn); 49 | void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp); 50 | void onBroadCast(int leaderid); 51 | void onSynced(uint64_t index, const std::string& data); 52 | void onStateChanged(raft::ServerState state); 53 | void onTakeSnapshot(uint64_t index, const raft::TakeSnapshotDoneCallback& cb); 54 | uint64_t onLoadSnapshot(uint64_t index); 55 | 56 | void handleMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf); 57 | void handleSynced(const std::string& data); 58 | void handleStateChanged(raft::ServerState state); 59 | void handleTakeSnapshot(uint64_t index, const raft::TakeSnapshotDoneCallback& cb); 60 | 61 | void initSessionId(); 62 | 63 | void initXid(); 64 | 65 | void logaccess(const requestPtr& r, uint64_t ret, const std::string& err); 66 | 67 | // 重新打开accesslog文件 68 | void reloadlog(); 69 | 70 | void replicate(const requestPtr& r); 71 | 72 | void resetKeyItems(); 73 | 74 | std::string snapshotFile(uint64_t idx); 75 | 76 | void advanceKey(const SessionPtr& s, const std::string& key, 77 | uint64_t value); 78 | 79 | 80 | muduo::net::EventLoop* loop_; 81 | muduo::net::TcpServer server_; 82 | raft::Raft* raft_; 83 | std::map sessions_; 84 | uint64_t nextSessionId_; 85 | std::string snapshotdir_; 86 | 87 | // 同步相关的变量,用于刚启动的时候指示日志是否同步完毕 88 | bool ready_; 89 | std::string synctag_; 90 | 91 | // 用于跟踪请求的id 92 | uint64_t xid_; 93 | 94 | // 处于等待状态的请求 95 | std::map pendingReqs_; 96 | 97 | // access log name 98 | std::string logFilePath_; 99 | 100 | // access log 101 | FILE* accesslog_; 102 | 103 | // access log last flush time 104 | muduo::Timestamp lastFlush_; 105 | 106 | // id keys 107 | std::map keyitems_; 108 | 109 | // advance step 110 | int advanceStep_; 111 | }; 112 | 113 | } /* namespace idgen */ 114 | #endif /* IDGEN_IDSERVER_H_ */ 115 | -------------------------------------------------------------------------------- /src/idserver_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace muduo; 14 | using namespace muduo::net; 15 | using namespace idgen; 16 | 17 | class TestServer { 18 | public: 19 | TestServer(EventLoop* loop, uint16_t port) 20 | : loop_(loop), 21 | raft_(), 22 | server_(loop, &raft_, InetAddress(port), "") 23 | {} 24 | 25 | void Start() 26 | { 27 | server_.Start(); 28 | raft_.Start(); 29 | } 30 | 31 | private: 32 | EventLoop* loop_; 33 | FakeRaft raft_; 34 | IdServer server_; 35 | 36 | }; 37 | 38 | class TestClient { 39 | public: 40 | TestClient(EventLoop* loop, uint16_t port) 41 | : loop_(loop), 42 | port_(port) 43 | {} 44 | 45 | void Start() 46 | { 47 | Thread thread(boost::bind(&TestClient::workThread, this), "idgenClient"); 48 | thread.start(); 49 | } 50 | 51 | private: 52 | void workThread() 53 | { 54 | ::usleep(1000 * 1000); 55 | char cmd[128]; 56 | 57 | // 准备相应的key 58 | snprintf(cmd, 128, "redis-cli -p %d set counter:rand:000000000000 0", port_); 59 | system(cmd); 60 | 61 | snprintf(cmd, 128, "redis-cli -p %d set counter:__rand_int__ 0", port_); 62 | system(cmd); 63 | 64 | snprintf(cmd, 128, "redis-benchmark -n 100000 -t incr,incrby -p %d", port_); 65 | system(cmd); 66 | 67 | snprintf(cmd, 128, "redis-benchmark -n 10000 -t set,get,del -p %d", port_); 68 | system(cmd); 69 | 70 | ::usleep(1000 * 1000); 71 | 72 | loop_->quit(); 73 | } 74 | 75 | EventLoop* loop_; 76 | uint16_t port_; 77 | }; 78 | 79 | void dummyOutput(const char* msg, int len) 80 | { 81 | } 82 | 83 | TEST(idgen, all) 84 | { 85 | Config& config = muduo::Singleton::instance(); 86 | config.AccessLog = "access.log"; 87 | config.AdvanceStep = 10000; 88 | 89 | Logger::setOutput(dummyOutput); 90 | 91 | EventLoop loop; 92 | TestServer server(&loop, 20010); 93 | TestClient client(&loop, 20010); 94 | server.Start(); 95 | client.Start(); 96 | loop.loop(); 97 | } 98 | -------------------------------------------------------------------------------- /src/keyitem.h: -------------------------------------------------------------------------------- 1 | #ifndef IDGEN_KEYITEM_H 2 | #define IDGEN_KEYITEM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace idgen { 10 | 11 | struct keyItem { 12 | // 当前的id值 13 | uint64_t Current; 14 | 15 | // 预申请到的id值 16 | uint64_t Limit; 17 | 18 | // 在预申请返回前积压的请求 19 | std::list Waitq; 20 | }; 21 | 22 | typedef boost::shared_ptr keyItemPtr; 23 | 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * server.cc 3 | * 4 | * Created on: Jan 21, 2014 5 | * Author: fan 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include "config.h" 23 | #include "idserver.h" 24 | 25 | using namespace idgen; 26 | using namespace raft; 27 | using namespace muduo; 28 | using namespace muduo::net; 29 | 30 | int main(int argc, char* argv[]) 31 | { 32 | toft::SetupBinaryVersion(); 33 | 34 | ::google::InitGoogleLogging(argv[0]); 35 | ::google::ParseCommandLineFlags(&argc, &argv, true); 36 | 37 | Config& config = muduo::Singleton::instance(); 38 | config.Init(); 39 | 40 | EventLoop loop; 41 | 42 | raft::sofarpc::Transporter trans; 43 | raft::Options raft_options; 44 | raft_options.MyID = config.Myid; 45 | raft_options.RaftLogDir = config.RaftLogDir; 46 | raft_options.ForceFlush = config.ForceFlush; 47 | raft_options.HeartbeatTimeout = config.HeartBeatTimeout; 48 | raft_options.ElectionTimeout = config.ElectionTimeout; 49 | raft_options.MaxCommitSize = config.MaxCommitSize; 50 | raft_options.SnapshotLogSize = config.SnapshotLogSize; 51 | raft_options.SnapshotInterval = config.SnapshotInterval; 52 | 53 | raft::Server raftServer(raft_options, &trans); 54 | 55 | raft::sofarpc::RpcServer raftRpcServer(config.RpcAddress[config.Myid].toIpPort().c_str(), raftServer.RpcEventChannel()); 56 | 57 | IdServer idserver(&loop, &raftServer, config.AppAddress[config.Myid], config.SnapshotDir); 58 | 59 | for (size_t i=1; i(config.Myid)) { 61 | continue; 62 | } 63 | std::string peerAddr = config.RpcAddress[i].toIpPort().c_str(); 64 | trans.AddPeer(peerAddr); 65 | raftServer.AddPeer(peerAddr); 66 | } 67 | 68 | EventLoopThread inspectThread; 69 | boost::scoped_ptr inspector; 70 | if (config.ProfPort != 0) { 71 | inspector.reset(new Inspector(inspectThread.startLoop(), InetAddress(config.ProfPort), "inspect")); 72 | } 73 | 74 | raftRpcServer.Start(); 75 | raftServer.Start(); 76 | 77 | idserver.Start(); 78 | 79 | loop.loop(); 80 | } 81 | -------------------------------------------------------------------------------- /src/request.h: -------------------------------------------------------------------------------- 1 | #ifndef IDGEN_REQUEST_H 2 | #define IDGEN_REQUEST_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace idgen { 11 | struct request { 12 | // 这次请求的session 13 | SessionWeakPtr Session; 14 | 15 | // 请求的pb 16 | proto::Request Req; 17 | 18 | // 请求的开始时间 19 | muduo::Timestamp Start; 20 | }; 21 | 22 | typedef boost::shared_ptr requestPtr; 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/session.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * session.cc 3 | * 4 | * Created on: Jan 22, 2014 5 | * Author: fan 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace muduo; 19 | using namespace muduo::net; 20 | 21 | namespace idgen { 22 | 23 | Session::Session(IdServer* server, const TcpConnectionPtr& conn, uint64_t sessionid) 24 | : server_(server), 25 | sessionid_(sessionid), 26 | conn_(conn), 27 | cmd_(), 28 | closed_(false) 29 | { 30 | } 31 | 32 | Session::~Session() 33 | { 34 | VLOG(1) << "~Session " << sessionid_; 35 | } 36 | 37 | void Session::Setup() 38 | { 39 | if (!server_->IsReady()) { 40 | LOG(INFO) << "server not ready"; 41 | Reply("-ERR not ready\r\n"); 42 | conn_->shutdown(); 43 | return; 44 | } 45 | 46 | if (!server_->IsLeader()) { 47 | InetAddress addr = server_->LeaderAddress(); 48 | conn_->setMessageCallback(boost::bind(&Session::onTunnelMessage, shared_from_this(), _1, _2, _3)); 49 | tunnel_.reset(new Tunnel(conn_->getLoop(), addr, conn_)); 50 | tunnel_->setup(); 51 | tunnel_->connect(); 52 | } else { 53 | conn_->setMessageCallback(boost::bind(&Session::onMessage, shared_from_this(), _1, _2, _3)); 54 | } 55 | } 56 | 57 | void Session::Teardown() 58 | { 59 | VLOG(1) << "Session::Teardown " << sessionid_; 60 | if (closed_) { 61 | return; 62 | } 63 | closed_ = true; 64 | 65 | if (tunnel_) { 66 | tunnel_->disconnect(); 67 | } 68 | CHECK(conn_); 69 | // 把conn_->this的引用消除 70 | conn_->setMessageCallback(muduo::net::defaultMessageCallback); 71 | conn_->shutdown(); 72 | } 73 | 74 | void Session::Reply(const std::string& data) 75 | { 76 | conn_->send(data); 77 | } 78 | 79 | void Session::ReplyError(const std::string& err) 80 | { 81 | std::string out; 82 | out.append("-ERR "); 83 | out.append(err); 84 | out.append("\r\n"); 85 | conn_->send(out); 86 | } 87 | 88 | void Session::ReplyInt(uint64_t n) 89 | { 90 | char buf[32]; 91 | snprintf(buf, 32, "%ld", n); 92 | std::string out; 93 | out.append(":"); 94 | out.append(buf); 95 | out.append("\r\n"); 96 | conn_->send(out); 97 | } 98 | 99 | void Session::ReplyString(const std::string& s) 100 | { 101 | std::string out; 102 | out.append("+"); 103 | out.append(s); 104 | out.append("\r\n"); 105 | conn_->send(out); 106 | } 107 | 108 | void Session::onTunnelMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp) 109 | { 110 | const TcpConnectionPtr& remote = tunnel_->remoteConn(); 111 | if (remote) { 112 | remote->send(buf); 113 | } 114 | } 115 | 116 | void Session::onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp) 117 | { 118 | bool cont = true; 119 | while (cont) { 120 | cont = false; 121 | std::string error; 122 | if (!cmd_.Parse(buf, &error)) { 123 | Reply("-ERR " + error + "\r\n"); 124 | conn->shutdown(); 125 | return; 126 | } 127 | 128 | if (cmd_.IsDone()) { 129 | cont = true; 130 | if (!cmd_.IsSupportCommand()) { 131 | Reply("-ERR command not supported.\r\n"); 132 | cmd_.Reset(); 133 | continue; 134 | } 135 | 136 | if (cmd_.Name() == "PING") { 137 | Reply("+PONG\r\n"); 138 | cmd_.Reset(); 139 | continue; 140 | } 141 | 142 | if (cmd_.Argc() < 2) { 143 | Reply("-ERR Protocol error:arg number error.\r\n"); 144 | cmd_.Reset(); 145 | continue; 146 | } 147 | 148 | const std::string& name = cmd_.Name(); 149 | const std::string& key = cmd_.Argv(1); 150 | proto::Request req; 151 | req.set_type(proto::NIL); 152 | req.set_key(key); 153 | req.set_value(0); 154 | 155 | if (name == "GET") { 156 | if (cmd_.Argc() != 2) { 157 | Reply("-ERR Protocol error:wrong number for get.\r\n"); 158 | } else { 159 | req.set_type(proto::GET); 160 | } 161 | } else if (name == "SET") { 162 | if (cmd_.Argc() != 3) { 163 | Reply("-ERR Protocol error:wrong number for set.\r\n"); 164 | } else { 165 | req.set_type(proto::SET); 166 | std::stringstream ss(cmd_.Argv(2)); 167 | uint64_t v; 168 | ss >> v; 169 | req.set_value(v); 170 | } 171 | } else if (name == "DEL") { 172 | if (cmd_.Argc() != 2) { 173 | Reply("-ERR Protocol error:wrong number for del.\r\n"); 174 | } else { 175 | req.set_type(proto::DEL); 176 | } 177 | } else if (name == "INCR") { 178 | if (cmd_.Argc() != 2) { 179 | Reply("-ERR Protocol error:wrong number for incr.\r\n"); 180 | } else { 181 | req.set_type(proto::INCR); 182 | req.set_value(1); 183 | } 184 | } else if (name == "INCRBY") { 185 | if (cmd_.Argc() != 3) { 186 | Reply("-ERR Protocol error:wrong number for incrby.\r\n"); 187 | } else { 188 | req.set_type(proto::INCR); 189 | std::stringstream ss(cmd_.Argv(2)); 190 | uint64_t v; 191 | ss >> v; 192 | req.set_value(v); 193 | } 194 | } 195 | cmd_.Reset(); 196 | 197 | if (req.type() != proto::NIL) { 198 | server_->Request(shared_from_this(), &req); 199 | } 200 | } 201 | } 202 | } 203 | 204 | } /* namespace idgen */ 205 | -------------------------------------------------------------------------------- /src/session.h: -------------------------------------------------------------------------------- 1 | /* 2 | * session.h 3 | * 4 | * Created on: Jan 22, 2014 5 | * Author: fan 6 | */ 7 | 8 | #ifndef IDGEN_SESSION_H_ 9 | #define IDGEN_SESSION_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "command.h" 17 | 18 | namespace idgen { 19 | class IdServer; 20 | class Tunnel; 21 | typedef boost::shared_ptr TunnelPtr; 22 | 23 | class Session : public boost::enable_shared_from_this{ 24 | public: 25 | Session(IdServer* server, const muduo::net::TcpConnectionPtr& conn, uint64_t sessionid); 26 | ~Session(); 27 | void Setup(); 28 | void Teardown(); 29 | void Reply(const std::string& data); 30 | 31 | // redis RESP protocol, see http://redis.io/topics/protocol 32 | void ReplyError(const std::string& err); 33 | void ReplyInt(uint64_t n); 34 | void ReplyString(const std::string& s); 35 | 36 | muduo::net::TcpConnectionPtr Conn() { return conn_; } 37 | 38 | private: 39 | void onTunnelMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp); 40 | void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp); 41 | IdServer* server_; 42 | uint64_t sessionid_; 43 | muduo::net::TcpConnectionPtr conn_; 44 | Command cmd_; 45 | TunnelPtr tunnel_; 46 | bool closed_; 47 | }; 48 | 49 | typedef boost::shared_ptr SessionPtr; 50 | typedef boost::weak_ptr SessionWeakPtr; 51 | 52 | } /* namespace idgen */ 53 | #endif /* IDGEN_SESSION_H_ */ 54 | -------------------------------------------------------------------------------- /src/tunnel.h: -------------------------------------------------------------------------------- 1 | #ifndef IDGEN_TUNNEL_H 2 | #define IDGEN_TUNNEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace idgen { 16 | class Tunnel : public boost::enable_shared_from_this, 17 | boost::noncopyable 18 | { 19 | public: 20 | Tunnel(muduo::net::EventLoop* loop, 21 | const muduo::net::InetAddress& serverAddr, 22 | const muduo::net::TcpConnectionPtr& serverConn) 23 | : client_(loop, serverAddr, serverConn->name()), 24 | serverConn_(serverConn) 25 | { 26 | VLOG(1) << "Tunnel " << serverConn->peerAddress().toIpPort() 27 | << " <-> " << serverAddr.toIpPort(); 28 | } 29 | 30 | ~Tunnel() 31 | { 32 | VLOG(1) << "~Tunnel"; 33 | } 34 | 35 | void setup() 36 | { 37 | client_.setConnectionCallback( 38 | boost::bind(&Tunnel::onClientConnection, shared_from_this(), _1)); 39 | client_.setMessageCallback( 40 | boost::bind(&Tunnel::onClientMessage, shared_from_this(), _1, _2, _3)); 41 | serverConn_->setHighWaterMarkCallback( 42 | boost::bind(&Tunnel::onHighWaterMarkWeak, boost::weak_ptr(shared_from_this()), _1, _2), 43 | 10*1024*1024); 44 | } 45 | 46 | void teardown() 47 | { 48 | client_.setConnectionCallback(muduo::net::defaultConnectionCallback); 49 | client_.setMessageCallback(muduo::net::defaultMessageCallback); 50 | CHECK(serverConn_); 51 | serverConn_->shutdown(); 52 | } 53 | 54 | void connect() 55 | { 56 | client_.connect(); 57 | } 58 | 59 | void disconnect() 60 | { 61 | client_.disconnect(); 62 | // serverConn_.reset(); 63 | } 64 | 65 | muduo::net::TcpConnectionPtr remoteConn() const 66 | { 67 | return client_.connection(); 68 | } 69 | 70 | void onClientConnection(const muduo::net::TcpConnectionPtr& conn) 71 | { 72 | if (conn->connected()) { 73 | // 在tunnel建立之前serverConn_可能已经关闭了连接 74 | if (!serverConn_->connected()) { 75 | VLOG(1) << "server connection has been shutdown before tunnel connected."; 76 | conn->shutdown(); 77 | return; 78 | } 79 | conn->setTcpNoDelay(true); 80 | conn->setHighWaterMarkCallback( 81 | boost::bind(&Tunnel::onHighWaterMarkWeak, boost::weak_ptr(shared_from_this()), _1, _2), 82 | 10*1024*1024); 83 | if (serverConn_->inputBuffer()->readableBytes() > 0) { 84 | conn->send(serverConn_->inputBuffer()); 85 | } 86 | } else { 87 | teardown(); 88 | } 89 | } 90 | 91 | void onClientMessage(const muduo::net::TcpConnectionPtr& conn, 92 | muduo::net::Buffer* buf, 93 | muduo::Timestamp) 94 | { 95 | if (serverConn_) { 96 | serverConn_->send(buf); 97 | } else { 98 | buf->retrieveAll(); 99 | abort(); 100 | } 101 | } 102 | 103 | void onHighWaterMark(const muduo::net::TcpConnectionPtr& conn, 104 | size_t bytesToSent) 105 | { 106 | LOG_INFO << "onHighWaterMark " << conn->name() 107 | << " bytes " << bytesToSent; 108 | disconnect(); 109 | } 110 | 111 | static void onHighWaterMarkWeak(const boost::weak_ptr& wkTunnel, 112 | const muduo::net::TcpConnectionPtr& conn, 113 | size_t bytesToSent) 114 | { 115 | boost::shared_ptr tunnel = wkTunnel.lock(); 116 | if (tunnel) { 117 | tunnel->onHighWaterMark(conn, bytesToSent); 118 | } 119 | } 120 | 121 | private: 122 | muduo::net::TcpClient client_; 123 | muduo::net::TcpConnectionPtr serverConn_; 124 | }; 125 | } 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /support-files/id.conf: -------------------------------------------------------------------------------- 1 | # server{$id}=rpc address & idmachine address 2 | server1=127.0.0.1:8001 & 127.0.0.1:6001 3 | server2=127.0.0.1:8002 & 127.0.0.1:6002 4 | server3=127.0.0.1:8003 & 127.0.0.1:6003 5 | 6 | # Raft message log dir 7 | raftlog=raftlog 8 | 9 | # snapshot dir 10 | snapshot=snapshot 11 | 12 | # Election timeout in ms 13 | electionTimeout=1000 14 | 15 | # Heart beat timeout in ms 16 | heartBeatTimeout=80 17 | 18 | # If false raft logs will be replicated in batch mode 19 | forceFlush=true 20 | 21 | # 单次最大提交日志的数量,太多会影响Leader和Follower之间的稳定性 22 | maxCommitSize=10000 23 | 24 | # 提交多少个日志之后做快照, 设置为0之后按照时间来做快照 25 | snapshotLogSize = 0 26 | 27 | # 隔多久做快照,默认一天(3600*24) 28 | snapshotInterval = 86400 29 | 30 | # acccess log file 31 | accesslog = log/access.log 32 | 33 | # 一次批量申请的步长 34 | advanceStep = 10000 35 | -------------------------------------------------------------------------------- /support-files/idgen_mon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eLong-opensource/idgen/258f60574eacc80cd62b9ad47c189678025b7849/support-files/idgen_mon -------------------------------------------------------------------------------- /support-files/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd `dirname $0` 6 | 7 | mkdir -pv raftlog snapshot log 8 | ./idgen --myid=$MYID --prof=0 --log_dir=log --conf=./id.conf &>./nohup.log 9 | -------------------------------------------------------------------------------- /support-files/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | cd `dirname $0` 6 | 7 | [ $# -lt 1 ] && echo 'usage ./start.sh $MYID' && exit 1 8 | 9 | chmod +x idgen_mon run.sh 10 | 11 | mkdir -p log 12 | 13 | export MYID=$1 14 | ./idgen_mon -l log/mon.log -d -m log/idgen_mon.pid ./run.sh 15 | -------------------------------------------------------------------------------- /support-files/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd `dirname $0` 4 | 5 | # 根据pid杀掉进程 6 | pid=`cat log/idgen_mon.pid` 7 | if [[ -e log/idgen_mon.pid ]] && \ 8 | ps -p "$pid" | grep idgen_mon && \ 9 | kill "$pid" 10 | then 11 | rm -v log/idgen_mon.pid 12 | echo kill $pid 13 | exit 0 14 | fi 15 | 16 | # 根据进程名,全路径匹配 17 | pids=`ps ax | grep '[i]dgen' | awk '{print $1}'` 18 | for pid in $pids; do 19 | p=`readlink /proc/$pid/exe` 20 | if [[ "$p" == "`pwd`/idgen_mon" ]]; then 21 | echo kill $pid 22 | kill $pid 23 | exit 0 24 | fi 25 | done 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /test/bench.c: -------------------------------------------------------------------------------- 1 | /* 2 | * bench.c 3 | * 4 | * Created on: Jan 23, 2014 5 | * Author: fan 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | 18 | int parse_conf(const char* path); 19 | void sync_send(); 20 | void async_send(); 21 | 22 | struct server_addr { 23 | char addr[32]; 24 | uint16_t port; 25 | }; 26 | 27 | struct server_addr server_list[5]; 28 | int server_list_len = 0; 29 | int size = 10000; 30 | float delay = 1; 31 | 32 | int parse_conf(const char* path) 33 | { 34 | FILE* fp = NULL; 35 | fp = fopen(path, "r"); 36 | if (fp == NULL) { 37 | perror("open conf"); 38 | return -1; 39 | } 40 | char buf[32] = {0}; 41 | int i = 0; 42 | while (fgets(buf, 32, fp) != NULL) { 43 | char* addr = strtok(buf, ":\n"); 44 | strncpy(server_list[i].addr, addr, sizeof(server_list[i])); 45 | char* port = strtok(NULL, ":\n"); 46 | server_list[i].port = atoi(port); 47 | i++; 48 | } 49 | return i; 50 | } 51 | 52 | void sync_send() 53 | { 54 | redisContext* c = NULL; 55 | redisReply* reply; 56 | 57 | int i = 0; 58 | for (;;) { 59 | 60 | i = (i + 1) % server_list_len; 61 | sleep(1); 62 | // connect idgen server 63 | char* addr = server_list[i].addr; 64 | uint16_t port = server_list[i].port; 65 | printf("Trying to connect to %s:%d\n", addr, port); 66 | c = redisConnect(addr, port); 67 | if (c == NULL || c->err) { 68 | if (c) { 69 | printf("Connection error:%s\n", c->errstr); 70 | redisFree(c); 71 | } 72 | continue; 73 | } 74 | 75 | // reset a and connect to leader 76 | printf("Connected.\n"); 77 | printf("Reset key a = 0\n"); 78 | reply = redisCommand(c, "SET a 0"); 79 | if (reply->type == REDIS_REPLY_ERROR) { 80 | printf("%s\n", reply->str); 81 | freeReplyObject(reply); 82 | redisFree(c); 83 | continue; 84 | } 85 | printf("%s\n", reply->str); 86 | freeReplyObject(reply); 87 | 88 | int cnt = 0; 89 | // loop send 90 | for (;;) { 91 | reply = redisCommand(c, "INCR a"); 92 | if (reply == NULL) { 93 | break; 94 | } 95 | 96 | if (reply->type == REDIS_REPLY_ERROR) { 97 | printf("%s\n", reply->str); 98 | freeReplyObject(reply); 99 | redisFree(c); 100 | continue; 101 | } else { 102 | if (++cnt >= size) { 103 | printf("%"PRIu64"\n", reply->integer); 104 | cnt = 0; 105 | } 106 | freeReplyObject(reply); 107 | } 108 | } 109 | } 110 | } 111 | 112 | void async_send() 113 | { 114 | 115 | } 116 | 117 | int main(int argc, char* argv[]) 118 | { 119 | char conf[32] = "raft.conf"; 120 | int async = 0; 121 | 122 | int o; 123 | while ((o = getopt(argc, argv, "s:d:f:ah")) != -1) { 124 | switch (o) { 125 | case 's': 126 | sscanf(optarg, "%d", &size); 127 | break; 128 | case 'd': 129 | sscanf(optarg, "%f", &delay); 130 | break; 131 | case 'f': 132 | strncpy(conf, optarg, 32); 133 | break; 134 | case 'a': 135 | async = 1; 136 | break; 137 | case 'h': 138 | printf("useage: %s -s print_size -d delay_in_ms -f conf_file\n", argv[0]); 139 | return 0; 140 | } 141 | } 142 | printf("size:%d\n", size); 143 | printf("delay:%f ms\n", delay); 144 | printf("conf:%s\n", conf); 145 | 146 | if ((server_list_len = parse_conf(conf)) == -1) { 147 | return -1; 148 | } 149 | 150 | int i = 0; 151 | for (i=0; i 9 | #include 10 | #include 11 | 12 | TEST(command, cmd_size) 13 | { 14 | muduo::net::Buffer buff; 15 | buff.append("*3$2\r\naa\r\n"); 16 | idgen::Command cmd; 17 | std::string error; 18 | ASSERT_TRUE(cmd.Parse(&buff, &error)); 19 | ASSERT_FALSE(cmd.IsDone()); 20 | 21 | buff.retrieveAll(); 22 | buff.append("*1\r\n$3\r\ncmd\r\n"); 23 | cmd.Reset(); 24 | ASSERT_TRUE(cmd.Parse(&buff, &error)) << error; 25 | ASSERT_TRUE(cmd.IsDone()) << cmd.Argc(); 26 | ASSERT_TRUE(cmd.Argc() == 1) << cmd.Argc(); 27 | } 28 | 29 | TEST(command, arg_size) 30 | { 31 | muduo::net::Buffer buff; 32 | idgen::Command cmd; 33 | std::string error; 34 | buff.append("*1\r\n$1\r\naa\r\n"); 35 | ASSERT_FALSE(cmd.Parse(&buff, &error)) << error; 36 | } 37 | 38 | TEST(command, cmd_argv) 39 | { 40 | muduo::net::Buffer buff; 41 | idgen::Command cmd; 42 | buff.append("*3\r\n$1\r\na\r\n$2\r\naa\r\n$3\r\naaa\r\n"); 43 | std::string error; 44 | ASSERT_TRUE(cmd.Parse(&buff, &error)) << error; 45 | ASSERT_TRUE(cmd.IsDone()); 46 | ASSERT_TRUE(cmd.Argc() == 3); 47 | ASSERT_TRUE(cmd.Name() == "A"); 48 | ASSERT_TRUE(cmd.Argv(0) == "A"); 49 | ASSERT_TRUE(cmd.Argv(1) == "aa"); 50 | ASSERT_TRUE(cmd.Argv(2) == "aaa"); 51 | } 52 | -------------------------------------------------------------------------------- /test/testProtocol.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * testProtocol.cc 3 | * 4 | * Created on: Jan 22, 2014 5 | * Author: fan 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace idgen; 17 | using namespace muduo; 18 | using namespace muduo::net; 19 | 20 | class RedisMock 21 | { 22 | public: 23 | RedisMock(EventLoop* loop) 24 | : server_(loop, InetAddress(6011), "redis") 25 | { 26 | server_.setConnectionCallback(boost::bind(&RedisMock::onConnection, this, _1)); 27 | server_.setMessageCallback(boost::bind(&RedisMock::onMessage, this, _1, _2, _3)); 28 | } 29 | 30 | void reply(const muduo::net::TcpConnectionPtr& conn, std::string msg) 31 | { 32 | conn->send(msg + "\r\n"); 33 | } 34 | 35 | void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp) 36 | { 37 | bool cont = true; 38 | while (cont) { 39 | std::string error; 40 | if (!cmd_.Parse(buf, &error)) { 41 | reply(conn, "-ERR " + error); 42 | conn->shutdown(); 43 | cont = false; 44 | } 45 | 46 | if (!cmd_.IsDone()) { 47 | cont = false; 48 | } 49 | 50 | if (cmd_.IsDone()) { 51 | printf("New command: %s\n", cmd_.DebugString().c_str()); 52 | const std::string& name = cmd_.Argv(0); 53 | if (name == "GET") { 54 | if (cmd_.Argc() != 2) { 55 | reply(conn, "-ERR Protocol error:wrong number for get."); 56 | } else { 57 | const std::string& key = cmd_.Argv(1); 58 | if (key == "error") { 59 | reply(conn, "-ERR error"); 60 | } else if (key == "nil"){ 61 | reply(conn, "$-1"); 62 | } else { 63 | char buff[32]; 64 | sprintf(buff, "$%zu\r\n%s", key.size(), key.c_str()); 65 | reply(conn, buff); 66 | } 67 | } 68 | } else if (name == "SET"){ 69 | if (cmd_.Argc() != 3) { 70 | reply(conn, "-ERR Protocol error:wrong number for set."); 71 | } else { 72 | const std::string& key = cmd_.Argv(1); 73 | if (key == "error") { 74 | reply(conn, "-ERR error"); 75 | } else { 76 | reply(conn, "+OK"); 77 | } 78 | } 79 | 80 | } else if (name == "INCR") { 81 | if (cmd_.Argc() != 2) { 82 | reply(conn, "-ERR Protocol error:wrong number for incr."); 83 | } else { 84 | const std::string& key = cmd_.Argv(1); 85 | if (key == "error") { 86 | reply(conn, "-ERR error"); 87 | } else { 88 | reply(conn, ":100"); 89 | } 90 | } 91 | } else if (name == "INCRBY"){ 92 | if (cmd_.Argc() != 3) { 93 | reply(conn, "-ERR Protocol error:wrong number for incrby."); 94 | } else { 95 | const std::string& key = cmd_.Argv(1); 96 | const std::string& value = cmd_.Argv(2); 97 | if (key == "error") { 98 | reply(conn, "-ERR error"); 99 | } else { 100 | reply(conn, ":"+value); 101 | } 102 | } 103 | } else if (name == "DEL") { 104 | if (cmd_.Argc() != 2) { 105 | reply(conn, "-ERR Protocol error:wrong number for del."); 106 | } else { 107 | const std::string& key = cmd_.Argv(1); 108 | if (key == "error") { 109 | reply(conn, "-ERR error"); 110 | } else if (key == "nonexists"){ 111 | reply(conn, ":0"); 112 | } else { 113 | reply(conn, ":1"); 114 | } 115 | } 116 | } else { 117 | reply(conn, "-ERR Protocol error: unknow command."); 118 | } 119 | cmd_.Reset(); 120 | } 121 | } 122 | } 123 | 124 | void onConnection(const TcpConnectionPtr& conn) 125 | { 126 | if (!conn->connected()) { 127 | cmd_.Reset(); 128 | } 129 | } 130 | 131 | void Start() 132 | { 133 | server_.start(); 134 | } 135 | 136 | private: 137 | TcpServer server_; 138 | Command cmd_; 139 | }; 140 | 141 | int main() 142 | { 143 | EventLoop loop; 144 | RedisMock redis(&loop); 145 | redis.Start(); 146 | printf("start on port 6011\n"); 147 | loop.loop(); 148 | } 149 | -------------------------------------------------------------------------------- /tools/check.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import select 3 | import sys 4 | 5 | timeout = 5000 6 | 7 | def error(str): 8 | print '\033[1;31m%s\033[m' %(str) 9 | 10 | def info(str): 11 | print '\033[1;36m%s\033[m' %(str) 12 | 13 | def send(sock, s): 14 | sock.sendall(s) 15 | p = select.poll() 16 | p.register(sock, select.POLLIN) 17 | if not p.poll(timeout): 18 | error('send request to %s:%d timeout'%sock.getpeername()) 19 | return None 20 | return sock.recv(1024).strip('\r\n').split(' ') 21 | 22 | def parseConf(path): 23 | l = [] 24 | f = open(path) 25 | for line in f: 26 | line = line.strip('\n') 27 | if not line: 28 | continue 29 | addr, port = line.split(':') 30 | l.append((addr, int(port))) 31 | return l 32 | 33 | def checkAlive(l): 34 | info('- check node alive') 35 | for addr in l: 36 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 37 | try: 38 | sock.connect(addr) 39 | print 'check node %s ok' %(repr(addr)) 40 | sock.close() 41 | except socket.error, e: 42 | error('check node %s error:%s'%(repr(addr), e[1])) 43 | 44 | def findLeader(l): 45 | for addr in l: 46 | try: 47 | print 'tring to connect %s:%d'%addr 48 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 49 | sock.connect(addr) 50 | t = send(sock, 'ADD checkleader\r\n') 51 | if not t or t[0] != 'TRANS': 52 | sock.close() 53 | continue 54 | 55 | addr, port = t[1].split(':') 56 | return (addr, int(port)) 57 | except socket.error, e: 58 | error('connect to %s error:%s'%(repr(addr), e[1])) 59 | 60 | def checkleader(l, token): 61 | info('- check leader node') 62 | t = findLeader(l) 63 | if not t: 64 | error('check leader error:no leader') 65 | return 66 | addr, port = t 67 | print 'find leader:%s'%(repr((addr, port))) 68 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 69 | try: 70 | sock.connect((addr, port)) 71 | except socket.error, e: 72 | error('connect leader error:%s'%(e[1])) 73 | t = send(sock, 'ADD checkleader %s\r\n'%(token)) 74 | if t and t[0] != 'OK': 75 | error('check create key error:%s'%(repr(t))) 76 | else: 77 | print 'check create key ok' 78 | 79 | t = send(sock, 'GET checkleader\r\n') 80 | if t and t[0] != 'ID': 81 | error('check get key error:%s'%(repr(t))) 82 | else: 83 | print 'check get key ok' 84 | 85 | t = send(sock,'DEL checkleader %s\r\n'%(token)) 86 | if t and t[0] != 'OK': 87 | error('check delete key error:%s'%(repr(t))) 88 | else: 89 | print 'check delete key ok' 90 | 91 | def main(): 92 | if len(sys.argv) < 3: 93 | print "usage check.py server.conf token" 94 | return 95 | l = parseConf(sys.argv[1]) 96 | checkAlive(l) 97 | checkleader(l, sys.argv[2]) 98 | 99 | if __name__ == '__main__': 100 | main() 101 | -------------------------------------------------------------------------------- /tools/idplugin.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * idplugin.cc 3 | * 4 | * Created on: Jan 17, 2014 5 | * Author: fan 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | int main() 15 | { 16 | int header; 17 | std::string body; 18 | while (read(0, &header, sizeof(header)) > 0) { 19 | body.resize(header); 20 | PCHECK(read(0, &body[0], header) == header); 21 | idgen::proto::Request req; 22 | if (!req.ParseFromString(body)) { 23 | body = ""; 24 | } else { 25 | body = req.DebugString(); 26 | } 27 | 28 | header = body.size(); 29 | PCHECK(write(1, &header, sizeof(header)) == sizeof(header)); 30 | PCHECK(write(1, &body[0], header) == header); 31 | 32 | } 33 | return 0; 34 | } 35 | 36 | --------------------------------------------------------------------------------