├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── Makefile ├── README.md ├── deps ├── CMakeLists.txt └── hiredis-0.14.0.tar.gz ├── r3c.cpp ├── r3c.h ├── r3c_helper.h ├── sha1.cpp ├── sha1.h ├── tests ├── CMakeLists.txt ├── Makefile ├── r3c_and_coroutine.cpp ├── r3c_cmd.cpp ├── r3c_robust.cpp ├── r3c_stream.cpp ├── r3c_stress.cpp ├── r3c_test.cpp ├── redis_command_extension.cpp └── redismodule.h ├── utils.cpp ├── utils.h └── xadd.lua /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .cproject 3 | .project 4 | .settings/language.settings.xml 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Writed by yijian (eyjian@qq.com) 2 | 3 | # 2.8.11 4 | cmake_minimum_required(VERSION 2.8.11) 5 | project(r3c) 6 | 7 | # 定义颜色值,message()时可用到 8 | if (NOT WIN32) 9 | string(ASCII 27 Esc) 10 | set(ColourReset "${Esc}[m") 11 | set(ColourBold "${Esc}[1m") 12 | set(Red "${Esc}[31m") 13 | set(Green "${Esc}[32m") 14 | set(Yellow "${Esc}[33m") 15 | set(Blue "${Esc}[34m") 16 | endif () 17 | 18 | set(CMAKE_VERBOSE_MAKEFILE ON) 19 | set(CMAKE_BUILD_TYPE "Debug") 20 | 21 | add_definitions(-DSLEEP_USE_POLL) 22 | #add_definitions(-DR3C_TEST) 23 | add_definitions(-D__STDC_FORMAT_MACROS=1) 24 | add_definitions(-D__STDC_CONSTANT_MACROS=1) 25 | #add_definitions(-O2) 26 | add_definitions(-g -ggdb -fPIC -Wall -W -Wwrite-strings -Wno-missing-field-initializers -fstrict-aliasing) 27 | 28 | include(CheckCXXCompilerFlag) 29 | # 如果编译环境支持C++20,则打开C++20开关 30 | CHECK_CXX_COMPILER_FLAG("-std=c++20" COMPILER_SUPPORTS_CXX20) 31 | if (COMPILER_SUPPORTS_CXX20) 32 | message("${Yellow}enable C++20${ColourReset}") 33 | add_definitions("-std=c++20") 34 | else () 35 | message("${Red}C++20 not supported${ColourReset}") 36 | 37 | # 如果编译环境支持C++17,则打开C++17开关 38 | CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17) 39 | if (COMPILER_SUPPORTS_CXX17) 40 | message("${Yellow}C++17 enabled${ColourReset}") 41 | add_definitions("-std=c++17") 42 | else () 43 | message("${Red}C++17 not supported${ColourReset}") 44 | 45 | # 如果编译环境支持C++14,则打开C++14开关 46 | CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) 47 | if (COMPILER_SUPPORTS_CXX14) 48 | message("${Yellow}C++14 enabled${ColourReset}") 49 | add_definitions("-std=c++14") 50 | else () 51 | message("${Red}C++14 not supported${ColourReset}") 52 | 53 | # 如果编译环境支持C++11,则打开C++11开关 54 | CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) 55 | if (COMPILER_SUPPORTS_CXX11) 56 | message("${Yellow}C++11 enabled${ColourReset}") 57 | #add_definitions("-std=c++11") 58 | add_compile_options("-std=c++11") 59 | else () 60 | message("${Red}C++11$ not supported{ColourReset}") 61 | endif() 62 | endif() 63 | endif() 64 | endif() 65 | 66 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 67 | link_directories(${CMAKE_CURRENT_SOURCE_DIR}) 68 | 69 | if (HIREDIS) 70 | set(HAVE_HIREDIS 1 CACHE INTERNAL HAVE_HIREDIS) 71 | include_directories(${HIREDIS}/include) 72 | link_directories(${HIREDIS}/lib) 73 | else () 74 | # hiredis.h 75 | find_path( 76 | HIREDIS_INCLUDE 77 | NAMES 78 | hiredis.h 79 | PATHS 80 | /usr/local/hiredis/include/hiredis 81 | /usr/include 82 | /usr/local/include/hiredis/ 83 | /usr/local/thirdparty/hiredis/include/hiredis 84 | ${CMAKE_INSTALL_PREFIX}/include/hiredis 85 | NO_DEFAULT_PATH 86 | ) 87 | # libhiredis.a 88 | find_path( 89 | HIREDIS_LIB 90 | NAMES 91 | libhiredis.a 92 | PATHS 93 | /usr/local/lib/ 94 | /usr/local/hiredis/lib 95 | /usr/lib 96 | /usr/local/thirdparty/hiredis/lib 97 | ${CMAKE_INSTALL_PREFIX}/lib 98 | NO_DEFAULT_PATH 99 | ) 100 | 101 | if ("${HIREDIS_INCLUDE}" STREQUAL "HIREDIS_INCLUDE-NOTFOUND" OR "${HIREDIS_LIB}" STREQUAL "HIREDIS_INCLUDE-NOTFOUND") 102 | message("${Red}`hiredis` not installed${ColourReset}") 103 | set(HAVE_HIREDIS 0 CACHE INTERNAL HAVE_HIREDIS) 104 | include_directories(${CMAKE_INSTALL_PREFIX}/include) 105 | link_directories(${CMAKE_INSTALL_PREFIX}/lib) 106 | else () 107 | message("${Yellow}Found `hiredis` at ${HIREDIS_LIB} [libhiredis.a] and ${HIREDIS_INCLUDE}/include [hiredis.h] ${ColourReset}") 108 | set(HAVE_HIREDIS 1 CACHE INTERNAL HAVE_HIREDIS) 109 | include_directories(${HIREDIS_INCLUDE}/..) 110 | link_directories(${HIREDIS_LIB}) 111 | endif () 112 | endif () 113 | 114 | # deps 115 | if (NOT HAVE_HIREDIS) 116 | add_subdirectory(deps) 117 | endif () 118 | 119 | # libr3c.a 120 | add_library( 121 | r3c 122 | STATIC 123 | r3c.cpp 124 | utils.cpp 125 | sha1.cpp 126 | ) 127 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 128 | link_directories(${CMAKE_CURRENT_SOURCE_DIR}) 129 | 130 | # tests 131 | add_subdirectory(tests) 132 | 133 | # 安装(-DCMAKE_INSTALL_PREFIX) 134 | install( 135 | TARGETS 136 | r3c 137 | DESTINATION lib 138 | ) 139 | install( 140 | FILES r3c.h r3c_helper.h 141 | DESTINATION include/r3c 142 | ) 143 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner]. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Writed by yijian (eyjian@gmail.com) 2 | # Redis Cluster Client Makefile 3 | # 4 | # Dependencies are stored in the Makefile.dep file. To rebuild this file 5 | # Just use 'make dep > Makefile.dep', but this is only needed by developers. 6 | 7 | LIBNAME=libr3c 8 | CMD=tests/r3c_cmd 9 | TEST=tests/r3c_test 10 | #STRESS=tests/r3c_stress 11 | ROBUST=tests/r3c_robust 12 | STREAM=tests/r3c_stream 13 | EXTENSION=tests/redis_command_extension.so 14 | 15 | HIREDIS?=/usr/local/hiredis 16 | PREFIX?=/usr/local/r3c 17 | INCLUDE_PATH?=include/r3c 18 | LIBRARY_PATH?=lib 19 | 20 | # redis-cluster configuration used for testing 21 | REDIS_CLUSTER_NODES?=192.168.1.31:6379,192.168.1.31:6380 22 | 23 | INSTALL_BIN=$(PREFIX)/bin 24 | INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH) 25 | INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH) 26 | INSTALL?=install 27 | 28 | # 9.1 => C++20 29 | ifeq ("$(CPLUSPLUS)","") 30 | CPLUSPLUS=$(shell gcc --version|awk -F[\ .]+ '/GCC/{if($$3>=9&&$$4>=1) printf(" -std=c++20");}') 31 | endif 32 | 33 | # 5.0 => C++17 34 | ifeq ("$(CPLUSPLUS)","") 35 | CPLUSPLUS=$(shell gcc --version|awk -F[\ .]+ '/GCC/{if($$3>=5&&$$4>=0) printf(" -std=c++17");}') 36 | endif 37 | 38 | # 4.9 => C++14 39 | ifeq ("$(CPLUSPLUS)","") 40 | CPLUSPLUS=$(shell gcc --version|awk -F[\ .]+ '/GCC/{if($$3>=4&&$$4>=9) printf(" -std=c++14");}') 41 | endif 42 | 43 | # 4.8.1 => C++11 44 | ifeq ("$(CPLUSPLUS)","") 45 | CPLUSPLUS=$(shell gcc --version|awk -F[\ .]+ '/GCC/{if($$3>=4&&$$4>=8&&$$5>=1) printf(" -std=c++11");}') 46 | endif 47 | 48 | #OPTIMIZATION?=-O2 49 | DEBUG?=-g -ggdb $(CPLUSPLUS) -DSLEEP_USE_POLL # -DR3C_TEST 50 | WARNINGS=-Wall -W -Wwrite-strings -Wno-missing-field-initializers 51 | REAL_CPPFLAGS=$(CPPFLAGS) -I. -I$(HIREDIS)/include -DSLEEP_USE_POLL=1 -D__STDC_FORMAT_MACROS=1 -D__STDC_CONSTANT_MACROS -fstrict-aliasing -fPIC -pthread $(DEBUG) $(OPTIMIZATION) $(WARNINGS) 52 | REAL_LDFLAGS=$(LDFLAGS) -fPIC -pthread $(HIREDIS)/lib/libhiredis.a 53 | 54 | CXX:=$(shell sh -c 'type $(CXX) >/dev/null 2>/dev/null && echo $(CXX) || echo g++') 55 | STLIBSUFFIX=a 56 | STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) 57 | STLIB_MAKE_CMD=ar rcs 58 | 59 | all: $(HIREDIS) $(STLIBNAME) $(CMD) $(TEST) $(STRESS) $(ROBUST) $(STREAM) $(EXTENSION) 60 | 61 | # Deps (use make dep to generate this) 62 | sha1.o: sha1.cpp 63 | utils.o: utils.h utils.cpp 64 | r3c.o: r3c.cpp r3c.h r3c.cpp utils.h utils.cpp 65 | tests/r3c_cmd.o: tests/r3c_cmd.cpp r3c.h r3c.cpp utils.h utils.cpp 66 | tests/r3c_test.o: tests/r3c_test.cpp r3c.h r3c.cpp utils.h utils.cpp 67 | tests/r3c_stress.o: tests/r3c_stress.cpp r3c.h r3c.cpp utils.cpp 68 | tests/r3c_robust.o: tests/r3c_robust.cpp r3c.h r3c.cpp utils.h utils.cpp 69 | tests/r3c_stream.o: tests/r3c_stream.cpp r3c.h r3c.cpp utils.h utils.cpp 70 | tests/redis_command_extension.o: tests/redis_command_extension.cpp r3c.h r3c.cpp utils.h utils.cpp 71 | 72 | sha1.o: sha1.cpp 73 | $(CXX) -o $@ -c $< $(REAL_CPPFLAGS) 74 | utils.o: utils.cpp 75 | $(CXX) -o $@ -c $< $(REAL_CPPFLAGS) 76 | r3c.o: r3c.cpp 77 | $(CXX) -o $@ -c $< $(REAL_CPPFLAGS) 78 | tests/r3c_cmd.o: tests/r3c_cmd.cpp 79 | $(CXX) -o $@ -c $< $(REAL_CPPFLAGS) 80 | tests/r3c_test.o: tests/r3c_test.cpp 81 | $(CXX) -o $@ -c $< $(REAL_CPPFLAGS) 82 | tests/r3c_stress.o: tests/r3c_stress.cpp 83 | $(CXX) -o $@ -c $< $(REAL_CPPFLAGS) 84 | tests/r3c_robust.o: tests/r3c_robust.cpp 85 | $(CXX) -o $@ -c $< $(REAL_CPPFLAGS) 86 | tests/r3c_stream.o: tests/r3c_stream.cpp 87 | $(CXX) -o $@ -c $< $(REAL_CPPFLAGS) 88 | tests/redis_command_extension.o: tests/redis_command_extension.cpp 89 | $(CXX) -o $@ -c $< $(REAL_CPPFLAGS) 90 | 91 | $(HIREDIS): 92 | @if test -d "$(HIREDIS)"; then \ 93 | echo -e "\033[1;33mFound hiredis at $(HIREDIS)"; \ 94 | true; \ 95 | else \ 96 | echo -e "\033[0;32;31mNot found hiredis at $(HIREDIS)\033[m"; \ 97 | echo -e "\033[1;33mUsage: make HIREDIS=hiredis-install-directory\033[m"; \ 98 | echo -e "\033[0;36mExample1: make HIREDIS=/usr/local/hiredis\033[m"; \ 99 | echo -e "\033[0;36mExample2: make install PREFIX=/usr/local/r3c\033[m"; \ 100 | echo -e "\033[0;36mExample3: make install HIREDIS=/usr/local/hiredis PREFIX=/usr/local/r3c\033[m"; \ 101 | false; \ 102 | fi 103 | 104 | $(STLIBNAME): sha1.o utils.o r3c.o 105 | rm -f $@;$(STLIB_MAKE_CMD) $@ $^ 106 | 107 | $(CMD): tests/r3c_cmd.o $(STLIBNAME) 108 | $(CXX) -o $@ $^ $(REAL_LDFLAGS) 109 | 110 | $(TEST): tests/r3c_test.o $(STLIBNAME) 111 | $(CXX) -o $@ $^ $(REAL_LDFLAGS) 112 | 113 | #$(STRESS): tests/r3c_stress.o $(STLIBNAME) 114 | # $(CXX) -o $@ $^ $(REAL_LDFLAGS) 115 | 116 | $(ROBUST): tests/r3c_robust.o $(STLIBNAME) 117 | $(CXX) -o $@ $^ $(REAL_LDFLAGS) -pthread 118 | 119 | $(STREAM): tests/r3c_stream.o $(STLIBNAME) 120 | $(CXX) -o $@ $^ $(REAL_LDFLAGS) -pthread 121 | 122 | $(EXTENSION): tests/redis_command_extension.o $(STLIBNAME) 123 | $(CXX) -o $@ -shared $^ $(REAL_LDFLAGS) 124 | 125 | clean: 126 | rm -f $(STLIBNAME) $(CMD) $(TEST) $(STRESS) $(ROBUST) $(STREAM) $(EXTENSION) *.o core core.* tests/*.o tests/core tests/core.* 127 | .PHONY: clean 128 | 129 | install: $(STLIBNAME) 130 | $(INSTALL) -d $(INSTALL_INCLUDE_PATH) 131 | $(INSTALL) -d $(INSTALL_LIBRARY_PATH) 132 | $(INSTALL) -m 664 r3c.h r3c_helper.h $(INSTALL_INCLUDE_PATH) 133 | $(INSTALL) -m 664 $(STLIBNAME) $(INSTALL_LIBRARY_PATH) 134 | 135 | dep: 136 | $(CXX) -MM *.cpp 137 | .PHONY: dep 138 | 139 | test: $(TEST) 140 | $(TEST) $(REDIS_CLUSTER_NODES) 141 | .PHONY: test 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Redis Cluster C++ Client, based on hiredis, support password and standalone, it's easy to make and use, not depends on C++11 or later. 2 | r3c::CRedisClient is not thread safe, you can use __thread to create a object of r3c::CRedisClient for every thread. 3 | 4 | r3c基于redis官方的c库hiredis实现,全称是redis cluster C++ client,支持redis cluster,支持密码访问。 5 | 非线程安全,GCC环境可以使用__thread为每个线程创建一个r3c::CRedisClient实例。 6 | 支持多种策略的从读,支持Redis-5.0新增的Stream操作。不支持异步,但可结合协程实现异步访问,可参照示例r3c_and_coroutine.cpp。 7 | 8 | 编译链接r3c时,默认认为hiredis的安装目录为/usr/local/hiredis, 9 | 但可以在执行make时指定hiredis安装目录,如假设hiredis安装目录为/tmp/hiredis:make HIREDIS=/tmp/hiredis, 10 | 或修改Makefile中变量HIREDIS的值来指定hiredis实现的安装目录。 11 | 12 | 编译r3c成功后,将生成libr3c.a静态库,没有共享库被生成。 13 | 也可以直接将r3c.h、r3c.cpp、utils.h、utils.cpp、sha1.h和sha1.cpp几个文件加入到自己项目代码中一起编译,而不独立编译r3c。 14 | 15 | --- 16 | 17 | r3c_cmd.cpp是r3c的非交互式命令行工具(command line tool),具备redis-cli的一些功能,但用法不尽相同,将逐步将覆盖redis-cli的所有功能。 18 | r3c_test.cpp是r3c的单元测试程序(unit test),执行make test即可。 19 | r3c_and_coroutine.cpp 在协程中使用r3c示例(异步) 20 | 21 | --- 22 | 23 | 支持两种编译和安装方式(make&cmake):
24 | **1) make**
25 | 编译:
26 | make
27 | 28 | 安装(PREFIX指定安装目录,如果不指定则为/usr/local):
29 | make install
30 | 或
31 | make install PREFIX=/usr/local/r3c
32 | 33 | 执行单元测试:
34 | make test
35 | 或
36 | make test REDIS_CLUSTER_NODES=192.168.1.31:6379,192.168.1.31:6380
37 | 38 | **2) cmake**
39 | 生成Makefile文件:
40 | cmake -DCMAKE_INSTALL_PREFIX=install-directory .
41 | 示例:
42 | cmake -DCMAKE_INSTALL_PREFIX=/usr/local/r3c .
43 | 44 | 编译:
45 | make
46 | 47 | 安装:
48 | make install
49 | 50 | --- 51 | 52 | 关于接口:
53 | 如果传给CRedisClient的nodes参数为单个节点字符串,如192.168.1.31:6379则为单机模式,为多节点字符串时则为Redis Cluster模式。 54 | 55 | --- 56 | 57 | 性能测试工具:
58 | https://github.com/eyjian/libmooon/blob/master/tools/r3c_stress.cpp 59 | 60 | 单机性能数据:
61 | r3c_stress --redis=192.168.0.88:6379 --requests=100000 --threads=20 62 | set: 63 | microseconds=18867143, milliseconds=18867, seconds=18 64 | total: 2000000, success: 2000000, failure: 0 65 | qps: 111111 66 | 67 | get: 68 | microseconds=16063882, milliseconds=16063, seconds=16 69 | total: 2000000, success: 2000000, failure: 0, not exists: 0 70 | qps: 125000 71 | 72 | hset: 73 | microseconds=16134011, milliseconds=16134, seconds=16 74 | total: 1999992, success: 1999992, failure: 0 75 | qps: 124999 76 | 77 | hget: 78 | microseconds=15249201, milliseconds=15249, seconds=15 79 | total: 2000000, success: 2000000, failure: 0, not exists: 0 80 | qps: 133333 81 | -------------------------------------------------------------------------------- /deps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Writed by yijian (eyjian@qq.com) 2 | 3 | set (HIREDIS hiredis-0.14.0) 4 | 5 | exec_program( 6 | tar 7 | ${CMAKE_CURRENT_SOURCE_DIR} 8 | ARGS 9 | xzf ${HIREDIS}.tar.gz 10 | RETURN_VALUE errcode 11 | ) 12 | if (errcode) 13 | return () 14 | endif () 15 | 16 | exec_program( 17 | make 18 | ${CMAKE_CURRENT_SOURCE_DIR}/${HIREDIS} 19 | ARGS 20 | install PREFIX=${CMAKE_INSTALL_PREFIX} 21 | RETURN_VALUE errcode 22 | ) 23 | if (errcode) 24 | return () 25 | endif () 26 | -------------------------------------------------------------------------------- /deps/hiredis-0.14.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eyjian/r3c/7a271cf08c06fdd641cff246e23fb5e4f9967901/deps/hiredis-0.14.0.tar.gz -------------------------------------------------------------------------------- /r3c_helper.h: -------------------------------------------------------------------------------- 1 | // Writed by yijian (eyjian@qq.com) 2 | #include 3 | #include 4 | #include 5 | namespace r3c { 6 | 7 | // Batch to lpop 8 | inline int lpop( 9 | CRedisClient* redis, const std::string& key, 10 | std::vector* values, int n, 11 | Node* which=NULL, int num_retries=0) 12 | { 13 | values->clear(); 14 | 15 | // LRANGE key start stop 16 | // Offsets start and stop are zero-based indexes, 17 | // with 0 being the first element of the list (the head of the list), 18 | // 1 being the next element and so on. 19 | // 20 | // LTRIM key start stop 21 | // Both start and stop are zero-based indexes, 22 | // where 0 is the first element of the list (the head), 23 | // 1 the next element and so on. 24 | const std::string lua_scripts = 25 | "local v=redis.call('LRANGE',KEYS[1],0,ARGV[1]-1);" 26 | "redis.call('LTRIM',KEYS[1],ARGV[1],-1);" 27 | "return v;"; 28 | std::vector parameters(1); 29 | if (n > 0) 30 | parameters[0] = int2string(n); 31 | else 32 | parameters[0] = "1"; 33 | const RedisReplyHelper redis_reply = redis->eval(key, lua_scripts, parameters, which, num_retries); 34 | if (redis_reply->type == REDIS_REPLY_ARRAY) 35 | return CRedisClient::get_values(redis_reply.get(), values); 36 | return 0; 37 | } 38 | 39 | inline bool hsetex( 40 | CRedisClient* redis, const std::string& key, 41 | const std::string& field, const std::string& value, uint32_t expired_seconds, 42 | Node* which=NULL, int num_retries=0) 43 | { 44 | const std::string lua_scripts = 45 | "local n;n=redis.call('HSET',KEYS[1],ARGV[1],ARGV[2]);" 46 | "if (n>0) then redis.call('EXPIRE',KEYS[1],ARGV[3]) end;return n;"; 47 | std::vector parameters(3); 48 | parameters[0] = field; 49 | parameters[1] = value; 50 | parameters[2] = int2string(expired_seconds); 51 | const RedisReplyHelper redis_reply = redis->eval(key, lua_scripts, parameters, which, num_retries); 52 | if (REDIS_REPLY_INTEGER == redis_reply->type) 53 | return 1 == redis_reply->integer; 54 | return true; 55 | } 56 | 57 | inline bool hsetnxex( 58 | CRedisClient* redis, const std::string& key, 59 | const std::string& field, const std::string& value, uint32_t expired_seconds, 60 | Node* which=NULL, int num_retries=0) 61 | { 62 | const std::string lua_scripts = 63 | "local n=redis.call('HLEN',KEYS[1]);" 64 | "local m=redis.call('HSETNX',KEYS[1],ARGV[1],ARGV[2]);" 65 | "if(n==0) then redis.call('EXPIRE',KEYS[1],ARGV[3]) end;return m;"; 66 | std::vector parameters(3); 67 | parameters[0] = field; 68 | parameters[1] = value; 69 | parameters[2] = int2string(expired_seconds); 70 | const RedisReplyHelper redis_reply = redis->eval(key, lua_scripts, parameters, which, num_retries); 71 | if (REDIS_REPLY_INTEGER == redis_reply->type) 72 | return 1 == redis_reply->integer; 73 | return true; 74 | } 75 | 76 | inline void hmincrby( 77 | CRedisClient* redis, const std::string& key, 78 | const std::vector >& increments, std::vector* newvalues, 79 | Node* which=NULL, int num_retries=0) 80 | { 81 | const std::string lua_scripts = 82 | "local j=1;local results={};" 83 | "for i=1,#ARGV,2 do local f=ARGV[i];" 84 | "local v=ARGV[i+1];" 85 | "results[j]=redis.call('HINCRBY',KEYS[1],f,v);j=j+1; end;" 86 | "return results;"; 87 | std::vector parameters(2*increments.size()); 88 | for (std::vector >::size_type i=0,j=0; i& increment = increments[i]; 91 | parameters[j] = increment.first; 92 | parameters[j+1] = int2string(increment.second); 93 | } 94 | const RedisReplyHelper redis_reply = redis->eval(key, lua_scripts, parameters, which, num_retries); 95 | if (REDIS_REPLY_ARRAY == redis_reply->type) 96 | CRedisClient::get_values(redis_reply.get(), newvalues); 97 | } 98 | 99 | inline void xadd( 100 | CRedisClient* redis, const std::string& key, 101 | int32_t maxlen, int32_t count, 102 | const std::vector& fvpairs, std::vector* values, 103 | Node* which=NULL, int num_retries=0) 104 | { 105 | static std::string xadd_lua_script = 106 | "local key=KEYS[1];" 107 | "local maxlen=ARGV[1];" 108 | "local count=ARGV[2];" 109 | "for i=3,#ARGV,2 do" 110 | " local field=ARGV[i];" 111 | " local value=ARGV[i+1];" 112 | " redis.call('XADD',key,'MAXLEN','~',maxlen,'*',field,value);" 113 | "end;" 114 | "if tonumber(count)>0 then" 115 | " return redis.call('XRANGE',key,'-','+','COUNT',count);" 116 | "end;" 117 | "return nil;"; 118 | std::vector parameters; 119 | 120 | parameters.push_back(int2string(maxlen)); 121 | parameters.push_back(int2string(count)); 122 | for (auto& fvpair: fvpairs) 123 | { 124 | parameters.push_back(fvpair.field); 125 | parameters.push_back(fvpair.value); 126 | } 127 | const RedisReplyHelper redis_reply = redis->eval(key, xadd_lua_script, parameters, which, num_retries); 128 | if (redis_reply->type != REDIS_REPLY_NIL && values != NULL) 129 | CRedisClient::get_values(redis_reply.get(), values); 130 | } 131 | 132 | } // namespace r3c { 133 | -------------------------------------------------------------------------------- /sha1.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* from valgrind tests */ 3 | 4 | /* ================ sha1.c ================ */ 5 | /* 6 | SHA-1 in C 7 | By Steve Reid 8 | 100% Public Domain 9 | 10 | Test Vectors (from FIPS PUB 180-1) 11 | "abc" 12 | A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D 13 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 14 | 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 15 | A million repetitions of "a" 16 | 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F 17 | */ 18 | 19 | /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ 20 | /* #define SHA1HANDSOFF * Copies data before messing with it. */ 21 | 22 | #define SHA1HANDSOFF 23 | 24 | #include 25 | #include 26 | #include 27 | #include "sha1.h" 28 | 29 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 30 | 31 | /* blk0() and blk() perform the initial expand. */ 32 | /* I got the idea of expanding during the round function from SSLeay */ 33 | #if BYTE_ORDER == LITTLE_ENDIAN 34 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ 35 | |(rol(block->l[i],8)&0x00FF00FF)) 36 | #elif BYTE_ORDER == BIG_ENDIAN 37 | #define blk0(i) block->l[i] 38 | #else 39 | #error "Endianness not defined!" 40 | #endif 41 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 42 | ^block->l[(i+2)&15]^block->l[i&15],1)) 43 | 44 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 45 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 46 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 47 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 48 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 49 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 50 | 51 | 52 | /* Hash a single 512-bit block. This is the core of the algorithm. */ 53 | 54 | void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) 55 | { 56 | uint32_t a, b, c, d, e; 57 | typedef union { 58 | unsigned char c[64]; 59 | uint32_t l[16]; 60 | } CHAR64LONG16; 61 | #ifdef SHA1HANDSOFF 62 | CHAR64LONG16 block[1]; /* use array to appear as a pointer */ 63 | memcpy(block, buffer, 64); 64 | #else 65 | /* The following had better never be used because it causes the 66 | * pointer-to-const buffer to be cast into a pointer to non-const. 67 | * And the result is written through. I threw a "const" in, hoping 68 | * this will cause a diagnostic. 69 | */ 70 | CHAR64LONG16* block = (const CHAR64LONG16*)buffer; 71 | #endif 72 | /* Copy context->state[] to working vars */ 73 | a = state[0]; 74 | b = state[1]; 75 | c = state[2]; 76 | d = state[3]; 77 | e = state[4]; 78 | /* 4 rounds of 20 operations each. Loop unrolled. */ 79 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); 80 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); 81 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); 82 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); 83 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); 84 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); 85 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); 86 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); 87 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); 88 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); 89 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); 90 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); 91 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); 92 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); 93 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); 94 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); 95 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); 96 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); 97 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); 98 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); 99 | /* Add the working vars back into context.state[] */ 100 | state[0] += a; 101 | state[1] += b; 102 | state[2] += c; 103 | state[3] += d; 104 | state[4] += e; 105 | /* Wipe variables */ 106 | a = b = c = d = e = 0; 107 | #ifdef SHA1HANDSOFF 108 | memset(block, '\0', sizeof(block)); 109 | #endif 110 | } 111 | 112 | 113 | /* SHA1Init - Initialize new context */ 114 | 115 | void SHA1Init(SHA1_CTX* context) 116 | { 117 | /* SHA1 initialization constants */ 118 | context->state[0] = 0x67452301; 119 | context->state[1] = 0xEFCDAB89; 120 | context->state[2] = 0x98BADCFE; 121 | context->state[3] = 0x10325476; 122 | context->state[4] = 0xC3D2E1F0; 123 | context->count[0] = context->count[1] = 0; 124 | } 125 | 126 | 127 | /* Run your data through this. */ 128 | 129 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len) 130 | { 131 | uint32_t i, j; 132 | 133 | j = context->count[0]; 134 | if ((context->count[0] += len << 3) < j) 135 | context->count[1]++; 136 | context->count[1] += (len>>29); 137 | j = (j >> 3) & 63; 138 | if ((j + len) > 63) { 139 | memcpy(&context->buffer[j], data, (i = 64-j)); 140 | SHA1Transform(context->state, context->buffer); 141 | for ( ; i + 63 < len; i += 64) { 142 | SHA1Transform(context->state, &data[i]); 143 | } 144 | j = 0; 145 | } 146 | else i = 0; 147 | memcpy(&context->buffer[j], &data[i], len - i); 148 | } 149 | 150 | 151 | /* Add padding and return the message digest. */ 152 | 153 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context) 154 | { 155 | unsigned i; 156 | unsigned char finalcount[8]; 157 | unsigned char c; 158 | 159 | #if 0 /* untested "improvement" by DHR */ 160 | /* Convert context->count to a sequence of bytes 161 | * in finalcount. Second element first, but 162 | * big-endian order within element. 163 | * But we do it all backwards. 164 | */ 165 | unsigned char *fcp = &finalcount[8]; 166 | 167 | for (i = 0; i < 2; i++) 168 | { 169 | uint32_t t = context->count[i]; 170 | int j; 171 | 172 | for (j = 0; j < 4; t >>= 8, j++) 173 | *--fcp = (unsigned char) t; 174 | } 175 | #else 176 | for (i = 0; i < 8; i++) { 177 | finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] 178 | >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ 179 | } 180 | #endif 181 | c = 0200; 182 | SHA1Update(context, &c, 1); 183 | while ((context->count[0] & 504) != 448) { 184 | c = 0000; 185 | SHA1Update(context, &c, 1); 186 | } 187 | SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ 188 | for (i = 0; i < 20; i++) { 189 | digest[i] = (unsigned char) 190 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); 191 | } 192 | /* Wipe variables */ 193 | memset(context, '\0', sizeof(*context)); 194 | memset(&finalcount, '\0', sizeof(finalcount)); 195 | } 196 | /* ================ end of sha1.c ================ */ 197 | 198 | #ifdef REDIS_TEST 199 | #define BUFSIZE 4096 200 | 201 | #define UNUSED(x) (void)(x) 202 | int sha1Test(int argc, char **argv) 203 | { 204 | SHA1_CTX ctx; 205 | unsigned char hash[20], buf[BUFSIZE]; 206 | int i; 207 | 208 | UNUSED(argc); 209 | UNUSED(argv); 210 | 211 | for(i=0;i 7 | 100% Public Domain 8 | */ 9 | 10 | typedef struct { 11 | uint32_t state[5]; 12 | uint32_t count[2]; 13 | unsigned char buffer[64]; 14 | } SHA1_CTX; 15 | 16 | void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); 17 | void SHA1Init(SHA1_CTX* context); 18 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); 19 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context); 20 | 21 | #ifdef REDIS_TEST 22 | int sha1Test(int argc, char **argv); 23 | #endif 24 | #endif 25 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Writed by yijian (eyjian@gmail.com) 2 | 3 | # r3c_cmd 4 | add_executable( 5 | r3c_cmd 6 | r3c_cmd.cpp 7 | ) 8 | target_link_libraries( 9 | r3c_cmd 10 | libr3c.a 11 | libhiredis.a 12 | ) 13 | 14 | # r3c_test 15 | add_executable( 16 | r3c_test 17 | r3c_test.cpp 18 | ) 19 | target_link_libraries( 20 | r3c_test 21 | libr3c.a 22 | libhiredis.a 23 | ) 24 | 25 | # r3c_robust 26 | add_executable( 27 | r3c_robust 28 | r3c_robust.cpp 29 | ) 30 | target_link_libraries( 31 | r3c_robust 32 | libr3c.a 33 | libhiredis.a 34 | ) 35 | 36 | # r3c_stress 37 | #add_executable( 38 | # r3c_stress 39 | # r3c_stress.cpp 40 | #) 41 | #target_link_libraries( 42 | # r3c_stress 43 | # libr3c.a 44 | # libhiredis.a 45 | #) 46 | 47 | # r3c_stream 48 | add_executable( 49 | r3c_stream 50 | r3c_stream.cpp 51 | ) 52 | target_link_libraries( 53 | r3c_stream 54 | libr3c.a 55 | libhiredis.a 56 | ) 57 | 58 | # redis_command_extension 59 | add_library( 60 | redis_command_extension 61 | SHARED 62 | redis_command_extension.cpp 63 | ) 64 | target_link_libraries( 65 | redis_command_extension 66 | libr3c.a 67 | libhiredis.a 68 | ) 69 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | # Writed by yijian (eyjian@gmail.com) 2 | 3 | default: all 4 | 5 | .DEFAULT: 6 | cd ..&&$(MAKE) $@ 7 | -------------------------------------------------------------------------------- /tests/r3c_and_coroutine.cpp: -------------------------------------------------------------------------------- 1 | // Writed by yijian (eyjian@qq.com) 2 | // 3 | // Compile example: 4 | // g++ -g -o r3c_and_coroutine r3c_and_coroutine.cpp -I/usr/local/libco/include -I/usr/local/r3c/include -I/usr/local/hiredis/include /usr/local/r3c/lib/libr3c.a /usr/local/hiredis/lib/libhiredis.a /usr/local/libco/lib/libcolib.a -ldl -pthread 5 | // 6 | // Run example (Standlone redis): 7 | // r3c_and_coroutine 3 127.0.0.1:6379 8 | // 9 | // Run example (Redis cluster): 10 | // r3c_and_coroutine 3 127.0.0.1:6379,127.0.0.1:6380 11 | #include // a coroutine library powered by tencent (https://github.com/Tencent/libco) 12 | #include 13 | #include 14 | #include "r3c.h" 15 | 16 | #define NUM_CYCLES 3 17 | 18 | static int sg_num_coroutines; 19 | static std::string sg_redis_nodes; 20 | 21 | static void* redis_routine(void* param) 22 | { 23 | co_enable_hook_sys(); 24 | 25 | try 26 | { 27 | const int i = *(int*)param; 28 | const int connect_timeout_milliseconds = 1000; 29 | const int data_timeout_milliseconds = 1000; 30 | const int retry_sleep_milliseconds = 1000; 31 | r3c::CRedisClient redis(sg_redis_nodes, connect_timeout_milliseconds, data_timeout_milliseconds, retry_sleep_milliseconds); 32 | 33 | for (int j=0; j %s\n", key.c_str(), value.c_str()); 39 | } 40 | } 41 | catch (r3c::CRedisException& ex) 42 | { 43 | fprintf(stderr, "Subcoroutine: %s\n", ex.str().c_str()); 44 | } 45 | 46 | --sg_num_coroutines; 47 | return NULL; 48 | } 49 | 50 | static int break_co_eventloop(void*) 51 | { 52 | return (0 == sg_num_coroutines)? -1: 0; 53 | } 54 | 55 | int main(int argc, char* argv[]) 56 | { 57 | if (argc != 3) 58 | { 59 | fprintf(stderr, "Usage: %s num_coroutines redis_nodes\n", argv[0]); 60 | fprintf(stderr, "Example: %s 3 127.0.0.1:6379\n", argv[0]); 61 | exit(1); 62 | } 63 | else 64 | { 65 | const int num_coroutines = atoi(argv[1]); 66 | sg_num_coroutines = num_coroutines; 67 | sg_redis_nodes = argv[2]; 68 | 69 | for (int i=0; i 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static void my_log_write(const char* format, ...) 11 | { 12 | va_list ap; 13 | va_start(ap, format); 14 | vprintf(format, ap); 15 | va_end(ap); 16 | } 17 | 18 | int main(int argc, char* argv[]) 19 | { 20 | if (argc < 2) 21 | { 22 | fprintf(stderr, "Usage: r3c_cmd command parameter ...\n"); 23 | exit(1); 24 | } 25 | 26 | r3c::set_info_log_write(my_log_write); 27 | r3c::set_debug_log_write(my_log_write); 28 | 29 | try 30 | { 31 | const char* cmd = argv[1]; 32 | const char* key = argv[2]; 33 | const char* nodes = getenv("H"); 34 | if (NULL == nodes) 35 | { 36 | fprintf(stderr, "Environment[H] not set, example: export H=127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381\n"); 37 | exit(1); 38 | } 39 | if ('\0' == *nodes) 40 | { 41 | fprintf(stderr, "Environment[H] without value, example: export H=127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381\n"); 42 | exit(1); 43 | } 44 | const char* colon = strchr(nodes, ':'); 45 | if (NULL == colon) 46 | { 47 | fprintf(stderr, "Environment[H] with error value, example: export H=127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381\n"); 48 | exit(1); 49 | } 50 | 51 | int i = 0; 52 | int ret = 0; 53 | int offset = 0; 54 | int count = 0; 55 | int end = 0; 56 | int start = 0; 57 | int64_t ret64 = 0; 58 | int64_t cursor = 0; 59 | int64_t min; 60 | int64_t max; 61 | int64_t increment; 62 | int64_t new_value_int64; 63 | int64_t result_int64; 64 | uint32_t seconds; 65 | std::string str; 66 | std::string value; 67 | std::vector fields; 68 | std::vector values; 69 | std::vector > values2; 70 | std::map map; 71 | std::map::iterator iter; 72 | std::vector vec; 73 | std::pair which_node; 74 | std::string password = (getenv("PASSWORD")!=NULL)? getenv("PASSWORD"): ""; 75 | r3c::CRedisClient redis_client(nodes, password); 76 | 77 | if (0 == strcasecmp(cmd, "sha1")) 78 | { 79 | if (argc != 3) 80 | { 81 | fprintf(stderr, "Usage: r3c_cmd sha1 string\n"); 82 | exit(1); 83 | } 84 | 85 | str = key; 86 | fprintf(stdout, "%s\n", r3c::strsha1(str).c_str()); 87 | } 88 | else if (0 == strcasecmp(cmd, "slot")) 89 | { 90 | if (argc != 3) 91 | { 92 | fprintf(stderr, "Usage: r3c_cmd slot key\n"); 93 | exit(1); 94 | } 95 | 96 | str = key; 97 | unsigned int slot = r3c::get_key_slot(&str); 98 | fprintf(stdout, "[%s] => %u\n", key, slot); 99 | } 100 | else if (0 == strcasecmp(cmd, "list")) 101 | { 102 | std::vector nodes_info; 103 | int num_nodes = redis_client.list_nodes(&nodes_info); 104 | 105 | fprintf(stdout, "number of nodes (from %s:%d): %d\n", which_node.first.c_str(), which_node.second, num_nodes); 106 | for (std::vector::size_type i=0; i parameters; 199 | for (int i=4; i parameters(argc-4); 213 | 214 | for (int i=4; i= 0) 242 | fprintf(stdout, "[%s] %d\n", key, ttl); 243 | else if (-1 == ttl) 244 | fprintf(stderr, "[%s] no associated expire\n", key); 245 | else if (-2 == ttl) 246 | fprintf(stderr, "[%s] not exist\n", key); 247 | else 248 | fprintf(stderr, "unknown error\n"); 249 | } 250 | else if (0 == strcasecmp(cmd, "set")) 251 | { 252 | // SET command 253 | if (argc != 4) 254 | { 255 | fprintf(stderr, "Usage: r3c_cmd set key value\n"); 256 | exit(1); 257 | } 258 | 259 | redis_client.set(key, argv[3], &which_node); 260 | } 261 | else if (0 == strcasecmp(cmd, "setnx")) 262 | { 263 | // SETNX command 264 | if (argc != 4) 265 | { 266 | fprintf(stderr, "Usage: r3c_cmd setnx key value\n"); 267 | exit(1); 268 | } 269 | 270 | if (redis_client.setnx(key, argv[3], &which_node)) 271 | fprintf(stdout, "[%s] ok\n", key); 272 | else 273 | fprintf(stderr, "[%s] exists\n", key); 274 | } 275 | else if (0 == strcasecmp(cmd, "setex")) 276 | { 277 | // SETEX command 278 | if (argc != 5) 279 | { 280 | fprintf(stderr, "Usage: r3c_cmd setex key seconds value\n"); 281 | exit(1); 282 | } 283 | 284 | seconds = atoi(argv[3]); 285 | redis_client.setex(key, argv[4], seconds, &which_node); 286 | fprintf(stdout, "[%s] ok\n", key); 287 | } 288 | else if (0 == strcasecmp(cmd, "setnxex")) 289 | { 290 | // SETNXEX command 291 | if (argc != 5) 292 | { 293 | fprintf(stderr, "Usage: r3c_cmd setnxex key seconds value\n"); 294 | exit(1); 295 | } 296 | 297 | seconds = atoi(argv[3]); 298 | if (redis_client.setnxex(key, argv[4], seconds, &which_node)) 299 | fprintf(stdout, "[%s] ok\n", key); 300 | else 301 | fprintf(stdout, "[%s] exists\n", key); 302 | } 303 | else if (0 == strcasecmp(cmd, "get")) 304 | { 305 | // GET command 306 | if (argc != 3) 307 | { 308 | fprintf(stderr, "Usage: r3c_cmd get key\n"); 309 | exit(1); 310 | } 311 | 312 | if (!redis_client.get(key, &value, &which_node)) 313 | { 314 | fprintf(stderr, "[%s] not exist\n", key); 315 | } 316 | else 317 | { 318 | fprintf(stdout, "[%s] => %s\n", key, value.c_str()); 319 | } 320 | } 321 | else if (0 == strcasecmp(cmd, "incrby")) 322 | { 323 | // INCRBY command 324 | if (argc != 4) 325 | { 326 | fprintf(stderr, "Usage1: r3c_cmd incrby key increment\n"); 327 | exit(1); 328 | } 329 | 330 | increment = atoll(argv[3]); 331 | new_value_int64 = redis_client.incrby(key, increment, &which_node); 332 | fprintf(stdout, "%" PRId64"\n", new_value_int64); 333 | } 334 | else if (0 == strcasecmp(cmd, "scan")) 335 | { 336 | // SCAN command 337 | if ((argc < 3) || (argc > 5)) 338 | { 339 | fprintf(stderr, "Usage1: r3c_cmd scan cursor\n"); 340 | fprintf(stderr, "Usage2: r3c_cmd scan cursor count\n"); 341 | fprintf(stderr, "Usage3: r3c_cmd scan cursor pattern\n"); 342 | fprintf(stderr, "Usage4: r3c_cmd scan cursor pattern count\n"); 343 | exit(1); 344 | } 345 | 346 | cursor = static_cast(atoll(argv[2])); 347 | if (3 == argc) 348 | { 349 | cursor = redis_client.scan(cursor, &values, &which_node); 350 | } 351 | else if (4 == argc) 352 | { 353 | count = atoi(argv[3]); 354 | if (count > 0) 355 | cursor = redis_client.scan(cursor, count, &values, &which_node); 356 | else 357 | cursor = redis_client.scan(cursor, argv[3], &values, &which_node); 358 | } 359 | else if (5 == argc) 360 | { 361 | cursor = redis_client.scan(cursor, argv[2], atoi(argv[3]), &values, &which_node); 362 | } 363 | 364 | fprintf(stdout, "cursor: %" PRId64", count: %d\n", cursor, static_cast(values.size())); 365 | for (i=0; i(values.size()); ++i) 366 | fprintf(stdout, "%s\n", values[i].c_str()); 367 | } 368 | //////////////////////////////////////////////////////////////////////////// 369 | // LIST 370 | else if (0 == strcasecmp(cmd, "llen")) 371 | { 372 | // LLEN command 373 | if (argc != 3) 374 | { 375 | fprintf(stderr, "Usage: r3c_cmd llen key\n"); 376 | exit(1); 377 | } 378 | 379 | result_int64 = redis_client.llen(key, &which_node); 380 | fprintf(stdout, "[%s] => %" PRId64"\n", key, result_int64); 381 | } 382 | else if (0 == strcasecmp(cmd, "lpop")) 383 | { 384 | // LPOP command 385 | if (argc != 3) 386 | { 387 | fprintf(stderr, "Usage: r3c_cmd lpop key\n"); 388 | exit(1); 389 | } 390 | 391 | if (redis_client.lpop(key, &value, &which_node)) 392 | fprintf(stdout, "[%s] => %s\n", key, value.c_str()); 393 | else 394 | fprintf(stdout, "empty\n"); 395 | } 396 | else if (0 == strcasecmp(cmd, "lpush")) 397 | { 398 | // LPUSH command 399 | if (argc < 4) 400 | { 401 | fprintf(stderr, "Usage: r3c_cmd lpush key value1 value2 ...\n"); 402 | exit(1); 403 | } 404 | 405 | values.clear(); 406 | for (i=3; i %d\n", key, count); 410 | } 411 | else if (0 == strcasecmp(cmd, "lrange")) 412 | { 413 | // LRANGE command 414 | if (argc != 5) 415 | { 416 | fprintf(stderr, "Usage: r3c_cmd lrange key start end\n"); 417 | exit(1); 418 | } 419 | 420 | end = atoi(argv[4]); 421 | start = atoi(argv[3]); 422 | count = redis_client.lrange(key, start, end, &values, &which_node); 423 | fprintf(stdout, "count: %d\n", count); 424 | for (i=0; i %d\n", key, count); 475 | } 476 | else if (0 == strcasecmp(cmd, "rpushx")) 477 | { 478 | // RPUSHX command 479 | if (argc != 4) 480 | { 481 | fprintf(stderr, "Usage: r3c_cmd rpushx key value\n"); 482 | exit(1); 483 | } 484 | 485 | count = redis_client.rpushx(key, argv[3], &which_node); 486 | fprintf(stdout, "[%s] => %d\n", key, count); 487 | } 488 | //////////////////////////////////////////////////////////////////////////// 489 | // HASH 490 | else if (0 == strcasecmp(cmd, "hdel")) 491 | { 492 | // HDEL command 493 | if (argc < 4) 494 | { 495 | fprintf(stderr, "Usage: r3c_cmd hdel key field1 field2 ...\n"); 496 | exit(1); 497 | } 498 | 499 | if (4 == argc) 500 | { 501 | if (redis_client.hdel(key, argv[3], &which_node)) 502 | fprintf(stdout, "[%s] deleted\n", key); 503 | else 504 | fprintf(stderr, "[%s] not exists\n", key); 505 | } 506 | else 507 | { 508 | std::vector fields; 509 | for (i=3; i 0) 513 | fprintf(stdout, "[%s] deleted: %d\n", key, count); 514 | else 515 | fprintf(stderr, "[%s] not exists\n", key); 516 | } 517 | } 518 | else if (0 == strcasecmp(cmd, "hexists")) 519 | { 520 | // HEXISTS command 521 | if (argc != 4) 522 | { 523 | fprintf(stderr, "Usage: r3c_cmd hexists key field\n"); 524 | exit(1); 525 | } 526 | 527 | if (redis_client.hexists(key, argv[3], &which_node)) 528 | fprintf(stdout, "[%s:%s] exist\n", key, argv[3]); 529 | else 530 | fprintf(stderr, "[%s:%s] not exist\n", key, argv[3]); 531 | } 532 | else if (0 == strcasecmp(cmd, "hlen")) 533 | { 534 | // HLEN command 535 | if (argc != 3) 536 | { 537 | fprintf(stderr, "Usage: r3c_cmd hlen key\n"); 538 | exit(1); 539 | } 540 | 541 | new_value_int64 = redis_client.hlen(key, &which_node); 542 | fprintf(stdout, "[%s] fields count: %" PRId64"\n", key, new_value_int64); 543 | } 544 | else if (0 == strcasecmp(cmd, "hset")) 545 | { 546 | // HSET command 547 | if (argc != 5) 548 | { 549 | fprintf(stderr, "Usage: r3c_cmd hset key field value\n"); 550 | exit(1); 551 | } 552 | 553 | redis_client.hset(key, argv[3], argv[4], &which_node); 554 | } 555 | else if (0 == strcasecmp(cmd, "hsetnx")) 556 | { 557 | // HSETNX command 558 | if (argc != 5) 559 | { 560 | fprintf(stderr, "Usage: r3c_cmd hsetnx key field value\n"); 561 | exit(1); 562 | } 563 | 564 | if (redis_client.hsetnx(key, argv[3], argv[4], &which_node)) 565 | fprintf(stdout, "[%s:%s] ok\n", key, argv[3]); 566 | else 567 | fprintf(stderr, "[%s:%s] exists\n", key, argv[3]); 568 | } 569 | else if (0 == strcasecmp(cmd, "hget")) 570 | { 571 | // HGET command 572 | if (argc != 4) 573 | { 574 | fprintf(stderr, "Usage: r3c_cmd hget key field\n"); 575 | exit(1); 576 | } 577 | 578 | if (!redis_client.hget(key, argv[3], &value, &which_node)) 579 | { 580 | fprintf(stdout, "[%s:%s] not exist\n", key, argv[3]); 581 | } 582 | else 583 | { 584 | fprintf(stdout, "[%s:%s] => %s\n", key, argv[3], value.c_str()); 585 | } 586 | } 587 | else if (0 == strcasecmp(cmd, "hincrby")) 588 | { 589 | // HINCRBY command 590 | if ((argc < 5) || (argc%2 != 1)) 591 | { 592 | fprintf(stderr, "Usage: r3c_cmd hincrby key field1 increment1\n"); 593 | exit(1); 594 | } 595 | increment = atoll(argv[4]); 596 | new_value_int64 = redis_client.hincrby(key, argv[3], increment, &which_node); 597 | fprintf(stdout, "%" PRId64"\n", new_value_int64); 598 | } 599 | else if (0 == strcasecmp(cmd, "hmset")) 600 | { 601 | // HMSET command 602 | if ((argc < 5) || (argc % 2 != 1)) 603 | { 604 | fprintf(stderr, "Usage: r3c_cmd hmset key field1 value1 field2 value2 ...\n"); 605 | exit(1); 606 | } 607 | 608 | for (i=3; i %s\n", iter->first.c_str(), iter->second.c_str()); 630 | } 631 | else if (0 == strcasecmp(cmd, "hgetall")) 632 | { 633 | // HGETALL command 634 | if (argc != 3) 635 | { 636 | fprintf(stderr, "Usage: r3c_cmd hgetall key\n"); 637 | exit(1); 638 | } 639 | 640 | count = redis_client.hgetall(key, &map, &which_node); 641 | if (0 == count) 642 | { 643 | fprintf(stderr, "[%s] not exists\n", key); 644 | } 645 | else 646 | { 647 | for (iter=map.begin(); iter!=map.end(); ++iter) 648 | fprintf(stdout, "%s => %s\n", iter->first.c_str(), iter->second.c_str()); 649 | } 650 | } 651 | else if (0 == strcasecmp(cmd, "hkeys")) 652 | { 653 | // HKEYS command 654 | if (argc != 3) 655 | { 656 | fprintf(stderr, "Usage: r3c_cmd hkeys key\n"); 657 | exit(1); 658 | } 659 | 660 | count = redis_client.hkeys(key, &fields, &which_node); 661 | if (0 == count) 662 | { 663 | fprintf(stderr, "[%s] not exists\n", key); 664 | } 665 | else 666 | { 667 | for (std::vector::size_type k=0; k::size_type k=0; k 6)) 695 | { 696 | fprintf(stderr, "Usage1: r3c_cmd hscan key cursor\n"); 697 | fprintf(stderr, "Usage2: r3c_cmd hscan key cursor count\n"); 698 | fprintf(stderr, "Usage3: r3c_cmd hscan key cursor pattern\n"); 699 | fprintf(stderr, "Usage4: r3c_cmd hscan key cursor pattern count\n"); 700 | exit(1); 701 | } 702 | 703 | if (4 == argc) 704 | cursor = redis_client.hscan(key, atoll(argv[3]), &map, &which_node); 705 | else if (6 == argc) 706 | cursor = redis_client.hscan(key, atoll(argv[3]), argv[4], atoi(argv[5]), &map, &which_node); 707 | else if (5 == argc) 708 | { 709 | count = atoi(argv[4]); 710 | if (count > 0) 711 | cursor = redis_client.hscan(key, atoll(argv[3]), count, &map, &which_node); 712 | else 713 | cursor = redis_client.hscan(key, atoll(argv[3]), argv[4], &map, &which_node); 714 | } 715 | 716 | fprintf(stdout, "cursor: %" PRId64", count: %d\n", cursor, static_cast(map.size())); 717 | for (iter=map.begin(); iter!=map.end(); ++iter) 718 | fprintf(stdout, "%s => %s\n", iter->first.c_str(), iter->second.c_str()); 719 | } 720 | //////////////////////////////////////////////////////////////////////////// 721 | // SET 722 | else if (0 == strcasecmp(cmd, "sadd")) 723 | { 724 | // SADD command 725 | if (argc < 4) 726 | { 727 | fprintf(stderr, "Usage: r3c_cmd sadd key value1 value2 ...\n"); 728 | exit(1); 729 | } 730 | 731 | if (4 == argc) 732 | { 733 | count = redis_client.sadd(key, argv[3], &which_node); 734 | } 735 | else 736 | { 737 | for (i=3; i 6)) 847 | { 848 | fprintf(stderr, "Usage1: r3c_cmd sscan key cursor\n"); 849 | fprintf(stderr, "Usage2: r3c_cmd sscan key cursor count\n"); 850 | fprintf(stderr, "Usage3: r3c_cmd sscan key cursor pattern\n"); 851 | fprintf(stderr, "Usage4: r3c_cmd sscan key cursor pattern count\n"); 852 | exit(1); 853 | } 854 | 855 | cursor = static_cast(atoll(argv[3])); 856 | if (4 == argc) 857 | { 858 | cursor = redis_client.sscan(key, cursor, &values, &which_node); 859 | } 860 | else if (5 == argc) 861 | { 862 | count = atoi(argv[4]); 863 | if (count > 0) 864 | cursor = redis_client.sscan(key, cursor, count, &values, &which_node); 865 | else 866 | cursor = redis_client.sscan(key, cursor, argv[4], &values, &which_node); 867 | } 868 | else if (6 == argc) 869 | { 870 | cursor = redis_client.sscan(key, cursor, argv[4], atoi(argv[5]), &values, &which_node); 871 | } 872 | 873 | fprintf(stdout, "cursor: %" PRId64", count: %d\n", cursor, static_cast(values.size())); 874 | for (i=0; i(values.size()); ++i) 875 | fprintf(stdout, "%s\n", values[i].c_str()); 876 | } 877 | //////////////////////////////////////////////////////////////////////////// 878 | // SORTED SET 879 | else if (0 == strcasecmp(cmd, "zrem")) 880 | { 881 | // ZREM command 882 | if (argc < 4) 883 | { 884 | fprintf(stderr, "Usage: r3c_cmd zrem key field1 score2 field2 ...\n"); 885 | exit(1); 886 | } 887 | 888 | for (i=3; i map; 910 | for (i=3; i > vec; 967 | ret = redis_client.zrange(key, start, end, true, &vec, &which_node); 968 | fprintf(stdout, "number: %d\n", ret); 969 | for (std::vector >::iterator iter=vec.begin(); iter!=vec.end(); ++iter,++i) 970 | fprintf(stdout, "[%d]%s => %" PRId64"\n", i, iter->first.c_str(), iter->second); 971 | } 972 | else if (0 == strcasecmp(cmd, "zrevrange")) 973 | { 974 | // ZREVRANGE command 975 | if (argc != 5) 976 | { 977 | fprintf(stderr, "Usage: r3c_cmd zrevrange key start end\n"); 978 | exit(1); 979 | } 980 | 981 | i = 0; 982 | start = atoi(argv[3]); 983 | end = atoi(argv[4]); 984 | std::vector > vec; 985 | ret = redis_client.zrevrange(key, start, end, true, &vec, &which_node); 986 | fprintf(stdout, "number: %d\n", ret); 987 | for (std::vector >::iterator iter=vec.begin(); iter!=vec.end(); ++iter,++i) 988 | fprintf(stdout, "[%d]%s => %" PRId64"\n", i, iter->first.c_str(), iter->second); 989 | } 990 | else if (0 == strcasecmp(cmd, "zrangebyscore")) 991 | { 992 | // ZRANGEBYSCORE command 993 | if ((argc != 5) && (argc != 7)) 994 | { 995 | fprintf(stderr, "Usage1: r3c_cmd zrangebyscore key min max\n"); 996 | fprintf(stderr, "Usage2: r3c_cmd zrangebyscore key min max offset count\n"); 997 | exit(1); 998 | } 999 | 1000 | i = 0; 1001 | min = atol(argv[3]); 1002 | max = atol(argv[4]); 1003 | std::vector > vec; 1004 | 1005 | if (5 == argc) 1006 | { 1007 | ret = redis_client.zrangebyscore(key, min, max, true, &vec, &which_node); 1008 | } 1009 | else 1010 | { 1011 | offset = atol(argv[5]); 1012 | count = atol(argv[6]); 1013 | ret = redis_client.zrangebyscore(key, min, max, offset, count, true, &vec, &which_node); 1014 | } 1015 | fprintf(stdout, "number: %d\n", ret); 1016 | for (std::vector >::iterator iter=vec.begin(); iter!=vec.end(); ++iter,++i) 1017 | fprintf(stdout, "[%d]%s => %" PRId64"\n", i, iter->first.c_str(), iter->second); 1018 | } 1019 | else if (0 == strcasecmp(cmd, "zrevrangebyscore")) 1020 | { 1021 | // ZREVRANGEBYSCORE command 1022 | if ((argc != 5) && (argc != 7)) 1023 | { 1024 | fprintf(stderr, "Usage1: r3c_cmd zrevrangebyscore key max min\n"); 1025 | fprintf(stderr, "Usage2: r3c_cmd zrangebyscore key max min offset count\n"); 1026 | exit(1); 1027 | } 1028 | 1029 | i = 0; 1030 | max = atol(argv[3]); 1031 | min = atol(argv[4]); 1032 | std::vector > vec; 1033 | 1034 | if (5 == argc) 1035 | { 1036 | ret = redis_client.zrevrangebyscore(key, max, min, true, &vec, &which_node); 1037 | } 1038 | else 1039 | { 1040 | offset = atol(argv[5]); 1041 | count = atol(argv[6]); 1042 | ret = redis_client.zrevrangebyscore(key, max, min, offset, count, true, &vec, &which_node); 1043 | } 1044 | fprintf(stdout, "number: %d\n", ret); 1045 | for (std::vector >::iterator iter=vec.begin(); iter!=vec.end(); ++iter,++i) 1046 | fprintf(stdout, "[%d]%s => %" PRId64"\n", i, iter->first.c_str(), iter->second); 1047 | } 1048 | else if (0 == strcasecmp(cmd, "zrank")) 1049 | { 1050 | // ZRANK command 1051 | if (argc != 4) 1052 | { 1053 | fprintf(stderr, "Usage: r3c_cmd zrank key field\n"); 1054 | exit(1); 1055 | } 1056 | 1057 | int rank = redis_client.zrank(key, argv[3], &which_node); 1058 | fprintf(stdout, "[%s] => %d\n", argv[3], rank); 1059 | } 1060 | else if (0 == strcasecmp(cmd, "zrevrank")) 1061 | { 1062 | // ZREVRANK command 1063 | if (argc != 4) 1064 | { 1065 | fprintf(stderr, "Usage: r3c_cmd zrevrank key field\n"); 1066 | exit(1); 1067 | } 1068 | 1069 | int rank = redis_client.zrevrank(key, argv[3], &which_node); 1070 | fprintf(stdout, "[%s] => %d\n", argv[3], rank); 1071 | } 1072 | else if (0 == strcasecmp(cmd, "zscore")) 1073 | { 1074 | // ZSCORE command 1075 | if (argc != 4) 1076 | { 1077 | fprintf(stderr, "Usage: r3c_cmd zscore key field\n"); 1078 | exit(1); 1079 | } 1080 | 1081 | double score = redis_client.zscore(key, argv[3], &which_node); 1082 | fprintf(stdout, "[%s] => %.2f\n", argv[3], score); 1083 | } 1084 | else if (0 == strcasecmp(cmd, "zscan")) 1085 | { 1086 | // ZSCAN command 1087 | if ((argc < 4) || (argc > 6)) 1088 | { 1089 | fprintf(stderr, "Usage1: r3c_cmd zscan key cursor\n"); 1090 | fprintf(stderr, "Usage2: r3c_cmd zscan key cursor count\n"); 1091 | fprintf(stderr, "Usage3: r3c_cmd zscan key cursor pattern\n"); 1092 | fprintf(stderr, "Usage4: r3c_cmd zscan key cursor pattern count\n"); 1093 | exit(1); 1094 | } 1095 | 1096 | cursor = static_cast(atoll(argv[3])); 1097 | if (4 == argc) 1098 | { 1099 | cursor = redis_client.zscan(key, cursor, &values2, &which_node); 1100 | } 1101 | else if (5 == argc) 1102 | { 1103 | count = atoi(argv[4]); 1104 | if (count > 0) 1105 | cursor = redis_client.zscan(key, cursor, count, &values2, &which_node); 1106 | else 1107 | cursor = redis_client.zscan(key, cursor, argv[4], &values2, &which_node); 1108 | } 1109 | else if (6 == argc) 1110 | { 1111 | cursor = redis_client.zscan(key, cursor, argv[4], atoi(argv[5]), &values2, &which_node); 1112 | } 1113 | 1114 | fprintf(stdout, "cursor: %" PRId64", count: %d\n", cursor, static_cast(values2.size())); 1115 | for (i=0; i(values2.size()); ++i) 1116 | fprintf(stdout, "%s => %" PRId64"\n", values2[i].first.c_str(), values2[i].second); 1117 | } 1118 | else 1119 | { 1120 | fprintf(stderr, "command[%s] not supported\n", cmd); 1121 | exit(1); 1122 | } 1123 | } 1124 | catch (r3c::CRedisException& ex) 1125 | { 1126 | fprintf(stderr, PRINT_COLOR_RED"%s" PRINT_COLOR_NONE"\n", ex.str().c_str()); 1127 | exit(1); 1128 | } 1129 | 1130 | return 0; 1131 | } 1132 | -------------------------------------------------------------------------------- /tests/r3c_robust.cpp: -------------------------------------------------------------------------------- 1 | // Writed by yijian (eyjian@qq.com) 2 | #include "r3c.h" 3 | #include "utils.h" 4 | #include // basename 5 | #include // atoi 6 | #include // strdup 7 | #include 8 | #if __cplusplus >= 201103L 9 | #include 10 | #endif // __cplusplus >= 201103L 11 | 12 | typedef void (*F)(r3c::CRedisClient& redis); 13 | static void fill_f(F f[]); 14 | 15 | int main(int argc, char* argv[]) 16 | { 17 | try 18 | { 19 | F f[2019]; 20 | char* s = strdup(argv[0]); 21 | char* b = basename(s); 22 | 23 | r3c::set_error_log_write(r3c::r3c_log_write); 24 | for (size_t i=0; i=static_cast(sizeof(f)/sizeof(f[0]))) 39 | { 40 | fprintf(stderr, "Usage: %s index redis_nodes, example: %s 1 127.0.0.1:6379\n", b, b); 41 | free(s); 42 | exit(1); 43 | } 44 | else 45 | { 46 | free(s); 47 | if (NULL == f[n]) 48 | { 49 | fprintf(stderr, "Not implemented\n"); 50 | } 51 | else 52 | { 53 | r3c::CRedisClient redis(redis_nodes); 54 | (*(f[n]))(redis); 55 | } 56 | return 0; 57 | } 58 | } 59 | } 60 | catch (r3c::CRedisException& ex) 61 | { 62 | fprintf(stderr, "%s\n", ex.str().c_str()); 63 | exit(1); 64 | } 65 | } 66 | 67 | // 可用来观察: 68 | // 1) 运行中,master异常 69 | // 2) 运行中,replica异常 70 | // 3) 运行中,master和replica都异常 71 | void f0(r3c::CRedisClient& redis) 72 | { 73 | const std::string k1 = "K1"; 74 | const std::string k2 = "{K1}K2"; 75 | std::string v; 76 | r3c::Node which; 77 | 78 | // k1 79 | redis.setex(k1, "11", 1800); 80 | redis.setex(k2, "22", 1800); 81 | for (int i=0; i<600; ++i) 82 | { 83 | try 84 | { 85 | // k1 86 | redis.get(k1, &v, &which); 87 | fprintf(stdout, "[%s][slot://%d][key://%s][redis://%s] %s => %s\n", 88 | r3c::get_formatted_current_datetime(true).c_str(), r3c::get_key_slot(&k1), 89 | k1.c_str(), r3c::node2string(which).c_str(), k1.c_str(), v.c_str()); 90 | 91 | // k2 92 | redis.get(k2, &v, &which); 93 | fprintf(stdout, "[%s][slot://%d][key://%s][redis://%s] %s => %s\n", 94 | r3c::get_formatted_current_datetime(true).c_str(), r3c::get_key_slot(&k2), 95 | k2.c_str(), r3c::node2string(which).c_str(), k2.c_str(), v.c_str()); 96 | } 97 | catch (r3c::CRedisException& ex) 98 | { 99 | fprintf(stderr, "[%s] `GET` failed: %s\n", 100 | r3c::get_formatted_current_datetime(true).c_str(), ex.str().c_str()); 101 | } 102 | 103 | r3c::millisleep(1000); 104 | } 105 | } 106 | 107 | // 观察eval在异常时的情况 108 | void f1(r3c::CRedisClient& redis) 109 | { 110 | const std::string lua_scripts = "local v=redis.call('get',KEYS[1]);return v;"; 111 | const std::string k1 = "K1"; 112 | const std::string k2 = "{K1}K2"; 113 | r3c::Node which; 114 | 115 | // k1 116 | redis.setex(k1, "11", 1800); 117 | redis.setex(k2, "22", 1800); 118 | for (int i=0; i<1800; ++i) 119 | { 120 | try 121 | { 122 | // k1 123 | redis.eval(k1, lua_scripts, &which); 124 | fprintf(stdout, "[%s][slot://%d][key://%s][redis://%s] %s => %s\n", 125 | r3c::get_formatted_current_datetime(true).c_str(), r3c::get_key_slot(&k1), 126 | k1.c_str(), r3c::node2string(which).c_str(), k1.c_str(), lua_scripts.c_str()); 127 | 128 | // k2 129 | redis.eval(k2, lua_scripts, &which); 130 | fprintf(stdout, "[%s][slot://%d][key://%s][redis://%s] %s => %s\n", 131 | r3c::get_formatted_current_datetime(true).c_str(), r3c::get_key_slot(&k2), 132 | k2.c_str(), r3c::node2string(which).c_str(), k2.c_str(), lua_scripts.c_str()); 133 | } 134 | catch (r3c::CRedisException& ex) 135 | { 136 | fprintf(stderr, "[%s] `EVAL` failed: %s\n", 137 | r3c::get_formatted_current_datetime(true).c_str(), ex.str().c_str()); 138 | } 139 | 140 | r3c::millisleep(1000); 141 | } 142 | } 143 | 144 | // 可用来观察整个集群挂掉后,请求是否倾斜 145 | void f2(r3c::CRedisClient& redis) 146 | { 147 | const std::string lua_scripts = "local v=redis.call('get',KEYS[1]);return v;"; 148 | r3c::Node which; 149 | 150 | for (int i=0; i<600; ++i) 151 | { 152 | // k1 153 | const std::string k1 = "K1"; 154 | try 155 | { 156 | redis.eval(k1, lua_scripts, &which); 157 | fprintf(stdout, "[%s][slot://%d][key://%s][redis://%s] %s => %s\n", 158 | r3c::get_formatted_current_datetime(true).c_str(), r3c::get_key_slot(&k1), 159 | k1.c_str(), r3c::node2string(which).c_str(), k1.c_str(), lua_scripts.c_str()); 160 | } 161 | catch (r3c::CRedisException& ex) 162 | { 163 | fprintf(stderr, "[%s][key:%s] `EVAL` failed: %s\n", 164 | r3c::get_formatted_current_datetime(true).c_str(), k1.c_str(), ex.str().c_str()); 165 | } 166 | 167 | // k2 168 | const std::string k2 = "K2"; 169 | try 170 | { 171 | redis.eval(k2, lua_scripts, &which); 172 | fprintf(stdout, "[%s][slot://%d][key://%s][redis://%s] %s => %s\n", 173 | r3c::get_formatted_current_datetime(true).c_str(), r3c::get_key_slot(&k2), 174 | k2.c_str(), r3c::node2string(which).c_str(), k2.c_str(), lua_scripts.c_str()); 175 | } 176 | catch (r3c::CRedisException& ex) 177 | { 178 | fprintf(stderr, "[%s][key:%s] `EVAL` failed: %s\n", 179 | r3c::get_formatted_current_datetime(true).c_str(), k2.c_str(), ex.str().c_str()); 180 | } 181 | 182 | r3c::millisleep(1000); 183 | } 184 | } 185 | 186 | void f3(r3c::CRedisClient& redis) 187 | { 188 | const std::string lua_scripts = "local v=redis.call('get','K2');return v;"; 189 | r3c::Node which; 190 | 191 | // k1 192 | for (int i=0; i<600; ++i) 193 | { 194 | try 195 | { 196 | const std::string k1 = "K1"; 197 | redis.eval(k1, lua_scripts, &which); 198 | fprintf(stdout, "[%s][slot://%d][key://%s][redis://%s] %s => %s\n", 199 | r3c::get_formatted_current_datetime(true).c_str(), r3c::get_key_slot(&k1), 200 | k1.c_str(), r3c::node2string(which).c_str(), k1.c_str(), lua_scripts.c_str()); 201 | } 202 | catch (r3c::CRedisException& ex) 203 | { 204 | fprintf(stderr, "[%s] `EVAL` failed: %s\n", 205 | r3c::get_formatted_current_datetime(true).c_str(), ex.str().c_str()); 206 | } 207 | 208 | r3c::millisleep(1000); 209 | } 210 | } 211 | 212 | void fill_f(F f[]) 213 | { 214 | f[0] = f0; 215 | f[1] = f1; 216 | f[2] = f2; 217 | f[3] = f3; 218 | } 219 | -------------------------------------------------------------------------------- /tests/r3c_stream.cpp: -------------------------------------------------------------------------------- 1 | // Writed by yijian (eyjian@qq.com) 2 | // For test stream 3 | #include "r3c.h" 4 | #include "utils.h" 5 | #include 6 | 7 | typedef void (*TESTCASE)(r3c::CRedisClient&); 8 | static void init_testcase(TESTCASE testcase[]); 9 | static void usage(char* argv[]); 10 | 11 | // argv[1] redis nodes 12 | // argv[2] specify the test case 13 | int main(int argc, char* argv[]) 14 | { 15 | if (argc != 3) 16 | { 17 | usage(argv); 18 | exit(1); 19 | } 20 | else 21 | { 22 | try 23 | { 24 | const int n = atoi(argv[2]); 25 | const int num_testcases = 10; 26 | TESTCASE testcase[num_testcases]; 27 | r3c::CRedisClient redis(argv[1], r3c::RP_READ_REPLICA); 28 | init_testcase(testcase); 29 | 30 | if (n<0 || n>=num_testcases-1) 31 | { 32 | usage(argv); 33 | exit(1); 34 | } 35 | else 36 | { 37 | (*testcase[n])(redis); 38 | return 0; 39 | } 40 | } 41 | catch (r3c::CRedisException& ex) 42 | { 43 | fprintf(stderr, "%s\n", ex.str().c_str()); 44 | exit(1); 45 | } 46 | } 47 | } 48 | 49 | void usage(char* argv[]) 50 | { 51 | fprintf(stderr, "Usage: %s testcase\n", argv[0]); 52 | fprintf(stderr, "Example: %s 192.168.1.61:6379,192.168.1.62:6379 1\n", argv[0]); 53 | } 54 | 55 | 56 | // test xreadgroup 57 | static void testcase0(r3c::CRedisClient& redis) 58 | { 59 | const std::string group = "group"; 60 | const std::string consumer = "consumer"; 61 | std::vector keys(2); 62 | std::vector ids(2); 63 | std::vector fvpairs(3); 64 | int count = 10; 65 | 66 | keys[0] = "k0"; 67 | keys[1] = "k1"; 68 | 69 | // XADD 70 | fvpairs[0].field = "field00"; 71 | fvpairs[0].value = "value00"; 72 | fvpairs[1].field = "field01"; 73 | fvpairs[1].value = "value01"; 74 | fvpairs[2].field = "field02"; 75 | fvpairs[2].value = "value02"; 76 | ids[0] = redis.xadd(keys[0], "*", fvpairs); 77 | 78 | fvpairs[0].field = "field10"; 79 | fvpairs[0].value = "value10"; 80 | fvpairs[1].field = "field11"; 81 | fvpairs[1].value = "value11"; 82 | fvpairs[2].field = "field12"; 83 | fvpairs[2].value = "value12"; 84 | ids[1] = redis.xadd(keys[1], "*", fvpairs); 85 | 86 | // XGROUP CREATE 87 | try 88 | { 89 | redis.xgroup_create(keys[0], group, "$"); 90 | } 91 | catch (r3c::CRedisException& ex) 92 | { 93 | if (!r3c::is_busygroup_error(ex.errtype())) 94 | throw; 95 | } 96 | try 97 | { 98 | redis.xgroup_create(keys[1], group, "$"); 99 | } 100 | catch (r3c::CRedisException& ex) 101 | { 102 | if (!r3c::is_busygroup_error(ex.errtype())) 103 | throw; 104 | } 105 | 106 | // XREADGROUP 107 | ids[0] = ">"; 108 | ids[1] = ">"; 109 | std::vector values; 110 | redis.xreadgroup(group, consumer, keys, ids, count, &values); 111 | std::cout << values << std::endl; 112 | } 113 | 114 | // test xreadgroup 115 | static void testcase1(r3c::CRedisClient& redis) 116 | { 117 | const std::string group = "group"; 118 | const std::string consumer = "consumer"; 119 | std::vector keys(1); 120 | std::vector fvpairs(3); 121 | int count = 10; 122 | int64_t block_milliseconds = 0; 123 | bool noack = true; 124 | 125 | keys[0] = "k000"; 126 | 127 | // XGROUP CREATE 128 | try 129 | { 130 | redis.xgroup_create(keys[0], group, "$", true); 131 | } 132 | catch (r3c::CRedisException& ex) 133 | { 134 | if (!r3c::is_busygroup_error(ex.errtype())) 135 | throw; 136 | } 137 | 138 | // XADD 139 | fvpairs[0].field = "field00"; 140 | fvpairs[0].value = "value00"; 141 | fvpairs[1].field = "field01"; 142 | fvpairs[1].value = "value01"; 143 | fvpairs[2].field = "field02"; 144 | fvpairs[2].value = "value02"; 145 | redis.xadd(keys[0], "*", fvpairs); 146 | 147 | // XREADGROUP 148 | std::vector values; 149 | redis.xreadgroup(group, consumer, keys[0], count, block_milliseconds, noack, &values); 150 | std::cout << values << std::endl; 151 | } 152 | 153 | // test xread 154 | static void testcase2(r3c::CRedisClient& redis) 155 | { 156 | std::vector keys(2); 157 | std::vector ids(2); 158 | std::vector fvpairs(3); 159 | int count = 10; 160 | 161 | keys[0] = "k2"; 162 | keys[1] = "k3"; 163 | 164 | // XADD 165 | fvpairs[0].field = "field00"; 166 | fvpairs[0].value = "value00"; 167 | fvpairs[1].field = "field01"; 168 | fvpairs[1].value = "value01"; 169 | fvpairs[2].field = "field02"; 170 | fvpairs[2].value = "value02"; 171 | ids[0] = redis.xadd(keys[0], "*", fvpairs); 172 | 173 | fvpairs[0].field = "field10"; 174 | fvpairs[0].value = "value10"; 175 | fvpairs[1].field = "field11"; 176 | fvpairs[1].value = "value11"; 177 | fvpairs[2].field = "field12"; 178 | fvpairs[2].value = "value12"; 179 | ids[1] = redis.xadd(keys[1], "*", fvpairs); 180 | 181 | // XREAD 182 | ids[0] = "0-0"; 183 | ids[1] = "0-0"; 184 | std::vector values; 185 | redis.xread(keys, ids, count, &values); 186 | std::cout << values << std::endl; 187 | } 188 | 189 | // test xrange 190 | static void testcase3(r3c::CRedisClient& redis) 191 | { 192 | std::string key = "k4"; 193 | std::string start, end; 194 | std::vector fvpairs(3); 195 | std::vector ids(2); 196 | std::vector values; 197 | 198 | // XADD 199 | fvpairs[0].field = "field00"; 200 | fvpairs[0].value = "value00"; 201 | fvpairs[1].field = "field01"; 202 | fvpairs[1].value = "value01"; 203 | fvpairs[2].field = "field02"; 204 | fvpairs[2].value = "value02"; 205 | redis.xadd(key, "*", fvpairs); 206 | 207 | // XADD 208 | fvpairs[0].field = "field10"; 209 | fvpairs[0].value = "value10"; 210 | fvpairs[1].field = "field11"; 211 | fvpairs[1].value = "value11"; 212 | fvpairs[2].field = "field12"; 213 | fvpairs[2].value = "value12"; 214 | redis.xadd(key, "*", fvpairs); 215 | 216 | start = "-"; 217 | end = "+"; 218 | redis.xrange(key, start, end, &values); 219 | std::cout << values << std::endl; 220 | } 221 | 222 | // test xpending 223 | static void testcase4(r3c::CRedisClient& redis) 224 | { 225 | std::string key = "k1"; 226 | std::string group = "g1"; 227 | struct r3c::GroupPending groups; 228 | 229 | // Group 230 | redis.xpending(key, group, &groups); 231 | } 232 | 233 | // test xpending 234 | static void testcase5(r3c::CRedisClient& redis) 235 | { 236 | std::string key = "k1"; 237 | std::string group = "g1"; 238 | struct r3c::GroupPending groups; 239 | 240 | // Group 241 | redis.xpending(key, group, &groups); 242 | fprintf(stdout, "COUNT:%d\n", groups.count); 243 | fprintf(stdout, "stratid: %s\n", groups.start.c_str()); 244 | fprintf(stdout, "endid: %s\n", groups.end.c_str()); 245 | for (std::vector::size_type i=0; i pendings; 254 | const int n = redis.xpending(key, group, groups.start, groups.end, groups.count, groups.consumers[0].name, &pendings); 255 | 256 | fprintf(stdout, "\n"); 257 | for (int i=0; i ids; 276 | std::vector entries; 277 | std::vector fvpairs(3); 278 | int64_t count = 10; 279 | int64_t block_milliseconds = -1; 280 | int64_t miniidle = 1; 281 | bool noack = true; 282 | 283 | redis.del(key); 284 | fvpairs[0].field = "f0"; 285 | fvpairs[0].value = "v0"; 286 | fvpairs[1].field = "f1"; 287 | fvpairs[1].value = "v1"; 288 | fvpairs[2].field = "f2"; 289 | fvpairs[2].value = "v2"; 290 | redis.xadd(key, id, fvpairs); 291 | 292 | redis.xgroup_create(key, group, "$", true); 293 | redis.xreadgroup(group, consumer, key, count, block_milliseconds, noack, &entries); 294 | std::cout << entries << std::endl; 295 | 296 | fprintf(stdout, "\n"); 297 | extract_ids(entries, &ids); 298 | entries.clear(); 299 | redis.xclaim(key, group, "c2", miniidle, ids, &entries); 300 | } 301 | 302 | // test: 303 | // xinfo_consumers 304 | // xinfo_groups 305 | // xinfo_stream 306 | static void testcase7(r3c::CRedisClient& redis) 307 | { 308 | std::string key = "k1"; 309 | std::string group = "g1"; 310 | std::string consumer = "c1"; 311 | std::string id = "*"; 312 | std::vector ids; 313 | std::vector entries; 314 | std::vector fvpairs(5); 315 | int64_t count = 10; 316 | int64_t block_milliseconds = -1; 317 | bool noack = true; 318 | 319 | redis.del(key); 320 | fvpairs[0].field = "f0"; 321 | fvpairs[0].value = "v0"; 322 | fvpairs[1].field = "f1"; 323 | fvpairs[1].value = "v1"; 324 | fvpairs[2].field = "f2"; 325 | fvpairs[2].value = "v2"; 326 | fvpairs[3].field = "f3"; 327 | fvpairs[3].value = "v3"; 328 | fvpairs[4].field = "f4"; 329 | fvpairs[4].value = "v4"; 330 | redis.xadd(key, id, fvpairs); 331 | 332 | redis.xgroup_create(key, group, "$", true); 333 | redis.xreadgroup(group, "c1", key, count, block_milliseconds, noack, &entries); 334 | std::cout << entries << std::endl; 335 | redis.xreadgroup(group, "c2", key, count, block_milliseconds, noack, &entries); 336 | std::cout << entries << std::endl; 337 | redis.xreadgroup(group, "c3", key, count, block_milliseconds, noack, &entries); 338 | std::cout << entries << std::endl; 339 | 340 | { 341 | std::vector consumers_info; 342 | std::vector groups_info; 343 | struct r3c::StreamInfo stream_info; 344 | 345 | fprintf(stdout, "info consumers:\n"); 346 | redis.xinfo_consumers(key, group, &consumers_info); 347 | fprintf(stdout, "info groups:\n"); 348 | redis.xinfo_groups(key, &groups_info); 349 | fprintf(stdout, "info stream:\n"); 350 | redis.xinfo_stream(key, &stream_info); 351 | std::cout << stream_info << std::endl; 352 | } 353 | } 354 | 355 | void init_testcase(TESTCASE testcase[]) 356 | { 357 | int i = 0; 358 | testcase[i++] = testcase0; 359 | testcase[i++] = testcase1; 360 | testcase[i++] = testcase2; 361 | testcase[i++] = testcase3; 362 | testcase[i++] = testcase4; 363 | testcase[i++] = testcase5; 364 | testcase[i++] = testcase6; 365 | testcase[i++] = testcase7; 366 | } 367 | -------------------------------------------------------------------------------- /tests/r3c_stress.cpp: -------------------------------------------------------------------------------- 1 | // Writed by yijian (eyjian@qq.com) 2 | #include "r3c.h" 3 | #include 4 | #include 5 | #include 6 | 7 | // 可用于测试异常时接口的正确性, 8 | // 比如集群中节点挂掉,或节点挂起等异常情况 9 | 10 | // argv[1] redis nodes 11 | // argv[2] number of keys 12 | // argv[3] number of cycles 13 | int main(int argc, char* argv[]) 14 | { 15 | if (argc != 4) 16 | { 17 | fprintf(stderr, "Usage: %s \n", argv[0]); 18 | fprintf(stderr, "Example: %s 192.168.1.61:6379,192.168.1.62:6379 100 1000000\n", argv[0]); 19 | exit(1); 20 | } 21 | else 22 | { 23 | const int num_retries = r3c::NUM_RETRIES; 24 | const uint32_t expired_seconds = 3600*24; 25 | const std::string& redis_nodes = argv[1]; 26 | const int num_keys = atoi(argv[2]); 27 | const int num_cycles = atoi(argv[3]); 28 | r3c::CRedisClient redis(redis_nodes); 29 | std::vector keys(num_keys); 30 | 31 | try 32 | { 33 | for (std::vector::size_type j=0; j 0) && (0 == i % 10000)) 43 | { 44 | fprintf(stdout, "%d\n", i); 45 | } 46 | for (std::vector::size_type j=0; j::size_type j=0; j %s\n", key.c_str(), value.c_str()); 60 | } 61 | else 62 | { 63 | fprintf(stdout, "%s => %s\n", key.c_str(), "NONE"); 64 | } 65 | } 66 | return 0; 67 | } 68 | catch (r3c::CRedisException& ex) 69 | { 70 | fprintf(stderr, "%s\n", ex.str().c_str()); 71 | exit(1); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/redis_command_extension.cpp: -------------------------------------------------------------------------------- 1 | // Writed by yijian (eyjian@qq.com) 2 | // Redis命令扩展module 3 | #include "redismodule.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // 带超时的INCRBY命令 11 | // 格式:INCRBYEX KEY SECONDS INCREMENT 12 | // 13 | // 示例: 14 | // ex.incrbyex k1 10 1 15 | // ex.incrbyex k1 10 2 16 | int incrbyex_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 17 | // When automatic memory management is enabled: 18 | // 1) don't need to close open keys 19 | // 2) don't need to free replies 20 | // 3) don't need to free RedisModuleString objects 21 | RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ 22 | if (argc != 4) return RedisModule_WrongArity(ctx); 23 | 24 | RedisModuleKey *key = (RedisModuleKey*)RedisModule_OpenKey(ctx, argv[1], 25 | REDISMODULE_READ|REDISMODULE_WRITE); 26 | int type = RedisModule_KeyType(key); 27 | if (type != REDISMODULE_KEYTYPE_STRING && 28 | type != REDISMODULE_KEYTYPE_EMPTY) 29 | { 30 | return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); 31 | } 32 | 33 | RedisModuleString* seconds = argv[2]; 34 | RedisModuleString* increment = argv[3]; 35 | long long ll_seconds; // 过期时长 36 | long long ll_newval; // 新的值 37 | if (RedisModule_StringToLongLong(seconds, &ll_seconds) != REDISMODULE_OK) { 38 | return RedisModule_ReplyWithError(ctx, "ERR value is not an integer or out of range"); 39 | } 40 | if (RedisModule_StringToLongLong(increment, &ll_newval) != REDISMODULE_OK) { 41 | return RedisModule_ReplyWithError(ctx, "ERR value is not an integer or out of range"); 42 | } 43 | 44 | size_t len; 45 | char *s = RedisModule_StringDMA(key, &len, REDISMODULE_READ|REDISMODULE_WRITE); 46 | if (len == 0 || s == NULL || *s == '\0') { 47 | // set必须在Expire之前,否则会冲掉Expire的作用, 48 | // 这也是else分支未用RedisModule_StringSet的原因 49 | RedisModule_StringSet(key, increment); 50 | RedisModule_SetExpire(key, ll_seconds*1000); // 以秒为单位,需要乘以1000 51 | } 52 | else { 53 | char* endptr; 54 | long long ll_oldval = strtoll(s, &endptr, 10); // s不一定是有效的数字,所以需要做检查 55 | ll_newval = ll_newval + ll_oldval; 56 | if ((errno == ERANGE && (ll_oldval == LLONG_MAX || ll_oldval == LLONG_MIN)) 57 | || (errno != 0 && ll_oldval == 0)) { 58 | return RedisModule_ReplyWithError(ctx, "ERR value is not an integer or out of range"); 59 | } 60 | if (endptr == s || *endptr != '\0') { 61 | return RedisModule_ReplyWithError(ctx, "ERR value is not an integer or out of range"); 62 | } 63 | 64 | size_t newval_len; 65 | RedisModuleString* newval = RedisModule_CreateStringFromLongLong(ctx, ll_newval); 66 | const char* newval_s = RedisModule_StringPtrLen(newval, &newval_len); 67 | if (newval_len > len) 68 | RedisModule_StringTruncate(key, newval_len); 69 | strncpy(s, newval_s, newval_len); 70 | } 71 | 72 | RedisModule_ReplicateVerbatim(ctx); // 写AOF和复制到slaves 73 | RedisModule_ReplyWithLongLong(ctx, ll_newval); 74 | return REDISMODULE_OK; 75 | } 76 | 77 | // 实现命令HMINCRBY,同时对HASH的多个field值进行增减操作 78 | // 格式:HMINCRBY KEY FIELD1 VALUE1 FIELD2 VALUE2 FIELD3 VALUE3 ...... 79 | // 80 | // 示例: 81 | // hmincrby k1 f1 1 f2 2 82 | // hmincrby k1 f5 1 f6 2 f7 3 83 | int hmincrby_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 84 | // When automatic memory management is enabled: 85 | // 1) don't need to close open keys 86 | // 2) don't need to free replies 87 | // 3) don't need to free RedisModuleString objects 88 | RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ 89 | if (argc < 4 || argc % 2 != 0) return RedisModule_WrongArity(ctx); 90 | 91 | RedisModuleKey *key = (RedisModuleKey*)RedisModule_OpenKey(ctx, argv[1], 92 | REDISMODULE_READ|REDISMODULE_WRITE); 93 | int type = RedisModule_KeyType(key); 94 | if (type != REDISMODULE_KEYTYPE_HASH && 95 | type != REDISMODULE_KEYTYPE_EMPTY) 96 | { 97 | return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); 98 | } 99 | 100 | const int count = argc/2 - 1; // 键值对个数 101 | std::vector newval_array(count); // 用来存储新值的数组 102 | for (int i=2; i::size_type i=0; i 5 | #include 6 | #include 7 | 8 | /* ---------------- Defines common between core and modules --------------- */ 9 | 10 | /* Error status return values. */ 11 | #define REDISMODULE_OK 0 12 | #define REDISMODULE_ERR 1 13 | 14 | /* API versions. */ 15 | #define REDISMODULE_APIVER_1 1 16 | 17 | /* API flags and constants */ 18 | #define REDISMODULE_READ (1<<0) 19 | #define REDISMODULE_WRITE (1<<1) 20 | 21 | #define REDISMODULE_LIST_HEAD 0 22 | #define REDISMODULE_LIST_TAIL 1 23 | 24 | /* Key types. */ 25 | #define REDISMODULE_KEYTYPE_EMPTY 0 26 | #define REDISMODULE_KEYTYPE_STRING 1 27 | #define REDISMODULE_KEYTYPE_LIST 2 28 | #define REDISMODULE_KEYTYPE_HASH 3 29 | #define REDISMODULE_KEYTYPE_SET 4 30 | #define REDISMODULE_KEYTYPE_ZSET 5 31 | #define REDISMODULE_KEYTYPE_MODULE 6 32 | 33 | /* Reply types. */ 34 | #define REDISMODULE_REPLY_UNKNOWN -1 35 | #define REDISMODULE_REPLY_STRING 0 36 | #define REDISMODULE_REPLY_ERROR 1 37 | #define REDISMODULE_REPLY_INTEGER 2 38 | #define REDISMODULE_REPLY_ARRAY 3 39 | #define REDISMODULE_REPLY_NULL 4 40 | 41 | /* Postponed array length. */ 42 | #define REDISMODULE_POSTPONED_ARRAY_LEN -1 43 | 44 | /* Expire */ 45 | #define REDISMODULE_NO_EXPIRE -1 46 | 47 | /* Sorted set API flags. */ 48 | #define REDISMODULE_ZADD_XX (1<<0) 49 | #define REDISMODULE_ZADD_NX (1<<1) 50 | #define REDISMODULE_ZADD_ADDED (1<<2) 51 | #define REDISMODULE_ZADD_UPDATED (1<<3) 52 | #define REDISMODULE_ZADD_NOP (1<<4) 53 | 54 | /* Hash API flags. */ 55 | #define REDISMODULE_HASH_NONE 0 56 | #define REDISMODULE_HASH_NX (1<<0) 57 | #define REDISMODULE_HASH_XX (1<<1) 58 | #define REDISMODULE_HASH_CFIELDS (1<<2) 59 | #define REDISMODULE_HASH_EXISTS (1<<3) 60 | 61 | /* Context Flags: Info about the current context returned by RM_GetContextFlags */ 62 | 63 | /* The command is running in the context of a Lua script */ 64 | #define REDISMODULE_CTX_FLAGS_LUA 0x0001 65 | /* The command is running inside a Redis transaction */ 66 | #define REDISMODULE_CTX_FLAGS_MULTI 0x0002 67 | /* The instance is a master */ 68 | #define REDISMODULE_CTX_FLAGS_MASTER 0x0004 69 | /* The instance is a slave */ 70 | #define REDISMODULE_CTX_FLAGS_SLAVE 0x0008 71 | /* The instance is read-only (usually meaning it's a slave as well) */ 72 | #define REDISMODULE_CTX_FLAGS_READONLY 0x0010 73 | /* The instance is running in cluster mode */ 74 | #define REDISMODULE_CTX_FLAGS_CLUSTER 0x0020 75 | /* The instance has AOF enabled */ 76 | #define REDISMODULE_CTX_FLAGS_AOF 0x0040 // 77 | /* The instance has RDB enabled */ 78 | #define REDISMODULE_CTX_FLAGS_RDB 0x0080 // 79 | /* The instance has Maxmemory set */ 80 | #define REDISMODULE_CTX_FLAGS_MAXMEMORY 0x0100 81 | /* Maxmemory is set and has an eviction policy that may delete keys */ 82 | #define REDISMODULE_CTX_FLAGS_EVICT 0x0200 83 | 84 | 85 | #define REDISMODULE_NOTIFY_GENERIC (1<<2) /* g */ 86 | #define REDISMODULE_NOTIFY_STRING (1<<3) /* $ */ 87 | #define REDISMODULE_NOTIFY_LIST (1<<4) /* l */ 88 | #define REDISMODULE_NOTIFY_SET (1<<5) /* s */ 89 | #define REDISMODULE_NOTIFY_HASH (1<<6) /* h */ 90 | #define REDISMODULE_NOTIFY_ZSET (1<<7) /* z */ 91 | #define REDISMODULE_NOTIFY_EXPIRED (1<<8) /* x */ 92 | #define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */ 93 | #define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED) /* A */ 94 | 95 | 96 | /* A special pointer that we can use between the core and the module to signal 97 | * field deletion, and that is impossible to be a valid pointer. */ 98 | #define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1) 99 | 100 | /* Error messages. */ 101 | #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value" 102 | 103 | #define REDISMODULE_POSITIVE_INFINITE (1.0/0.0) 104 | #define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0) 105 | 106 | #define REDISMODULE_NOT_USED(V) ((void) V) 107 | 108 | /* ------------------------- End of common defines ------------------------ */ 109 | 110 | #ifndef REDISMODULE_CORE 111 | 112 | typedef long long mstime_t; 113 | 114 | /* Incomplete structures for compiler checks but opaque access. */ 115 | typedef struct RedisModuleCtx RedisModuleCtx; 116 | typedef struct RedisModuleKey RedisModuleKey; 117 | typedef struct RedisModuleString RedisModuleString; 118 | typedef struct RedisModuleCallReply RedisModuleCallReply; 119 | typedef struct RedisModuleIO RedisModuleIO; 120 | typedef struct RedisModuleType RedisModuleType; 121 | typedef struct RedisModuleDigest RedisModuleDigest; 122 | typedef struct RedisModuleBlockedClient RedisModuleBlockedClient; 123 | 124 | typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, RedisModuleString **argv, int argc); 125 | 126 | typedef int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key); 127 | typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver); 128 | typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value); 129 | typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value); 130 | typedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value); 131 | typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value); 132 | typedef void (*RedisModuleTypeFreeFunc)(void *value); 133 | 134 | #define REDISMODULE_TYPE_METHOD_VERSION 1 135 | typedef struct RedisModuleTypeMethods { 136 | uint64_t version; 137 | RedisModuleTypeLoadFunc rdb_load; 138 | RedisModuleTypeSaveFunc rdb_save; 139 | RedisModuleTypeRewriteFunc aof_rewrite; 140 | RedisModuleTypeMemUsageFunc mem_usage; 141 | RedisModuleTypeDigestFunc digest; 142 | RedisModuleTypeFreeFunc free; 143 | } RedisModuleTypeMethods; 144 | 145 | #define REDISMODULE_GET_API(name) \ 146 | RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name)) 147 | 148 | #define REDISMODULE_API_FUNC(x) (*x) 149 | 150 | 151 | void *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes); 152 | void *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes); 153 | void REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr); 154 | void *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size); 155 | char *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str); 156 | int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *); 157 | int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep); 158 | void REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver); 159 | int REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name); 160 | int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx); 161 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll); 162 | int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx); 163 | int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid); 164 | void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode); 165 | void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp); 166 | int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp); 167 | size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp); 168 | int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele); 169 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where); 170 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 171 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len); 172 | void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply); 173 | int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply); 174 | long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply); 175 | size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply); 176 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx); 177 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len); 178 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll); 179 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str); 180 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...); 181 | void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str); 182 | const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len); 183 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err); 184 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg); 185 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len); 186 | void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len); 187 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len); 188 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str); 189 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx); 190 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d); 191 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply); 192 | int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll); 193 | int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d); 194 | void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx); 195 | int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 196 | int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx); 197 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len); 198 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply); 199 | int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key); 200 | int REDISMODULE_API_FUNC(RedisModule_UnlinkKey)(RedisModuleKey *key); 201 | int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str); 202 | char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode); 203 | int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen); 204 | mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key); 205 | int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire); 206 | int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr); 207 | int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore); 208 | int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score); 209 | int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted); 210 | void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key); 211 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 212 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 213 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 214 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 215 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score); 216 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key); 217 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key); 218 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key); 219 | int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...); 220 | int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...); 221 | int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx); 222 | void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos); 223 | unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx); 224 | int REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx); 225 | void *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes); 226 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods); 227 | int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value); 228 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key); 229 | void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key); 230 | void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value); 231 | uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io); 232 | void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value); 233 | int64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io); 234 | void REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...); 235 | void REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s); 236 | void REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len); 237 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io); 238 | char *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr); 239 | void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value); 240 | double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io); 241 | void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value); 242 | float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io); 243 | void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...); 244 | void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...); 245 | int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len); 246 | void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str); 247 | int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b); 248 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io); 249 | long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void); 250 | void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len); 251 | void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele); 252 | void REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md); 253 | 254 | /* Experimental APIs */ 255 | #ifdef REDISMODULE_EXPERIMENTAL_API 256 | RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms); 257 | int REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata); 258 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx); 259 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx); 260 | void *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx); 261 | int REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc); 262 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc); 263 | void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx); 264 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx); 265 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx); 266 | int REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb); 267 | 268 | #endif 269 | 270 | /* This is included inline inside each Redis module. */ 271 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused)); 272 | static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { 273 | void *getapifuncptr = ((void**)ctx)[0]; 274 | RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr; 275 | REDISMODULE_GET_API(Alloc); 276 | REDISMODULE_GET_API(Calloc); 277 | REDISMODULE_GET_API(Free); 278 | REDISMODULE_GET_API(Realloc); 279 | REDISMODULE_GET_API(Strdup); 280 | REDISMODULE_GET_API(CreateCommand); 281 | REDISMODULE_GET_API(SetModuleAttribs); 282 | REDISMODULE_GET_API(IsModuleNameBusy); 283 | REDISMODULE_GET_API(WrongArity); 284 | REDISMODULE_GET_API(ReplyWithLongLong); 285 | REDISMODULE_GET_API(ReplyWithError); 286 | REDISMODULE_GET_API(ReplyWithSimpleString); 287 | REDISMODULE_GET_API(ReplyWithArray); 288 | REDISMODULE_GET_API(ReplySetArrayLength); 289 | REDISMODULE_GET_API(ReplyWithStringBuffer); 290 | REDISMODULE_GET_API(ReplyWithString); 291 | REDISMODULE_GET_API(ReplyWithNull); 292 | REDISMODULE_GET_API(ReplyWithCallReply); 293 | REDISMODULE_GET_API(ReplyWithDouble); 294 | REDISMODULE_GET_API(ReplySetArrayLength); 295 | REDISMODULE_GET_API(GetSelectedDb); 296 | REDISMODULE_GET_API(SelectDb); 297 | REDISMODULE_GET_API(OpenKey); 298 | REDISMODULE_GET_API(CloseKey); 299 | REDISMODULE_GET_API(KeyType); 300 | REDISMODULE_GET_API(ValueLength); 301 | REDISMODULE_GET_API(ListPush); 302 | REDISMODULE_GET_API(ListPop); 303 | REDISMODULE_GET_API(StringToLongLong); 304 | REDISMODULE_GET_API(StringToDouble); 305 | REDISMODULE_GET_API(Call); 306 | REDISMODULE_GET_API(CallReplyProto); 307 | REDISMODULE_GET_API(FreeCallReply); 308 | REDISMODULE_GET_API(CallReplyInteger); 309 | REDISMODULE_GET_API(CallReplyType); 310 | REDISMODULE_GET_API(CallReplyLength); 311 | REDISMODULE_GET_API(CallReplyArrayElement); 312 | REDISMODULE_GET_API(CallReplyStringPtr); 313 | REDISMODULE_GET_API(CreateStringFromCallReply); 314 | REDISMODULE_GET_API(CreateString); 315 | REDISMODULE_GET_API(CreateStringFromLongLong); 316 | REDISMODULE_GET_API(CreateStringFromString); 317 | REDISMODULE_GET_API(CreateStringPrintf); 318 | REDISMODULE_GET_API(FreeString); 319 | REDISMODULE_GET_API(StringPtrLen); 320 | REDISMODULE_GET_API(AutoMemory); 321 | REDISMODULE_GET_API(Replicate); 322 | REDISMODULE_GET_API(ReplicateVerbatim); 323 | REDISMODULE_GET_API(DeleteKey); 324 | REDISMODULE_GET_API(UnlinkKey); 325 | REDISMODULE_GET_API(StringSet); 326 | REDISMODULE_GET_API(StringDMA); 327 | REDISMODULE_GET_API(StringTruncate); 328 | REDISMODULE_GET_API(GetExpire); 329 | REDISMODULE_GET_API(SetExpire); 330 | REDISMODULE_GET_API(ZsetAdd); 331 | REDISMODULE_GET_API(ZsetIncrby); 332 | REDISMODULE_GET_API(ZsetScore); 333 | REDISMODULE_GET_API(ZsetRem); 334 | REDISMODULE_GET_API(ZsetRangeStop); 335 | REDISMODULE_GET_API(ZsetFirstInScoreRange); 336 | REDISMODULE_GET_API(ZsetLastInScoreRange); 337 | REDISMODULE_GET_API(ZsetFirstInLexRange); 338 | REDISMODULE_GET_API(ZsetLastInLexRange); 339 | REDISMODULE_GET_API(ZsetRangeCurrentElement); 340 | REDISMODULE_GET_API(ZsetRangeNext); 341 | REDISMODULE_GET_API(ZsetRangePrev); 342 | REDISMODULE_GET_API(ZsetRangeEndReached); 343 | REDISMODULE_GET_API(HashSet); 344 | REDISMODULE_GET_API(HashGet); 345 | REDISMODULE_GET_API(IsKeysPositionRequest); 346 | REDISMODULE_GET_API(KeyAtPos); 347 | REDISMODULE_GET_API(GetClientId); 348 | REDISMODULE_GET_API(GetContextFlags); 349 | REDISMODULE_GET_API(PoolAlloc); 350 | REDISMODULE_GET_API(CreateDataType); 351 | REDISMODULE_GET_API(ModuleTypeSetValue); 352 | REDISMODULE_GET_API(ModuleTypeGetType); 353 | REDISMODULE_GET_API(ModuleTypeGetValue); 354 | REDISMODULE_GET_API(SaveUnsigned); 355 | REDISMODULE_GET_API(LoadUnsigned); 356 | REDISMODULE_GET_API(SaveSigned); 357 | REDISMODULE_GET_API(LoadSigned); 358 | REDISMODULE_GET_API(SaveString); 359 | REDISMODULE_GET_API(SaveStringBuffer); 360 | REDISMODULE_GET_API(LoadString); 361 | REDISMODULE_GET_API(LoadStringBuffer); 362 | REDISMODULE_GET_API(SaveDouble); 363 | REDISMODULE_GET_API(LoadDouble); 364 | REDISMODULE_GET_API(SaveFloat); 365 | REDISMODULE_GET_API(LoadFloat); 366 | REDISMODULE_GET_API(EmitAOF); 367 | REDISMODULE_GET_API(Log); 368 | REDISMODULE_GET_API(LogIOError); 369 | REDISMODULE_GET_API(StringAppendBuffer); 370 | REDISMODULE_GET_API(RetainString); 371 | REDISMODULE_GET_API(StringCompare); 372 | REDISMODULE_GET_API(GetContextFromIO); 373 | REDISMODULE_GET_API(Milliseconds); 374 | REDISMODULE_GET_API(DigestAddStringBuffer); 375 | REDISMODULE_GET_API(DigestAddLongLong); 376 | REDISMODULE_GET_API(DigestEndSequence); 377 | 378 | #ifdef REDISMODULE_EXPERIMENTAL_API 379 | REDISMODULE_GET_API(GetThreadSafeContext); 380 | REDISMODULE_GET_API(FreeThreadSafeContext); 381 | REDISMODULE_GET_API(ThreadSafeContextLock); 382 | REDISMODULE_GET_API(ThreadSafeContextUnlock); 383 | REDISMODULE_GET_API(BlockClient); 384 | REDISMODULE_GET_API(UnblockClient); 385 | REDISMODULE_GET_API(IsBlockedReplyRequest); 386 | REDISMODULE_GET_API(IsBlockedTimeoutRequest); 387 | REDISMODULE_GET_API(GetBlockedClientPrivateData); 388 | REDISMODULE_GET_API(AbortBlock); 389 | REDISMODULE_GET_API(SubscribeToKeyspaceEvents); 390 | 391 | #endif 392 | 393 | if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR; 394 | RedisModule_SetModuleAttribs(ctx,name,ver,apiver); 395 | return REDISMODULE_OK; 396 | } 397 | 398 | #else 399 | 400 | /* Things only defined for the modules core, not exported to modules 401 | * including this file. */ 402 | #define RedisModuleString robj 403 | 404 | #endif /* REDISMODULE_CORE */ 405 | #endif /* REDISMOUDLE_H */ 406 | -------------------------------------------------------------------------------- /utils.cpp: -------------------------------------------------------------------------------- 1 | // Writed by yijian (eyjian@qq.com) 2 | #include "utils.h" 3 | #include "r3c.h" 4 | #include "sha1.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | std::ostream& operator <<(std::ostream& os, const struct redisReply& redis_reply) 15 | { 16 | if (REDIS_REPLY_STRING == redis_reply.type) 17 | { 18 | os << "type: string" << std::endl 19 | << redis_reply.str << std::endl; 20 | } 21 | else if (REDIS_REPLY_ARRAY == redis_reply.type) 22 | { 23 | os << "type: array" << std::endl; 24 | } 25 | else if (REDIS_REPLY_INTEGER == redis_reply.type) 26 | { 27 | os << "type: integer" << std::endl 28 | << redis_reply.integer << std::endl; 29 | } 30 | else if (REDIS_REPLY_NIL == redis_reply.type) 31 | { 32 | os << "type: nil" << std::endl; 33 | } 34 | else if (REDIS_REPLY_STATUS == redis_reply.type) 35 | { 36 | os << "type: status" << std::endl 37 | << redis_reply.integer << std::endl; 38 | } 39 | else if (REDIS_REPLY_ERROR == redis_reply.type) 40 | { 41 | os << "type: error" << std::endl 42 | << redis_reply.str << std::endl; 43 | } 44 | else 45 | { 46 | os << "type: unknown" << std::endl; 47 | } 48 | 49 | return os; 50 | } 51 | 52 | namespace r3c { 53 | 54 | std::ostream& operator <<(std::ostream& os, const struct NodeInfo& node_info) 55 | { 56 | os << node_info.id << " " << node_info.node.first << ":" << node_info.node.second << " " << node_info.flags << " " 57 | << node_info.master_id << " " << node_info.ping_sent << " " << node_info.pong_recv << " " << node_info.epoch << " "; 58 | 59 | if (node_info.connected) 60 | os << "connected" << " "; 61 | else 62 | os << "disconnected" << " "; 63 | 64 | for (std::vector >::size_type i=0; i 0) 67 | os << " "; 68 | if (node_info.slots[i].first == node_info.slots[i].second) 69 | os << node_info.slots[i].first; 70 | else 71 | os << node_info.slots[i].first << "-" << node_info.slots[i].second; 72 | } 73 | 74 | return os; 75 | } 76 | 77 | std::ostream& operator <<(std::ostream& os, const std::vector& streams) 78 | { 79 | for (std::vector::size_type i=0; i::size_type j=0; j::size_type k=0; k " << fvpair.value << std::endl; 96 | } 97 | } 98 | } 99 | 100 | return os; 101 | } 102 | 103 | std::ostream& operator <<(std::ostream& os, const std::vector& entries) 104 | { 105 | for (std::vector::size_type j=0; j::size_type k=0; k::size_type i=0; i::size_type i=0; i& entries, std::vector* ids) 149 | { 150 | const int num_ids = static_cast(entries.size()); 151 | 152 | if (num_ids > 0) 153 | { 154 | ids->resize(num_ids); 155 | for (int i=0; i> 4) & 0x0f]; 195 | result[j+1] = hex_table[hash[i] & 0x0f]; 196 | } 197 | 198 | #if __cplusplus < 201103L 199 | return result; 200 | #else 201 | return std::move(result); 202 | #endif // __cplusplus < 201103L 203 | } 204 | 205 | void debug_redis_reply(const char* command, const redisReply* redis_reply, int depth, int index) 206 | { 207 | if (0==depth && command!=NULL) 208 | { 209 | fprintf(stderr, "[%d]\033[0;32;31m%s\033[m\n", depth, command); 210 | } 211 | if (NULL == redis_reply) 212 | { 213 | fprintf(stderr, "[%d]REPLY_NULL\n", depth); 214 | } 215 | else 216 | { 217 | const std::string spaces((depth+1)*2, ' '); 218 | 219 | if (REDIS_REPLY_NIL == redis_reply->type) 220 | { 221 | fprintf(stderr, "%s[%d]REPLY_NIL\n", spaces.c_str(), depth); 222 | } 223 | else if (REDIS_REPLY_STATUS == redis_reply->type) 224 | { 225 | fprintf(stderr, "%s[%d]REPLY_STATUS: %s\n", spaces.c_str(), depth, redis_reply->str); 226 | } 227 | else if (REDIS_REPLY_ERROR == redis_reply->type) 228 | { 229 | fprintf(stderr, "%s[%d]REPLY_ERROR: %s\n", spaces.c_str(), depth, redis_reply->str); 230 | } 231 | else if (REDIS_REPLY_INTEGER == redis_reply->type) 232 | { 233 | fprintf(stderr, "%s[%d]REPLY_INTEGER: %lld\n", spaces.c_str(), depth, redis_reply->integer); 234 | } 235 | else if (REDIS_REPLY_STRING == redis_reply->type) 236 | { 237 | // len在hiredis-0.14.0类型由之前的int改成了size_t,加强制类型转换以消除如下编译警告: 238 | // warning: field precision specifier ‘.*’ expects argument of type ‘int’, but argument 6 has type ‘size_t {aka long unsigned int}’ [-Wformat=] 239 | fprintf(stderr, "%s[%d:%d]\033[0;32;32mREPLY_STRING\033[m: %.*s\n", 240 | spaces.c_str(), depth, index, static_cast(redis_reply->len), redis_reply->str); 241 | } 242 | else if (REDIS_REPLY_ARRAY == redis_reply->type) 243 | { 244 | const int depth_ = depth + 1; 245 | 246 | if (0==depth && command!=NULL) 247 | { 248 | fprintf(stderr, "%s[%d]REPLY_ARRAY(%zu)\n", spaces.c_str(), depth, redis_reply->elements); 249 | } 250 | for (size_t i=0; ielements; ++i) 251 | { 252 | const struct redisReply* child_redis_reply = redis_reply->element[i]; 253 | 254 | if (REDIS_REPLY_ARRAY == child_redis_reply->type) 255 | { 256 | fprintf(stderr, "%s%s[%d:%zu]\033[1;33mREPLY_ARRAY\033[m(%zu)\n", spaces.c_str(), spaces.c_str(), depth, i, child_redis_reply->elements); 257 | debug_redis_reply(NULL, child_redis_reply, depth_, i); 258 | } 259 | else if (REDIS_REPLY_INTEGER == child_redis_reply->type) 260 | { 261 | fprintf(stderr, "%s%s[%d:%zu]REPLY_INTEGER: %lld\n", spaces.c_str(), spaces.c_str(), depth, i, child_redis_reply->integer); 262 | } 263 | else if (REDIS_REPLY_STRING == child_redis_reply->type || 264 | REDIS_REPLY_STATUS == redis_reply->type) 265 | { 266 | fprintf(stderr, "%s%s[%d:%zu]\033[0;32;32mREPLY_STRING\033[m: %.*s\n", 267 | spaces.c_str(), spaces.c_str(), depth, i, static_cast(child_redis_reply->len), child_redis_reply->str); 268 | } 269 | else 270 | { 271 | fprintf(stderr, "%s%s[%d:%zu]REPLY_UNKNOWN: %d\n", spaces.c_str(), spaces.c_str(), depth, i, redis_reply->type); 272 | } 273 | } 274 | } 275 | else 276 | { 277 | fprintf(stderr, "REPLY_UNKNOWN: %d\n", redis_reply->type); 278 | } 279 | } 280 | if (command != NULL) 281 | { 282 | fprintf(stderr, "\n"); 283 | } 284 | } 285 | 286 | /* Copied from redis source code (crc16.c) 287 | * 288 | * CRC16 implementation according to CCITT standards. 289 | * 290 | * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the 291 | * following parameters: 292 | * 293 | * Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN" 294 | * Width : 16 bit 295 | * Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1) 296 | * Initialization : 0000 297 | * Reflect Input byte : False 298 | * Reflect Output CRC : False 299 | * Xor constant to output CRC : 0000 300 | * Output for "123456789" : 31C3 301 | */ 302 | 303 | static const uint16_t crc16tab[256]= { 304 | 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 305 | 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 306 | 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 307 | 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 308 | 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 309 | 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 310 | 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 311 | 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 312 | 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 313 | 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 314 | 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 315 | 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 316 | 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 317 | 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 318 | 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 319 | 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 320 | 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 321 | 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 322 | 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 323 | 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 324 | 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 325 | 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 326 | 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 327 | 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 328 | 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 329 | 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 330 | 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 331 | 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 332 | 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 333 | 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 334 | 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 335 | 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 336 | }; 337 | 338 | /* Copied from redis source code (crc16.c) 339 | */ 340 | uint16_t crc16(const char *buf, int len) { 341 | int counter; 342 | uint16_t crc = 0; 343 | for (counter = 0; counter < len; counter++) 344 | crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF]; 345 | return crc; 346 | } 347 | 348 | /* Copied from redis source code (crc64.c) 349 | */ 350 | static const uint64_t crc64_tab[256] = { 351 | UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979), 352 | UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b), 353 | UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6), 354 | UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04), 355 | UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c), 356 | UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe), 357 | UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183), 358 | UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371), 359 | UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8), 360 | UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a), 361 | UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077), 362 | UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285), 363 | UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d), 364 | UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f), 365 | UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02), 366 | UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0), 367 | UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b), 368 | UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489), 369 | UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4), 370 | UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206), 371 | UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e), 372 | UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc), 373 | UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81), 374 | UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73), 375 | UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa), 376 | UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08), 377 | UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75), 378 | UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87), 379 | UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f), 380 | UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d), 381 | UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100), 382 | UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2), 383 | UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416), 384 | UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4), 385 | UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299), 386 | UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b), 387 | UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63), 388 | UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891), 389 | UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec), 390 | UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e), 391 | UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97), 392 | UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965), 393 | UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18), 394 | UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea), 395 | UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2), 396 | UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710), 397 | UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d), 398 | UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f), 399 | UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14), 400 | UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6), 401 | UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b), 402 | UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69), 403 | UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561), 404 | UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793), 405 | UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee), 406 | UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c), 407 | UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495), 408 | UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667), 409 | UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a), 410 | UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8), 411 | UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0), 412 | UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812), 413 | UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f), 414 | UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d), 415 | UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc), 416 | UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e), 417 | UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643), 418 | UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1), 419 | UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9), 420 | UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b), 421 | UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836), 422 | UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4), 423 | UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d), 424 | UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf), 425 | UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2), 426 | UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30), 427 | UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138), 428 | UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca), 429 | UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7), 430 | UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545), 431 | UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce), 432 | UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c), 433 | UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941), 434 | UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3), 435 | UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb), 436 | UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349), 437 | UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734), 438 | UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6), 439 | UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f), 440 | UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd), 441 | UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0), 442 | UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432), 443 | UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a), 444 | UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8), 445 | UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5), 446 | UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47), 447 | UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3), 448 | UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51), 449 | UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c), 450 | UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de), 451 | UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6), 452 | UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124), 453 | UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559), 454 | UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab), 455 | UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222), 456 | UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0), 457 | UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad), 458 | UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f), 459 | UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57), 460 | UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5), 461 | UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8), 462 | UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a), 463 | UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1), 464 | UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053), 465 | UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e), 466 | UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc), 467 | UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4), 468 | UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26), 469 | UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b), 470 | UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9), 471 | UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20), 472 | UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2), 473 | UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf), 474 | UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d), 475 | UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355), 476 | UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7), 477 | UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da), 478 | UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728), 479 | }; 480 | 481 | /* Copied from redis source code (crc64.c) 482 | */ 483 | uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) { 484 | uint64_t j; 485 | 486 | for (j = 0; j < l; j++) { 487 | uint8_t byte = s[j]; 488 | crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8); 489 | } 490 | return crc; 491 | } 492 | 493 | /* Copied from redis source code (cluster.c) 494 | * 495 | * We have 16384 hash slots. The hash slot of a given key is obtained 496 | * as the least significant 14 bits of the crc16 of the key. 497 | * 498 | * However if the key contains the {...} pattern, only the part between 499 | * { and } is hashed. This may be useful in the future to force certain 500 | * keys to be in the same node (assuming no resharding is in progress). 501 | */ 502 | int keyHashSlot(const char *key, size_t keylen) { 503 | size_t s, e; /* start-end indexes of { and } */ 504 | 505 | for (s = 0; s < keylen; s++) 506 | if (key[s] == '{') break; 507 | 508 | /* No '{' ? Hash the whole key. This is the base case. */ 509 | if (s == keylen) return crc16(key,keylen) & 0x3FFF; 510 | 511 | /* '{' found? Check if we have the corresponding '}'. */ 512 | for (e = s+1; e < keylen; e++) 513 | if (key[e] == '}') break; 514 | 515 | /* No '}' or nothing betweeen {} ? Hash the whole key. */ 516 | if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF; 517 | 518 | /* If we are here there is both a { and a } on its right. Hash 519 | * what is in the middle between { and }. */ 520 | return crc16(key+s+1,e-s-1) & 0x3FFF; // 0x3FFF == 16383 521 | } 522 | 523 | // 单机模式,key可以为空 524 | int get_key_slot(const std::string* key) { 525 | if ((key != NULL) && !key->empty()) 526 | { 527 | return keyHashSlot(key->c_str(), key->size()); 528 | } 529 | else 530 | { 531 | struct timeval tv; 532 | gettimeofday(&tv, NULL); 533 | srandom(tv.tv_usec); 534 | return random() & 0x3FFF; 535 | } 536 | } 537 | 538 | bool keys_crossslots(const std::vector& keys) 539 | { 540 | if (!keys.empty()) 541 | { 542 | const int first_key_slot = get_key_slot(&keys[0]); 543 | for (std::vector::size_type i=1; i(buffer.data()); 603 | int expected = 0; 604 | va_list ap; 605 | 606 | while (true) 607 | { 608 | va_start(ap, format); 609 | expected = vsnprintf(buffer_p, size, format, ap); 610 | 611 | va_end(ap); 612 | if (expected>-1 && expected(size)) 613 | { 614 | break; 615 | } 616 | else 617 | { 618 | /* The functions snprintf() and vsnprintf() do not write more than size bytes 619 | * (including the terminating null byte ('\0')). 620 | * 621 | * If the output was truncated due to this limit then the return value is the number of characters 622 | * (excluding the terminating null byte) which would have been written to the final 623 | * string if enough space had been available. 624 | * 625 | * Thus, a return value of size or more means that the output was truncated. 626 | * 627 | * The glibc implementation of the functions snprintf() and vsnprintf() conforms to the C99 standard, 628 | * that is, behaves as described above, since glibc version 2.1. 629 | * Until glibc 2.0.6 they would return -1 when the output was truncated. 630 | */ 631 | 632 | /* Else try again with more space. */ 633 | if (expected > -1) /* glibc 2.1 */ 634 | size = static_cast(expected + 1); /* precisely what is needed */ 635 | else /* glibc 2.0 */ 636 | size *= 2; /* twice the old size */ 637 | 638 | buffer.resize(size); 639 | buffer_p = const_cast(buffer.data()); 640 | } 641 | } 642 | 643 | // expected不包含字符串结尾符号,其值等于:strlen(buffer_p) 644 | #if __cplusplus < 201103L 645 | return std::string(buffer_p, expected>0?expected:0); 646 | #else 647 | return std::move(std::string(buffer_p, expected>0?expected:0)); 648 | #endif // __cplusplus < 201103L 649 | } 650 | 651 | int parse_nodes(std::vector >* nodes, const std::string& nodes_string) 652 | { 653 | std::string::size_type len = 0; 654 | std::string::size_type pos = 0; 655 | std::string::size_type comma_pos = 0; 656 | 657 | nodes->clear(); 658 | while (comma_pos != std::string::npos) 659 | { 660 | comma_pos = nodes_string.find(',', pos); 661 | if (comma_pos != std::string::npos) 662 | len = comma_pos - pos; 663 | else 664 | len = nodes_string.size() - comma_pos; 665 | 666 | if (len > 0) 667 | { 668 | const std::string& str = nodes_string.substr(pos, len); 669 | const std::string::size_type colon_pos = str.find(':'); 670 | if (colon_pos != std::string::npos) 671 | { 672 | const std::string& ip_str = str.substr(0, colon_pos); 673 | const std::string& port_str = str.substr(colon_pos + 1); 674 | nodes->push_back(std::make_pair(ip_str, (uint16_t)atoi(port_str.c_str()))); 675 | } 676 | } 677 | 678 | pos = comma_pos + 1; // Next node 679 | } 680 | 681 | return static_cast(nodes->size()); 682 | } 683 | 684 | int split(std::vector* tokens, const std::string& source, const std::string& sep, bool skip_sep) 685 | { 686 | if (sep.empty()) 687 | { 688 | tokens->push_back(source); 689 | } 690 | else if (!source.empty()) 691 | { 692 | std::string str = source; 693 | std::string::size_type pos = str.find(sep); 694 | 695 | while (true) 696 | { 697 | const std::string& token = str.substr(0, pos); 698 | tokens->push_back(token); 699 | 700 | if (std::string::npos == pos) 701 | { 702 | break; 703 | } 704 | if (skip_sep) 705 | { 706 | bool end = false; 707 | while (0 == strncmp(sep.c_str(), &str[pos+1], sep.size())) 708 | { 709 | pos += sep.size(); 710 | if (pos >= str.size()) 711 | { 712 | end = true; 713 | tokens->push_back(std::string("")); 714 | break; 715 | } 716 | } 717 | 718 | if (end) 719 | break; 720 | } 721 | 722 | str = str.substr(pos + sep.size()); 723 | pos = str.find(sep); 724 | } 725 | } 726 | 727 | return static_cast(tokens->size()); 728 | } 729 | 730 | bool parse_node_string(const std::string& node_string, std::string* ip, uint16_t* port) 731 | { 732 | // node_string在3.0版本时的格式: 127.0.0.1:1381 733 | // node_string在4.0版本时的格式:127.0.0.1:1381@11381 734 | const std::string::size_type colon_pos = node_string.find(':'); 735 | 736 | if (colon_pos == std::string::npos) 737 | { 738 | return false; 739 | } 740 | else 741 | { 742 | const std::string& port_str = node_string.substr(colon_pos+1); 743 | *port = atoi(port_str.c_str()); // 不管是1381或1381@11381,均可正确得到端口号 744 | *ip = node_string.substr(0, colon_pos); 745 | return true; 746 | } 747 | } 748 | 749 | void parse_slot_string(const std::string& slot_string, int* start_slot, int* end_slot) 750 | { 751 | const std::string::size_type bar_pos = slot_string.find('-'); 752 | 753 | if (bar_pos == std::string::npos) 754 | { 755 | *start_slot = atoi(slot_string.c_str()); 756 | *end_slot = *start_slot; 757 | } 758 | else 759 | { 760 | const std::string& end_slot_str = slot_string.substr(bar_pos+1); 761 | *end_slot = atoi(end_slot_str.c_str()); 762 | *start_slot = atoi(slot_string.substr(0, bar_pos).c_str()); 763 | } 764 | } 765 | 766 | // MOVED 9166 10.240.84.140:6379 767 | bool parse_moved_string(const std::string& moved_string, std::pair* node) 768 | { 769 | do 770 | { 771 | const std::string::size_type space_pos = moved_string.rfind(' '); 772 | if (space_pos == std::string::npos) 773 | break; 774 | 775 | const std::string& ip_and_port_string = moved_string.substr(space_pos+1); 776 | const std::string::size_type colon_pos = ip_and_port_string.find(':'); 777 | if (colon_pos == std::string::npos) 778 | break; 779 | 780 | node->first = ip_and_port_string.substr(0, colon_pos); 781 | node->second = (uint16_t)atoi(ip_and_port_string.substr(colon_pos+1).c_str()); 782 | if (0 == node->second) 783 | break; 784 | 785 | return true; 786 | } while(false); 787 | 788 | return false; 789 | } 790 | 791 | /* Copied from redis source code (util.c) 792 | * 793 | * Return the number of digits of 'v' when converted to string in radix 10. 794 | * See ll2string() for more information. */ 795 | static uint32_t digits10(uint64_t v) { 796 | if (v < 10) return 1; 797 | if (v < 100) return 2; 798 | if (v < 1000) return 3; 799 | if (v < UINT64_C(1000000000000)) { 800 | if (v < UINT64_C(100000000)) { 801 | if (v < 1000000) { 802 | if (v < 10000) return 4; 803 | return 5 + (v >= 100000); 804 | } 805 | return 7 + (v >= UINT64_C(10000000)); 806 | } 807 | if (v < UINT64_C(10000000000)) { 808 | return 9 + (v >= UINT64_C(1000000000)); 809 | } 810 | return 11 + (v >= UINT64_C(100000000000)); 811 | } 812 | return 12 + digits10(v / UINT64_C(1000000000000)); 813 | } 814 | 815 | /* Copied from redis source code (util.c) 816 | * 817 | * Convert a long long into a string. Returns the number of 818 | * characters needed to represent the number. 819 | * If the buffer is not big enough to store the string, 0 is returned. 820 | * 821 | * Based on the following article (that apparently does not provide a 822 | * novel approach but only publicizes an already used technique): 823 | * 824 | * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920 825 | * 826 | * Modified in order to handle signed integers since the original code was 827 | * designed for unsigned integers. */ 828 | static int ll2string(char *dst, size_t dstlen, long long svalue) { 829 | static const char digits[201] = 830 | "0001020304050607080910111213141516171819" 831 | "2021222324252627282930313233343536373839" 832 | "4041424344454647484950515253545556575859" 833 | "6061626364656667686970717273747576777879" 834 | "8081828384858687888990919293949596979899"; 835 | int negative; 836 | unsigned long long value; 837 | 838 | /* The main loop works with 64bit unsigned integers for simplicity, so 839 | * we convert the number here and remember if it is negative. */ 840 | if (svalue < 0) { 841 | if (svalue != LLONG_MIN) { 842 | value = -svalue; 843 | } else { 844 | value = ((unsigned long long) LLONG_MAX)+1; 845 | } 846 | negative = 1; 847 | } else { 848 | value = svalue; 849 | negative = 0; 850 | } 851 | 852 | /* Check length. */ 853 | uint32_t const length = digits10(value)+negative; 854 | if (length >= dstlen) return 0; 855 | 856 | /* Null term. */ 857 | uint32_t next = length; 858 | dst[next] = '\0'; 859 | next--; 860 | while (value >= 100) { 861 | int const i = (value % 100) * 2; 862 | value /= 100; 863 | dst[next] = digits[i + 1]; 864 | dst[next - 1] = digits[i]; 865 | next -= 2; 866 | } 867 | 868 | /* Handle last 1-2 digits. */ 869 | if (value < 10) { 870 | dst[next] = '0' + (uint32_t) value; 871 | } else { 872 | int i = (uint32_t) value * 2; 873 | dst[next] = digits[i + 1]; 874 | dst[next - 1] = digits[i]; 875 | } 876 | 877 | /* Add sign. */ 878 | if (negative) dst[0] = '-'; 879 | return length; 880 | } 881 | 882 | /* Copied from redis source code (util.c) 883 | * 884 | * Convert a string into a long long. Returns 1 if the string could be parsed 885 | * into a (non-overflowing) long long, 0 otherwise. The value will be set to 886 | * the parsed value when appropriate. 887 | * 888 | * Note that this function demands that the string strictly represents 889 | * a long long: no spaces or other characters before or after the string 890 | * representing the number are accepted, nor zeroes at the start if not 891 | * for the string "0" representing the zero number. 892 | * 893 | * Because of its strictness, it is safe to use this function to check if 894 | * you can convert a string into a long long, and obtain back the string 895 | * from the number without any loss in the string representation. */ 896 | static int string2ll(const char *s, size_t slen, long long *value) { 897 | const char *p = s; 898 | size_t plen = 0; 899 | int negative = 0; 900 | unsigned long long v; 901 | 902 | /* A zero length string is not a valid number. */ 903 | if (plen == slen) 904 | return 0; 905 | 906 | /* Special case: first and only digit is 0. */ 907 | if (slen == 1 && p[0] == '0') { 908 | if (value != NULL) *value = 0; 909 | return 1; 910 | } 911 | 912 | /* Handle negative numbers: just set a flag and continue like if it 913 | * was a positive number. Later convert into negative. */ 914 | if (p[0] == '-') { 915 | negative = 1; 916 | p++; plen++; 917 | 918 | /* Abort on only a negative sign. */ 919 | if (plen == slen) 920 | return 0; 921 | } 922 | 923 | /* First digit should be 1-9, otherwise the string should just be 0. */ 924 | if (p[0] >= '1' && p[0] <= '9') { 925 | v = p[0]-'0'; 926 | p++; plen++; 927 | } else { 928 | return 0; 929 | } 930 | 931 | /* Parse all the other digits, checking for overflow at every step. */ 932 | while (plen < slen && p[0] >= '0' && p[0] <= '9') { 933 | if (v > (ULLONG_MAX / 10)) /* Overflow. */ 934 | return 0; 935 | v *= 10; 936 | 937 | if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */ 938 | return 0; 939 | v += p[0]-'0'; 940 | 941 | p++; plen++; 942 | } 943 | 944 | /* Return if not all bytes were used. */ 945 | if (plen < slen) 946 | return 0; 947 | 948 | /* Convert to negative if needed, and do the final overflow check when 949 | * converting from unsigned long long to long long. */ 950 | if (negative) { 951 | if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */ 952 | return 0; 953 | if (value != NULL) *value = -v; 954 | } else { 955 | if (v > LLONG_MAX) /* Overflow. */ 956 | return 0; 957 | if (value != NULL) *value = v; 958 | } 959 | return 1; 960 | } 961 | 962 | std::string int2string(int64_t n) 963 | { 964 | std::string str(sizeof("18446744073709551615")+1, '\0'); 965 | char* str_p = const_cast(str.c_str()); 966 | const int len = ll2string(str_p, str.size(), n); 967 | str.resize(len); 968 | 969 | #if __cplusplus < 201103L 970 | return str; 971 | #else 972 | return std::move(str); 973 | #endif // __cplusplus < 201103L 974 | } 975 | 976 | std::string int2string(int32_t n) 977 | { 978 | return int2string(static_cast(n)); 979 | } 980 | 981 | std::string int2string(int16_t n) 982 | { 983 | return int2string(static_cast(n)); 984 | } 985 | 986 | std::string int2string(uint64_t n) 987 | { 988 | return int2string(static_cast(n)); 989 | } 990 | 991 | std::string int2string(uint32_t n) 992 | { 993 | return int2string(static_cast(n)); 994 | } 995 | 996 | std::string int2string(uint16_t n) 997 | { 998 | return int2string(static_cast(n)); 999 | } 1000 | 1001 | bool string2int(const char* s, size_t len, int64_t* val, int64_t errval) 1002 | { 1003 | long long llval = 0; 1004 | if (1 == string2ll(s, len, &llval)) 1005 | { 1006 | *val = static_cast(llval); 1007 | return true; 1008 | } 1009 | else 1010 | { 1011 | *val = errval; 1012 | return false; 1013 | } 1014 | } 1015 | 1016 | bool string2int(const char* s, size_t len, int32_t* val, int32_t errval) 1017 | { 1018 | const int64_t errval64 = errval; 1019 | int64_t val64 = 0; 1020 | 1021 | if (string2int(s, len, &val64, errval64)) 1022 | { 1023 | *val = static_cast(val64); 1024 | return true; 1025 | } 1026 | else 1027 | { 1028 | *val = errval; 1029 | return false; 1030 | } 1031 | } 1032 | 1033 | uint64_t get_random_number(uint64_t base) 1034 | { 1035 | struct timeval tv; 1036 | gettimeofday(&tv, NULL); 1037 | srandom(tv.tv_usec); 1038 | return base + random(); 1039 | } 1040 | 1041 | } // namespace r3c { 1042 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | // Writed by yijian (eyjian@qq.com) 2 | #ifndef REDIS_CLUSTER_CLIENT_UTILS_H 3 | #define REDIS_CLUSTER_CLIENT_UTILS_H 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef __GNUC__ 16 | # define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) 17 | #else 18 | # define UNUSED(x) UNUSED_ ## x 19 | #endif 20 | 21 | #define PRINT_COLOR_NONE "\033[m" 22 | #define PRINT_COLOR_RED "\033[0;32;31m" 23 | #define PRINT_COLOR_YELLOW "\033[1;33m" 24 | #define PRINT_COLOR_BLUE "\033[0;32;34m" 25 | #define PRINT_COLOR_GREEN "\033[0;32;32m" 26 | #define PRINT_COLOR_WHITE "\033[1;37m" 27 | #define PRINT_COLOR_CYAN "\033[0;36m" 28 | #define PRINT_COLOR_PURPLE "\033[0;35m" 29 | #define PRINT_COLOR_BROWN "\033[0;33m" 30 | #define PRINT_COLOR_DARY_GRAY "\033[1;30m" 31 | #define PRINT_COLOR_LIGHT_RED "\033[1;31m" 32 | #define PRINT_COLOR_LIGHT_GREEN "\033[1;32m" 33 | #define PRINT_COLOR_LIGHT_BLUE "\033[1;34m" 34 | #define PRINT_COLOR_LIGHT_CYAN "\033[1;36m" 35 | #define PRINT_COLOR_LIGHT_PURPLE "\033[1;35m" 36 | #define PRINT_COLOR_LIGHT_GRAY "\033[0;37m" 37 | 38 | std::ostream& operator <<(std::ostream& os, const struct redisReply& redis_reply); 39 | 40 | namespace r3c { 41 | extern void null_log_write(const char* UNUSED(format), ...) __attribute__((format(printf, 1, 2))); // Discard log 42 | extern void r3c_log_write(const char* format, ...) __attribute__((format(printf, 1, 2))); // Ouput log to stdout 43 | 44 | extern int keyHashSlot(const char *key, size_t keylen); 45 | extern int parse_nodes(std::vector >* nodes, const std::string& nodes_string); 46 | extern bool parse_node_string(const std::string& node_string, std::string* ip, uint16_t* port); 47 | extern void parse_slot_string(const std::string& slot_string, int* start_slot, int* end_slot); 48 | extern bool parse_moved_string(const std::string& moved_string, std::pair* node); 49 | extern uint64_t get_random_number(uint64_t base); 50 | 51 | } // namespace r3c { 52 | #endif // REDIS_CLUSTER_CLIENT_UTILS_H 53 | -------------------------------------------------------------------------------- /xadd.lua: -------------------------------------------------------------------------------- 1 | -- Batch xadd one by one 2 | -- 3 | -- Usage: 4 | -- redis-cli --no-auth-warning -a PASSWORD -h HOST -p PORT -c --eval ./xadd.lua k , 10 2 f1 v1 f2 v2 f3 v3 f4 v4 f5 v5 5 | -- 6 | -- Compile xadd.lua to C++ code (xadd_lua &xadd_lua_len): 7 | -- xxd -i xadd.lua xadd.cpp 8 | -- 9 | -- KEYS[1] key of stream 10 | -- ARGV[1] maxlen 11 | -- ARGV[2] count 12 | -- ARGV[3] field 13 | -- ARGV[4] value 14 | -- ARGV[5] field 15 | -- ARGV[6] value 16 | local key=KEYS[1] 17 | local maxlen=ARGV[1] 18 | local count=ARGV[2] 19 | 20 | -- table.remove(ARGV,1) 21 | -- redis.call('XADD',key,'MAXLEN','~',maxlen,'*',unpack(ARGV)) 22 | for i=3,#ARGV,2 do 23 | local field=ARGV[i] 24 | local value=ARGV[i+1] 25 | redis.call('XADD',key,'MAXLEN','~',maxlen,'*',field,value) 26 | end 27 | if tonumber(count)>0 then 28 | return redis.call('XRANGE',key,'-','+','COUNT',count) 29 | end 30 | return nil 31 | 32 | --[[ 33 | 34 | extern unsigned char xadd_lua[]; 35 | extern unsigned int xadd_lua_len; 36 | 37 | inline void xadd( 38 | CRedisClient* redis, const std::string& key, 39 | int64_t maxlen, int64_t count, 40 | const std::vector& fvpairs, std::vector* values, 41 | Node* which=NULL, int num_retries=0) 42 | { 43 | static std::string xadd_lua_script(reinterpret_cast(xadd_lua), static_cast(xadd_lua_len)); 44 | std::vector parameters; 45 | 46 | parameters.emplace_back(int2string(maxlen)); 47 | parameters.emplace_back(int2string(count)); 48 | for (auto& fvpair: fvpairs) 49 | { 50 | parameters.emplace_back(fvpair.field); 51 | parameters.emplace_back(fvpair.value); 52 | } 53 | const RedisReplyHelper redis_reply = redis->eval(key, xadd_lua_script, parameters, which, num_retries); 54 | if (redis_reply->type != REDIS_REPLY_NIL) 55 | CRedisClient::get_values(redis_reply.get(), values); 56 | } 57 | 58 | --]] 59 | --------------------------------------------------------------------------------