├── .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 |
--------------------------------------------------------------------------------