├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── github │ └── fangzy │ ├── redis │ ├── AbstractCallback.java │ ├── JLock.java │ ├── JedisCallbackFilter.java │ ├── JedisHolder.java │ ├── JedisProxy.java │ ├── JedisResource.java │ ├── Redis.java │ ├── RedisAdvisor.java │ ├── RedisInterceptor.java │ ├── jedis │ │ ├── JedisCallback.java │ │ └── JedisFactoryBean.java │ └── shard │ │ ├── ShardedJedisCallback.java │ │ └── ShardedJedisFactoryBean.java │ └── redisconfig │ ├── DefaultRedisConfiguration.java │ └── DefaultShardedRedisConfiguration.java └── test ├── java └── com │ └── github │ └── fangzy │ ├── redis │ ├── AbstractTests.java │ ├── JLockTest.java │ ├── JedisHolderTest.java │ ├── JedisNested.java │ ├── JedisServiceDemo.java │ ├── JedisTest.java │ ├── ShardedJedisDemo.java │ ├── ShardedJedisInterface.java │ └── ShardedJedisTest.java │ └── redisconfig │ └── TestRedisConfiguration.java └── resources ├── logback-test.xml └── redisPool.properties /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /*.iml 3 | /target 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | services: 5 | - redis-server 6 | install: 7 | - "wget http://download.redis.io/releases/redis-3.0.6.tar.gz" 8 | - "tar xzf redis-3.0.6.tar.gz" 9 | - "cd redis-3.0.6" 10 | - "make" 11 | - "src/redis-server --port 6380 --daemonize yes" 12 | - "cd .." 13 | script: 14 | - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 15 | - mvn test -B -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | spring-redis-plugin [![Build Status](https://travis-ci.org/fangzy/spring-redis-plugin.svg?branch=master)](https://travis-ci.org/fangzy/spring-redis-plugin) [![Release](https://jitpack.io/v/fangzy/spring-redis-plugin.svg)](https://jitpack.io/#fangzy/spring-redis-plugin) 2 | =========== 3 | 本项目是[Jedis](https://github.com/xetorthio/jedis)基于spring的增强版,实现了自动获取连接和关闭连接,提供基于redis的分布式锁实现. 4 | 5 | ## 1 Maven POM 配置 6 | 7 | 使用[Jitpack](https://jitpack.io/#fangzy/spring-redis-plugin)仓库 8 | 9 | ### 1.1 添加仓库地址 10 | 11 | ```xml 12 | 13 | 14 | jitpack.io 15 | https://jitpack.io 16 | 17 | 18 | ``` 19 | 20 | ### 1.2 添加依赖 21 | 22 | ```xml 23 | 24 | com.github.fangzy 25 | spring-redis-plugin 26 | 1.5.1 27 | 28 | ``` 29 | 30 | ### 1.3 依赖`spring-context`项目 31 | 32 | ```xml 33 | 34 | org.springframework 35 | spring-context 36 | 4.X.X.RELEASE 37 | 38 | 39 | commons-logging 40 | commons-logging 41 | 42 | 43 | 44 | ``` 45 | 46 | ## 2 Jedis使用方法 47 | 48 | ### 2.1 Spring Configuration配置文件 49 | 50 | > 直接使用默认配置 51 | 52 | ```java 53 | @Configuration 54 | @PropertySource("classpath:redisPool.properties") 55 | @Import(DefaultRedisConfiguration.class) 56 | public class RedisConfig { 57 | 58 | } 59 | 60 | ``` 61 | 62 | > 自定义配置参见[`DefaultRedisConfiguration`](/src/main/java/com/github/fangzy/redisconfig/DefaultRedisConfiguration.java)、[`DefaultShardedRedisConfiguration`](/src/main/java/com/github/fangzy/redisconfig/DefaultShardedRedisConfiguration.java) 63 | 64 | ### 2.2 Spring XML配置文件 65 | 66 | ```xml 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | ``` 118 | 119 | > 请按照需要选择配置 120 | 121 | ### 2.3 依赖注入 122 | 123 | ```java 124 | @Service 125 | public class JedisDemo { 126 | 127 | @Autowired 128 | private Jedis jedis; 129 | 130 | public void setAndGet(String key,int times) { 131 | for (int i=0;i 如果需要使用pipelined,watch,unwatch,multi 方法必须开启注解,详见2.4 141 | 142 | ### 2.4 使用注解 143 | 144 | > jedisPool的default为默认数据源,必须存在 145 | 146 | ```java 147 | @Service 148 | public class JedisDemo { 149 | 150 | @Autowired 151 | private Jedis jedis; 152 | 153 | @Redis 154 | public void incr(String key,int times) { 155 | Pipeline pipeline = jedis.pipelined(); 156 | pipeline.set(key, "1"); 157 | for (int i = 0; i < times; i++) { 158 | pipeline.incr(key); 159 | } 160 | Response response = pipeline.get(key); 161 | pipeline.sync(); 162 | jedis.del(key); 163 | } 164 | } 165 | ``` 166 | 167 | ### 2.5 使用JedisProxy 168 | 169 | ```java 170 | public class JedisDemo { 171 | 172 | public static void setAndGet(String key,int times) { 173 | Jedis jedis = JedisProxy.create(); 174 | for (int i=0;i JedisProxy 通常用于静态方法,但是需要在spring容器全部加载完毕后使用 184 | 如果需要使用pipelined,watch,unwatch,multi 方法必须开启注解,详见2.4 185 | 186 | ### 2.6 使用多数据源 187 | 188 | ```java 189 | @Service 190 | public class JedisDemo { 191 | 192 | @Autowired 193 | private Jedis jedis; 194 | 195 | @Redis("anotherRedis") 196 | public void incr(String key,int times) { 197 | Pipeline pipeline = jedis.pipelined(); 198 | pipeline.set(key, "1"); 199 | for (int i = 0; i < times; i++) { 200 | pipeline.incr(key); 201 | } 202 | Response response = pipeline.get(key); 203 | pipeline.sync(); 204 | jedis.del(key); 205 | } 206 | } 207 | ``` 208 | 209 | > java config 使用`@Bean(name="anotherRedis")`来定义数据源名称 210 | 211 | ```java 212 | @Configuration 213 | public class RedisConfiguration { 214 | @Bean(name = "default", destroyMethod = "destroy") 215 | public JedisPool defaultJedisPool() { 216 | return new JedisPool(defaultJedisPoolConfig(), 217 | env.getProperty("redis.host", "127.0.0.1"), 218 | env.getProperty("redis.port", Integer.class, 6379), 219 | Protocol.DEFAULT_TIMEOUT, 220 | StringUtils.isEmpty(env.getProperty("redis.password")) ? null : env.getProperty("redis.password")); 221 | } 222 | 223 | @Bean(name = "anotherRedis", destroyMethod = "destroy") 224 | public JedisPool testJedisPool() { 225 | return new JedisPool(defaultJedisPoolConfig(), 226 | env.getProperty("redis.host2", "127.0.0.1"), 227 | env.getProperty("redis.port2", Integer.class, 6379), 228 | Protocol.DEFAULT_TIMEOUT, 229 | StringUtils.isEmpty(env.getProperty("redis.password2")) ? null : env.getProperty("redis.password2")); 230 | } 231 | } 232 | 233 | ``` 234 | 235 | > xml配置 236 | 237 | ```xml 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | ``` 249 | 250 | > jedisPool的default为默认数据源,必须存在 251 | 252 | ### 2.7 使用shard 253 | 254 | ```java 255 | @Service 256 | public class ShardedJedisDemo { 257 | 258 | @Autowired 259 | private ShardedJedis shardedJedis; 260 | 261 | @Redis(shard = true) 262 | public void incr(String key, int times) { 263 | ShardedJedisPipeline pipeline = shardedJedis.pipelined(); 264 | for (int i = 0; i < times; i++) { 265 | pipeline.incr(key + i); 266 | } 267 | Response response = pipeline.get(key + 1); 268 | pipeline.sync(); 269 | LOGGER.info(response.get()); 270 | } 271 | } 272 | ``` 273 | 274 | > java config配置 275 | 276 | ```java 277 | @Configuration 278 | public class RedisConfiguration { 279 | @Bean(destroyMethod = "destroy") 280 | public ShardedJedisPool defaultShardedJedisPool() { 281 | String hoststr = env.getProperty("redis.sharded.hosts", "127.0.0.1"); 282 | String portstr = env.getProperty("redis.sharded.ports", "6379"); 283 | List hosts = Arrays.asList(hoststr.split(",")); 284 | List ports = Stream.of(portstr.split(",")).map(Integer::new).collect(Collectors.toList()); 285 | 286 | List list = IntStream.range(0, hosts.size()).mapToObj(i -> new JedisShardInfo(hosts.get(i), ports.get(i))).collect(Collectors.toList()); 287 | return new ShardedJedisPool(defaultJedisPoolConfig(), list); 288 | } 289 | } 290 | ``` 291 | 292 | > xml配置 293 | 294 | ```xml 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | ``` 315 | 316 | ## 3 JLock分布式锁 317 | 318 | - void waitLock(String key) 一直等待解锁 319 | - boolean getLock(String key) 获得锁,超时退出 320 | - boolean checkLock(String key) 检查锁是否存在,立即返回 321 | - void releaseLock(String key) 手动释放锁 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.github.fangzy 6 | 7 | spring-redis-plugin 8 | 1.5.2 9 | jar 10 | 11 | spring-redis-plugin 12 | Jedis plugin for spring 13 | https://github.com/fangzy/spring-redis-plugin 14 | 15 | 16 | 17 | 18 | The Apache Software License, Version 2.0 19 | http://www.apache.org/licenses/LICENSE-2.0.txt 20 | 21 | 22 | 23 | scm:git:git://github.com/fangzy/spring-redis-plugin.git 24 | scm:git:git@github.com:fangzy/spring-redis-plugin.git 25 | git@github.com:fangzy/spring-redis-plugin.git 26 | 1.5.RELEASE 27 | 28 | 29 | 30 | 31 | fangzy 32 | zyuanf@gmail.com 33 | 34 | 35 | 36 | 37 | GitHub Issues 38 | https://github.com/fangzy/spring-redis-plugin/issues 39 | 40 | 41 | 42 | UTF-8 43 | UTF-8 44 | 4.3.14.RELEASE 45 | 2.9.0 46 | 1.7.25 47 | 48 | 49 | 50 | 51 | 52 | org.springframework 53 | spring-context 54 | ${org.springframework-version} 55 | provided 56 | 57 | 58 | org.aspectj 59 | aspectjweaver 60 | 1.8.13 61 | compile 62 | 63 | 64 | 65 | 66 | redis.clients 67 | jedis 68 | ${jedis.version} 69 | compile 70 | 71 | 72 | 73 | 74 | org.slf4j 75 | slf4j-api 76 | ${slf4j-api.version} 77 | compile 78 | 79 | 80 | org.slf4j 81 | log4j-over-slf4j 82 | ${slf4j-api.version} 83 | provided 84 | 85 | 86 | ch.qos.logback 87 | logback-classic 88 | 1.2.3 89 | provided 90 | 91 | 92 | org.slf4j 93 | jcl-over-slf4j 94 | ${slf4j-api.version} 95 | provided 96 | 97 | 98 | 99 | 100 | org.springframework 101 | spring-test 102 | ${org.springframework-version} 103 | test 104 | 105 | 106 | junit 107 | junit 108 | 4.12 109 | test 110 | 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-compiler-plugin 118 | 3.2 119 | 120 | 1.8 121 | 1.8 122 | 123 | 124 | 125 | org.apache.maven.plugins 126 | maven-source-plugin 127 | 2.4 128 | 129 | 130 | attach-sources 131 | 132 | jar-no-fork 133 | 134 | 135 | 136 | 137 | 138 | org.apache.maven.plugins 139 | maven-javadoc-plugin 140 | 2.10.1 141 | 142 | 143 | attach-javadocs 144 | 145 | jar 146 | 147 | 148 | 149 | 150 | 151 | org.apache.maven.plugins 152 | maven-release-plugin 153 | 2.5.1 154 | 155 | @{project.version} 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/AbstractCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis; 18 | 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.cglib.proxy.MethodInterceptor; 21 | 22 | import java.lang.reflect.Method; 23 | 24 | /** 25 | * Created on 2014/9/26. 26 | * 27 | * @author FZY 28 | */ 29 | public abstract class AbstractCallback implements MethodInterceptor { 30 | 31 | @Autowired 32 | protected JedisHolder jedisHolder; 33 | 34 | protected void filterMethod(Method method) { 35 | boolean status = jedisHolder.hasJedis(); 36 | filterNoSupportMethod(method, "pipelined", status); 37 | filterNoSupportMethod(method, "watch", status); 38 | filterNoSupportMethod(method, "unwatch", status); 39 | filterNoSupportMethod(method, "multi", status); 40 | } 41 | 42 | protected void filterNoSupportMethod(Method method, String methodName, boolean status) { 43 | if (method.getName().equals(methodName) && !status) { 44 | throw new UnsupportedOperationException("Jedis proxy does not support " + methodName + " method. Please use @Redis annotation."); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/JLock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis; 18 | 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import redis.clients.jedis.Jedis; 22 | 23 | /** 24 | * redis实现的分布式锁 25 | * Created by fzy on 2014/9/1. 26 | */ 27 | public final class JLock { 28 | 29 | public static final long EXPIRE = 5000L; 30 | private static final String LOCK = "redis:lock:%s"; 31 | private static final Logger LOGGER = LoggerFactory.getLogger(JLock.class); 32 | 33 | private JLock() { 34 | 35 | } 36 | 37 | /** 38 | * 获得锁,未取得时一直阻塞,超时时间默认5000ms; 39 | * 40 | * @param id 41 | */ 42 | public static void waitLock(String id) { 43 | getLock(id, 0); 44 | } 45 | 46 | /** 47 | * 获得锁,超时退出 48 | * 49 | * @param id 50 | * @param timeout 超时时间(ms) 51 | * @return 52 | */ 53 | public static boolean getLock(String id, long timeout) { 54 | Jedis jedis = JedisProxy.create(); 55 | long lock = 0; 56 | long start = System.currentTimeMillis(); 57 | long pexpire = timeout > 0 ? timeout : EXPIRE * 1000; 58 | while (lock != 1) { 59 | long now = System.currentTimeMillis(); 60 | //判断超时 61 | if (timeout > 0 && now > start + timeout) { 62 | return false; 63 | } 64 | long timestamp = now + EXPIRE + 1; 65 | String key = String.format(LOCK, id); 66 | lock = jedis.setnx(key, String.valueOf(timestamp)); 67 | if (lock == 1) { 68 | jedis.pexpire(key, pexpire); 69 | LOGGER.trace("redis lock setnx"); 70 | } else { 71 | sleep(); 72 | } 73 | } 74 | return true; 75 | } 76 | 77 | public static boolean getLock(String id) { 78 | return getLock(id, EXPIRE); 79 | } 80 | 81 | /** 82 | * 检查锁是否存在,立刻返回 83 | * 84 | * @param id 85 | * @param timeout 86 | * @return 87 | */ 88 | public static boolean checkLock(String id, long timeout) { 89 | Jedis jedis = JedisProxy.create(); 90 | String key = String.format(LOCK, id); 91 | long exists = jedis.setnx(key, String.valueOf(System.currentTimeMillis())); 92 | if (exists == 0) { 93 | return false; 94 | } 95 | jedis.pexpire(key, timeout); 96 | return true; 97 | } 98 | 99 | public static boolean checkLock(String id) { 100 | return checkLock(id, EXPIRE); 101 | } 102 | 103 | private static void sleep() { 104 | try { 105 | Thread.sleep(50); 106 | } catch (InterruptedException e) { 107 | LOGGER.error("An unexpected error occurred.", e); 108 | } 109 | } 110 | 111 | /** 112 | * 释放锁 113 | * 114 | * @param id 115 | */ 116 | public static void releaseLock(String id) { 117 | Jedis jedis = JedisProxy.create(); 118 | String key = String.format(LOCK, id); 119 | String val = jedis.get(key); 120 | if (val == null) { 121 | return; 122 | } 123 | long lastLockTime = Long.parseLong(val); 124 | if (lastLockTime > System.currentTimeMillis() / 1000) { 125 | jedis.del(key); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/JedisCallbackFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis; 18 | 19 | import org.springframework.cglib.proxy.CallbackFilter; 20 | 21 | import java.lang.reflect.Method; 22 | import java.util.concurrent.ThreadLocalRandom; 23 | 24 | /** 25 | * Created on 2014/9/26. 26 | * 27 | * @author FZY 28 | */ 29 | public class JedisCallbackFilter implements CallbackFilter { 30 | 31 | private int id = ThreadLocalRandom.current().nextInt(); 32 | 33 | @Override 34 | public int accept(Method method) { 35 | if ("finalize".equals(method.getName()) && 36 | method.getParameterTypes().length == 0 && 37 | method.getReturnType() == Void.TYPE) { 38 | return 0; 39 | } 40 | return 1; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) { 46 | return true; 47 | } 48 | if (!(o instanceof JedisCallbackFilter)) { 49 | return false; 50 | } 51 | 52 | JedisCallbackFilter that = (JedisCallbackFilter) o; 53 | 54 | return id == that.id; 55 | 56 | } 57 | 58 | @Override 59 | public int hashCode() { 60 | return id; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/JedisHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis; 18 | 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.beans.BeansException; 22 | import org.springframework.beans.factory.InitializingBean; 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | import org.springframework.context.ApplicationContext; 25 | import org.springframework.context.ApplicationContextAware; 26 | import org.springframework.core.NamedThreadLocal; 27 | import org.springframework.stereotype.Component; 28 | import redis.clients.jedis.Jedis; 29 | import redis.clients.jedis.JedisPool; 30 | import redis.clients.jedis.ShardedJedis; 31 | import redis.clients.jedis.ShardedJedisPool; 32 | 33 | import java.util.Map; 34 | 35 | /** 36 | * Created on 2014/9/26. 37 | * 38 | * @author FZY 39 | */ 40 | @Component 41 | public class JedisHolder implements InitializingBean, ApplicationContextAware { 42 | 43 | public static final String DEFAULT = "default"; 44 | private static final Logger LOGGER = LoggerFactory.getLogger(JedisHolder.class); 45 | private static final NamedThreadLocal> JEDIS_THREAD_LOCAL = new NamedThreadLocal>("jedis resource holder") { 46 | @Override 47 | protected JedisResource initialValue() { 48 | return new JedisResource<>(null); 49 | } 50 | }; 51 | 52 | private static final NamedThreadLocal> SHARDED_JEDIS_THREAD_LOCAL = new NamedThreadLocal>("shardedJedis resource holder") { 53 | @Override 54 | protected JedisResource initialValue() { 55 | return new JedisResource<>(null); 56 | } 57 | }; 58 | 59 | private Map jedisPoolMap; 60 | 61 | private ShardedJedisPool shardedJedisPool; 62 | 63 | @Autowired 64 | private RedisInterceptor redisInterceptor; 65 | 66 | public Jedis get() { 67 | JedisResource jedisResource = JEDIS_THREAD_LOCAL.get(); 68 | if (jedisResource.hasJedis()) { 69 | return jedisResource.getJedis(); 70 | } 71 | return jedisPoolMap.get(DEFAULT).getResource(); 72 | } 73 | 74 | public ShardedJedis getShardedJedis() { 75 | JedisResource jedisResource = SHARDED_JEDIS_THREAD_LOCAL.get(); 76 | if (jedisResource.hasJedis()) { 77 | return jedisResource.getJedis(); 78 | } else { 79 | throw new IllegalArgumentException("no sharded jedis connection."); 80 | } 81 | } 82 | 83 | public boolean hasJedis() { 84 | return JEDIS_THREAD_LOCAL.get().hasJedis(); 85 | } 86 | 87 | public boolean hasShardJedis() { 88 | return SHARDED_JEDIS_THREAD_LOCAL.get().hasJedis(); 89 | } 90 | 91 | public void createResource(String val) { 92 | Jedis jedis; 93 | JedisResource jedisResource = JEDIS_THREAD_LOCAL.get(); 94 | if (jedisResource.hasJedis()) { 95 | jedisResource.incrementAndGet(); 96 | return; 97 | } 98 | if (val.isEmpty()) { 99 | jedis = jedisPoolMap.get(DEFAULT).getResource(); 100 | } else { 101 | jedis = jedisPoolMap.get(val).getResource(); 102 | } 103 | jedisResource.setJedis(jedis); 104 | } 105 | 106 | public void createShardedResource() { 107 | JedisResource jedisResource = SHARDED_JEDIS_THREAD_LOCAL.get(); 108 | if (jedisResource.hasJedis()) { 109 | jedisResource.incrementAndGet(); 110 | return; 111 | } 112 | ShardedJedis shardedJedis = shardedJedisPool.getResource(); 113 | jedisResource.setJedis(shardedJedis); 114 | } 115 | 116 | public void release(Jedis jedis) { 117 | if (JEDIS_THREAD_LOCAL.get().hasJedis()) { 118 | return; 119 | } 120 | returnResource(jedis); 121 | JEDIS_THREAD_LOCAL.remove(); 122 | } 123 | 124 | public void releaseForce() { 125 | JedisResource jedisResource = JEDIS_THREAD_LOCAL.get(); 126 | if (!jedisResource.hasJedis()) { 127 | return; 128 | } 129 | int c = jedisResource.decrementAndGet(); 130 | if (c == 0) { 131 | returnResource(jedisResource.getJedis()); 132 | JEDIS_THREAD_LOCAL.remove(); 133 | } 134 | } 135 | 136 | public void releaseShardedForce() { 137 | JedisResource jedisResource = SHARDED_JEDIS_THREAD_LOCAL.get(); 138 | if (!jedisResource.hasJedis()) { 139 | return; 140 | } 141 | int c = jedisResource.decrementAndGet(); 142 | if (c > 0) { 143 | return; 144 | } 145 | ShardedJedis shardedJedis = jedisResource.getJedis(); 146 | shardedJedis.close(); 147 | SHARDED_JEDIS_THREAD_LOCAL.remove(); 148 | } 149 | 150 | private void returnResource(Jedis jedis) { 151 | if (jedis != null) { 152 | jedis.close(); 153 | } 154 | } 155 | 156 | public void setJedisPoolMap(Map jedisPoolMap) { 157 | this.jedisPoolMap = jedisPoolMap; 158 | } 159 | 160 | public void setShardedJedisPool(ShardedJedisPool shardedJedisPool) { 161 | this.shardedJedisPool = shardedJedisPool; 162 | } 163 | 164 | public int getConnectionNum(String connName) { 165 | return jedisPoolMap.getOrDefault(connName, jedisPoolMap.get(DEFAULT)).getNumActive(); 166 | } 167 | 168 | @Override 169 | public void afterPropertiesSet() throws Exception { 170 | redisInterceptor.setJedisHolder(this); 171 | } 172 | 173 | @Override 174 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 175 | try { 176 | Map jedisPoolMap = applicationContext.getBeansOfType(JedisPool.class); 177 | setJedisPoolMap(jedisPoolMap); 178 | ShardedJedisPool shardedJedisPool = applicationContext.getBean(ShardedJedisPool.class); 179 | setShardedJedisPool(shardedJedisPool); 180 | } catch (BeansException e) { 181 | LOGGER.warn(e.getMessage()); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/JedisProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis; 18 | 19 | import org.springframework.context.ApplicationContext; 20 | import org.springframework.context.ApplicationContextAware; 21 | import org.springframework.stereotype.Component; 22 | import redis.clients.jedis.Jedis; 23 | 24 | /** 25 | * Jedis代理 26 | * Created by fzy on 2014/7/6. 27 | */ 28 | @Component 29 | public class JedisProxy implements ApplicationContextAware { 30 | 31 | private static volatile ApplicationContext ac; 32 | 33 | /** 34 | * 创建Jedis 代理 35 | * 36 | * @return 37 | */ 38 | public static Jedis create() { 39 | return ac.getBean(Jedis.class); 40 | } 41 | 42 | @Override 43 | public void setApplicationContext(ApplicationContext applicationContext) { 44 | if (ac != null) { 45 | return; 46 | } 47 | synchronized (JedisProxy.class) { 48 | if (ac != null) { 49 | return; 50 | } 51 | ac = applicationContext; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/JedisResource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis; 18 | 19 | import redis.clients.jedis.BinaryJedisCommands; 20 | 21 | /** 22 | * Created on 2014/12/11. 23 | * 24 | * @author FZY 25 | */ 26 | public class JedisResource { 27 | 28 | private T t; 29 | 30 | private int connectionNum = 0; 31 | 32 | public JedisResource(T t) { 33 | this.t = t; 34 | } 35 | 36 | public T getJedis() { 37 | return t; 38 | } 39 | 40 | public void setJedis(T t) { 41 | this.t = t; 42 | incrementAndGet(); 43 | } 44 | 45 | public int incrementAndGet() { 46 | this.connectionNum++; 47 | return connectionNum; 48 | } 49 | 50 | public int decrementAndGet() { 51 | this.connectionNum--; 52 | return connectionNum; 53 | } 54 | 55 | public boolean hasJedis() { 56 | return t != null; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/Redis.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis; 18 | 19 | import java.lang.annotation.*; 20 | 21 | /** 22 | * JedisProxy注解 23 | *

使用注解后支持pipelined,watch,unwatch,multi方法

24 | * Created on 2014/9/26. 25 | * 26 | * @author FZY 27 | */ 28 | @Target({ElementType.METHOD}) 29 | @Retention(RetentionPolicy.RUNTIME) 30 | @Inherited 31 | @Documented 32 | public @interface Redis { 33 | 34 | String value() default ""; 35 | 36 | boolean shard() default false; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/RedisAdvisor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis; 18 | 19 | import org.aopalliance.aop.Advice; 20 | import org.springframework.aop.Pointcut; 21 | import org.springframework.aop.support.AbstractPointcutAdvisor; 22 | import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | import org.springframework.stereotype.Component; 25 | 26 | /** 27 | * Created on 2014/9/26. 28 | * 29 | * @author FZY 30 | */ 31 | @Component 32 | public class RedisAdvisor extends AbstractPointcutAdvisor { 33 | 34 | private Pointcut pointcut = new AnnotationMatchingPointcut(null, Redis.class); 35 | 36 | @Autowired 37 | private RedisInterceptor redisInterceptor; 38 | 39 | @Override 40 | public Pointcut getPointcut() { 41 | return pointcut; 42 | } 43 | 44 | @Override 45 | public Advice getAdvice() { 46 | return redisInterceptor; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/RedisInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis; 18 | 19 | import org.aopalliance.intercept.MethodInterceptor; 20 | import org.aopalliance.intercept.MethodInvocation; 21 | import org.springframework.stereotype.Component; 22 | 23 | /** 24 | * Created on 2014/9/27. 25 | * 26 | * @author FZY 27 | */ 28 | @Component 29 | public class RedisInterceptor implements MethodInterceptor { 30 | 31 | private JedisHolder jedisHolder; 32 | 33 | @Override 34 | public Object invoke(MethodInvocation invocation) throws Throwable { 35 | Redis redis = invocation.getMethod().getDeclaredAnnotation(Redis.class); 36 | if (redis == null) { 37 | throw new IllegalArgumentException("Can not find @Redis annotation."); 38 | } 39 | if (redis.shard()) { 40 | return shardedRedis(invocation); 41 | } else { 42 | return normalRedis(invocation, redis); 43 | } 44 | } 45 | 46 | private Object shardedRedis(MethodInvocation invocation) throws Throwable { 47 | try { 48 | jedisHolder.createShardedResource(); 49 | return invocation.proceed(); 50 | } finally { 51 | jedisHolder.releaseShardedForce(); 52 | } 53 | } 54 | 55 | private Object normalRedis(MethodInvocation invocation, Redis redis) throws Throwable { 56 | String val = redis.value(); 57 | try { 58 | jedisHolder.createResource(val); 59 | return invocation.proceed(); 60 | } finally { 61 | jedisHolder.releaseForce(); 62 | } 63 | } 64 | 65 | public void setJedisHolder(JedisHolder jedisHolder) { 66 | this.jedisHolder = jedisHolder; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/jedis/JedisCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis.jedis; 18 | 19 | import com.github.fangzy.redis.AbstractCallback; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.springframework.cglib.proxy.MethodProxy; 23 | import org.springframework.stereotype.Component; 24 | import redis.clients.jedis.Jedis; 25 | 26 | import java.lang.reflect.Method; 27 | 28 | /** 29 | * Jedis拦截器 30 | * Created by fzy on 2014/7/6. 31 | */ 32 | @Component 33 | public class JedisCallback extends AbstractCallback { 34 | 35 | private static final Logger LOGGER = LoggerFactory.getLogger(JedisCallback.class); 36 | 37 | @Override 38 | public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 39 | Jedis jedis = null; 40 | filterMethod(method); 41 | try { 42 | jedis = jedisHolder.get(); 43 | return methodProxy.invoke(jedis, objects); 44 | } catch (Exception e) { 45 | LOGGER.error(e.getMessage()); 46 | throw e; 47 | } finally { 48 | release(jedis); 49 | } 50 | } 51 | 52 | private void release(Jedis jedis) { 53 | if (jedis == null) { 54 | return; 55 | } 56 | jedisHolder.release(jedis); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/jedis/JedisFactoryBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis.jedis; 18 | 19 | import com.github.fangzy.redis.JedisCallbackFilter; 20 | import org.springframework.beans.factory.FactoryBean; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.cglib.proxy.Callback; 23 | import org.springframework.cglib.proxy.CallbackFilter; 24 | import org.springframework.cglib.proxy.Enhancer; 25 | import org.springframework.cglib.proxy.NoOp; 26 | import org.springframework.stereotype.Component; 27 | import redis.clients.jedis.Jedis; 28 | 29 | /** 30 | * Jedis代理 31 | * Created by fzy on 2014/7/6. 32 | */ 33 | @Component 34 | public class JedisFactoryBean implements FactoryBean { 35 | 36 | private final CallbackFilter finalizeFilter = new JedisCallbackFilter(); 37 | @Autowired 38 | private JedisCallback jedisCallback; 39 | private Jedis jedis; 40 | 41 | @Override 42 | public Jedis getObject() throws Exception { 43 | if (jedis != null) { 44 | return jedis; 45 | } 46 | Enhancer en = new Enhancer(); 47 | en.setSuperclass(Jedis.class); 48 | en.setCallbackFilter(finalizeFilter); 49 | en.setCallbacks(new Callback[]{NoOp.INSTANCE, jedisCallback}); 50 | jedis = (Jedis) en.create(new Class[]{String.class}, new Object[]{"JedisProxy"}); 51 | return jedis; 52 | } 53 | 54 | @Override 55 | public Class getObjectType() { 56 | return Jedis.class; 57 | } 58 | 59 | @Override 60 | public boolean isSingleton() { 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/shard/ShardedJedisCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis.shard; 18 | 19 | import com.github.fangzy.redis.AbstractCallback; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.springframework.cglib.proxy.MethodProxy; 23 | import org.springframework.stereotype.Component; 24 | import redis.clients.jedis.ShardedJedis; 25 | 26 | import java.lang.reflect.Method; 27 | 28 | /** 29 | * ShardedJedis拦截器 30 | * Created by fzy on 2014/7/6. 31 | */ 32 | @Component 33 | public class ShardedJedisCallback extends AbstractCallback { 34 | 35 | private static final Logger LOGGER = LoggerFactory.getLogger(ShardedJedisCallback.class); 36 | 37 | @Override 38 | public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 39 | ShardedJedis jedis; 40 | boolean status = jedisHolder.hasShardJedis(); 41 | if (!status) { 42 | throw new UnsupportedOperationException("ShardedJedis proxy need use @Redis annotation."); 43 | } 44 | try { 45 | jedis = jedisHolder.getShardedJedis(); 46 | return methodProxy.invoke(jedis, objects); 47 | } catch (Exception e) { 48 | LOGGER.error(e.getMessage()); 49 | throw e; 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redis/shard/ShardedJedisFactoryBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 ,fangzy (zyuanf@gmail.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.fangzy.redis.shard; 18 | 19 | import com.github.fangzy.redis.JedisCallbackFilter; 20 | import org.springframework.beans.factory.FactoryBean; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.cglib.proxy.Callback; 23 | import org.springframework.cglib.proxy.CallbackFilter; 24 | import org.springframework.cglib.proxy.Enhancer; 25 | import org.springframework.cglib.proxy.NoOp; 26 | import org.springframework.stereotype.Component; 27 | import redis.clients.jedis.JedisShardInfo; 28 | import redis.clients.jedis.ShardedJedis; 29 | 30 | import java.util.Arrays; 31 | import java.util.List; 32 | 33 | /** 34 | * ShardedJedis代理 35 | * Created by fzy on 2014/7/6. 36 | */ 37 | @Component 38 | public class ShardedJedisFactoryBean implements FactoryBean { 39 | 40 | private final CallbackFilter finalizeFilter = new JedisCallbackFilter(); 41 | @Autowired 42 | private ShardedJedisCallback jedisCallback; 43 | private ShardedJedis jedis; 44 | 45 | @Override 46 | public ShardedJedis getObject() throws Exception { 47 | if (jedis != null) { 48 | return jedis; 49 | } 50 | Enhancer en = new Enhancer(); 51 | en.setSuperclass(ShardedJedis.class); 52 | en.setCallbackFilter(finalizeFilter); 53 | en.setCallbacks(new Callback[]{NoOp.INSTANCE, jedisCallback}); 54 | jedis = (ShardedJedis) en.create(new Class[]{List.class}, new Object[]{Arrays.asList(new JedisShardInfo("shardedJedisProxy"))}); 55 | return jedis; 56 | } 57 | 58 | @Override 59 | public Class getObjectType() { 60 | return ShardedJedis.class; 61 | } 62 | 63 | @Override 64 | public boolean isSingleton() { 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redisconfig/DefaultRedisConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redisconfig; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 10 | import org.springframework.core.env.Environment; 11 | import org.springframework.util.StringUtils; 12 | import redis.clients.jedis.JedisPool; 13 | import redis.clients.jedis.JedisPoolConfig; 14 | import redis.clients.jedis.Protocol; 15 | 16 | @Configuration 17 | @ComponentScan("com.github.fangzy.redis") 18 | @EnableAspectJAutoProxy(proxyTargetClass = true) 19 | public class DefaultRedisConfiguration { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRedisConfiguration.class); 22 | 23 | @Autowired 24 | private Environment env; 25 | 26 | @Bean 27 | public JedisPoolConfig defaultJedisPoolConfig() { 28 | JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); 29 | jedisPoolConfig.setMaxTotal(env.getProperty("redis.maxTotal", Integer.class, 200)); 30 | jedisPoolConfig.setMaxIdle(env.getProperty("redis.maxIdle", Integer.class, 20)); 31 | jedisPoolConfig.setMinIdle(env.getProperty("redis.minIdle", Integer.class, 0)); 32 | jedisPoolConfig.setMaxWaitMillis(env.getProperty("redis.maxWait", Integer.class, 3000)); 33 | jedisPoolConfig.setMinEvictableIdleTimeMillis(env.getProperty("redis.minEvictableIdleTimeMillis", Long.class, 60000L)); 34 | jedisPoolConfig.setTimeBetweenEvictionRunsMillis(env.getProperty("redis.timeBetweenEvictionRunsMillis", Long.class, 120000L)); 35 | jedisPoolConfig.setNumTestsPerEvictionRun(env.getProperty("redis.numTestsPerEvictionRun", Integer.class, 1)); 36 | jedisPoolConfig.setTestOnBorrow(env.getProperty("redis.testOnBorrow", Boolean.class, false)); 37 | jedisPoolConfig.setTestOnReturn(env.getProperty("redis.testOnReturn", Boolean.class, false)); 38 | jedisPoolConfig.setTestWhileIdle(env.getProperty("redis.testWhileIdle", Boolean.class, true)); 39 | return jedisPoolConfig; 40 | } 41 | 42 | @Bean(name = "default", destroyMethod = "destroy") 43 | public JedisPool defaultJedisPool() { 44 | return new JedisPool(defaultJedisPoolConfig(), 45 | env.getProperty("redis.host", "127.0.0.1"), 46 | env.getProperty("redis.port", Integer.class, 6379), 47 | Protocol.DEFAULT_TIMEOUT, 48 | StringUtils.isEmpty(env.getProperty("redis.password")) ? null : env.getProperty("redis.password")); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/fangzy/redisconfig/DefaultShardedRedisConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redisconfig; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 10 | import org.springframework.core.env.Environment; 11 | import redis.clients.jedis.JedisPoolConfig; 12 | import redis.clients.jedis.JedisShardInfo; 13 | import redis.clients.jedis.ShardedJedisPool; 14 | 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.stream.Collectors; 18 | import java.util.stream.IntStream; 19 | import java.util.stream.Stream; 20 | 21 | @Configuration 22 | @ComponentScan("com.github.fangzy.redis") 23 | @EnableAspectJAutoProxy(proxyTargetClass = true) 24 | public class DefaultShardedRedisConfiguration { 25 | 26 | private static final Logger LOGGER = LoggerFactory.getLogger(DefaultShardedRedisConfiguration.class); 27 | 28 | @Autowired 29 | private Environment env; 30 | 31 | @Bean 32 | public JedisPoolConfig defaultJedisPoolConfig() { 33 | JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); 34 | jedisPoolConfig.setMaxTotal(env.getProperty("redis.maxTotal", Integer.class, 200)); 35 | jedisPoolConfig.setMaxIdle(env.getProperty("redis.maxIdle", Integer.class, 20)); 36 | jedisPoolConfig.setMinIdle(env.getProperty("redis.minIdle", Integer.class, 0)); 37 | jedisPoolConfig.setMaxWaitMillis(env.getProperty("redis.maxWait", Integer.class, 3000)); 38 | jedisPoolConfig.setMinEvictableIdleTimeMillis(env.getProperty("redis.minEvictableIdleTimeMillis", Long.class, 60000L)); 39 | jedisPoolConfig.setTimeBetweenEvictionRunsMillis(env.getProperty("redis.timeBetweenEvictionRunsMillis", Long.class, 120000L)); 40 | jedisPoolConfig.setNumTestsPerEvictionRun(env.getProperty("redis.numTestsPerEvictionRun", Integer.class, 1)); 41 | jedisPoolConfig.setTestOnBorrow(env.getProperty("redis.testOnBorrow", Boolean.class, false)); 42 | jedisPoolConfig.setTestOnReturn(env.getProperty("redis.testOnReturn", Boolean.class, false)); 43 | jedisPoolConfig.setTestWhileIdle(env.getProperty("redis.testWhileIdle", Boolean.class, true)); 44 | return jedisPoolConfig; 45 | } 46 | 47 | @Bean(destroyMethod = "destroy") 48 | public ShardedJedisPool defaultShardedJedisPool() { 49 | String hoststr = env.getProperty("redis.sharded.hosts", "127.0.0.1"); 50 | String portstr = env.getProperty("redis.sharded.ports", "6379"); 51 | List hosts = Arrays.asList(hoststr.split(",")); 52 | List ports = Stream.of(portstr.split(",")).map(Integer::new).collect(Collectors.toList()); 53 | 54 | List list = IntStream.range(0, hosts.size()).mapToObj(i -> new JedisShardInfo(hosts.get(i), ports.get(i))).collect(Collectors.toList()); 55 | return new ShardedJedisPool(defaultJedisPoolConfig(), list); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redis/AbstractTests.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redis; 2 | 3 | import com.github.fangzy.redisconfig.TestRedisConfiguration; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.test.context.ContextConfiguration; 6 | import org.springframework.test.context.TestPropertySource; 7 | import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; 8 | 9 | /** 10 | * Created on 2014/9/27. 11 | * 12 | * @author FZY 13 | */ 14 | @Configuration 15 | @TestPropertySource("classpath:redisPool.properties") 16 | @ContextConfiguration(classes = TestRedisConfiguration.class) 17 | public abstract class AbstractTests extends AbstractJUnit4SpringContextTests { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redis/JLockTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redis; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import redis.clients.jedis.Jedis; 10 | 11 | import java.util.concurrent.*; 12 | 13 | /** 14 | * Created on 2014/9/27. 15 | * 16 | * @author FZY 17 | */ 18 | public class JLockTest extends AbstractTests { 19 | private static final Logger LOGGER = LoggerFactory.getLogger(JLockTest.class); 20 | 21 | @Autowired 22 | private Jedis jedis; 23 | 24 | @Before 25 | public void setUp() throws Exception { 26 | jedis.del("redis:lock:test"); 27 | } 28 | 29 | @Test 30 | public void testGetLock() throws Exception { 31 | ExecutorService exec = Executors.newCachedThreadPool(); 32 | CompletionService completionService = new ExecutorCompletionService<>(exec); 33 | for (int n = 0; n < 20; n++) { 34 | completionService.submit(new TestThread(0)); 35 | } 36 | int i = 0; 37 | for (int n = 0; n < 20; n++) { 38 | i += completionService.take().get(); 39 | } 40 | exec.shutdown(); 41 | Assert.assertEquals(20, i); 42 | } 43 | 44 | @Test 45 | public void testGetLock1() throws Exception { 46 | ExecutorService exec = Executors.newFixedThreadPool(25); 47 | CompletionService completionService = new ExecutorCompletionService<>(exec); 48 | JLock.getLock("test", 5000); 49 | for (int n = 0; n < 20; n++) { 50 | completionService.submit(new TestThread(2000)); 51 | } 52 | int i = 0; 53 | for (int n = 0; n < 20; n++) { 54 | i += completionService.take().get(); 55 | } 56 | exec.shutdown(); 57 | Assert.assertEquals(0, i); 58 | } 59 | 60 | @Test 61 | public void testCheckLock() throws Exception { 62 | ExecutorService exec = Executors.newCachedThreadPool(); 63 | CompletionService completionService = new ExecutorCompletionService<>(exec); 64 | for (int n = 0; n < 10; n++) { 65 | completionService.submit(new TestCheckLockThread(2000)); 66 | } 67 | int i = 0; 68 | for (int n = 0; n < 10; n++) { 69 | i += completionService.take().get(); 70 | } 71 | exec.shutdown(); 72 | Assert.assertEquals(1, i); 73 | } 74 | 75 | @Test 76 | public void testReleaseLock() throws Exception { 77 | JLock.getLock("test"); 78 | boolean existsBefore = jedis.exists("redis:lock:test"); 79 | JLock.releaseLock("test"); 80 | boolean existsAfter = jedis.exists("redis:lock:test"); 81 | Assert.assertTrue(existsBefore); 82 | Assert.assertFalse(existsAfter); 83 | } 84 | 85 | class TestThread implements Callable { 86 | 87 | private final Logger logger = LoggerFactory.getLogger(getClass()); 88 | 89 | private int i = 0; 90 | 91 | public TestThread(int i) { 92 | this.i = i; 93 | } 94 | 95 | @Override 96 | public Integer call() throws Exception { 97 | int m = 0; 98 | try { 99 | logger.debug("start"); 100 | if (!JLock.getLock("test", i)) { 101 | logger.debug("timeout"); 102 | m = 0; 103 | } else { 104 | logger.debug("get lock"); 105 | if (i == 0) { 106 | Thread.sleep(1000); 107 | JLock.releaseLock("test"); 108 | } 109 | m = 1; 110 | } 111 | } catch (Exception e) { 112 | logger.error("An unexpected error occurred.", e); 113 | } 114 | return m; 115 | } 116 | } 117 | 118 | class TestCheckLockThread implements Callable { 119 | 120 | private final Logger logger = LoggerFactory.getLogger(getClass()); 121 | 122 | private int i = 0; 123 | 124 | public TestCheckLockThread(int i) { 125 | this.i = i; 126 | } 127 | 128 | @Override 129 | public Integer call() throws Exception { 130 | int m = 0; 131 | try { 132 | logger.debug("start"); 133 | if (!JLock.checkLock("test", i)) { 134 | logger.debug("return"); 135 | m = 0; 136 | } else { 137 | logger.debug("get lock"); 138 | Thread.sleep(1000); 139 | m = 1; 140 | } 141 | } catch (Exception e) { 142 | logger.error("An unexpected error occurred.", e); 143 | } 144 | return m; 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redis/JedisHolderTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redis; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | 9 | /** 10 | * Created on 2014/12/11. 11 | * 12 | * @author FZY 13 | */ 14 | public class JedisHolderTest extends AbstractTests { 15 | private static final Logger LOGGER = LoggerFactory.getLogger(JedisHolderTest.class); 16 | 17 | @Autowired 18 | private JedisNested jedisNested; 19 | 20 | @Autowired 21 | private JedisHolder jedisHolder; 22 | 23 | private int times = 5; 24 | 25 | private String key = "test:jedis"; 26 | 27 | @Test 28 | public void testCreateAndRelease() { 29 | jedisNested.incr(key, times); 30 | LOGGER.info(String.valueOf(jedisHolder.getConnectionNum(null))); 31 | Assert.assertEquals(0, jedisHolder.getConnectionNum(null)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redis/JedisNested.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redis; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * Created on 2014/12/11. 10 | * 11 | * @author FZY 12 | */ 13 | @Service 14 | public class JedisNested { 15 | private static final Logger LOGGER = LoggerFactory.getLogger(JedisNested.class); 16 | 17 | @Autowired 18 | private JedisServiceDemo demo; 19 | 20 | @Autowired 21 | private ShardedJedisDemo shardedDemo; 22 | 23 | @Redis 24 | public void incr(String key, int times) { 25 | for (int i = 0; i < 100; i++) { 26 | demo.incr(key, times); 27 | shardedDemo.incr(key, times); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redis/JedisServiceDemo.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redis; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import redis.clients.jedis.Jedis; 8 | import redis.clients.jedis.Pipeline; 9 | import redis.clients.jedis.Response; 10 | 11 | /** 12 | * Created on 2014/9/26. 13 | * 14 | * @author FZY 15 | */ 16 | @Service 17 | public class JedisServiceDemo { 18 | private static final Logger LOGGER = LoggerFactory.getLogger(JedisServiceDemo.class); 19 | 20 | @Autowired 21 | private Jedis jedis; 22 | 23 | @Redis 24 | public void incr(String key, int times) { 25 | Pipeline pipeline = jedis.pipelined(); 26 | pipeline.set(key, "1"); 27 | for (int i = 0; i < times; i++) { 28 | pipeline.incr(key + i); 29 | } 30 | Response response = pipeline.get(key + 1); 31 | pipeline.sync(); 32 | LOGGER.info(response.get()); 33 | jedis.del(key); 34 | } 35 | 36 | public void setAndGet(String key, int times) { 37 | for (int i = 0; i < times; i++) { 38 | jedis.set(key, "test1"); 39 | jedis.get(key); 40 | } 41 | LOGGER.info(jedis.get(key)); 42 | jedis.del(key); 43 | } 44 | 45 | @Redis("test") 46 | public void setAndGet2(String key, int times) { 47 | for (int i = 0; i < times; i++) { 48 | jedis.set(key, "test1"); 49 | jedis.get(key); 50 | } 51 | LOGGER.info(jedis.get(key)); 52 | jedis.del(key); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redis/JedisTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redis; 2 | 3 | import org.junit.Test; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.annotation.Qualifier; 8 | import org.springframework.util.StopWatch; 9 | import redis.clients.jedis.Jedis; 10 | import redis.clients.jedis.JedisPool; 11 | import redis.clients.jedis.Pipeline; 12 | import redis.clients.jedis.Response; 13 | 14 | /** 15 | * Created on 2014/9/26. 16 | * 17 | * @author FZY 18 | */ 19 | public class JedisTest extends AbstractTests { 20 | private static final Logger LOGGER = LoggerFactory.getLogger(JedisTest.class); 21 | 22 | @Autowired 23 | private JedisServiceDemo demo; 24 | 25 | @Autowired 26 | @Qualifier("default") 27 | private JedisPool jedisPool; 28 | 29 | private int times = 10000; 30 | 31 | private String key = "test:jedis"; 32 | 33 | @Test 34 | public void testPipelineBenchmark() throws Exception { 35 | StopWatch stopWatch = new StopWatch("jedis pipeline benchmark"); 36 | demo.incr(key, times); 37 | jedisIncr(key, times); 38 | 39 | System.gc(); 40 | Thread.sleep(5000); 41 | 42 | stopWatch.start("jedis normal pipeline"); 43 | jedisIncr(key, times); 44 | stopWatch.stop(); 45 | 46 | System.gc(); 47 | Thread.sleep(5000); 48 | 49 | stopWatch.start("jedis proxy pipeline"); 50 | demo.incr(key, times); 51 | stopWatch.stop(); 52 | 53 | LOGGER.info(stopWatch.prettyPrint()); 54 | LOGGER.info("jedis pool info:{}", jedisPool.getNumActive()); 55 | } 56 | 57 | @Test 58 | public void testSetAndGetBenchmark() throws Exception { 59 | StopWatch stopWatch = new StopWatch("jedis setAndGet benchmark"); 60 | demo.setAndGet(key, times / 100); 61 | demo.setAndGet2(key, times / 100); 62 | jedisSetAndGet(key, times / 100); 63 | 64 | System.gc(); 65 | Thread.sleep(5000); 66 | 67 | stopWatch.start("jedis proxy setAndGet"); 68 | demo.setAndGet(key, times / 100); 69 | stopWatch.stop(); 70 | 71 | System.gc(); 72 | Thread.sleep(5000); 73 | 74 | stopWatch.start("jedis normal setAndGet"); 75 | jedisSetAndGet(key, times / 100); 76 | stopWatch.stop(); 77 | 78 | System.gc(); 79 | Thread.sleep(5000); 80 | 81 | stopWatch.start("jedis proxy setAndGet2"); 82 | demo.setAndGet2(key, times / 100); 83 | stopWatch.stop(); 84 | 85 | LOGGER.info(stopWatch.prettyPrint()); 86 | LOGGER.info("jedis pool info:{}", jedisPool.getNumActive()); 87 | } 88 | 89 | private void jedisIncr(String key, int times) { 90 | Jedis jedis = null; 91 | try { 92 | jedis = jedisPool.getResource(); 93 | Pipeline pipeline = jedis.pipelined(); 94 | pipeline.set(key, "1"); 95 | for (int i = 0; i < times; i++) { 96 | pipeline.incr(key); 97 | } 98 | Response response = pipeline.get(key); 99 | pipeline.sync(); 100 | LOGGER.info(response.get()); 101 | jedis.del(key); 102 | } catch (Exception e) { 103 | LOGGER.error(e.getMessage(), e); 104 | } finally { 105 | release(jedis); 106 | } 107 | } 108 | 109 | 110 | private void jedisSetAndGet(String key, int times) { 111 | Jedis jedis = null; 112 | try { 113 | jedis = jedisPool.getResource(); 114 | for (int i = 0; i < times; i++) { 115 | jedis.set(key, "test1"); 116 | jedis.get(key); 117 | } 118 | LOGGER.info(jedis.get(key)); 119 | jedis.del(key); 120 | } catch (Exception e) { 121 | LOGGER.error(e.getMessage(), e); 122 | } finally { 123 | release(jedis); 124 | } 125 | } 126 | 127 | private void release(Jedis jedis) { 128 | if (jedis != null) { 129 | jedis.close(); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redis/ShardedJedisDemo.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redis; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import redis.clients.jedis.Response; 8 | import redis.clients.jedis.ShardedJedis; 9 | import redis.clients.jedis.ShardedJedisPipeline; 10 | 11 | /** 12 | * Created on 2014/10/30. 13 | * 14 | * @author FZY 15 | */ 16 | @Service 17 | public class ShardedJedisDemo implements ShardedJedisInterface { 18 | private static final Logger LOGGER = LoggerFactory.getLogger(ShardedJedisDemo.class); 19 | 20 | @Autowired 21 | private ShardedJedis shardedJedis; 22 | 23 | @Override 24 | @Redis(shard = true) 25 | public void incr(String key, int times) { 26 | ShardedJedisPipeline pipeline = shardedJedis.pipelined(); 27 | for (int i = 0; i < times; i++) { 28 | pipeline.incr(key + i); 29 | } 30 | Response response = pipeline.get(key + 1); 31 | pipeline.sync(); 32 | LOGGER.info(response.get()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redis/ShardedJedisInterface.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redis; 2 | 3 | /** 4 | * Created on 2014/10/30. 5 | * 6 | * @author FZY 7 | */ 8 | public interface ShardedJedisInterface { 9 | 10 | void incr(String key, int times); 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redis/ShardedJedisTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redis; 2 | 3 | import org.junit.Test; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.annotation.Qualifier; 8 | import org.springframework.util.StopWatch; 9 | import redis.clients.jedis.Jedis; 10 | import redis.clients.jedis.JedisPool; 11 | 12 | import java.util.Set; 13 | 14 | /** 15 | * Created on 2014/10/30. 16 | * 17 | * @author FZY 18 | */ 19 | public class ShardedJedisTest extends AbstractTests { 20 | private static final Logger LOGGER = LoggerFactory.getLogger(ShardedJedisTest.class); 21 | 22 | @Autowired 23 | private ShardedJedisInterface shardedJedisDemo; 24 | 25 | @Autowired 26 | private JedisServiceDemo demo; 27 | 28 | private String key = "test:shardedJedis"; 29 | 30 | private int times = 100000; 31 | 32 | @Autowired 33 | @Qualifier("default") 34 | private JedisPool jedisPool; 35 | 36 | @Autowired 37 | @Qualifier("test") 38 | private JedisPool jedisPool6380; 39 | 40 | @Test 41 | public void shardedPiplineTest() { 42 | StopWatch stopWatch = new StopWatch("jedis pipeline benchmark"); 43 | 44 | stopWatch.start("jedis proxy pipeline"); 45 | demo.incr(key, times); 46 | stopWatch.stop(); 47 | 48 | stopWatch.start("shardedJedis proxy pipeline"); 49 | shardedJedisDemo.incr(key, times); 50 | stopWatch.stop(); 51 | 52 | LOGGER.info(stopWatch.prettyPrint()); 53 | 54 | Jedis jedis1 = jedisPool.getResource(); 55 | Set set1 = jedis1.keys("test:shardedJedis*"); 56 | LOGGER.info(String.valueOf(set1.size())); 57 | jedis1.del(set1.toArray(new String[set1.size()])); 58 | 59 | Jedis jedis2 = jedisPool6380.getResource(); 60 | Set set2 = jedis2.keys("test:shardedJedis*"); 61 | LOGGER.info(String.valueOf(set2.size())); 62 | jedis2.del(set2.toArray(new String[set2.size()])); 63 | 64 | jedis1.close(); 65 | jedis2.close(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/com/github/fangzy/redisconfig/TestRedisConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.fangzy.redisconfig; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 10 | import org.springframework.core.env.Environment; 11 | import org.springframework.util.StringUtils; 12 | import redis.clients.jedis.*; 13 | 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.IntStream; 18 | import java.util.stream.Stream; 19 | 20 | @Configuration 21 | @ComponentScan("com.github.fangzy.redis") 22 | @EnableAspectJAutoProxy(proxyTargetClass = true) 23 | public class TestRedisConfiguration { 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(TestRedisConfiguration.class); 26 | 27 | @Autowired 28 | private Environment env; 29 | 30 | @Bean 31 | public JedisPoolConfig defaultJedisPoolConfig() { 32 | JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); 33 | jedisPoolConfig.setMaxTotal(env.getProperty("redis.maxTotal", Integer.class, 200)); 34 | jedisPoolConfig.setMaxIdle(env.getProperty("redis.maxIdle", Integer.class, 20)); 35 | jedisPoolConfig.setMinIdle(env.getProperty("redis.minIdle", Integer.class, 0)); 36 | jedisPoolConfig.setMaxWaitMillis(env.getProperty("redis.maxWait", Integer.class, 3000)); 37 | jedisPoolConfig.setMinEvictableIdleTimeMillis(env.getProperty("redis.minEvictableIdleTimeMillis", Long.class, 60000L)); 38 | jedisPoolConfig.setTimeBetweenEvictionRunsMillis(env.getProperty("redis.timeBetweenEvictionRunsMillis", Long.class, 120000L)); 39 | jedisPoolConfig.setNumTestsPerEvictionRun(env.getProperty("redis.numTestsPerEvictionRun", Integer.class, 1)); 40 | jedisPoolConfig.setTestOnBorrow(env.getProperty("redis.testOnBorrow", Boolean.class, false)); 41 | jedisPoolConfig.setTestOnReturn(env.getProperty("redis.testOnReturn", Boolean.class, false)); 42 | jedisPoolConfig.setTestWhileIdle(env.getProperty("redis.testWhileIdle", Boolean.class, true)); 43 | return jedisPoolConfig; 44 | } 45 | 46 | @Bean(name = "default", destroyMethod = "destroy") 47 | public JedisPool defaultJedisPool() { 48 | return new JedisPool(defaultJedisPoolConfig(), 49 | env.getProperty("redis.host", "127.0.0.1"), 50 | env.getProperty("redis.port", Integer.class, 6379), 51 | Protocol.DEFAULT_TIMEOUT, 52 | StringUtils.isEmpty(env.getProperty("redis.password")) ? null : env.getProperty("redis.password")); 53 | } 54 | 55 | @Bean(name = "test", destroyMethod = "destroy") 56 | public JedisPool testJedisPool() { 57 | return new JedisPool(defaultJedisPoolConfig(), 58 | env.getProperty("redis.host2", "127.0.0.1"), 59 | env.getProperty("redis.port2", Integer.class, 6379), 60 | Protocol.DEFAULT_TIMEOUT, 61 | StringUtils.isEmpty(env.getProperty("redis.password2")) ? null : env.getProperty("redis.password2")); 62 | } 63 | 64 | @Bean(destroyMethod = "destroy") 65 | public ShardedJedisPool defaultShardedJedisPool() { 66 | String hoststr = env.getProperty("redis.sharded.hosts", "127.0.0.1"); 67 | String portstr = env.getProperty("redis.sharded.ports", "6379"); 68 | List hosts = Arrays.asList(hoststr.split(",")); 69 | List ports = Stream.of(portstr.split(",")).map(Integer::new).collect(Collectors.toList()); 70 | 71 | List list = IntStream.range(0, hosts.size()).mapToObj(i -> new JedisShardInfo(hosts.get(i), ports.get(i))).collect(Collectors.toList()); 72 | return new ShardedJedisPool(defaultJedisPoolConfig(), list); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %date [%thread] %-5level %logger{80} - %msg%n 6 | UTF-8 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/test/resources/redisPool.properties: -------------------------------------------------------------------------------- 1 | redis.maxTotal=2000 2 | redis.maxIdle=6 3 | redis.maxWait=15000 4 | redis.minEvictableIdleTimeMillis=100000 5 | redis.minIdle=0 6 | redis.numTestsPerEvictionRun=1 7 | redis.testOnBorrow =false 8 | redis.testOnReturn =false 9 | redis.testWhileIdle=true 10 | redis.timeBetweenEvictionRunsMillis=120000 11 | redis.host=127.0.0.1 12 | redis.port=6379 13 | redis.password= 14 | redis.host2=127.0.0.1 15 | redis.port2=6380 16 | redis.sharded.hosts=127.0.0.1,127.0.0.1 17 | redis.sharded.ports=6379,6380 --------------------------------------------------------------------------------