├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml └── src ├── example ├── java │ └── com │ │ └── wmz7year │ │ └── thrift │ │ └── pool │ │ └── example │ │ ├── Example.java │ │ ├── ExampleServer.java │ │ └── Other.java └── thrift │ ├── example.thrift │ └── other.thrift ├── main └── java │ └── com │ └── wmz7year │ └── thrift │ └── pool │ ├── AbstractThriftConnectionStrategy.java │ ├── AcquireFailConfig.java │ ├── CustomThreadFactory.java │ ├── DefaultThriftConnectionStrategy.java │ ├── PoolWatchThread.java │ ├── TaskEngine.java │ ├── ThriftConnectionHandle.java │ ├── ThriftConnectionMaxAgeThread.java │ ├── ThriftConnectionPartition.java │ ├── ThriftConnectionPool.java │ ├── ThriftConnectionStrategy.java │ ├── ThriftConnectionTesterThread.java │ ├── config │ ├── ThriftConnectionPoolConfig.java │ └── ThriftServerInfo.java │ ├── connection │ ├── DefaultThriftConnection.java │ ├── MulitServiceThriftConnecion.java │ └── ThriftConnection.java │ └── exception │ └── ThriftConnectionPoolException.java └── test └── java └── com └── wmz7year └── thrift └── pool ├── BasicAbstractTest.java ├── CheckConnectionAlive.java ├── DynamicServersTest.java ├── GetConnectionFromServerFailedCounterTest.java ├── MultiplexedServiceTest.java ├── NoThriftServerStartPoolTest.java ├── ThrfitConnectionPoolRemoveServerStopThreadTest.java ├── ThriftServerBindIDTest.java ├── TimeoutConnectionTest.java └── example ├── Example.java └── Other.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | #eclipse 7 | .classpath 8 | .project 9 | .settings/ 10 | 11 | #maven 12 | target/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.ear 18 | 19 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 20 | hs_err_pid* 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | - oraclejdk7 5 | 6 | script: 7 | - mvn clean package install 8 | -------------------------------------------------------------------------------- /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 | #Thrift连接池实现 2 | 3 | [![Join the chat at https://gitter.im/wmz7year/Thrift-Connection-Pool](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/wmz7year/Thrift-Connection-Pool?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | [![Build Status](https://travis-ci.org/wmz7year/Thrift-Connection-Pool.svg)](https://travis-ci.org/wmz7year/Thrift-Connection-Pool) 5 | 6 | ##特性 7 | * 1、支持服务器之间的负载均衡
8 | * 2、每个服务器拥有一个独立的连接分区 所有的连接分区合并一起为整个连接池
9 | * 3、连接池支持自动创建连接、管理超时连接、管理失效连接
10 | * 4、支持服务器列表动态增加或者移除
11 | * 5、支持自动调取ping方法(在thrift描述文件添加方法void ping(),)检测连接可用性
12 | * 6、支持当服务不可用时自动将对应的服务器剔除连接池的功能
13 | * 7、添加多服务接口支持
14 | 15 | ###下载 16 | ><dependency> 17 | >        <groupId>com.github.wmz7year</groupId> 18 | >        <artifactId>ThriftConnectionPool</artifactId> 19 | >        <version>1.0.6-RELEASE</version> 20 | ></dependency> 21 | 22 | 23 | ###示例 24 | ####单服务示例 25 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(); 26 | config.setConnectTimeout(3000); 27 | config.setThriftProtocol(TProtocolType.BINARY); 28 | config.setClientClass(Example.Client.class); 29 | config.addThriftServer("127.0.0.1", 9119); 30 | config.setMaxConnectionPerServer(2); 31 | config.setMinConnectionPerServer(1); 32 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 33 | config.setMaxConnectionAge(2); 34 | config.setLazyInit(false); 35 | try { 36 | ThriftConnectionPool pool = new ThriftConnectionPool(config); 37 | Example.Client client = pool.getConnection().getClient(); 38 | client.ping(); 39 | pool.close(); 40 | } catch (ThriftConnectionPoolException e) { 41 | e.printStackTrace(); 42 | } catch (TException e) { 43 | e.printStackTrace(); 44 | } catch (IOException e) { 45 | e.printStackTrace(); 46 | } 47 | 48 | ####多接口服务示例 49 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(ThriftServiceType.MULTIPLEXED_INTERFACE); 50 | config.setConnectTimeout(3000); 51 | config.setThriftProtocol(TProtocolType.BINARY); 52 | config.addThriftServer("127.0.0.1", 9119); 53 | config.addThriftClientClass("other", Other.Client.class); 54 | config.addThriftClientClass("example", Example.Client.class); 55 | 56 | config.setMaxConnectionPerServer(2); 57 | config.setMinConnectionPerServer(1); 58 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 59 | config.setMaxConnectionAge(2); 60 | config.setLazyInit(false); 61 | config.setAcquireIncrement(2); 62 | config.setAcquireRetryDelay(2000); 63 | 64 | config.setAcquireRetryAttempts(1); 65 | config.setMaxConnectionCreateFailedCount(1); 66 | config.setConnectionTimeoutInMs(5000); 67 | 68 | config.check(); 69 | 70 | ThriftConnectionPool pool = new ThriftConnectionPool(config); 71 | ThriftConnection connection = pool.getConnection(); 72 | // example service 73 | com.wmz7year.thrift.pool.example.Example.Client exampleServiceClient = connection.getClient("example", 74 | Example.Client.class); 75 | exampleServiceClient.ping(); 76 | 77 | // other service 78 | com.wmz7year.thrift.pool.example.Other.Client otherServiceClient = connection.getClient("other", 79 | Other.Client.class); 80 | otherServiceClient.ping(); 81 | pool.close(); 82 | 83 | 84 | 85 | ####接下来需要完善内容: 86 | 1、补充文档
87 | 2、补充性能测试
88 | 3、完善使用例子
89 | 4、操作重试机制?
90 | 91 | 92 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.github.wmz7year 6 | ThriftConnectionPool 7 | 1.0.6-RELEASE 8 | jar 9 | 10 | ThriftConnectionPool 11 | https://github.com/wmz7year/Thrift-Connection-Pool/ 12 | 13 | Apache Thrift Client connection pool 14 | 2016 15 | 16 | 17 | 18 | Jiang wei 19 | ydswcy513@gmail.com 20 | 21 | 22 | George Kankava 23 | george.kankava@devfactory.com 24 | 25 | 26 | 27 | 28 | 29 | scm:git:https://github.com/wmz7year/Thrift-Connection-Pool.git 30 | 31 | 32 | scm:git:https://github.com/wmz7year/Thrift-Connection-Pool.git 33 | 34 | https://github.com/wmz7year/Thrift-Connection-Pool/ 35 | HEAD 36 | 37 | 38 | 39 | GitHub Issues 40 | https://github.com/wmz7year/Thrift-Connection-Pool/issues 41 | 42 | 43 | 44 | 45 | The Apache License, Version 2.0 46 | http://www.apache.org/licenses/LICENSE-2.0.txt 47 | 48 | 49 | 50 | 51 | 52 | UTF-8 53 | 1.7 54 | 1.7 55 | 1.7 56 | 57 | 0.9.3 58 | 3.8.1 59 | 1.1.3 60 | 1.7.12 61 | 16.0 62 | 63 | 2.2.1 64 | 2.9.1 65 | 1.5 66 | 67 | 68 | 69 | 70 | com.google.guava 71 | guava 72 | ${guava.version} 73 | 74 | 75 | org.apache.thrift 76 | libthrift 77 | ${thrift.version} 78 | 79 | 80 | ch.qos.logback 81 | logback-core 82 | ${logback.version} 83 | compile 84 | 85 | 86 | ch.qos.logback 87 | logback-classic 88 | ${logback.version} 89 | 90 | 91 | org.slf4j 92 | slf4j-api 93 | ${slf4j.version} 94 | compile 95 | 96 | 97 | junit 98 | junit 99 | ${junit.version} 100 | test 101 | 102 | 103 | 104 | 105 | 106 | ossrh 107 | https://oss.sonatype.org/content/repositories/snapshots 108 | 109 | 110 | ossrh 111 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 112 | 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-source-plugin 120 | ${maven.source.plugin} 121 | 122 | 123 | attach-sources 124 | 125 | jar-no-fork 126 | 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-javadoc-plugin 133 | ${maven.javadoc.plugin} 134 | 135 | 136 | attach-javadocs 137 | 138 | jar 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | release 149 | 150 | 151 | ossrh 152 | https://oss.sonatype.org/content/repositories/snapshots/ 153 | 154 | 155 | ossrh 156 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 157 | 158 | 159 | 160 | 161 | 162 | org.sonatype.plugins 163 | nexus-staging-maven-plugin 164 | 1.6.3 165 | true 166 | 167 | ossrh 168 | https://oss.sonatype.org/ 169 | true 170 | 171 | 172 | 173 | org.apache.maven.plugins 174 | maven-release-plugin 175 | 2.5 176 | 177 | true 178 | false 179 | release 180 | deploy 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-compiler-plugin 186 | 3.0 187 | 188 | 1.7 189 | 1.7 190 | 191 | 192 | 193 | org.apache.maven.plugins 194 | maven-gpg-plugin 195 | 1.5 196 | 197 | 198 | sign-artifacts 199 | verify 200 | 201 | sign 202 | 203 | 204 | 205 | 206 | 207 | org.apache.maven.plugins 208 | maven-source-plugin 209 | 2.2.1 210 | 211 | 212 | attach-sources 213 | 214 | jar-no-fork 215 | 216 | 217 | 218 | 219 | 220 | org.apache.maven.plugins 221 | maven-javadoc-plugin 222 | 2.9 223 | 224 | 225 | attach-javadocs 226 | 227 | jar 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /src/example/java/com/wmz7year/thrift/pool/example/ExampleServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool.example; 18 | 19 | import org.apache.thrift.TException; 20 | import org.apache.thrift.protocol.TBinaryProtocol; 21 | import org.apache.thrift.protocol.TBinaryProtocol.Factory; 22 | import org.apache.thrift.server.TServer; 23 | import org.apache.thrift.server.TThreadPoolServer; 24 | import org.apache.thrift.server.TThreadPoolServer.Args; 25 | import org.apache.thrift.transport.TServerSocket; 26 | import org.apache.thrift.transport.TServerTransport; 27 | import org.apache.thrift.transport.TTransportException; 28 | 29 | import com.wmz7year.thrift.pool.example.Example.Iface; 30 | import com.wmz7year.thrift.pool.example.Example.Processor; 31 | 32 | /** 33 | * 测试thrift服务器 34 | * 35 | * @Title: ExampleServer.java 36 | * @Package com.wmz7year.thrift.pool.example 37 | * @author jiangwei (ydswcy513@gmail.com) 38 | * @date 2015年11月18日 上午9:48:15 39 | * @version V1.0 40 | */ 41 | public class ExampleServer { 42 | public static void main(String[] args) { 43 | try { 44 | int port = 9119; 45 | TServerTransport serverTransport = new TServerSocket(port); 46 | Factory proFactory = new TBinaryProtocol.Factory(); 47 | Processor processor = new Example.Processor(new Example.Iface() { 48 | 49 | @Override 50 | public void pong() throws TException { 51 | System.out.println("pong"); 52 | } 53 | 54 | @Override 55 | public void ping() throws TException { 56 | System.out.println("ping"); 57 | } 58 | }); 59 | Args thriftArgs = new Args(serverTransport); 60 | thriftArgs.processor(processor); 61 | thriftArgs.protocolFactory(proFactory); 62 | TServer tserver = new TThreadPoolServer(thriftArgs); 63 | System.out.println("启动监听:" + port); 64 | tserver.serve(); 65 | } catch (TTransportException e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/example/thrift/example.thrift: -------------------------------------------------------------------------------- 1 | namespace java com.wmz7year.thrift.pool.example 2 | 3 | service Example { 4 | void ping(), 5 | void pong(), 6 | } -------------------------------------------------------------------------------- /src/example/thrift/other.thrift: -------------------------------------------------------------------------------- 1 | namespace java com.wmz7year.thrift.pool.example 2 | 3 | service Other { 4 | void ping(), 5 | void pong(), 6 | } -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/AbstractThriftConnectionStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.io.Serializable; 20 | import java.util.concurrent.locks.Lock; 21 | import java.util.concurrent.locks.ReentrantLock; 22 | 23 | import org.apache.thrift.TServiceClient; 24 | 25 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 26 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 27 | 28 | /** 29 | * 连接操作策略接口抽象实现类 30 | * 31 | * @author jiangwei (ydswcy513@gmail.com) 32 | * @version V1.0 33 | */ 34 | public abstract class AbstractThriftConnectionStrategy 35 | implements ThriftConnectionStrategy, Serializable { 36 | private static final long serialVersionUID = 8901255269360914953L; 37 | /** 38 | * 连接池对象 39 | */ 40 | protected ThriftConnectionPool pool; 41 | 42 | /** 43 | * 关闭锁,防止一次性关闭所有连接时出现问题 44 | */ 45 | protected Lock terminationLock = new ReentrantLock(); 46 | 47 | /** 48 | * 准备一个新连接的方法 49 | * 50 | * @return 如果启动统计 返回获取连接的时间 51 | * @throws ThriftConnectionPoolException 52 | * 当获取连接出现问题时抛出该异常 53 | */ 54 | protected long preConnection() throws ThriftConnectionPoolException { 55 | long statsObtainTime = 0; 56 | 57 | if (this.pool.poolShuttingDown) { 58 | throw new ThriftConnectionPoolException(this.pool.shutdownStackTrace); 59 | } 60 | 61 | return statsObtainTime; 62 | } 63 | 64 | /** 65 | * 获取连接后执行其他任务 66 | * 67 | * @param handle 68 | * 连接代理类 69 | * @param statsObtainTime 70 | * 准备获取连接的时间 71 | */ 72 | protected void postConnection(ThriftConnectionHandle handle, long statsObtainTime) { 73 | 74 | handle.renewConnection(); // 重置连接代理类中的信息 变成新连接 75 | 76 | } 77 | 78 | /* 79 | * @see com.wmz7year.thrift.pool.ThriftConnectionStrategy#getConnection() 80 | */ 81 | @Override 82 | public ThriftConnection getConnection() throws ThriftConnectionPoolException { 83 | long statsObtainTime = preConnection(); 84 | 85 | ThriftConnectionHandle result = (ThriftConnectionHandle) getConnectionInternal(); 86 | if (result != null) { 87 | postConnection(result, statsObtainTime); 88 | } 89 | 90 | return result; 91 | } 92 | 93 | /* 94 | * @see 95 | * com.wmz7year.thrift.pool.ThriftConnectionStrategy#getConnection(byte[]) 96 | */ 97 | @Override 98 | public ThriftConnection getConnection(byte[] nodeID) throws ThriftConnectionPoolException { 99 | long statsObtainTime = preConnection(); 100 | 101 | ThriftConnectionHandle result = (ThriftConnectionHandle) getConnectionInternal(nodeID); 102 | if (result != null) { 103 | postConnection(result, statsObtainTime); 104 | } 105 | 106 | return result; 107 | } 108 | 109 | /** 110 | * 获取连接代理对象的方法 111 | * 112 | * @return thrift服务器连接对象 113 | * @throws ThriftConnectionPoolException 114 | * 当获取连接出现问题时抛出该异常 115 | */ 116 | protected abstract ThriftConnection getConnectionInternal() throws ThriftConnectionPoolException; 117 | 118 | /** 119 | * 使用指定服务器节点获取连接代理对象的方法 120 | * 121 | * @param nodeID 122 | * thrift服务器节点ID 123 | * @return thrift服务器连接对象 124 | * @throws ThriftConnectionPoolException 125 | * 当获取连接出现问题时抛出该异常 126 | */ 127 | protected abstract ThriftConnection getConnectionInternal(byte[] nodeID) throws ThriftConnectionPoolException; 128 | 129 | /* 130 | * @see 131 | * com.wmz7year.thrift.pool.ThriftConnectionStrategy#cleanupConnection(com. 132 | * wmz7year.thrift.pool.ThriftConnectionHandle, 133 | * com.wmz7year.thrift.pool.ThriftConnectionHandle) 134 | */ 135 | @Override 136 | public void cleanupConnection(ThriftConnectionHandle oldHandler, ThriftConnectionHandle newHandler) { 137 | // do nothing 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/AcquireFailConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.concurrent.atomic.AtomicInteger; 20 | 21 | /** 22 | * 连接获取失败时重试配置 23 | * 24 | * @author jiangwei (ydswcy513@gmail.com) 25 | * @version V1.0 26 | */ 27 | public class AcquireFailConfig { 28 | /** 29 | * 记录日志的内容 30 | */ 31 | private String logMessage = ""; 32 | /** 33 | * 重试次数 34 | */ 35 | private AtomicInteger acquireRetryAttempts = new AtomicInteger(); 36 | /** 37 | * 重试等待时间 38 | */ 39 | private long acquireRetryDelayInMs; 40 | 41 | public void setAcquireRetryAttempts(AtomicInteger acquireRetryAttempts) { 42 | this.acquireRetryAttempts = acquireRetryAttempts; 43 | } 44 | 45 | public void setAcquireRetryDelayInMs(long acquireRetryDelayInMs) { 46 | this.acquireRetryDelayInMs = acquireRetryDelayInMs; 47 | } 48 | 49 | public void setLogMessage(String logMessage) { 50 | this.logMessage = logMessage; 51 | } 52 | 53 | public String getLogMessage() { 54 | return logMessage; 55 | } 56 | 57 | public AtomicInteger getAcquireRetryAttempts() { 58 | return acquireRetryAttempts; 59 | } 60 | 61 | public long getAcquireRetryDelayInMs() { 62 | return acquireRetryDelayInMs; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/CustomThreadFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.lang.Thread.UncaughtExceptionHandler; 20 | import java.util.concurrent.ThreadFactory; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | /** 26 | * 负责创建线程的工厂类
27 | * 用于设置线程名称以及异常捕获 28 | * 29 | * @author jiangwei (ydswcy513@gmail.com) 30 | * @version V1.0 31 | */ 32 | public class CustomThreadFactory implements ThreadFactory, UncaughtExceptionHandler { 33 | private static final Logger logger = LoggerFactory.getLogger(CustomThreadFactory.class); 34 | 35 | /** 是否是守护线程的表识位 */ 36 | private boolean daemon; 37 | /** 线程名称 */ 38 | private String threadName; 39 | 40 | public CustomThreadFactory(String threadName, boolean daemon) { 41 | this.threadName = threadName; 42 | this.daemon = daemon; 43 | } 44 | 45 | /** 46 | * {@inheritDoc} 47 | * 48 | * @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable) 49 | */ 50 | @Override 51 | public Thread newThread(Runnable r) { 52 | Thread t = new Thread(r, this.threadName); 53 | t.setDaemon(this.daemon); 54 | t.setUncaughtExceptionHandler(this); 55 | return t; 56 | } 57 | 58 | /** 59 | * {@inheritDoc} 60 | * 61 | * @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread, 62 | * java.lang.Throwable) 63 | */ 64 | public void uncaughtException(Thread thread, Throwable throwable) { 65 | logger.error("Uncaught Exception in thread " + thread.getName(), throwable); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/DefaultThriftConnectionStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.Arrays; 20 | import java.util.Collections; 21 | import java.util.LinkedList; 22 | import java.util.List; 23 | import java.util.concurrent.TimeUnit; 24 | 25 | import org.apache.thrift.TServiceClient; 26 | 27 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 28 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 29 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 30 | 31 | /** 32 | * 默认连接池获取策略实现类 33 | * 34 | * @author jiangwei (ydswcy513@gmail.com) 35 | * @version V1.0 36 | */ 37 | public class DefaultThriftConnectionStrategy extends AbstractThriftConnectionStrategy { 38 | private static final long serialVersionUID = 142121086900189271L; 39 | 40 | public DefaultThriftConnectionStrategy(ThriftConnectionPool pool) { 41 | this.pool = pool; 42 | } 43 | 44 | /* 45 | * @see com.wmz7year.thrift.pool.AbstractThriftConnectionStrategy# 46 | * getConnectionInternal() 47 | */ 48 | @Override 49 | protected ThriftConnection getConnectionInternal() throws ThriftConnectionPoolException { 50 | ThriftConnection result = pollConnection(); 51 | // 如果立即获取连接失败 则换一个分区继续获取 52 | // TODO 设置当连接获取超时返回null? 53 | if (result == null) { 54 | if (this.pool.getThriftServerCount() == 0) { 55 | throw new ThriftConnectionPoolException("当前没有可用的服务器 无法获取连接"); 56 | } 57 | int partition = (int) (Thread.currentThread().getId() % this.pool.thriftServerCount); 58 | 59 | ThriftConnectionPartition thriftConnectionPartition = this.pool.partitions.get(partition); 60 | 61 | try { 62 | result = thriftConnectionPartition.poolFreeConnection(this.pool.connectionTimeoutInMs, 63 | TimeUnit.MILLISECONDS); 64 | if (result == null) { 65 | 66 | throw new ThriftConnectionPoolException("Timed out waiting for a free available connection."); 67 | } 68 | } catch (InterruptedException e) { 69 | throw new ThriftConnectionPoolException(e); 70 | } 71 | } 72 | return result; 73 | } 74 | 75 | /* 76 | * @see com.wmz7year.thrift.pool.AbstractThriftConnectionStrategy# 77 | * getConnectionInternal(byte[]) 78 | */ 79 | @Override 80 | protected ThriftConnection getConnectionInternal(byte[] nodeID) throws ThriftConnectionPoolException { 81 | if (nodeID == null) { 82 | throw new NullPointerException(); 83 | } 84 | if (this.pool.getThriftServerCount() == 0) { 85 | throw new ThriftConnectionPoolException("当前没有可用的服务器 无法获取连接"); 86 | } 87 | 88 | List> partitions = Collections.unmodifiableList(this.pool.partitions); 89 | ThriftConnectionPartition thriftConnectionPartition = null; 90 | for (ThriftConnectionPartition tempPartition : partitions) { 91 | ThriftServerInfo thriftServerInfo = tempPartition.getThriftServerInfo(); 92 | if (Arrays.equals(thriftServerInfo.getServerID(), nodeID)) { 93 | thriftConnectionPartition = tempPartition; 94 | break; 95 | } 96 | } 97 | if (thriftConnectionPartition == null) { 98 | throw new ThriftConnectionPoolException("没有找到对应服务器节点:" + Arrays.toString(nodeID)); 99 | } 100 | 101 | ThriftConnection result = null; 102 | try { 103 | result = thriftConnectionPartition.poolFreeConnection(this.pool.connectionTimeoutInMs, 104 | TimeUnit.MILLISECONDS); 105 | if (result == null) { 106 | 107 | throw new ThriftConnectionPoolException("Timed out waiting for a free available connection."); 108 | } 109 | } catch (InterruptedException e) { 110 | throw new ThriftConnectionPoolException(e); 111 | } 112 | return result; 113 | } 114 | 115 | /* 116 | * @see 117 | * com.wmz7year.thrift.pool.ThriftConnectionStrategy#terminateAllConnections 118 | * () 119 | */ 120 | @Override 121 | public void terminateAllConnections() { 122 | this.terminationLock.lock(); 123 | try { 124 | for (int i = 0; i < this.pool.thriftServerCount; i++) { 125 | this.pool.partitions.get(i).setUnableToCreateMoreTransactions(false); 126 | List> clist = new LinkedList<>(); 127 | this.pool.partitions.get(i).getFreeConnections().drainTo(clist); 128 | for (ThriftConnectionHandle c : clist) { 129 | this.pool.destroyConnection(c); 130 | } 131 | 132 | } 133 | } finally { 134 | this.terminationLock.unlock(); 135 | } 136 | } 137 | 138 | /* 139 | * @see com.wmz7year.thrift.pool.ThriftConnectionStrategy#pollConnection() 140 | */ 141 | @Override 142 | public ThriftConnection pollConnection() { 143 | ThriftConnection result; 144 | if (pool.getThriftServerCount() == 0) { 145 | throw new IllegalStateException("当前无可用连接服务器"); 146 | } 147 | int partition = (int) (Thread.currentThread().getId() % this.pool.thriftServerCount); 148 | 149 | ThriftConnectionPartition thriftConnectionPartition = this.pool.partitions.get(partition); 150 | 151 | result = thriftConnectionPartition.poolFreeConnection(); 152 | if (result == null) { 153 | for (int i = 0; i < this.pool.thriftServerCount; i++) { 154 | if (i == partition) { 155 | continue; 156 | } 157 | result = this.pool.partitions.get(i).poolFreeConnection(); 158 | if (result != null) { 159 | thriftConnectionPartition = this.pool.partitions.get(i); 160 | break; 161 | } 162 | } 163 | } 164 | 165 | if (!thriftConnectionPartition.isUnableToCreateMoreTransactions()) { 166 | this.pool.maybeSignalForMoreConnections(thriftConnectionPartition); 167 | } 168 | return result; 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/PoolWatchThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import org.apache.thrift.TServiceClient; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | /** 24 | * 连接池内部连接数量监控线程 25 | * 26 | * @author jiangwei (ydswcy513@gmail.com) 27 | * @version V1.0 28 | */ 29 | public class PoolWatchThread implements Runnable { 30 | private static final Logger logger = LoggerFactory.getLogger(PoolWatchThread.class); 31 | 32 | /** 33 | * 连接池对象 34 | */ 35 | private ThriftConnectionPool thriftConnectionPool; 36 | 37 | /** 38 | * 连接分区对象 39 | */ 40 | private ThriftConnectionPartition thriftConnectionPartition; 41 | 42 | /** 43 | * 是否为懒加载 44 | */ 45 | protected boolean lazyInit; 46 | 47 | /** 48 | * 连接池连接数比例阈值 49 | */ 50 | private int poolAvailabilityThreshold; 51 | 52 | /** 53 | * 获取连接失败时的重试间隔事件 54 | */ 55 | private long acquireRetryDelayInMs = 1000L; 56 | 57 | /** 58 | * 最大连接获取失效次数 59 | */ 60 | private int maxConnectionCreateFailedCount; 61 | 62 | /** 63 | * 持续获取连接失败的次数 64 | */ 65 | private int currentConnectionFailedCount; 66 | /** 67 | * 是否运行的表识位 68 | */ 69 | private boolean run; 70 | 71 | public PoolWatchThread(ThriftConnectionPartition thriftConnectionPartition, 72 | ThriftConnectionPool thriftConnectionPool) { 73 | this.thriftConnectionPool = thriftConnectionPool; 74 | this.thriftConnectionPartition = thriftConnectionPartition; 75 | this.lazyInit = this.thriftConnectionPool.getConfig().isLazyInit(); 76 | this.acquireRetryDelayInMs = this.thriftConnectionPool.getConfig().getAcquireRetryDelayInMs(); 77 | this.poolAvailabilityThreshold = this.thriftConnectionPool.getConfig().getPoolAvailabilityThreshold(); 78 | this.maxConnectionCreateFailedCount = this.thriftConnectionPool.getConfig().getMaxConnectionCreateFailedCount(); 79 | this.run = true; 80 | this.thriftConnectionPartition.registPoolWatchThread(this); 81 | } 82 | 83 | /* 84 | * @see java.lang.Runnable#run() 85 | */ 86 | @Override 87 | public void run() { 88 | int maxNewConnections; 89 | 90 | while (run) { 91 | maxNewConnections = 0; 92 | try { 93 | if (this.lazyInit) { // 无视第一次的信号量 94 | this.thriftConnectionPartition.getPoolWatchThreadSignalQueue().take(); 95 | } 96 | 97 | maxNewConnections = this.thriftConnectionPartition.getMaxConnections() 98 | - this.thriftConnectionPartition.getCreatedConnections(); 99 | 100 | while (maxNewConnections == 0 || (this.thriftConnectionPartition.getAvailableConnections() * 100 101 | / this.thriftConnectionPartition.getMaxConnections() > this.poolAvailabilityThreshold)) { 102 | if (maxNewConnections == 0) { 103 | this.thriftConnectionPartition.setUnableToCreateMoreTransactions(true); 104 | } 105 | 106 | this.thriftConnectionPartition.getPoolWatchThreadSignalQueue().take(); 107 | if (!run) { 108 | break; 109 | } 110 | maxNewConnections = this.thriftConnectionPartition.getMaxConnections() 111 | - this.thriftConnectionPartition.getCreatedConnections(); 112 | 113 | } 114 | 115 | if (maxNewConnections > 0 && !this.thriftConnectionPool.poolShuttingDown) { 116 | fillConnections(Math.min(maxNewConnections, this.thriftConnectionPartition.getAcquireIncrement())); 117 | 118 | if (this.thriftConnectionPartition.getCreatedConnections() < this.thriftConnectionPartition 119 | .getMinConnections()) { 120 | fillConnections(this.thriftConnectionPartition.getMinConnections() 121 | - this.thriftConnectionPartition.getCreatedConnections()); 122 | 123 | } 124 | } 125 | 126 | if (this.thriftConnectionPool.poolShuttingDown) { 127 | return; 128 | } 129 | 130 | } catch (InterruptedException e) { 131 | // 发生中断事件 停止运行监听器 132 | if (logger.isDebugEnabled()) { 133 | logger.debug("Terminating pool watch thread"); 134 | } 135 | return; 136 | } 137 | } 138 | } 139 | 140 | /** 141 | * 触发创建连接的方法
142 | * 新的连接会添加到分区中 143 | * 144 | * @param connectionsToCreate 145 | * 需要创建的连接数 146 | * @throws InterruptedException 147 | * 当添加操作被中断的时候抛出该异常 148 | */ 149 | private void fillConnections(int connectionsToCreate) throws InterruptedException { 150 | try { 151 | for (int i = 0; i < connectionsToCreate; i++) { 152 | if (this.thriftConnectionPool.poolShuttingDown) { 153 | break; 154 | } 155 | if (!run) { 156 | break; 157 | } 158 | this.thriftConnectionPartition.addFreeConnection(new ThriftConnectionHandle(null, 159 | this.thriftConnectionPartition, this.thriftConnectionPool, false)); 160 | } 161 | // 如果正确的获取连接则失败次数重置为0 162 | currentConnectionFailedCount = 0; 163 | } catch (Exception e) { 164 | currentConnectionFailedCount++; 165 | // 判断连续次数是否是超限了 166 | if (currentConnectionFailedCount == maxConnectionCreateFailedCount) { 167 | logger.error("Error in trying to obtain a connection in " + currentConnectionFailedCount 168 | + " count will remove the server", e); 169 | thriftConnectionPool.destroyThriftConnectionPartition(thriftConnectionPartition); 170 | } else { 171 | logger.error("Error in trying to obtain a connection. Retrying in " + this.acquireRetryDelayInMs + "ms", 172 | e); 173 | Thread.sleep(this.acquireRetryDelayInMs); 174 | } 175 | } 176 | } 177 | 178 | /** 179 | * 停止检测线程的方法 180 | */ 181 | public void stop() { 182 | this.run = false; 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/TaskEngine.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.Date; 20 | import java.util.Map; 21 | import java.util.Timer; 22 | import java.util.TimerTask; 23 | import java.util.concurrent.*; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | 26 | /** 27 | * 线程调度任务引擎
28 | * 负责执行线程或者定时执行线程等操作 29 | * 30 | * @author jiangwei (ydswcy513@gmail.com) 31 | * @version V1.0 32 | */ 33 | public class TaskEngine { 34 | private static TaskEngine instance = null; 35 | 36 | private Timer timer; 37 | private ExecutorService executor; 38 | private Map wrappedTasks = new ConcurrentHashMap<>(); 39 | 40 | private TaskEngine() { 41 | timer = new Timer("ThriftConnectionPool-TaskEngine-timer", true); 42 | executor = Executors.newCachedThreadPool(new ThreadFactory() { 43 | 44 | final AtomicInteger threadNumber = new AtomicInteger(1); 45 | 46 | @Override 47 | public Thread newThread(Runnable runnable) { 48 | // Use our own naming scheme for the threads. 49 | Thread thread = new Thread(Thread.currentThread().getThreadGroup(), runnable, 50 | "ThriftConnectionPool-TaskEngine-pool-" + threadNumber.getAndIncrement(), 0); 51 | // Make workers daemon threads. 52 | thread.setDaemon(true); 53 | if (thread.getPriority() != Thread.NORM_PRIORITY) { 54 | thread.setPriority(Thread.NORM_PRIORITY); 55 | } 56 | return thread; 57 | } 58 | }); 59 | } 60 | 61 | /** 62 | * Returns a task engine instance (singleton). 63 | * 64 | * @return a task engine. 65 | */ 66 | public static synchronized TaskEngine getInstance() { 67 | if (instance == null) { 68 | instance = new TaskEngine(); 69 | } 70 | return instance; 71 | } 72 | 73 | /** 74 | * Submits a Runnable task for execution and returns a Future representing 75 | * that task. 76 | * 77 | * @param task 78 | * the task to submit. 79 | * @return a Future representing pending completion of the task, and whose 80 | * get() method will return null upon completion. 81 | * @throws java.util.concurrent.RejectedExecutionException 82 | * if task cannot be scheduled for execution. 83 | * @throws NullPointerException 84 | * if task null. 85 | */ 86 | public Future submit(Runnable task) { 87 | return executor.submit(task); 88 | } 89 | 90 | /** 91 | * Schedules the specified task for execution after the specified delay. 92 | * 93 | * @param task 94 | * task to be scheduled. 95 | * @param delay 96 | * delay in milliseconds before task is to be executed. 97 | * @throws IllegalArgumentException 98 | * if delay is negative, or 99 | * delay + System.currentTimeMillis() is negative. 100 | * @throws IllegalStateException 101 | * if task was already scheduled or cancelled, or timer was 102 | * cancelled. 103 | */ 104 | public void schedule(TimerTask task, long delay) { 105 | timer.schedule(new TimerTaskWrapper(task), delay); 106 | } 107 | 108 | /** 109 | * Schedules the specified task for execution at the specified time. If the 110 | * time is in the past, the task is scheduled for immediate execution. 111 | * 112 | * @param task 113 | * task to be scheduled. 114 | * @param time 115 | * time at which task is to be executed. 116 | * @throws IllegalArgumentException 117 | * if time.getTime() is negative. 118 | * @throws IllegalStateException 119 | * if task was already scheduled or cancelled, timer was 120 | * cancelled, or timer thread terminated. 121 | */ 122 | public void schedule(TimerTask task, Date time) { 123 | timer.schedule(new TimerTaskWrapper(task), time); 124 | } 125 | 126 | /** 127 | * Schedules the specified task for repeated fixed-delay execution, 128 | * beginning after the specified delay. Subsequent executions take place at 129 | * approximately regular intervals separated by the specified period. 130 | * 131 | *

132 | * In fixed-delay execution, each execution is scheduled relative to the 133 | * actual execution time of the previous execution. If an execution is 134 | * delayed for any reason (such as garbage collection or other background 135 | * activity), subsequent executions will be delayed as well. In the long 136 | * run, the frequency of execution will generally be slightly lower than the 137 | * reciprocal of the specified period (assuming the system clock underlying 138 | * Object.wait(long) is accurate). 139 | * 140 | *

141 | * Fixed-delay execution is appropriate for recurring activities that 142 | * require "smoothness." In other words, it is appropriate for activities 143 | * where it is more important to keep the frequency accurate in the short 144 | * run than in the long run. This includes most animation tasks, such as 145 | * blinking a cursor at regular intervals. It also includes tasks wherein 146 | * regular activity is performed in response to human input, such as 147 | * automatically repeating a character as long as a key is held down. 148 | * 149 | * @param task 150 | * task to be scheduled. 151 | * @param delay 152 | * delay in milliseconds before task is to be executed. 153 | * @param period 154 | * time in milliseconds between successive task executions. 155 | * @throws IllegalArgumentException 156 | * if delay is negative, or 157 | * delay + System.currentTimeMillis() is negative. 158 | * @throws IllegalStateException 159 | * if task was already scheduled or cancelled, timer was 160 | * cancelled, or timer thread terminated. 161 | */ 162 | public void schedule(TimerTask task, long delay, long period) { 163 | TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task); 164 | wrappedTasks.put(task, taskWrapper); 165 | timer.schedule(taskWrapper, delay, period); 166 | } 167 | 168 | /** 169 | * Schedules the specified task for repeated fixed-delay execution, 170 | * beginning at the specified time. Subsequent executions take place at 171 | * approximately regular intervals, separated by the specified period. 172 | * 173 | *

174 | * In fixed-delay execution, each execution is scheduled relative to the 175 | * actual execution time of the previous execution. If an execution is 176 | * delayed for any reason (such as garbage collection or other background 177 | * activity), subsequent executions will be delayed as well. In the long 178 | * run, the frequency of execution will generally be slightly lower than the 179 | * reciprocal of the specified period (assuming the system clock underlying 180 | * Object.wait(long) is accurate). 181 | * 182 | *

183 | * Fixed-delay execution is appropriate for recurring activities that 184 | * require "smoothness." In other words, it is appropriate for activities 185 | * where it is more important to keep the frequency accurate in the short 186 | * run than in the long run. This includes most animation tasks, such as 187 | * blinking a cursor at regular intervals. It also includes tasks wherein 188 | * regular activity is performed in response to human input, such as 189 | * automatically repeating a character as long as a key is held down. 190 | * 191 | * @param task 192 | * task to be scheduled. 193 | * @param firstTime 194 | * First time at which task is to be executed. 195 | * @param period 196 | * time in milliseconds between successive task executions. 197 | * @throws IllegalArgumentException 198 | * if time.getTime() is negative. 199 | * @throws IllegalStateException 200 | * if task was already scheduled or cancelled, timer was 201 | * cancelled, or timer thread terminated. 202 | */ 203 | public void schedule(TimerTask task, Date firstTime, long period) { 204 | TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task); 205 | wrappedTasks.put(task, taskWrapper); 206 | timer.schedule(taskWrapper, firstTime, period); 207 | } 208 | 209 | /** 210 | * Schedules the specified task for repeated fixed-rate execution, 211 | * beginning after the specified delay. Subsequent executions take place at 212 | * approximately regular intervals, separated by the specified period. 213 | * 214 | *

215 | * In fixed-rate execution, each execution is scheduled relative to the 216 | * scheduled execution time of the initial execution. If an execution is 217 | * delayed for any reason (such as garbage collection or other background 218 | * activity), two or more executions will occur in rapid succession to 219 | * "catch up." In the long run, the frequency of execution will be exactly 220 | * the reciprocal of the specified period (assuming the system clock 221 | * underlying Object.wait(long) is accurate). 222 | * 223 | *

224 | * Fixed-rate execution is appropriate for recurring activities that are 225 | * sensitive to absolute time, such as ringing a chime every hour on 226 | * the hour, or running scheduled maintenance every day at a particular 227 | * time. It is also appropriate for recurring activities where the total 228 | * time to perform a fixed number of executions is important, such as a 229 | * countdown timer that ticks once every second for ten seconds. Finally, 230 | * fixed-rate execution is appropriate for scheduling multiple repeating 231 | * timer tasks that must remain synchronized with respect to one another. 232 | * 233 | * @param task 234 | * task to be scheduled. 235 | * @param delay 236 | * delay in milliseconds before task is to be executed. 237 | * @param period 238 | * time in milliseconds between successive task executions. 239 | * @throws IllegalArgumentException 240 | * if delay is negative, or 241 | * delay + System.currentTimeMillis() is negative. 242 | * @throws IllegalStateException 243 | * if task was already scheduled or cancelled, timer was 244 | * cancelled, or timer thread terminated. 245 | */ 246 | public void scheduleAtFixedRate(TimerTask task, long delay, long period) { 247 | TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task); 248 | wrappedTasks.put(task, taskWrapper); 249 | timer.scheduleAtFixedRate(taskWrapper, delay, period); 250 | } 251 | 252 | /** 253 | * Schedules the specified task for repeated fixed-rate execution, 254 | * beginning at the specified time. Subsequent executions take place at 255 | * approximately regular intervals, separated by the specified period. 256 | * 257 | *

258 | * In fixed-rate execution, each execution is scheduled relative to the 259 | * scheduled execution time of the initial execution. If an execution is 260 | * delayed for any reason (such as garbage collection or other background 261 | * activity), two or more executions will occur in rapid succession to 262 | * "catch up." In the long run, the frequency of execution will be exactly 263 | * the reciprocal of the specified period (assuming the system clock 264 | * underlying Object.wait(long) is accurate). 265 | * 266 | *

267 | * Fixed-rate execution is appropriate for recurring activities that are 268 | * sensitive to absolute time, such as ringing a chime every hour on 269 | * the hour, or running scheduled maintenance every day at a particular 270 | * time. It is also appropriate for recurring activities where the total 271 | * time to perform a fixed number of executions is important, such as a 272 | * countdown timer that ticks once every second for ten seconds. Finally, 273 | * fixed-rate execution is appropriate for scheduling multiple repeating 274 | * timer tasks that must remain synchronized with respect to one another. 275 | * 276 | * @param task 277 | * task to be scheduled. 278 | * @param firstTime 279 | * First time at which task is to be executed. 280 | * @param period 281 | * time in milliseconds between successive task executions. 282 | * @throws IllegalArgumentException 283 | * if time.getTime() is negative. 284 | * @throws IllegalStateException 285 | * if task was already scheduled or cancelled, timer was 286 | * cancelled, or timer thread terminated. 287 | */ 288 | public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) { 289 | TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task); 290 | wrappedTasks.put(task, taskWrapper); 291 | timer.scheduleAtFixedRate(taskWrapper, firstTime, period); 292 | } 293 | 294 | /** 295 | * Cancels the execution of a scheduled task. 296 | * {@link java.util.TimerTask#cancel()} 297 | * 298 | * @param task 299 | * the scheduled task to cancel. 300 | */ 301 | public void cancelScheduledTask(TimerTask task) { 302 | TaskEngine.TimerTaskWrapper taskWrapper = wrappedTasks.remove(task); 303 | if (taskWrapper != null) { 304 | taskWrapper.cancel(); 305 | } 306 | } 307 | 308 | /** 309 | * Shuts down the task engine service. 310 | */ 311 | public void shutdown() { 312 | if (executor != null) { 313 | executor.shutdown(); 314 | executor = null; 315 | } 316 | 317 | if (timer != null) { 318 | timer.cancel(); 319 | timer = null; 320 | } 321 | 322 | instance = null; 323 | } 324 | 325 | /** 326 | * Wrapper class for a standard TimerTask. It simply executes the TimerTask 327 | * using the executor's thread pool. 328 | */ 329 | private class TimerTaskWrapper extends TimerTask { 330 | 331 | private TimerTask task; 332 | 333 | public TimerTaskWrapper(TimerTask task) { 334 | this.task = task; 335 | } 336 | 337 | @Override 338 | public void run() { 339 | executor.submit(task); 340 | } 341 | } 342 | } -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/ThriftConnectionHandle.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.io.IOException; 20 | import java.io.Serializable; 21 | import java.util.Map; 22 | import java.util.concurrent.TimeUnit; 23 | import java.util.concurrent.atomic.AtomicBoolean; 24 | 25 | import org.apache.thrift.TServiceClient; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 30 | import com.wmz7year.thrift.pool.connection.MulitServiceThriftConnecion; 31 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 32 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 33 | 34 | /** 35 | * thrift连接代理类
36 | * 37 | * 38 | * @author jiangwei (ydswcy513@gmail.com) 39 | * @version V1.0 40 | */ 41 | public class ThriftConnectionHandle implements ThriftConnection, Serializable { 42 | protected static Logger logger = LoggerFactory.getLogger(ThriftConnectionHandle.class); 43 | private static final long serialVersionUID = 8927450495285911268L; 44 | 45 | /** 46 | * 连接所在的分区 47 | */ 48 | private ThriftConnectionPartition thriftConnectionPartition; 49 | /** 50 | * thrift服务器信息 51 | */ 52 | private ThriftServerInfo thriftServerInfo; 53 | /** 54 | * 连接超时时间 55 | */ 56 | private long connectionTimeout; 57 | /** 58 | * 连接最大存活时间 59 | */ 60 | protected long maxConnectionAgeInMs; 61 | /** 62 | * 连接代理类持有的真实连接对象 63 | */ 64 | private ThriftConnection thriftConnection; 65 | 66 | /** 是否调用关闭连接方法的表识位 */ 67 | protected AtomicBoolean logicallyClosed = new AtomicBoolean(); 68 | 69 | /** 70 | * 连接池对象 71 | */ 72 | private ThriftConnectionPool thriftConnectionPool; 73 | 74 | /** 连接最后使用时间 */ 75 | private long connectionLastUsedInMs; 76 | /** 连接最后重置时间 */ 77 | private long connectionLastResetInMs; 78 | /** 连接创建时间 */ 79 | protected long connectionCreationTimeInMs; 80 | /** 连接是否损坏的标识位 */ 81 | protected boolean possiblyBroken; 82 | /** 使用该连接的线程 */ 83 | protected Thread threadUsingConnection; 84 | /** 连接是否出现问题需要关闭的标识位 */ 85 | private boolean available = true; 86 | 87 | /** 88 | * 连接监控线程 89 | */ 90 | private volatile Thread threadWatch; 91 | 92 | public ThriftConnectionHandle(ThriftConnection thriftConnection, 93 | ThriftConnectionPartition thriftConnectionPartition, ThriftConnectionPool thriftConnectionPool, 94 | boolean recreating) throws ThriftConnectionPoolException { 95 | // 判断是否是新连接 96 | boolean newConnection = thriftConnection == null; 97 | 98 | if (!recreating) { 99 | connectionLastUsedInMs = System.currentTimeMillis(); 100 | connectionLastResetInMs = System.currentTimeMillis(); 101 | connectionCreationTimeInMs = System.currentTimeMillis(); 102 | } 103 | 104 | this.thriftConnectionPool = thriftConnectionPool; 105 | this.thriftConnectionPartition = thriftConnectionPartition; 106 | this.thriftServerInfo = thriftConnectionPartition.getThriftServerInfo(); 107 | this.connectionTimeout = thriftConnectionPool.getConfig().getConnectTimeout(); 108 | this.connectionTimeout = thriftConnectionPool.getConfig().getMaxConnectionAge(TimeUnit.MILLISECONDS); 109 | 110 | this.maxConnectionAgeInMs = thriftConnectionPool.getConfig().getMaxConnectionAge(TimeUnit.MILLISECONDS); 111 | 112 | try { 113 | this.thriftConnection = newConnection ? thriftConnectionPool.obtainInternalConnection(this) 114 | : thriftConnection; 115 | } catch (ThriftConnectionPoolException e) { 116 | possiblyBroken = true; 117 | throw e; 118 | } 119 | } 120 | 121 | /* 122 | * @see java.io.Closeable#close() 123 | */ 124 | @Override 125 | public void close() throws IOException { 126 | try { 127 | if (this.logicallyClosed.compareAndSet(false, true) && available) { 128 | 129 | // 中断连接监视线程 130 | if (this.threadWatch != null) { 131 | this.threadWatch.interrupt(); 132 | this.threadWatch = null; 133 | } 134 | 135 | ThriftConnectionHandle handle = null; 136 | try { 137 | handle = this.recreateConnectionHandle(); 138 | this.thriftConnectionPool.connectionStrategy.cleanupConnection(this, handle); 139 | this.thriftConnectionPool.releaseConnection(handle); 140 | } catch (ThriftConnectionPoolException e) { 141 | possiblyBroken = true; 142 | // 检查连接是否关闭了 143 | if (!isClosed()) { 144 | this.thriftConnectionPool.connectionStrategy.cleanupConnection(this, handle); 145 | this.thriftConnectionPool.releaseConnection(this); 146 | } 147 | throw new IOException(e); 148 | } 149 | } else { 150 | this.thriftConnectionPool.postDestroyConnection(this); 151 | } 152 | } catch (Exception e) { 153 | possiblyBroken = true; 154 | throw new IOException(e); 155 | } 156 | if (thriftConnection != null) { 157 | thriftConnection.close(); 158 | } 159 | } 160 | 161 | /** 162 | * 创建新的连接代理对象
163 | * 用于清理可能存在的奇奇怪怪配置 164 | * 165 | * @return ConnectionHandle 新创建的连接对象 166 | * @throws ThriftConnectionPoolException 167 | * 当产生错误时抛出该异常 168 | */ 169 | public ThriftConnectionHandle recreateConnectionHandle() throws ThriftConnectionPoolException { 170 | ThriftConnectionHandle handle = new ThriftConnectionHandle<>(this.thriftConnection, 171 | this.thriftConnectionPartition, this.thriftConnectionPool, true); 172 | handle.thriftConnectionPartition = this.thriftConnectionPartition; 173 | handle.connectionCreationTimeInMs = this.connectionCreationTimeInMs; 174 | handle.connectionLastResetInMs = this.connectionLastResetInMs; 175 | handle.connectionLastUsedInMs = this.connectionLastUsedInMs; 176 | handle.possiblyBroken = this.possiblyBroken; 177 | this.thriftConnection = null; 178 | 179 | return handle; 180 | } 181 | 182 | /* 183 | * @see com.wmz7year.thrift.pool.connection.ThriftConnection#getClient() 184 | */ 185 | @Override 186 | public T getClient() { 187 | if (thriftConnection != null) { 188 | return thriftConnection.getClient(); 189 | } 190 | throw new IllegalStateException("连接代理类没有绑定的原始连接信息"); 191 | } 192 | 193 | /* 194 | * @see 195 | * com.wmz7year.thrift.pool.connection.ThriftConnection#getClient(java.lang. 196 | * String, java.lang.Class) 197 | */ 198 | @Override 199 | public K getClient(String serviceName, Class clazz) { 200 | if (thriftConnection != null) { 201 | return thriftConnection.getClient(serviceName, clazz); 202 | } 203 | throw new IllegalStateException("连接代理类没有绑定的原始连接信息"); 204 | } 205 | 206 | /** 207 | * 获取连接代理对象绑定的原始连接的方法 208 | * 209 | * @return 原始连接对象 210 | */ 211 | public ThriftConnection getInternalConnection() { 212 | return this.thriftConnection; 213 | } 214 | 215 | /** 216 | * 设置代理对象绑定的原始连接的方法 217 | * 218 | * @param thriftConnection 219 | * 原始连接对象 220 | */ 221 | public void setInternalConnection(ThriftConnection thriftConnection) { 222 | this.thriftConnection = thriftConnection; 223 | } 224 | 225 | /** 226 | * 获取连接代理对象所对应的thrift服务器信息对象 227 | * 228 | * @return thrift服务器信息对象 229 | */ 230 | public ThriftServerInfo getThriftServerInfo() { 231 | return this.thriftServerInfo; 232 | } 233 | 234 | /** 235 | * 获取连接代理类所在的分区对象 236 | * 237 | * @return 分区对象 238 | */ 239 | public ThriftConnectionPartition getConnectionPartition() { 240 | return this.thriftConnectionPartition; 241 | } 242 | 243 | /** 244 | * 设置连接代理类所属的连接分区的方法 245 | * 246 | * @param thriftConnectionPartition 247 | * 连接分区对象 248 | */ 249 | public void setOriginatingPartition(ThriftConnectionPartition thriftConnectionPartition) { 250 | this.thriftConnectionPartition = thriftConnectionPartition; 251 | } 252 | 253 | /** 254 | * 获取连接超时时间的方法 255 | * 256 | * @return 连接超时时间 257 | */ 258 | public long getConnectionTimeout() { 259 | return this.connectionTimeout; 260 | } 261 | 262 | /** 263 | * 关闭代理对象中的原始连接的方法 264 | * 265 | * @throws ThriftConnectionPoolException 266 | * 当关闭连接出现问题抛出该异常 267 | */ 268 | public void internalClose() throws ThriftConnectionPoolException { 269 | try { 270 | if (this.thriftConnection != null) { 271 | this.thriftConnection.close(); 272 | } 273 | logicallyClosed.set(true); 274 | } catch (IOException e) { 275 | throw new ThriftConnectionPoolException(e); 276 | } 277 | } 278 | 279 | /** 280 | * 获取多服务模式下所有thrift客户端的方法 281 | * 282 | * @return 多服务下所有thrift客户端集合 283 | */ 284 | public Map getMuiltServiceClients() { 285 | if (this.thriftConnection instanceof MulitServiceThriftConnecion) { 286 | MulitServiceThriftConnecion connection = (MulitServiceThriftConnecion) thriftConnection; 287 | return connection.getMuiltServiceClients(); 288 | } 289 | throw new IllegalStateException("单服务运行模式下不允许调用该方法"); 290 | } 291 | 292 | /* 293 | * @see com.wmz7year.thrift.pool.connection.ThriftConnection#isClosed() 294 | */ 295 | @Override 296 | public boolean isClosed() { 297 | return this.logicallyClosed.get(); 298 | } 299 | 300 | /** 301 | * 判断连接是否已经过期的方法 302 | * 303 | * @return true为已过期 false为未过期 304 | */ 305 | public boolean isExpired() { 306 | return this.maxConnectionAgeInMs > 0 && isExpired(System.currentTimeMillis()); 307 | } 308 | 309 | /** 310 | * 使用指定的时间判断连接是否过期的方法 311 | * 312 | * @param currentTime 313 | * 指定的时间戳 314 | * @return true为已过期 false为未过期 315 | */ 316 | protected boolean isExpired(long currentTime) { 317 | return this.maxConnectionAgeInMs > 0 318 | && (currentTime - this.connectionCreationTimeInMs) > this.maxConnectionAgeInMs; 319 | } 320 | 321 | /** 322 | * 判断连接是否出现问题的方法 323 | * 324 | * @return true为发生过异常 false为未发生异常 325 | */ 326 | public boolean isPossiblyBroken() { 327 | return this.possiblyBroken; 328 | } 329 | 330 | /** 331 | * 更新最后重置时间的方法 332 | * 333 | * @param connectionLastReset 334 | * 最后的重置时间 335 | */ 336 | public void setConnectionLastResetInMs(long connectionLastReset) { 337 | this.connectionLastResetInMs = connectionLastReset; 338 | } 339 | 340 | /** 341 | * 设置连接最后使用的时间 342 | * 343 | * @param connectionLastUsed 344 | * 连接最后使用的时间 345 | */ 346 | public void setConnectionLastUsedInMs(long connectionLastUsed) { 347 | this.connectionLastUsedInMs = connectionLastUsed; 348 | } 349 | 350 | /** 351 | * 逻辑上重新打开连接 352 | */ 353 | public void renewConnection() { 354 | this.logicallyClosed.set(false); 355 | this.threadUsingConnection = Thread.currentThread(); 356 | } 357 | 358 | /** 359 | * 获取连接的创建时间 360 | * 361 | * @return 连接的创建时间 362 | */ 363 | public long getConnectionCreationTimeInMs() { 364 | return this.connectionCreationTimeInMs; 365 | } 366 | 367 | /** 368 | * 获取连接最后的使用时间 369 | * 370 | * @return 连接最后的使用时间 371 | */ 372 | public long getConnectionLastUsedInMs() { 373 | return this.connectionLastUsedInMs; 374 | } 375 | 376 | /** 377 | * 获取连接最后重置时间的方法 378 | * 379 | * @return 连接最后重置时间 380 | */ 381 | public long getConnectionLastResetInMs() { 382 | return this.connectionLastResetInMs; 383 | } 384 | 385 | /* 386 | * @see com.wmz7year.thrift.pool.connection.ThriftConnection#setAvailable(boolean) 387 | */ 388 | @Override 389 | public void setAvailable(boolean available) { 390 | this.available = available; 391 | } 392 | 393 | } 394 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/ThriftConnectionMaxAgeThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.TimerTask; 20 | 21 | import org.apache.thrift.TServiceClient; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.ServiceOrder; 26 | 27 | /** 28 | * thrift连接最大时间检测线程 29 | * 30 | * @author jiangwei (ydswcy513@gmail.com) 31 | * @version V1.0 32 | */ 33 | public class ThriftConnectionMaxAgeThread extends TimerTask { 34 | private static final Logger logger = LoggerFactory.getLogger(ThriftConnectionMaxAgeThread.class); 35 | 36 | /** 37 | * 连接分区对象 38 | */ 39 | private ThriftConnectionPartition thriftConnectionPartition; 40 | /** 41 | * 连接池对象 42 | */ 43 | private ThriftConnectionPool thriftConnectionPool; 44 | /** 45 | * lifo模式 46 | */ 47 | private ServiceOrder lifoMode; 48 | /** 49 | * 最大生存时间 50 | */ 51 | private long maxConnectionAge; 52 | 53 | public ThriftConnectionMaxAgeThread(ThriftConnectionPartition thriftConnectionPartition, 54 | ThriftConnectionPool thriftConnectionPool, long maxConnectionAge, ServiceOrder lifoMode) { 55 | this.thriftConnectionPartition = thriftConnectionPartition; 56 | this.maxConnectionAge = maxConnectionAge; 57 | this.lifoMode = lifoMode; 58 | this.thriftConnectionPool = thriftConnectionPool; 59 | this.thriftConnectionPartition.registConnectionMaxAgeThread(this); 60 | } 61 | 62 | /* 63 | * @see java.lang.Runnable#run() 64 | */ 65 | @Override 66 | public void run() { 67 | ThriftConnectionHandle connection; 68 | long tmp; 69 | long nextCheckInMs = this.maxConnectionAge; 70 | 71 | int partitionSize = this.thriftConnectionPartition.getAvailableConnections(); 72 | long currentTime = System.currentTimeMillis(); 73 | for (int i = 0; i < partitionSize; i++) { 74 | try { 75 | connection = this.thriftConnectionPartition.poolFreeConnection(); 76 | 77 | if (connection != null) { 78 | connection.setOriginatingPartition(this.thriftConnectionPartition); 79 | 80 | tmp = this.maxConnectionAge - (currentTime - connection.getConnectionCreationTimeInMs()); 81 | 82 | if (tmp < nextCheckInMs) { 83 | nextCheckInMs = tmp; 84 | } 85 | 86 | // 判断连接是否过期 87 | if (connection.isExpired(currentTime)) { 88 | // 关闭连接 89 | closeConnection(connection); 90 | continue; 91 | } 92 | 93 | if (lifoMode == ServiceOrder.LIFO) { 94 | if (!(connection.getConnectionPartition().getFreeConnections().offer(connection))) { 95 | connection.internalClose(); 96 | } 97 | } else { 98 | this.thriftConnectionPool.putConnectionBackInPartition(connection); 99 | } 100 | // 避免cpu使用率过高 进行一段时间休眠 101 | Thread.sleep(20L); 102 | } 103 | } catch (Throwable e) { 104 | logger.error("Connection max age thread exception.", e); 105 | } 106 | } 107 | 108 | } 109 | 110 | /** 111 | * 关闭thrift超时连接的方法 112 | * 113 | * @param connection 114 | * 需要关闭的超时连接 115 | */ 116 | protected void closeConnection(ThriftConnectionHandle connection) { 117 | if (connection != null) { 118 | try { 119 | connection.internalClose(); 120 | } catch (Throwable t) { 121 | logger.error("Destroy connection exception", t); 122 | } finally { 123 | this.thriftConnectionPool.postDestroyConnection(connection); 124 | } 125 | } 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/ThriftConnectionPartition.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.io.Serializable; 20 | import java.util.concurrent.ArrayBlockingQueue; 21 | import java.util.concurrent.BlockingQueue; 22 | import java.util.concurrent.TimeUnit; 23 | import java.util.concurrent.atomic.AtomicBoolean; 24 | import java.util.concurrent.locks.ReentrantReadWriteLock; 25 | 26 | import org.apache.thrift.TServiceClient; 27 | 28 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 29 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 30 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 31 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 32 | 33 | /** 34 | * 连接分区实体类
35 | * 每台服务器都意味着一个连接分区
36 | * 不同服务器之间的分区是各自独立的 37 | * 38 | * @author jiangwei (ydswcy513@gmail.com) 39 | * @version V1.0 40 | */ 41 | public class ThriftConnectionPartition implements Serializable { 42 | private static final long serialVersionUID = 1575062547601396682L; 43 | 44 | /** 45 | * 空闲连接队列 46 | */ 47 | private BlockingQueue> freeConnections; 48 | 49 | /** 50 | * thrift服务器是否关闭的表识位 51 | */ 52 | private AtomicBoolean serverIsDown = new AtomicBoolean(); 53 | 54 | /** 55 | * 分区所绑定的服务器信息 56 | */ 57 | private ThriftServerInfo thriftServerInfo; 58 | 59 | /** 60 | * 连接创建统计锁 61 | */ 62 | protected ReentrantReadWriteLock statsLock = new ReentrantReadWriteLock(); 63 | 64 | /** 65 | * 创建的连接数量 66 | */ 67 | private int createdConnections = 0; 68 | /** 69 | * 分区支持的最大连接数 70 | */ 71 | private final int maxConnections; 72 | /** 73 | * 分区支持的最小连接数 74 | */ 75 | private final int minConnections; 76 | /** 77 | * 每批连接创建的数量 78 | */ 79 | private final int acquireIncrement; 80 | /** 81 | * 连接检测操作信号处理队列 82 | */ 83 | private BlockingQueue poolWatchThreadSignalQueue = new ArrayBlockingQueue<>(1); 84 | 85 | /** 86 | * 连接测试线程 87 | */ 88 | private ThriftConnectionTesterThread thriftConnectionTesterThread; 89 | 90 | /** 91 | * 连接最大时间检测线程 92 | */ 93 | private ThriftConnectionMaxAgeThread thriftConnectionMaxAgeThread; 94 | 95 | /** 96 | * 连接池监控线程 97 | */ 98 | private PoolWatchThread poolWatchThread; 99 | 100 | /** 101 | * true为不需要创建更多的连接 说明连接已经到了最大数量 102 | */ 103 | private boolean unableToCreateMoreTransactions = false; 104 | protected ReentrantReadWriteLock unableToCreateMoreTransactionsLock = new ReentrantReadWriteLock(); 105 | 106 | public ThriftConnectionPartition(ThriftConnectionPool thriftConnectionPool, ThriftServerInfo thriftServerInfo) { 107 | ThriftConnectionPoolConfig config = thriftConnectionPool.getConfig(); 108 | this.thriftServerInfo = thriftServerInfo; 109 | this.maxConnections = config.getMaxConnectionPerServer(); 110 | this.minConnections = config.getMinConnectionPerServer(); 111 | this.acquireIncrement = config.getAcquireIncrement(); 112 | } 113 | 114 | /** 115 | * 设置空闲连接队列的方法 116 | * 117 | * @param freeConnections 118 | * 空闲连接队列 119 | */ 120 | protected void setFreeConnections(BlockingQueue> freeConnections) { 121 | this.freeConnections = freeConnections; 122 | } 123 | 124 | /** 125 | * 获取分区所对应的thrift服务器信息的方法 126 | * 127 | * @return thrift服务器信息对象 128 | */ 129 | public ThriftServerInfo getThriftServerInfo() { 130 | return thriftServerInfo; 131 | } 132 | 133 | /** 134 | * 添加空闲连接的方法 135 | * 136 | * @param thriftConnectionHandle 137 | * thrift连接代理对象 138 | * @throws ThriftConnectionPoolException 139 | * 当向连接分区添加空闲连接出现问题时抛出该异常 140 | */ 141 | public void addFreeConnection(ThriftConnectionHandle thriftConnectionHandle) 142 | throws ThriftConnectionPoolException { 143 | thriftConnectionHandle.setOriginatingPartition(this); 144 | // 更新创建的连接数 创建数 +1 145 | updateCreatedConnections(1); 146 | 147 | if (!this.freeConnections.offer(thriftConnectionHandle)) { 148 | // 将连接放入队列失败 创建数 - 1 149 | updateCreatedConnections(-1); 150 | 151 | // 关闭原始连接 152 | thriftConnectionHandle.internalClose(); 153 | } 154 | } 155 | 156 | /** 157 | * 更新连接创建统计数的方法 158 | * 159 | * @param increment 160 | * 更新数量 增加或者减少 161 | */ 162 | protected void updateCreatedConnections(int increment) { 163 | 164 | try { 165 | this.statsLock.writeLock().lock(); 166 | this.createdConnections += increment; 167 | } finally { 168 | this.statsLock.writeLock().unlock(); 169 | } 170 | } 171 | 172 | /** 173 | * 获取服务器是否关闭标识位的方法 174 | * 175 | * @return 服务器是否关闭标识位 176 | */ 177 | public AtomicBoolean getServerIsDown() { 178 | return serverIsDown; 179 | } 180 | 181 | /** 182 | * 设置连接可创建的状态 183 | * 184 | * @param unableToCreateMoreTransactions 185 | * true为不能继续创建连接 false为可以继续创建连接 186 | */ 187 | public void setUnableToCreateMoreTransactions(boolean unableToCreateMoreTransactions) { 188 | try { 189 | unableToCreateMoreTransactionsLock.writeLock().lock(); 190 | this.unableToCreateMoreTransactions = unableToCreateMoreTransactions; 191 | } finally { 192 | unableToCreateMoreTransactionsLock.writeLock().unlock(); 193 | } 194 | } 195 | 196 | /** 197 | * 获取是否能继续创建连接的方法 198 | * 199 | * @return true为不能创建连接了 false为可以继续创建连接 200 | */ 201 | public boolean isUnableToCreateMoreTransactions() { 202 | return this.unableToCreateMoreTransactions; 203 | } 204 | 205 | /** 206 | * 获取可用连接数量的方法 207 | * 208 | * @return 可用连接数量 209 | */ 210 | public int getAvailableConnections() { 211 | return this.freeConnections.size(); 212 | } 213 | 214 | /** 215 | * 获取分区支持的最大连接数的方法 216 | * 217 | * @return 分区支持的最大连接数 218 | */ 219 | public int getMaxConnections() { 220 | return this.maxConnections; 221 | } 222 | 223 | /** 224 | * 获取连接分区最小的连接数的方法 225 | * 226 | * @return 连接分区最小的连接数 227 | */ 228 | public int getMinConnections() { 229 | return this.minConnections; 230 | } 231 | 232 | /** 233 | * 获取连接池检测信号队列的方法 234 | * 235 | * @return 检测信号队列 236 | */ 237 | public BlockingQueue getPoolWatchThreadSignalQueue() { 238 | return this.poolWatchThreadSignalQueue; 239 | } 240 | 241 | /** 242 | * 直接获取一个连接的方法 243 | * 244 | * @return 连接代理对象 245 | */ 246 | protected ThriftConnectionHandle poolFreeConnection() { 247 | return this.freeConnections.poll(); 248 | } 249 | 250 | public ThriftConnection poolFreeConnection(long timeout, TimeUnit unit) throws InterruptedException { 251 | return this.freeConnections.poll(timeout, unit); 252 | } 253 | 254 | /** 255 | * 获取所有空闲连接的方法 256 | * 257 | * @return 空闲连接队列 258 | */ 259 | public BlockingQueue> getFreeConnections() { 260 | return this.freeConnections; 261 | } 262 | 263 | /** 264 | * 获取创建的连接数量的方法 265 | * 266 | * @return 分区创建的连接数 267 | */ 268 | public int getCreatedConnections() { 269 | try { 270 | this.statsLock.readLock().lock(); 271 | return this.createdConnections; 272 | } finally { 273 | this.statsLock.readLock().unlock(); 274 | } 275 | } 276 | 277 | /** 278 | * 获取每批次连接创建数量的方法 279 | * 280 | * @return 每批连接创建数量 281 | */ 282 | protected int getAcquireIncrement() { 283 | return this.acquireIncrement; 284 | } 285 | 286 | /** 287 | * 注册连接测试线程的方法 288 | * 289 | * @param thriftConnectionTesterThread 290 | * 连接测试线程对象 291 | */ 292 | public void registConnectionTesterThread(ThriftConnectionTesterThread thriftConnectionTesterThread) { 293 | this.thriftConnectionTesterThread = thriftConnectionTesterThread; 294 | } 295 | 296 | /** 297 | * 注册连接最大时间检测线程的方法 298 | * 299 | * @param thriftConnectionMaxAgeThread 300 | * 连接最大时间检测线程对象 301 | */ 302 | public void registConnectionMaxAgeThread(ThriftConnectionMaxAgeThread thriftConnectionMaxAgeThread) { 303 | this.thriftConnectionMaxAgeThread = thriftConnectionMaxAgeThread; 304 | } 305 | 306 | /** 307 | * 注册连接池监控线程的方法 308 | * 309 | * @param poolWatchThread 310 | * 连接池监控线程 311 | */ 312 | public void registPoolWatchThread(PoolWatchThread poolWatchThread) { 313 | this.poolWatchThread = poolWatchThread; 314 | } 315 | 316 | /** 317 | * 停止连接分区对应线程的方法 318 | */ 319 | public void stopThreads() { 320 | // 停止连接测试线程 321 | TaskEngine.getInstance().cancelScheduledTask(thriftConnectionTesterThread); 322 | 323 | // 停止连接最大时间检测线程 324 | TaskEngine.getInstance().cancelScheduledTask(thriftConnectionMaxAgeThread); 325 | 326 | // 停止分区检测线程 327 | poolWatchThread.stop(); 328 | this.poolWatchThreadSignalQueue.offer(new Object()); 329 | } 330 | 331 | } 332 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/ThriftConnectionPool.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.io.Closeable; 20 | import java.io.IOException; 21 | import java.io.Serializable; 22 | import java.lang.reflect.Method; 23 | import java.util.ArrayList; 24 | import java.util.Iterator; 25 | import java.util.LinkedList; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.TimerTask; 29 | import java.util.concurrent.BlockingQueue; 30 | import java.util.concurrent.Callable; 31 | import java.util.concurrent.Executors; 32 | import java.util.concurrent.LinkedBlockingQueue; 33 | import java.util.concurrent.TimeUnit; 34 | import java.util.concurrent.atomic.AtomicInteger; 35 | import java.util.concurrent.locks.ReentrantReadWriteLock; 36 | 37 | import org.apache.thrift.TServiceClient; 38 | import org.slf4j.Logger; 39 | import org.slf4j.LoggerFactory; 40 | 41 | import com.google.common.util.concurrent.ListenableFuture; 42 | import com.google.common.util.concurrent.ListeningExecutorService; 43 | import com.google.common.util.concurrent.MoreExecutors; 44 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 45 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.ServiceOrder; 46 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.ThriftServiceType; 47 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 48 | import com.wmz7year.thrift.pool.connection.DefaultThriftConnection; 49 | import com.wmz7year.thrift.pool.connection.MulitServiceThriftConnecion; 50 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 51 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 52 | 53 | /** 54 | * thrift连接池主类 55 | * 56 | * @author jiangwei (ydswcy513@gmail.com) 57 | * @version V1.0 58 | */ 59 | public class ThriftConnectionPool implements Serializable, Closeable { 60 | private static final Logger logger = LoggerFactory.getLogger(ThriftConnectionPool.class); 61 | private static final long serialVersionUID = 6524222103868846620L; 62 | 63 | /** 64 | * 连接池配置对象 65 | */ 66 | private ThriftConnectionPoolConfig config; 67 | 68 | /** 69 | * 连接超时时间 70 | */ 71 | private int connectionTimeOut; 72 | 73 | /** 74 | * 配置的服务器列表 75 | */ 76 | protected List thriftServers; 77 | 78 | /** 79 | * 服务器数量 80 | */ 81 | protected int thriftServerCount = 0; 82 | 83 | /** 84 | * 用于异步方式获取连接的服务 85 | */ 86 | private ListeningExecutorService asyncExecutor; 87 | 88 | /** 89 | * 保存分区的连接信息 90 | */ 91 | protected List> partitions; 92 | 93 | /** 94 | * 判断连接池是否在关闭过程中的表识位 95 | */ 96 | protected volatile boolean poolShuttingDown; 97 | 98 | /** 99 | * 当分区连接小于x%的时候触发创建连接信号 100 | */ 101 | protected final int poolAvailabilityThreshold; 102 | 103 | /** 104 | * 连接获取策略处理类 105 | */ 106 | protected ThriftConnectionStrategy connectionStrategy; 107 | /** 108 | * 关闭连接池的原因 109 | */ 110 | protected String shutdownStackTrace; 111 | /** 112 | * 连接获取超时时间 113 | */ 114 | protected long connectionTimeoutInMs; 115 | 116 | /** 117 | * 连接创建统计锁 118 | */ 119 | protected ReentrantReadWriteLock serverListLock = new ReentrantReadWriteLock(); 120 | 121 | /** 122 | * thrift服务类型 单服务还是多服务 123 | */ 124 | protected ThriftServiceType thriftServiceType; 125 | 126 | /** 127 | * 构造器 128 | * 129 | * @param config 130 | * 连接池配置对象 131 | * @throws ThriftConnectionPoolException 132 | * 当发生错误的时候抛出该异常信息 133 | */ 134 | public ThriftConnectionPool(ThriftConnectionPoolConfig config) throws ThriftConnectionPoolException { 135 | this.config = config; 136 | // 检查配置内容是否正确 137 | this.config.check(); 138 | this.connectionTimeOut = this.config.getConnectTimeout(); 139 | 140 | // 获取配置的服务器列表 141 | this.thriftServers = this.config.getThriftServers(); 142 | this.thriftServerCount = this.thriftServers.size(); 143 | 144 | // 判断是否是懒加载 如果是则验证连接 145 | if (!this.config.isLazyInit()) { 146 | // 需要删除的服务器列表 147 | List needToDelete = new ArrayList<>(); 148 | 149 | // 尝试获取一个连接 150 | for (int i = 0; i < thriftServerCount; i++) { 151 | ThriftServerInfo thriftServerInfo = thriftServers.get(i); 152 | try { 153 | ThriftConnection connection = obtainRawInternalConnection(thriftServerInfo); 154 | connection.close(); 155 | } catch (Exception e) { 156 | needToDelete.add(thriftServerInfo); 157 | logger.error("无法从服务器 " + thriftServerInfo.toString() + "中获取连接 将移除该服务器"); 158 | } 159 | } 160 | 161 | // 删除服务器信息 162 | for (ThriftServerInfo thriftServerInfo : needToDelete) { 163 | thriftServers.remove(thriftServerInfo); 164 | } 165 | 166 | // 移除完毕检查数量 167 | thriftServerCount = thriftServers.size(); 168 | if (thriftServerCount == 0 && !this.config.isNoServerStartUp()) { 169 | throw new ThriftConnectionPoolException("无可用thrift服务器,连接池启动失败"); 170 | } 171 | } 172 | 173 | this.asyncExecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()); 174 | 175 | // 创建线程调度引擎 176 | TaskEngine.getInstance(); 177 | 178 | // 创建分区列表 179 | this.partitions = new ArrayList<>(thriftServerCount); 180 | 181 | this.poolAvailabilityThreshold = this.config.getPoolAvailabilityThreshold(); 182 | this.connectionStrategy = new DefaultThriftConnectionStrategy<>(this); 183 | this.connectionTimeoutInMs = this.config.getConnectionTimeoutInMs(); 184 | this.thriftServiceType = this.config.getThriftServiceType(); 185 | 186 | if (this.connectionTimeoutInMs == 0) { 187 | this.connectionTimeoutInMs = Long.MAX_VALUE; 188 | } 189 | 190 | // 根据服务器配置创建不同的连接分区 191 | for (int p = 0; p < thriftServerCount; p++) { 192 | ThriftServerInfo thriftServerInfo = thriftServers.get(p); 193 | ThriftConnectionPartition thriftConnectionPartition = createThriftConnectionPartition(thriftServerInfo); 194 | partitions.add(thriftConnectionPartition); 195 | } 196 | } 197 | 198 | /** 199 | * 添加新的thrift服务器的方法 200 | * 201 | * @param thriftServerInfo 202 | * 需要添加的服务器信息 203 | * @return true为添加成功 false为添加失败 204 | * @throws ThriftConnectionPoolException 205 | * 当添加thrift服务器出现问题时抛出该异常 206 | */ 207 | public boolean addThriftServer(ThriftServerInfo thriftServerInfo) throws ThriftConnectionPoolException { 208 | serverListLock.writeLock().lock(); 209 | try { 210 | // 验证服务器是否可用 211 | try { 212 | ThriftConnection connection = obtainRawInternalConnection(thriftServerInfo); 213 | connection.close(); 214 | } catch (Exception e) { 215 | logger.error("无法添加Thrfit服务器到连接池 ip:" + thriftServerInfo.getHost() + " 端口:" + thriftServerInfo.getPort(), 216 | e); 217 | return false; 218 | } 219 | ThriftConnectionPartition thriftConnectionPartition = createThriftConnectionPartition(thriftServerInfo); 220 | 221 | partitions.add(thriftConnectionPartition); 222 | thriftServers.add(thriftServerInfo); 223 | thriftServerCount = partitions.size(); 224 | return true; 225 | } catch (Exception e) { 226 | logger.error("无法添加Thrfit服务器到连接池 ip:" + thriftServerInfo.getHost() + " 端口:" + thriftServerInfo.getPort(), e); 227 | return false; 228 | } finally { 229 | serverListLock.writeLock().unlock(); 230 | } 231 | } 232 | 233 | /** 234 | * 移除现有thrift服务器的方法
235 | * 注意 如果没有可用的thrift则无法获取到连接 236 | * 237 | * @param thriftServerInfo 238 | * 需要删除的服务器信息 239 | * @return true为删除成功 false为删除失败 240 | */ 241 | public boolean removeThriftServer(ThriftServerInfo thriftServerInfo) { 242 | // 不存在这台服务器 直接返回失败 243 | if (!thriftServers.contains(thriftServerInfo)) { 244 | return false; 245 | } 246 | 247 | serverListLock.writeLock().lock(); 248 | try { 249 | Iterator> iterator = partitions.iterator(); 250 | while (iterator.hasNext()) { 251 | ThriftConnectionPartition thriftConnectionPartition = iterator.next(); 252 | if (thriftConnectionPartition.getThriftServerInfo().equals(thriftServerInfo)) { 253 | thriftConnectionPartition.setUnableToCreateMoreTransactions(false); 254 | List> clist = new LinkedList<>(); 255 | thriftConnectionPartition.getFreeConnections().drainTo(clist); 256 | for (ThriftConnectionHandle c : clist) { 257 | destroyConnection(c); 258 | } 259 | thriftConnectionPartition.stopThreads(); 260 | partitions.remove(thriftConnectionPartition); 261 | thriftServers.remove(thriftServerInfo); 262 | thriftServerCount = partitions.size(); 263 | logger.info("移除服务器操作完成 剩余服务器数量:" + getThriftServerCount()); 264 | if (getThriftServerCount() == 0) { 265 | logger.warn("连接池中没有可用thrift服务器 无法获取连接"); 266 | } 267 | return true; 268 | } 269 | } 270 | } catch (Exception e) { 271 | logger.error("移除Thrift服务器失败 ip:" + thriftServerInfo.getHost() + " 端口:" + thriftServerInfo.getPort()); 272 | return false; 273 | } finally { 274 | serverListLock.writeLock().unlock(); 275 | } 276 | return false; 277 | } 278 | 279 | /** 280 | * 根据配置信息对象创建连接分区的方法 281 | * 282 | * @param thriftServerInfo 283 | * 配置信息对象 284 | * @return 连接分区对象 285 | * @throws ThriftConnectionPoolException 286 | * 创建连接分区过程中可能产生的异常 287 | */ 288 | private ThriftConnectionPartition createThriftConnectionPartition(ThriftServerInfo thriftServerInfo) 289 | throws ThriftConnectionPoolException { 290 | // 队列模式 291 | ServiceOrder serviceOrder = this.config.getServiceOrder(); 292 | 293 | ThriftConnectionPartition thriftConnectionPartition = new ThriftConnectionPartition<>(this, 294 | thriftServerInfo); 295 | // 添加空闲连接队列 296 | BlockingQueue> connectionHandles = new LinkedBlockingQueue<>( 297 | this.config.getMaxConnectionPerServer()); 298 | thriftConnectionPartition.setFreeConnections(connectionHandles); 299 | 300 | if (!this.config.isLazyInit()) { 301 | for (int i = 0; i < this.config.getMinConnectionPerServer(); i++) { 302 | // 初始化连接代理对象 303 | thriftConnectionPartition 304 | .addFreeConnection(new ThriftConnectionHandle(null, thriftConnectionPartition, this, false)); 305 | } 306 | 307 | } 308 | 309 | // 连接过期时间监控 310 | if (this.config.getIdleConnectionTestPeriod(TimeUnit.SECONDS) > 0 311 | || this.config.getIdleMaxAge(TimeUnit.SECONDS) > 0) { 312 | 313 | final TimerTask connectionTester = new ThriftConnectionTesterThread(thriftConnectionPartition, this, 314 | this.config.getIdleMaxAge(TimeUnit.MILLISECONDS), 315 | this.config.getIdleConnectionTestPeriod(TimeUnit.MILLISECONDS), serviceOrder); 316 | long delayInSeconds = this.config.getIdleConnectionTestPeriod(TimeUnit.SECONDS); 317 | if (delayInSeconds == 0L) { 318 | delayInSeconds = this.config.getIdleMaxAge(TimeUnit.SECONDS); 319 | } 320 | if (this.config.getIdleMaxAge(TimeUnit.SECONDS) < delayInSeconds 321 | && this.config.getIdleConnectionTestPeriod(TimeUnit.SECONDS) != 0 322 | && this.config.getIdleMaxAge(TimeUnit.SECONDS) != 0) { 323 | delayInSeconds = this.config.getIdleMaxAge(TimeUnit.SECONDS); 324 | } 325 | TaskEngine.getInstance().scheduleAtFixedRate(connectionTester, delayInSeconds, delayInSeconds); 326 | } 327 | 328 | // 连接最长存活时间监控 329 | if (this.config.getMaxConnectionAgeInSeconds() > 0) { 330 | final TimerTask connectionMaxAgeTester = new ThriftConnectionMaxAgeThread(thriftConnectionPartition, 331 | this, this.config.getMaxConnectionAge(TimeUnit.MILLISECONDS), serviceOrder); 332 | TaskEngine.getInstance().scheduleAtFixedRate(connectionMaxAgeTester, 333 | this.config.getMaxConnectionAgeInSeconds(), this.config.getMaxConnectionAgeInSeconds()); 334 | } 335 | // 连接数量监控 336 | TaskEngine.getInstance().submit(new PoolWatchThread(thriftConnectionPartition, this)); 337 | 338 | return thriftConnectionPartition; 339 | 340 | } 341 | 342 | /** 343 | * 根据配置获取原始连接的方法 344 | * 345 | * @param serverInfo 346 | * thrift服务器信息 347 | * @return thrift客户端连接对象 348 | * @throws ThriftConnectionPoolException 349 | * 当获取连接出现问题时抛出该异常 350 | */ 351 | private ThriftConnection obtainRawInternalConnection(ThriftServerInfo serverInfo) 352 | throws ThriftConnectionPoolException { 353 | // 判断单服务还是多服务模式 354 | ThriftConnection connection; 355 | if (this.thriftServiceType == ThriftServiceType.SINGLE_INTERFACE) { 356 | connection = new DefaultThriftConnection<>(serverInfo.getHost(), serverInfo.getPort(), 357 | this.connectionTimeOut, this.config.getThriftProtocol(), this.config.getClientClass()); 358 | } else { 359 | connection = new MulitServiceThriftConnecion<>(serverInfo.getHost(), serverInfo.getPort(), 360 | this.connectionTimeOut, this.config.getThriftProtocol(), this.config.getThriftClientClasses()); 361 | } 362 | return connection; 363 | } 364 | 365 | /* 366 | * @see java.io.Closeable#close() 367 | */ 368 | @Override 369 | public void close() throws IOException { 370 | shutdown(); 371 | } 372 | 373 | /** 374 | * 关闭连接池的方法 375 | */ 376 | public synchronized void shutdown() { 377 | if (!this.poolShuttingDown) { 378 | logger.info("开始关闭thrift连接池..."); 379 | this.poolShuttingDown = true; 380 | this.shutdownStackTrace = captureStackTrace("开始关闭thrift连接池"); 381 | 382 | TaskEngine.getInstance().shutdown(); 383 | 384 | this.asyncExecutor.shutdownNow(); 385 | 386 | try { 387 | this.asyncExecutor.awaitTermination(5, TimeUnit.SECONDS); 388 | 389 | } catch (InterruptedException e) { 390 | // do nothing 391 | } 392 | 393 | this.connectionStrategy.terminateAllConnections(); 394 | logger.info("关闭thrift连接池完成"); 395 | } 396 | } 397 | 398 | /** 399 | * 获取方法调用堆栈信息的方法 400 | * 401 | * @param message 402 | * 提示语 403 | * @return 堆栈信息 404 | */ 405 | protected String captureStackTrace(String message) { 406 | StringBuilder stringBuilder = new StringBuilder(String.format(message, Thread.currentThread().getName())); 407 | StackTraceElement[] trace = Thread.currentThread().getStackTrace(); 408 | for (int i = 0; i < trace.length; i++) { 409 | stringBuilder.append(' ').append(trace[i]).append("\r\n"); 410 | } 411 | 412 | stringBuilder.append(""); 413 | 414 | return stringBuilder.toString(); 415 | } 416 | 417 | /** 418 | * 从连接池中获取一个连接的方法 419 | * 420 | * @return 连接对象 421 | * @throws ThriftConnectionPoolException 422 | * 当获取连接出现错误时抛出该异常 423 | */ 424 | public ThriftConnection getConnection() throws ThriftConnectionPoolException { 425 | return this.connectionStrategy.getConnection(); 426 | } 427 | 428 | /** 429 | * 使用指定服务器ID从连接池中获取一个连接的方法 430 | * 431 | * @param nodeID 432 | * 服务器节点的ID 433 | * @return 连接对象 434 | * @throws ThriftConnectionPoolException 435 | * 当获取连接出现错误时抛出该异常 436 | */ 437 | public ThriftConnection getConnection(byte[] nodeID) throws ThriftConnectionPoolException { 438 | return this.connectionStrategy.getConnection(nodeID); 439 | } 440 | 441 | /** 442 | * 异步获取连接的方法 443 | * 444 | * @return 连接代理对象 445 | */ 446 | public ListenableFuture> getAsyncConnection() { 447 | return this.asyncExecutor.submit(new Callable>() { 448 | 449 | public ThriftConnection call() throws Exception { 450 | return getConnection(); 451 | } 452 | }); 453 | } 454 | 455 | /** 456 | * 获取连接池配置对象的方法 457 | * 458 | * @return 连接池配置对象 459 | */ 460 | public ThriftConnectionPoolConfig getConfig() { 461 | return this.config; 462 | } 463 | 464 | /** 465 | * 获取一个thrift原始连接对象的方法 466 | * 467 | * @param thriftConnectionHandle 468 | * thrift连接代理类对象 469 | * @return thrift连接对象 470 | * @throws ThriftConnectionPoolException 471 | * 当获取连接出现问题时抛出该异常 472 | */ 473 | public ThriftConnection obtainInternalConnection(ThriftConnectionHandle thriftConnectionHandle) 474 | throws ThriftConnectionPoolException { 475 | boolean tryAgain; 476 | ThriftConnection result; 477 | ThriftConnection oldRawConnection = thriftConnectionHandle.getInternalConnection(); 478 | ThriftServerInfo thriftServerInfo = thriftConnectionHandle.getThriftServerInfo(); 479 | 480 | int acquireRetryAttempts = this.getConfig().getAcquireRetryAttempts(); 481 | long acquireRetryDelayInMs = this.getConfig().getAcquireRetryDelayInMs(); 482 | AcquireFailConfig acquireConfig = new AcquireFailConfig(); 483 | acquireConfig.setAcquireRetryAttempts(new AtomicInteger(acquireRetryAttempts)); 484 | acquireConfig.setAcquireRetryDelayInMs(acquireRetryDelayInMs); 485 | acquireConfig.setLogMessage("Failed to acquire connection to " + thriftServerInfo.toString()); 486 | 487 | do { 488 | result = null; 489 | 490 | try { 491 | // 尝试获取原始连接 492 | result = this.obtainRawInternalConnection(thriftServerInfo); 493 | tryAgain = false; 494 | 495 | if (acquireRetryAttempts != this.getConfig().getAcquireRetryAttempts()) { 496 | logger.info("Successfully re-established connection to " + thriftServerInfo.toString()); 497 | } 498 | 499 | thriftConnectionHandle.getConnectionPartition().getServerIsDown().set(false); 500 | 501 | thriftConnectionHandle.setInternalConnection(result); 502 | 503 | } catch (ThriftConnectionPoolException e) { 504 | logger.error(String.format("Failed to acquire connection to %s. Sleeping for %d ms. Attempts left: %d", 505 | thriftServerInfo.toString(), acquireRetryDelayInMs, acquireRetryAttempts), e); 506 | 507 | try { 508 | if (acquireRetryAttempts > 0) { 509 | Thread.sleep(acquireRetryDelayInMs); 510 | } 511 | tryAgain = (acquireRetryAttempts--) > 0; 512 | } catch (InterruptedException e1) { 513 | tryAgain = false; 514 | } 515 | 516 | if (!tryAgain) { 517 | if (oldRawConnection != null) { 518 | try { 519 | oldRawConnection.close(); 520 | } catch (IOException e1) { 521 | throw new ThriftConnectionPoolException(e1); 522 | } 523 | } 524 | thriftConnectionHandle.setInternalConnection(oldRawConnection); 525 | throw e; 526 | } 527 | 528 | } 529 | } while (tryAgain); 530 | return result; 531 | } 532 | 533 | /** 534 | * 将连接返回到连接池中的方法
535 | * 该方法在调用connection.close()的时候触发
536 | * 但是连接并不会真正的关掉 而是清理后返回连接池中 537 | * 538 | * @param connection 539 | * 需要回收的连接对象 540 | * @throws ThriftConnectionPoolException 541 | * 回收过程中可能产生的异常 542 | */ 543 | public void releaseConnection(ThriftConnection connection) throws ThriftConnectionPoolException { 544 | ThriftConnectionHandle handle = (ThriftConnectionHandle) connection; 545 | 546 | // 判断连接池是否是在关闭中 如果不是在关闭中则执行回收操作 547 | if (!this.poolShuttingDown) { 548 | internalReleaseConnection(handle); 549 | } 550 | } 551 | 552 | /** 553 | * 将连接代理对象回收到连接池中的方法 554 | * 555 | * @param connectionHandle 556 | * 连接代理对象 557 | * @throws ThriftConnectionPoolException 558 | * 回收过程中可能产生的异常 559 | */ 560 | protected void internalReleaseConnection(ThriftConnectionHandle connectionHandle) 561 | throws ThriftConnectionPoolException { 562 | if (connectionHandle.isExpired() || (!this.poolShuttingDown && connectionHandle.isPossiblyBroken() 563 | && !isConnectionHandleAlive(connectionHandle))) { 564 | 565 | if (connectionHandle.isExpired()) { 566 | connectionHandle.internalClose(); 567 | } 568 | 569 | ThriftConnectionPartition connectionPartition = connectionHandle.getConnectionPartition(); 570 | postDestroyConnection(connectionHandle); 571 | 572 | maybeSignalForMoreConnections(connectionPartition); 573 | return; 574 | } 575 | connectionHandle.setConnectionLastUsedInMs(System.currentTimeMillis()); 576 | if (!this.poolShuttingDown) { 577 | putConnectionBackInPartition(connectionHandle); 578 | } else { 579 | connectionHandle.internalClose(); 580 | } 581 | } 582 | 583 | /** 584 | * 将连接放回对应分区的方法 585 | * 586 | * @param connectionHandle 587 | * 连接代理对象 588 | * @throws ThriftConnectionPoolException 589 | * 回收连接过程出现错误时抛出该异常 590 | */ 591 | protected void putConnectionBackInPartition(ThriftConnectionHandle connectionHandle) 592 | throws ThriftConnectionPoolException { 593 | BlockingQueue> queue = connectionHandle.getConnectionPartition().getFreeConnections(); 594 | if (!queue.offer(connectionHandle)) { // 如果无法放回则关闭 595 | connectionHandle.internalClose(); 596 | } 597 | } 598 | 599 | /** 600 | * 判断连接代理类是否可用的方法 601 | * 602 | * @param connection 603 | * 需要检测的连接代理类对象 604 | * @return true为可用 false为不可用 605 | */ 606 | public boolean isConnectionHandleAlive(ThriftConnectionHandle connection) { 607 | boolean result = false; 608 | boolean logicallyClosed = connection.logicallyClosed.get(); 609 | try { 610 | connection.logicallyClosed.compareAndSet(true, false); 611 | 612 | // 反射调用ping方法 613 | T client = null; 614 | if (this.thriftServiceType == ThriftServiceType.SINGLE_INTERFACE) { 615 | client = connection.getClient(); 616 | } else { 617 | Map muiltServiceClients = connection.getMuiltServiceClients(); 618 | if (muiltServiceClients.size() == 0) { // 没有可用的客户端 直接返回 619 | return false; 620 | } 621 | Iterator iterator = muiltServiceClients.values().iterator(); 622 | if (iterator.hasNext()) { 623 | client = iterator.next(); 624 | } else { 625 | return false; 626 | } 627 | } 628 | try { 629 | Method method = client.getClass().getMethod("ping"); 630 | method.invoke(client); 631 | } catch (Exception e) { 632 | return false; 633 | } 634 | 635 | result = true; 636 | } finally { 637 | connection.logicallyClosed.set(logicallyClosed); 638 | connection.setConnectionLastResetInMs(System.currentTimeMillis()); 639 | } 640 | return result; 641 | } 642 | 643 | /** 644 | * 准备销毁连接的方法
645 | * 通知连接分区准备创建新的连接 646 | * 647 | * @param handle 648 | * 需要销毁的连接地理对象 649 | */ 650 | protected void postDestroyConnection(ThriftConnectionHandle handle) { 651 | ThriftConnectionPartition partition = handle.getConnectionPartition(); 652 | 653 | partition.updateCreatedConnections(-1); 654 | partition.setUnableToCreateMoreTransactions(false); 655 | } 656 | 657 | /** 658 | * 检查连接分区的连接数量
659 | * 如果连接数量不足则向事件队列中添加新的创建连接信号 660 | * 661 | * @param connectionPartition 662 | * 需要检测的连接分区 663 | */ 664 | protected void maybeSignalForMoreConnections(ThriftConnectionPartition connectionPartition) { 665 | if (!connectionPartition.isUnableToCreateMoreTransactions() && !this.poolShuttingDown 666 | && connectionPartition.getAvailableConnections() * 100 667 | / connectionPartition.getMaxConnections() <= this.poolAvailabilityThreshold) { 668 | connectionPartition.getPoolWatchThreadSignalQueue().offer(new Object()); 669 | } 670 | } 671 | 672 | /** 673 | * 销毁连接的方法 674 | * 675 | * @param conn 676 | * 需要销毁的连接代理对象 677 | */ 678 | protected void destroyConnection(ThriftConnectionHandle conn) { 679 | postDestroyConnection(conn); 680 | try { 681 | conn.internalClose(); 682 | } catch (ThriftConnectionPoolException e) { 683 | logger.error("Error in attempting to close connection", e); 684 | } 685 | } 686 | 687 | /** 688 | * 销毁单个连接分区的方法 689 | * 690 | * @param thriftConnectionPartition 691 | * 需要销毁的连接分区对象 692 | */ 693 | public void destroyThriftConnectionPartition(ThriftConnectionPartition thriftConnectionPartition) { 694 | try { 695 | serverListLock.writeLock().lock(); 696 | // 首先移除分区信息 697 | partitions.remove(thriftConnectionPartition); 698 | thriftConnectionPartition.stopThreads(); 699 | ThriftServerInfo thriftServerInfo = thriftConnectionPartition.getThriftServerInfo(); 700 | thriftServers.remove(thriftServerInfo); 701 | thriftServerCount = partitions.size(); 702 | logger.info("连接池移除服务器信息:" + thriftServerInfo.getHost() + " 端口:" + thriftServerInfo.getPort()); 703 | if (getThriftServerCount() == 0) { 704 | logger.error("当前连接池中无可用服务器 无法获取新的客户端连接"); 705 | } 706 | } finally { 707 | serverListLock.writeLock().unlock(); 708 | } 709 | List> clist = new LinkedList<>(); 710 | thriftConnectionPartition.getFreeConnections().drainTo(clist); 711 | for (ThriftConnectionHandle c : clist) { 712 | destroyConnection(c); 713 | } 714 | } 715 | 716 | /** 717 | * 获取thrift服务器数量的方法 718 | * 719 | * @return 服务器数量 720 | */ 721 | public int getThriftServerCount() { 722 | return thriftServerCount; 723 | } 724 | 725 | } 726 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/ThriftConnectionStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import org.apache.thrift.TServiceClient; 20 | 21 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 22 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 23 | 24 | /** 25 | * 连接操作策略接口 26 | * 27 | * @author jiangwei (ydswcy513@gmail.com) 28 | * @version V1.0 29 | */ 30 | public interface ThriftConnectionStrategy { 31 | 32 | /** 33 | * 获取一个thrift连接的方法 34 | * 35 | * @return thrift连接代理对象 36 | * @throws ThriftConnectionPoolException 37 | * 当获取连接出现问题的时候抛出该异常 38 | */ 39 | public ThriftConnection getConnection() throws ThriftConnectionPoolException; 40 | 41 | /** 42 | * 使用指定服务器ID从连接池中获取一个连接的方法 43 | * 44 | * @param nodeID 45 | * 服务器节点的ID 46 | * @return 连接对象 47 | * @throws ThriftConnectionPoolException 48 | * 当获取连接出现错误时抛出该异常 49 | */ 50 | public ThriftConnection getConnection(byte[] nodeID) throws ThriftConnectionPoolException; 51 | 52 | /** 53 | * 获取一个不阻塞的thrift代理连接 54 | * 55 | * @return thrift连接代理对象 56 | */ 57 | public ThriftConnection pollConnection(); 58 | 59 | /** 60 | * 关闭所有连接的方法 61 | */ 62 | public void terminateAllConnections(); 63 | 64 | /** 65 | * 清理连接代理类的方法 66 | * 67 | * @param oldHandler 68 | * 旧的连接代理类 69 | * @param newHandler 70 | * 新的连接代理类 71 | */ 72 | public void cleanupConnection(ThriftConnectionHandle oldHandler, ThriftConnectionHandle newHandler); 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/ThriftConnectionTesterThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.TimerTask; 20 | 21 | import org.apache.thrift.TServiceClient; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.ServiceOrder; 26 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 27 | 28 | /** 29 | * 连接测试线程
30 | * 用于测试分区中的连接可用性 31 | * 32 | * @author jiangwei (ydswcy513@gmail.com) 33 | * @version V1.0 34 | */ 35 | public class ThriftConnectionTesterThread extends TimerTask { 36 | private static final Logger logger = LoggerFactory.getLogger(ThriftConnectionTesterThread.class); 37 | /** 38 | * 当连接闲置多长时间后进行连接测试 39 | */ 40 | private long idleConnectionTestPeriodInMs; 41 | /** 42 | * 连接的最大闲置时间 超过该时间会关闭连接 43 | */ 44 | private long idleMaxAgeInMs; 45 | /** 46 | * 连接分区对象 47 | */ 48 | private ThriftConnectionPartition thriftConnectionPartition; 49 | /** 50 | * 连接池对象 51 | */ 52 | private ThriftConnectionPool thriftConnectionPool; 53 | /** 54 | * lifo模式 55 | */ 56 | private ServiceOrder lifoMode; 57 | 58 | public ThriftConnectionTesterThread(ThriftConnectionPartition thriftConnectionPartition, 59 | ThriftConnectionPool thriftConnectionPool, long idleMaxAge, long idleConnectionTestPeriod, 60 | ServiceOrder lifoMode) { 61 | this.thriftConnectionPartition = thriftConnectionPartition; 62 | this.idleMaxAgeInMs = idleMaxAge; 63 | this.idleConnectionTestPeriodInMs = idleConnectionTestPeriod; 64 | this.thriftConnectionPool = thriftConnectionPool; 65 | this.lifoMode = lifoMode; 66 | this.thriftConnectionPartition.registConnectionTesterThread(this); 67 | } 68 | 69 | /* 70 | * @see java.lang.Runnable#run() 71 | */ 72 | @Override 73 | public void run() { 74 | ThriftConnectionHandle connection = null; 75 | long tmp; 76 | try { 77 | long nextCheckInMs = this.idleConnectionTestPeriodInMs; 78 | if (this.idleMaxAgeInMs > 0) { 79 | if (this.idleConnectionTestPeriodInMs == 0) { 80 | nextCheckInMs = this.idleMaxAgeInMs; 81 | } else { 82 | nextCheckInMs = Math.min(nextCheckInMs, this.idleMaxAgeInMs); 83 | } 84 | } 85 | 86 | int partitionSize = this.thriftConnectionPartition.getAvailableConnections(); 87 | long currentTimeInMs = System.currentTimeMillis(); 88 | for (int i = 0; i < partitionSize; i++) { 89 | // 从分区中获取一个连接进行测试 90 | connection = this.thriftConnectionPartition.getFreeConnections().poll(); 91 | if (connection != null) { 92 | connection.setOriginatingPartition(this.thriftConnectionPartition); 93 | 94 | // 检查连接是否出现问题或者过期 95 | if (connection.isPossiblyBroken() || ((this.idleMaxAgeInMs > 0) && (System.currentTimeMillis() 96 | - connection.getConnectionLastUsedInMs() > this.idleMaxAgeInMs))) { 97 | closeConnection(connection); 98 | continue; 99 | } 100 | 101 | // 检测连接是否可用以及是否到了测试时间 102 | if (this.idleConnectionTestPeriodInMs > 0 103 | && (currentTimeInMs 104 | - connection.getConnectionLastUsedInMs() > this.idleConnectionTestPeriodInMs) 105 | && (currentTimeInMs 106 | - connection.getConnectionLastResetInMs() >= this.idleConnectionTestPeriodInMs)) { 107 | // 检测连接是否可用 108 | if (!this.thriftConnectionPool.isConnectionHandleAlive(connection)) { 109 | closeConnection(connection); 110 | continue; 111 | } 112 | 113 | tmp = this.idleConnectionTestPeriodInMs; 114 | if (this.idleMaxAgeInMs > 0) { 115 | tmp = Math.min(tmp, this.idleMaxAgeInMs); 116 | } 117 | } else { 118 | tmp = Math.abs(this.idleConnectionTestPeriodInMs 119 | - (currentTimeInMs - connection.getConnectionLastResetInMs())); 120 | long tmp2 = Math 121 | .abs(this.idleMaxAgeInMs - (currentTimeInMs - connection.getConnectionLastUsedInMs())); 122 | if (this.idleMaxAgeInMs > 0) { 123 | tmp = Math.min(tmp, tmp2); 124 | } 125 | 126 | } 127 | if (tmp < nextCheckInMs) { 128 | nextCheckInMs = tmp; 129 | } 130 | 131 | if (lifoMode == ServiceOrder.LIFO) { 132 | // 如果不能正确的把链接还给分区 则关闭 133 | if (!(connection.getConnectionPartition().getFreeConnections().offer(connection))) { 134 | connection.internalClose(); 135 | } 136 | } else { 137 | this.thriftConnectionPool.putConnectionBackInPartition(connection); 138 | } 139 | 140 | // 避免cpu使用率过高 进行一段时间休眠 141 | Thread.sleep(20L); 142 | } 143 | 144 | } 145 | 146 | } catch (Throwable e) { 147 | logger.error("Connection tester thread interrupted", e); 148 | } 149 | 150 | } 151 | 152 | /** 153 | * 关闭连接的方法 154 | * 155 | * @param connection 156 | * 需要关闭的连接对象 157 | */ 158 | protected synchronized void closeConnection(ThriftConnectionHandle connection) { 159 | 160 | if (connection != null && !connection.isClosed()) { 161 | try { 162 | connection.internalClose(); 163 | } catch (ThriftConnectionPoolException e) { 164 | logger.error("Destroy connection exception", e); 165 | } finally { 166 | this.thriftConnectionPool.postDestroyConnection(connection); 167 | connection.getConnectionPartition().getPoolWatchThreadSignalQueue().offer(new Object()); 168 | } 169 | } 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/config/ThriftConnectionPoolConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool.config; 18 | 19 | import static com.google.common.base.Preconditions.checkNotNull; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Collection; 23 | import java.util.HashMap; 24 | import java.util.HashSet; 25 | import java.util.Iterator; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.Map.Entry; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | import org.apache.thrift.TServiceClient; 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | 35 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 36 | 37 | /** 38 | * thrift连接池配置类
39 | * 40 | * @author jiangwei (ydswcy513@gmail.com) 41 | * @version V1.0 42 | */ 43 | public class ThriftConnectionPoolConfig { 44 | private static final Logger logger = LoggerFactory.getLogger(ThriftConnectionPoolConfig.class); 45 | /** 46 | * 连接池名称 47 | */ 48 | private String poolName; 49 | 50 | /** 51 | * 连接通讯管道类型 52 | */ 53 | private TProtocolType thriftProtocol; 54 | 55 | /** 56 | * 连接超时时间 57 | */ 58 | private int connectTimeout; 59 | 60 | /** 61 | * thrift客户端类对象 62 | */ 63 | private Class clientClass; 64 | 65 | /** 66 | * 多服务情况下的客户端列表 67 | */ 68 | private Map> clientClasses = new HashMap<>(); 69 | 70 | /** 71 | * 配置的服务器列表 72 | */ 73 | private Collection thriftServers = new HashSet<>(); 74 | 75 | /** 76 | * 是否是懒加载连接 77 | */ 78 | private boolean lazyInit; 79 | 80 | /** 81 | * 每台服务器最大的连接数 82 | */ 83 | private int maxConnectionPerServer = 0; 84 | 85 | /** 86 | * 每台服务器最小连接数 87 | */ 88 | private int minConnectionPerServer = 0; 89 | 90 | /** 91 | * 连接检测时间周期 92 | */ 93 | private long idleConnectionTestPeriodInSeconds = 10; 94 | 95 | /** 96 | * 未使用连接关闭时间 97 | */ 98 | private long idleMaxAgeInSeconds = 20; 99 | 100 | /** 101 | * 连接最大存活时间 102 | */ 103 | private long maxConnectionAgeInSeconds = 0; 104 | 105 | /** 106 | * 当连接获取失败时 重试获取的次数 107 | */ 108 | private int acquireRetryAttempts = 5; 109 | 110 | /** 111 | * 当连接获取失败时 重试获取的时间间隔 112 | */ 113 | private long acquireRetryDelayInMs = 7000; 114 | 115 | /** 116 | * 队列模式 117 | */ 118 | private ServiceOrder serviceOrder = ServiceOrder.FIFO; 119 | /** 120 | * 当需要创建连接时检查的分区连接空闲连接比例 121 | */ 122 | private int poolAvailabilityThreshold = 0; 123 | /** 124 | * 连接获取的超时时间 125 | */ 126 | private long connectionTimeoutInMs = 0; 127 | 128 | /** 129 | * 每批连接创建的数量 130 | */ 131 | private int acquireIncrement = 2; 132 | 133 | /** 134 | * 最大连续获取失败连接的次数
135 | * 如果到了该值依然无法获取连接 连接池则会排出对应问题连接的服务器 136 | */ 137 | private int maxConnectionCreateFailedCount = 3; 138 | 139 | /** 140 | * 是否支持没有服务器时启动连接池
141 | * 在这种情况下无法获取连接 需要等待有服务器时才可以获取连接 142 | */ 143 | private boolean noServerStartUp; 144 | 145 | /** 146 | * thrift接口模式 147 | */ 148 | private ThriftServiceType thriftServiceType; 149 | 150 | public ThriftConnectionPoolConfig() { 151 | this(ThriftServiceType.SINGLE_INTERFACE); 152 | } 153 | 154 | public ThriftConnectionPoolConfig(ThriftServiceType thriftServiceType) { 155 | this.thriftServiceType = thriftServiceType; 156 | } 157 | 158 | public TProtocolType getThriftProtocol() { 159 | return thriftProtocol; 160 | } 161 | 162 | public void setThriftProtocol(TProtocolType thriftProtocol) { 163 | this.thriftProtocol = thriftProtocol; 164 | } 165 | 166 | public int getConnectTimeout() { 167 | return connectTimeout; 168 | } 169 | 170 | public void setConnectTimeout(int connectTimeout) { 171 | this.connectTimeout = connectTimeout; 172 | } 173 | 174 | public Class getClientClass() { 175 | return clientClass; 176 | } 177 | 178 | public void setClientClass(Class clientClass) { 179 | this.clientClass = clientClass; 180 | } 181 | 182 | /** 183 | * 添加thrift服务器信息的方法 184 | * 185 | * @param host 186 | * 服务器地址 187 | * @param port 188 | * 服务器端口 189 | */ 190 | public void addThriftServer(String host, int port) { 191 | addThriftServer(host, port, null); 192 | } 193 | 194 | /** 195 | * 添加thrift服务器信息的方法 196 | * 197 | * @param host 198 | * 服务器地址 199 | * @param port 200 | * 服务器端口 201 | * @param serverID 202 | * 服务器ID 203 | */ 204 | public void addThriftServer(String host, int port, byte[] serverID) { 205 | addThriftServer(new ThriftServerInfo(host, port, serverID)); 206 | } 207 | 208 | /** 209 | * 添加thrift服务信息对象的方法 210 | * 211 | * @param serverInfo 212 | * 服务器信息对象 213 | */ 214 | public void addThriftServer(ThriftServerInfo serverInfo) { 215 | thriftServers.add(serverInfo); 216 | } 217 | 218 | /** 219 | * 获取配置的thrift服务器列表的方法 220 | * 221 | * @return thrift服务器列表集合 222 | */ 223 | public List getThriftServers() { 224 | List servers = new ArrayList<>(); 225 | servers.addAll(thriftServers); 226 | return servers; 227 | } 228 | 229 | public boolean isLazyInit() { 230 | return lazyInit; 231 | } 232 | 233 | public void setLazyInit(boolean lazyInit) { 234 | this.lazyInit = lazyInit; 235 | } 236 | 237 | public String getPoolName() { 238 | return poolName; 239 | } 240 | 241 | public void setPoolName(String poolName) { 242 | this.poolName = poolName; 243 | } 244 | 245 | public int getMaxConnectionPerServer() { 246 | return maxConnectionPerServer; 247 | } 248 | 249 | public void setMaxConnectionPerServer(int maxConnectionPerServer) { 250 | this.maxConnectionPerServer = maxConnectionPerServer; 251 | } 252 | 253 | public int getMinConnectionPerServer() { 254 | return minConnectionPerServer; 255 | } 256 | 257 | public void setMinConnectionPerServer(int minConnectionPerServer) { 258 | this.minConnectionPerServer = minConnectionPerServer; 259 | } 260 | 261 | public void setIdleConnectionTestPeriodInSeconds(long idleConnectionTestPeriod, TimeUnit timeUnit) { 262 | this.idleConnectionTestPeriodInSeconds = TimeUnit.SECONDS.convert(idleConnectionTestPeriod, 263 | checkNotNull(timeUnit)); 264 | } 265 | 266 | public long getIdleConnectionTestPeriod(TimeUnit timeUnit) { 267 | return timeUnit.convert(this.idleConnectionTestPeriodInSeconds, TimeUnit.SECONDS); 268 | } 269 | 270 | public void setIdleMaxAgeInSeconds(long idleMaxAge) { 271 | setIdleMaxAge(idleMaxAge, TimeUnit.SECONDS); 272 | } 273 | 274 | public void setIdleMaxAge(long idleMaxAge, TimeUnit timeUnit) { 275 | this.idleMaxAgeInSeconds = TimeUnit.SECONDS.convert(idleMaxAge, checkNotNull(timeUnit)); 276 | } 277 | 278 | public long getIdleMaxAge(TimeUnit timeUnit) { 279 | return timeUnit.convert(this.idleMaxAgeInSeconds, TimeUnit.SECONDS); 280 | } 281 | 282 | public void setMaxConnectionAge(long maxConnectionAgeInSeconds) { 283 | this.maxConnectionAgeInSeconds = maxConnectionAgeInSeconds; 284 | } 285 | 286 | public long getMaxConnectionAge(TimeUnit timeUnit) { 287 | return timeUnit.convert(this.maxConnectionAgeInSeconds, TimeUnit.SECONDS); 288 | } 289 | 290 | public long getMaxConnectionAgeInSeconds() { 291 | return this.maxConnectionAgeInSeconds; 292 | } 293 | 294 | public ServiceOrder getServiceOrder() { 295 | return serviceOrder; 296 | } 297 | 298 | public void setServiceOrder(ServiceOrder serviceOrder) { 299 | this.serviceOrder = serviceOrder; 300 | } 301 | 302 | public int getAcquireRetryAttempts() { 303 | return this.acquireRetryAttempts; 304 | } 305 | 306 | public void setAcquireRetryAttempts(int acquireRetryAttempts) { 307 | this.acquireRetryAttempts = acquireRetryAttempts; 308 | } 309 | 310 | public long getAcquireRetryDelayInMs() { 311 | return this.acquireRetryDelayInMs; 312 | } 313 | 314 | public void setAcquireRetryDelay(int acquireRetryDelayInMs) { 315 | this.acquireRetryDelayInMs = acquireRetryDelayInMs; 316 | } 317 | 318 | /** 319 | * 获取当分区连接小于x%的时候触发创建连接信号量 320 | * 321 | * @return 最小分区空闲连接比例 322 | */ 323 | public int getPoolAvailabilityThreshold() { 324 | return this.poolAvailabilityThreshold; 325 | } 326 | 327 | public void setPoolAvailabilityThreshold(int poolAvailabilityThreshold) { 328 | this.poolAvailabilityThreshold = poolAvailabilityThreshold; 329 | } 330 | 331 | public long getConnectionTimeoutInMs() { 332 | return this.connectionTimeoutInMs; 333 | } 334 | 335 | public void setConnectionTimeoutInMs(long connectionTimeoutinMs) { 336 | setConnectionTimeout(connectionTimeoutinMs, TimeUnit.MILLISECONDS); 337 | } 338 | 339 | public void setConnectionTimeout(long connectionTimeout, TimeUnit timeUnit) { 340 | this.connectionTimeoutInMs = TimeUnit.MILLISECONDS.convert(connectionTimeout, timeUnit); 341 | } 342 | 343 | public int getAcquireIncrement() { 344 | return acquireIncrement; 345 | } 346 | 347 | public void setAcquireIncrement(int acquireIncrement) { 348 | this.acquireIncrement = acquireIncrement; 349 | } 350 | 351 | public int getMaxConnectionCreateFailedCount() { 352 | return maxConnectionCreateFailedCount; 353 | } 354 | 355 | public void setMaxConnectionCreateFailedCount(int maxConnectionCreateFailedCount) { 356 | this.maxConnectionCreateFailedCount = maxConnectionCreateFailedCount; 357 | } 358 | 359 | public void addThriftClientClass(String serviceName, Class clazz) { 360 | this.clientClasses.put(serviceName, clazz); 361 | } 362 | 363 | public Map> getThriftClientClasses() { 364 | return this.clientClasses; 365 | } 366 | 367 | public ThriftServiceType getThriftServiceType() { 368 | return thriftServiceType; 369 | } 370 | 371 | public boolean isNoServerStartUp() { 372 | return noServerStartUp; 373 | } 374 | 375 | public void setNoServerStartUp(boolean noServerStartUp) { 376 | this.noServerStartUp = noServerStartUp; 377 | } 378 | 379 | /** 380 | * 检查配置信息的方法 381 | * 382 | * @throws ThriftConnectionPoolException 383 | * 当配置信息出现问题时抛出该异常 384 | */ 385 | public void check() throws ThriftConnectionPoolException { 386 | if (connectTimeout <= 0) { 387 | throw new ThriftConnectionPoolException("连接超时时间必须大于0"); 388 | } 389 | 390 | // 判断是否是单接口模式 如果是则只检测单接口 391 | if (getThriftServiceType() == ThriftServiceType.SINGLE_INTERFACE) { 392 | if (clientClass == null) { 393 | throw new ThriftConnectionPoolException("thrift客户端实现类未设置"); 394 | } 395 | // 检测ping方法 396 | try { 397 | clientClass.getMethod("ping"); 398 | } catch (NoSuchMethodException e) { 399 | throw new ThriftConnectionPoolException("Thrift客户端实现类必须带有ping()方法用于检测连接"); 400 | } 401 | } else if (getThriftServiceType() == ThriftServiceType.MULTIPLEXED_INTERFACE) { 402 | if (clientClasses.size() == 0) { 403 | throw new ThriftConnectionPoolException("多服务thrift客户端实现类未设置"); 404 | } 405 | // 检测所有接口 406 | List toRemoveClasses = new ArrayList<>(); 407 | Iterator>> iterator = clientClasses.entrySet().iterator(); 408 | while (iterator.hasNext()) { 409 | Entry> entry = iterator.next(); 410 | Class clazz = entry.getValue(); 411 | try { 412 | clazz.getMethod("ping"); 413 | } catch (NoSuchMethodException e) { 414 | toRemoveClasses.add(entry.getKey()); 415 | logger.warn("接口:" + entry.getKey() + " 没有实现ping方法 无法创建对应的服务客户端"); 416 | } 417 | } 418 | for (String toRemoveClass : toRemoveClasses) { 419 | clientClasses.remove(toRemoveClass); 420 | } 421 | Iterator servicesNameIterator = clientClasses.keySet().iterator(); 422 | while (servicesNameIterator.hasNext()) { 423 | logger.info("注册服务客户端:" + servicesNameIterator.next()); 424 | } 425 | } 426 | 427 | if (maxConnectionPerServer <= 0) { 428 | throw new ThriftConnectionPoolException("每台服务器的最大连接数必须大于0"); 429 | } 430 | 431 | if (minConnectionPerServer < 0) { 432 | throw new ThriftConnectionPoolException("每台服务器最小连接数不能小于0"); 433 | } 434 | 435 | if (minConnectionPerServer > maxConnectionPerServer) { 436 | throw new ThriftConnectionPoolException("每台服务器的最小连接数不能超过最大连接数配置"); 437 | } 438 | 439 | if (idleConnectionTestPeriodInSeconds <= 0) { 440 | throw new ThriftConnectionPoolException("检测时间周期必须大于0秒"); 441 | } 442 | 443 | if (idleMaxAgeInSeconds <= 0) { 444 | throw new ThriftConnectionPoolException("未使用连接关闭时间必须大于0秒"); 445 | } 446 | 447 | if (maxConnectionAgeInSeconds <= 0) { 448 | throw new ThriftConnectionPoolException("连接最大生存时间必须大于0秒"); 449 | } 450 | 451 | if (acquireRetryAttempts < 0) { 452 | throw new ThriftConnectionPoolException("获取连接重试次数不能小于0"); 453 | } 454 | 455 | if (acquireRetryDelayInMs < 0) { 456 | throw new ThriftConnectionPoolException("获取连接重试等待时间不能小于0毫秒"); 457 | } 458 | 459 | if (connectionTimeoutInMs < 0) { 460 | throw new ThriftConnectionPoolException("获取连接等待时间不能小于0毫秒"); 461 | } 462 | 463 | if (acquireIncrement <= 0) { 464 | throw new ThriftConnectionPoolException("每次创建原始连接的数量必须大于0个"); 465 | } 466 | } 467 | 468 | /** 469 | * thrift 通讯管道类型 470 | * 471 | * @author jiangwei (ydswcy513@gmail.com) 472 | * @version V1.0 473 | */ 474 | public enum TProtocolType { 475 | /** 476 | * 二进制通讯管道类型 477 | */ 478 | BINARY, 479 | /** 480 | * JSON通讯管道类型 481 | */ 482 | JSON 483 | } 484 | 485 | /** 486 | * 队列模式
487 | * 先进先出还是先进后出 488 | * 489 | * @author jiangwei (ydswcy513@gmail.com) 490 | * @version V1.0 491 | */ 492 | public enum ServiceOrder { 493 | FIFO, LIFO 494 | } 495 | 496 | /** 497 | * thrift服务类型
498 | * 单接口模式还是多接口模式 499 | * 500 | * @author jiangwei (ydswcy513@gmail.com) 501 | * @version V1.0 502 | */ 503 | public enum ThriftServiceType { 504 | /** 505 | * 单接口模式 506 | */ 507 | SINGLE_INTERFACE, 508 | /** 509 | * 多接口模式 510 | */ 511 | MULTIPLEXED_INTERFACE 512 | } 513 | 514 | } 515 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/config/ThriftServerInfo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool.config; 18 | 19 | import java.util.Arrays; 20 | 21 | /** 22 | * 封装thrift信息的实体类
23 | * 目前只有服务器地址、端口两个属性
24 | * 日后考虑添加权重、备注一类的属性 25 | * 26 | * @author jiangwei (ydswcy513@gmail.com) 27 | * @version V1.0 28 | */ 29 | public class ThriftServerInfo { 30 | /** 31 | * 服务器地址 32 | */ 33 | private String host; 34 | /** 35 | * 服务器端口 36 | */ 37 | private int port; 38 | /** 39 | * 服务器ID 40 | */ 41 | private byte[] serverID; 42 | 43 | public ThriftServerInfo(String host, int port) { 44 | super(); 45 | this.host = host; 46 | this.port = port; 47 | this.serverID = null; 48 | } 49 | 50 | public ThriftServerInfo(String host, int port, byte[] serverID) { 51 | super(); 52 | this.host = host; 53 | this.port = port; 54 | this.serverID = serverID; 55 | } 56 | 57 | public String getHost() { 58 | return host; 59 | } 60 | 61 | public void setHost(String host) { 62 | this.host = host; 63 | } 64 | 65 | public int getPort() { 66 | return port; 67 | } 68 | 69 | public void setPort(int port) { 70 | this.port = port; 71 | } 72 | 73 | public byte[] getServerID() { 74 | return serverID; 75 | } 76 | 77 | public void setServerID(byte[] serverID) { 78 | this.serverID = serverID; 79 | } 80 | 81 | /* 82 | * @see java.lang.Object#hashCode() 83 | */ 84 | @Override 85 | public int hashCode() { 86 | final int prime = 31; 87 | int result = 1; 88 | result = prime * result + ((host == null) ? 0 : host.hashCode()); 89 | result = prime * result + port; 90 | result = prime * result + Arrays.hashCode(serverID); 91 | return result; 92 | } 93 | 94 | /* 95 | * @see java.lang.Object#equals(java.lang.Object) 96 | */ 97 | @Override 98 | public boolean equals(Object obj) { 99 | if (this == obj) 100 | return true; 101 | if (obj == null) 102 | return false; 103 | if (getClass() != obj.getClass()) 104 | return false; 105 | ThriftServerInfo other = (ThriftServerInfo) obj; 106 | if (host == null) { 107 | if (other.host != null) 108 | return false; 109 | } else if (!host.equals(other.host)) 110 | return false; 111 | if (port != other.port) 112 | return false; 113 | if (!Arrays.equals(serverID, other.serverID)) 114 | return false; 115 | return true; 116 | } 117 | 118 | /* 119 | * @see java.lang.Object#toString() 120 | */ 121 | @Override 122 | public String toString() { 123 | return "ThriftServerInfo [host=" + host + ", port=" + port + ", serverID=" + Arrays.toString(serverID) + "]"; 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/connection/DefaultThriftConnection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool.connection; 18 | 19 | import java.io.IOException; 20 | import java.lang.reflect.Constructor; 21 | 22 | import org.apache.thrift.TServiceClient; 23 | import org.apache.thrift.protocol.TBinaryProtocol; 24 | import org.apache.thrift.protocol.TJSONProtocol; 25 | import org.apache.thrift.protocol.TProtocol; 26 | import org.apache.thrift.transport.TSocket; 27 | import org.apache.thrift.transport.TTransport; 28 | import org.slf4j.Logger; 29 | import org.slf4j.LoggerFactory; 30 | 31 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 32 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 33 | 34 | /** 35 | * 默认实现的thrift客户端对象 36 | * 37 | * @author jiangwei (ydswcy513@gmail.com) 38 | * @version V1.0 39 | */ 40 | public class DefaultThriftConnection implements ThriftConnection { 41 | private static final Logger logger = LoggerFactory.getLogger(DefaultThriftConnection.class); 42 | 43 | /** 44 | * 服务器地址 45 | */ 46 | private String host; 47 | /** 48 | * 服务器端口 49 | */ 50 | private int port; 51 | /** 52 | * 连接超时时间 53 | */ 54 | private int connectionTimeOut; 55 | /** 56 | * thrift管道类型 57 | */ 58 | private TProtocolType tProtocolType; 59 | 60 | /** 61 | * thrift连接对象 62 | */ 63 | private TTransport transport; 64 | 65 | /** 66 | * thrift客户端对象 67 | */ 68 | private T client; 69 | 70 | /** 71 | * 客户端类对象 72 | */ 73 | private Class clientClass; 74 | 75 | public DefaultThriftConnection(String host, int port, int connectionTimeOut, TProtocolType tProtocolType, 76 | Class clientClass) throws ThriftConnectionPoolException { 77 | this.host = host; 78 | this.port = port; 79 | this.connectionTimeOut = connectionTimeOut; 80 | this.tProtocolType = tProtocolType; 81 | this.clientClass = clientClass; 82 | 83 | // 创建连接 84 | createConnection(); 85 | } 86 | 87 | /** 88 | * 创建原始连接的方法 89 | * 90 | * @throws ThriftConnectionPoolException 91 | * 创建连接出现问题时抛出该异常 92 | */ 93 | @SuppressWarnings("unchecked") 94 | private void createConnection() throws ThriftConnectionPoolException { 95 | try { 96 | transport = new TSocket(host, port, connectionTimeOut); 97 | transport.open(); 98 | TProtocol protocol = createTProtocol(transport); 99 | // 反射实例化客户端对象 100 | Constructor clientConstructor = clientClass.getConstructor(TProtocol.class); 101 | client = (T) clientConstructor.newInstance(protocol); 102 | if (logger.isDebugEnabled()) { 103 | logger.debug("创建新连接成功:" + host + " 端口:" + port); 104 | } 105 | } catch (Exception e) { 106 | throw new ThriftConnectionPoolException("无法连接服务器:" + host + " 端口:" + port); 107 | } 108 | } 109 | 110 | /** 111 | * 根据配置创建thrift管道的方法 112 | * 113 | */ 114 | private TProtocol createTProtocol(TTransport transport) { 115 | if (tProtocolType == TProtocolType.BINARY) { 116 | return new TBinaryProtocol(transport); 117 | } else if (tProtocolType == TProtocolType.JSON) { 118 | return new TJSONProtocol(transport); 119 | } 120 | throw new IllegalStateException("暂不支持的管道类型:" + tProtocolType); 121 | } 122 | 123 | /* 124 | * @see java.io.Closeable#close() 125 | */ 126 | @Override 127 | public void close() throws IOException { 128 | if (transport != null) { 129 | transport.close(); 130 | } 131 | } 132 | 133 | /* 134 | * @see com.wmz7year.thrift.pool.connection.ThriftConnection#getClient() 135 | */ 136 | @Override 137 | public T getClient() { 138 | return client; 139 | } 140 | 141 | @SuppressWarnings("unchecked") 142 | @Override 143 | public K getClient(String serviceName, Class clazz) { 144 | return (K) getClient(); 145 | } 146 | 147 | /* 148 | * @see com.wmz7year.thrift.pool.connection.ThriftConnection#isClosed() 149 | */ 150 | @Override 151 | public boolean isClosed() { 152 | return !transport.isOpen(); 153 | } 154 | 155 | /* 156 | * @see com.wmz7year.thrift.pool.connection.ThriftConnection#setAvailable(boolean) 157 | */ 158 | @Override 159 | public void setAvailable(boolean available) { 160 | throw new UnsupportedOperationException(); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/connection/MulitServiceThriftConnecion.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool.connection; 18 | 19 | import java.io.IOException; 20 | import java.lang.reflect.Constructor; 21 | import java.util.HashMap; 22 | import java.util.Iterator; 23 | import java.util.Map; 24 | import java.util.Map.Entry; 25 | 26 | import org.apache.thrift.TServiceClient; 27 | import org.apache.thrift.protocol.TBinaryProtocol; 28 | import org.apache.thrift.protocol.TJSONProtocol; 29 | import org.apache.thrift.protocol.TMultiplexedProtocol; 30 | import org.apache.thrift.protocol.TProtocol; 31 | import org.apache.thrift.transport.TSocket; 32 | import org.apache.thrift.transport.TTransport; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 37 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 38 | 39 | /** 40 | * 多服务支持的thrift连接对象 41 | * 42 | * @author jiangwei (ydswcy513@gmail.com) 43 | * @version V1.0 44 | */ 45 | public class MulitServiceThriftConnecion implements ThriftConnection { 46 | private static final Logger logger = LoggerFactory.getLogger(MulitServiceThriftConnecion.class); 47 | /** 48 | * 服务器地址 49 | */ 50 | private String host; 51 | /** 52 | * 服务器端口 53 | */ 54 | private int port; 55 | /** 56 | * 连接超时时间 57 | */ 58 | private int connectionTimeOut; 59 | /** 60 | * thrift管道类型 61 | */ 62 | private TProtocolType tProtocolType; 63 | 64 | /** 65 | * thrift客户端对象 66 | */ 67 | private Map> thriftClientClasses; 68 | 69 | /** 70 | * thrift连接对象 71 | */ 72 | private TTransport transport; 73 | 74 | /** 75 | * 实例化后的客户端对象 76 | */ 77 | private Map clients = new HashMap<>(); 78 | 79 | public MulitServiceThriftConnecion(String host, int port, int connectionTimeOut, TProtocolType tProtocolType, 80 | Map> thriftClientClasses) throws ThriftConnectionPoolException { 81 | this.host = host; 82 | this.port = port; 83 | this.connectionTimeOut = connectionTimeOut; 84 | this.tProtocolType = tProtocolType; 85 | this.thriftClientClasses = thriftClientClasses; 86 | 87 | // 创建连接 88 | createConnection(); 89 | } 90 | 91 | /** 92 | * 创建原始连接的方法 93 | * 94 | * @throws ThriftConnectionPoolException 95 | * 创建连接出现问题时抛出该异常 96 | */ 97 | @SuppressWarnings("unchecked") 98 | private void createConnection() throws ThriftConnectionPoolException { 99 | try { 100 | transport = new TSocket(host, port, connectionTimeOut); 101 | transport.open(); 102 | TProtocol protocol = createTProtocol(transport); 103 | 104 | Iterator>> iterator = thriftClientClasses.entrySet() 105 | .iterator(); 106 | while (iterator.hasNext()) { 107 | Entry> entry = iterator.next(); 108 | String serviceName = entry.getKey(); 109 | Class clientClass = entry.getValue(); 110 | TMultiplexedProtocol multiProtocol = new TMultiplexedProtocol(protocol, serviceName); 111 | // 反射实例化客户端对象 112 | Constructor clientConstructor = clientClass.getConstructor(TProtocol.class); 113 | T client = (T) clientConstructor.newInstance(multiProtocol); 114 | clients.put(serviceName, client); 115 | if (logger.isDebugEnabled()) { 116 | logger.debug("创建新连接成功:" + host + " 端口:" + port); 117 | } 118 | } 119 | 120 | } catch (Exception e) { 121 | e.printStackTrace(); 122 | throw new ThriftConnectionPoolException("无法连接服务器:" + host + " 端口:" + port, e); 123 | } 124 | } 125 | 126 | /* 127 | * @see com.wmz7year.thrift.pool.connection.ThriftConnection#getClient() 128 | */ 129 | @Override 130 | public T getClient() { 131 | throw new UnsupportedOperationException("多服务情况下不允许调用单服务获取客户端的方法"); 132 | } 133 | 134 | /* 135 | * @see 136 | * com.wmz7year.thrift.pool.connection.ThriftConnection#getClient(java.lang. 137 | * String, java.lang.Class) 138 | */ 139 | @SuppressWarnings("unchecked") 140 | @Override 141 | public K getClient(String serviceName, Class clazz) { 142 | T serviceClient = this.clients.get(serviceName); 143 | if (serviceClient == null) { 144 | throw new IllegalArgumentException("未知的服务名称:" + serviceName); 145 | } 146 | 147 | return (K) serviceClient; 148 | } 149 | 150 | /* 151 | * @see com.wmz7year.thrift.pool.connection.ThriftConnection#isClosed() 152 | */ 153 | @Override 154 | public boolean isClosed() { 155 | return !transport.isOpen(); 156 | } 157 | 158 | /** 159 | * 根据配置创建thrift管道的方法 160 | * 161 | */ 162 | private TProtocol createTProtocol(TTransport transport) { 163 | if (tProtocolType == TProtocolType.BINARY) { 164 | return new TBinaryProtocol(transport); 165 | } else if (tProtocolType == TProtocolType.JSON) { 166 | return new TJSONProtocol(transport); 167 | } 168 | throw new IllegalStateException("暂不支持的管道类型:" + tProtocolType); 169 | } 170 | 171 | /* 172 | * @see java.io.Closeable#close() 173 | */ 174 | @Override 175 | public void close() throws IOException { 176 | if (transport != null) { 177 | transport.close(); 178 | } 179 | } 180 | 181 | /** 182 | * 获取多服务模式下所有thrift客户端的方法 183 | * 184 | * @return 多服务下所有thrift客户端集合 185 | */ 186 | public Map getMuiltServiceClients() { 187 | return this.clients; 188 | } 189 | 190 | /* 191 | * @see com.wmz7year.thrift.pool.connection.ThriftConnection#setAvailable(boolean) 192 | */ 193 | @Override 194 | public void setAvailable(boolean available) { 195 | throw new UnsupportedOperationException(); 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/connection/ThriftConnection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool.connection; 18 | 19 | import java.io.Closeable; 20 | 21 | import org.apache.thrift.TServiceClient; 22 | 23 | /** 24 | * Thrift连接对象 25 | * 26 | * @author jiangwei (ydswcy513@gmail.com) 27 | * @version V1.0 28 | */ 29 | public interface ThriftConnection extends Closeable { 30 | 31 | /** 32 | * 获取客户端的方法
33 | * 仅在单服务情况下试用 如果在多服务情况下试用则会抛出UnsupportedOperationException异常 34 | * 35 | * @return 客户端对象 36 | */ 37 | public T getClient(); 38 | 39 | /** 40 | * 根据服务名称获取客户端的方法
41 | * 在多服务情况下试用 如果在单服务情况下试用则直接返回客户端
42 | * 如果服务名错误则返回null 43 | * 44 | * @param 45 | * thrift客户端类 46 | * @param serviceName 47 | * 服务名称 48 | * @param clazz 49 | * thrift客户端class类 50 | * @return 客户端对象 51 | */ 52 | public K getClient(String serviceName, Class clazz); 53 | 54 | /** 55 | * 判断连接是否关闭的表识位 56 | * 57 | * @return true为连接已经关闭 false为连接未关闭 58 | */ 59 | public boolean isClosed(); 60 | 61 | /** 62 | * 设置该链接的状态的方法
63 | * 该状态默认为true 当连接状态设置为false时,该链接不会被连接池回收
64 | * 典型的应用案例为当远端服务器处理请求缓慢超时时 thrift报超时异常
65 | * 但是连接池本身并不知道连接已经存在问题
66 | * 当这个问题连接被回收到连接池被再次获取时
67 | * 则会报请求序号不一致的问题
68 | * 69 | * @param available 连接的状态 70 | */ 71 | public void setAvailable(boolean available); 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/wmz7year/thrift/pool/exception/ThriftConnectionPoolException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool.exception; 18 | 19 | import java.io.PrintStream; 20 | import java.io.PrintWriter; 21 | 22 | /** 23 | * thrift连接池相关的异常对象 24 | * 25 | * @author jiangwei (ydswcy513@gmail.com) 26 | * @version V1.0 27 | */ 28 | public class ThriftConnectionPoolException extends Exception { 29 | private static final long serialVersionUID = 6276176462644093117L; 30 | 31 | private Throwable nestedThrowable = null; 32 | 33 | public ThriftConnectionPoolException() { 34 | super(); 35 | } 36 | 37 | public ThriftConnectionPoolException(String msg) { 38 | super(msg); 39 | } 40 | 41 | public ThriftConnectionPoolException(Throwable nestedThrowable) { 42 | super(nestedThrowable); 43 | this.nestedThrowable = nestedThrowable; 44 | } 45 | 46 | public ThriftConnectionPoolException(String msg, Throwable nestedThrowable) { 47 | super(msg, nestedThrowable); 48 | this.nestedThrowable = nestedThrowable; 49 | } 50 | 51 | @Override 52 | public void printStackTrace() { 53 | super.printStackTrace(); 54 | if (nestedThrowable != null) { 55 | nestedThrowable.printStackTrace(); 56 | } 57 | } 58 | 59 | @Override 60 | public void printStackTrace(PrintStream ps) { 61 | super.printStackTrace(ps); 62 | if (nestedThrowable != null) { 63 | nestedThrowable.printStackTrace(ps); 64 | } 65 | } 66 | 67 | @Override 68 | public void printStackTrace(PrintWriter pw) { 69 | super.printStackTrace(pw); 70 | if (nestedThrowable != null) { 71 | nestedThrowable.printStackTrace(pw); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/com/wmz7year/thrift/pool/BasicAbstractTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.io.IOException; 20 | import java.net.ServerSocket; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.concurrent.TimeoutException; 24 | import java.util.concurrent.atomic.AtomicReference; 25 | 26 | import org.apache.thrift.TException; 27 | import org.apache.thrift.TMultiplexedProcessor; 28 | import org.apache.thrift.protocol.TBinaryProtocol; 29 | import org.apache.thrift.protocol.TBinaryProtocol.Factory; 30 | import org.apache.thrift.server.TServer; 31 | import org.apache.thrift.server.TThreadPoolServer; 32 | import org.apache.thrift.server.TThreadPoolServer.Args; 33 | import org.apache.thrift.transport.TServerSocket; 34 | import org.apache.thrift.transport.TServerTransport; 35 | import org.apache.thrift.transport.TTransportException; 36 | import org.slf4j.Logger; 37 | import org.slf4j.LoggerFactory; 38 | 39 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 40 | import com.wmz7year.thrift.pool.example.Example; 41 | import com.wmz7year.thrift.pool.example.Example.Iface; 42 | import com.wmz7year.thrift.pool.example.Example.Processor; 43 | import com.wmz7year.thrift.pool.example.Other; 44 | 45 | import junit.framework.TestCase; 46 | 47 | /** 48 | * 基础测试类
49 | * 所有单元测试都需要继承该类进行测试
50 | * 会在测试开始时自定义启动N个服务器
51 | * 已经服务器关闭操作 52 | * 53 | * @Title: BasicAbstractTest.java 54 | * @Package com.wmz7year.thrift.pool.config 55 | * @author jiangwei (ydswcy513@gmail.com) 56 | * @date 2015年11月19日 下午1:23:04 57 | * @version V1.0 58 | */ 59 | public abstract class BasicAbstractTest extends TestCase { 60 | protected static final Logger logger = LoggerFactory.getLogger(BasicAbstractTest.class); 61 | /** 62 | * 最小服务器端口 63 | */ 64 | protected static final int MIN_PORT = 40000; 65 | /** 66 | * 最大服务器端口 67 | */ 68 | protected static final int MAX_PORT = 41000; 69 | /** 70 | * 本机地址 71 | */ 72 | protected static final String LOACLHOST = "127.0.0.1"; 73 | 74 | /** 75 | * 单元测试运行超时时间 76 | */ 77 | private static final long DFLT_TEST_TIMEOUT = 5 * 60 * 1000; 78 | 79 | /** 80 | * 在停止单元测试的时候是否存在异常信息 81 | */ 82 | private boolean stopErr; 83 | 84 | /** 85 | * 测试运行时间 86 | */ 87 | private static long ts = System.currentTimeMillis(); 88 | 89 | /** 90 | * 启动的服务器列表 91 | */ 92 | private List servers = new ArrayList(); 93 | 94 | /** 95 | * 在单元测试最开始时调用的方法 96 | * 97 | * @throws Exception 98 | * 当发生错误时抛出该异常 99 | */ 100 | protected abstract void beforeTest() throws Exception; 101 | 102 | /** 103 | * 在单元测试最后调用的方法 104 | * 105 | * @throws Exception 106 | * 当发生错误时抛出该异常 107 | */ 108 | protected abstract void afterTest() throws Exception; 109 | 110 | /* 111 | * @see junit.framework.TestCase#setUp() 112 | */ 113 | @Override 114 | protected void setUp() throws Exception { 115 | stopErr = false; 116 | 117 | try { 118 | beforeTest(); 119 | } catch (Exception t) { 120 | t.printStackTrace(); 121 | 122 | tearDown(); 123 | } 124 | 125 | ts = System.currentTimeMillis(); 126 | } 127 | 128 | /** 129 | * 基础运行单元测试的方法 130 | * 131 | * @throws Throwable 132 | * 当发生错误时抛出该异常 133 | */ 134 | private void runTestInternal() throws Throwable { 135 | super.runTest(); 136 | } 137 | 138 | /* 139 | * @see junit.framework.TestCase#runTest() 140 | */ 141 | @Override 142 | protected void runTest() throws Throwable { 143 | final AtomicReference ex = new AtomicReference(); 144 | 145 | Thread runner = new Thread("test-runner") { 146 | @Override 147 | public void run() { 148 | try { 149 | runTestInternal(); 150 | } catch (Throwable e) { 151 | ex.set(e); 152 | } 153 | } 154 | }; 155 | 156 | runner.start(); 157 | 158 | runner.join(isDebug() ? 0 : DFLT_TEST_TIMEOUT); 159 | 160 | if (runner.isAlive()) { 161 | throw new TimeoutException("单元测试运行时间超时 [测试名称:" + getName() + " 超时时间:" + DFLT_TEST_TIMEOUT + "]"); 162 | } 163 | 164 | Throwable t = ex.get(); 165 | if (t != null) { 166 | throw t; 167 | } 168 | 169 | assert !stopErr : "结束单元测试发生错误"; 170 | } 171 | 172 | /* 173 | * @see junit.framework.TestCase#tearDown() 174 | */ 175 | @Override 176 | protected void tearDown() throws Exception { 177 | long dur = System.currentTimeMillis() - ts; 178 | logger.info("测试结束 测试名称:" + getName() + " 运行时间:" + dur + "ms"); 179 | 180 | // 结束测试 181 | afterTest(); 182 | 183 | // 关闭所有服务的方法 184 | stopAllServers(); 185 | } 186 | 187 | /** 188 | * 关闭所有thrift服务的方法 189 | */ 190 | protected void stopAllServers() { 191 | for (TServer tServer : servers) { 192 | tServer.stop(); 193 | } 194 | } 195 | 196 | /** 197 | * 判断是否是debug运行模式的方法
198 | * 依据-DDEBUG参数是否设置 199 | * 200 | * @return true为debug模式 false为非debug模式 201 | */ 202 | protected boolean isDebug() { 203 | return System.getProperty("DEBUG") != null; 204 | } 205 | 206 | /** 207 | * 启动本地服务器的方法
208 | * 为了方便测试使用 209 | * 210 | * @param count 211 | * 启动的服务器数量 212 | * @return 启动的服务器信息列表 213 | */ 214 | protected List startServers(int count) throws Exception { 215 | List result = new ArrayList(); 216 | for (int i = 0; i < count; i++) { 217 | try { 218 | ThriftServerInfo startServer = startServer(); 219 | result.add(startServer); 220 | } catch (Throwable e) { 221 | continue; 222 | } 223 | } 224 | return result; 225 | } 226 | 227 | /** 228 | * 启动本地服务器的方法
229 | * 为了方便测试使用
230 | * 该服务是thrift超时调用的服务 231 | * 232 | * @param count 233 | * 启动的服务器数量 234 | * @return 启动的服务器信息列表 235 | */ 236 | protected List startTimeoutServers(int count) throws Exception { 237 | List result = new ArrayList(); 238 | for (int i = 0; i < count; i++) { 239 | try { 240 | ThriftServerInfo startServer = startTimeoutServer(); 241 | result.add(startServer); 242 | } catch (Throwable e) { 243 | continue; 244 | } 245 | } 246 | return result; 247 | } 248 | 249 | /** 250 | * 启动多服务本地测试服务器的方法 251 | * 252 | * @param count 253 | * 启动的服务器数量 254 | * @return 启动的服务器信息列表 255 | */ 256 | protected List startMulitServiceServers(int count) throws Exception { 257 | List result = new ArrayList(); 258 | for (int i = 0; i < count; i++) { 259 | try { 260 | ThriftServerInfo startServer = startMulitServiceServer(); 261 | result.add(startServer); 262 | } catch (Throwable e) { 263 | continue; 264 | } 265 | } 266 | return result; 267 | } 268 | 269 | protected ThriftServerInfo startServer() throws Throwable { 270 | // 获取一个监听端口 271 | final int port = choseListenPort(); 272 | ThriftServerInfo serverInfo = new ThriftServerInfo(LOACLHOST, port); 273 | final AtomicReference ex = new AtomicReference(); 274 | 275 | Thread runner = new Thread("thrift-server-starter") { 276 | @Override 277 | public void run() { 278 | try { 279 | TServerTransport serverTransport = new TServerSocket(port); 280 | Factory proFactory = new TBinaryProtocol.Factory(); 281 | Processor processor = new Example.Processor(new Example.Iface() { 282 | 283 | @Override 284 | public void pong() throws TException { 285 | logger.info("pong"); 286 | } 287 | 288 | @Override 289 | public void ping() throws TException { 290 | logger.info("ping"); 291 | } 292 | }); 293 | Args thriftArgs = new Args(serverTransport); 294 | thriftArgs.processor(processor); 295 | thriftArgs.protocolFactory(proFactory); 296 | TServer tserver = new TThreadPoolServer(thriftArgs); 297 | servers.add(tserver); 298 | logger.info("启动测试服务监听:" + port); 299 | tserver.serve(); 300 | } catch (TTransportException e) { 301 | logger.error("thrift服务器启动失败", e); 302 | ex.set(e); 303 | } 304 | } 305 | }; 306 | 307 | runner.start(); 308 | 309 | Throwable throwable = ex.get(); 310 | if (throwable != null) { 311 | throw throwable; 312 | } 313 | // 等待服务器启动 314 | Thread.sleep(1000); 315 | return serverInfo; 316 | } 317 | 318 | protected ThriftServerInfo startTimeoutServer() throws Throwable { 319 | // 获取一个监听端口 320 | final int port = choseListenPort(); 321 | ThriftServerInfo serverInfo = new ThriftServerInfo(LOACLHOST, port); 322 | final AtomicReference ex = new AtomicReference(); 323 | 324 | Thread runner = new Thread("thrift-timeout-server-starter") { 325 | @Override 326 | public void run() { 327 | try { 328 | TServerTransport serverTransport = new TServerSocket(port); 329 | Factory proFactory = new TBinaryProtocol.Factory(); 330 | Processor processor = new Example.Processor(new Example.Iface() { 331 | 332 | @Override 333 | public void pong() throws TException { 334 | try { 335 | Thread.sleep(3100); 336 | } catch (InterruptedException e) { 337 | // ignore 338 | } 339 | } 340 | 341 | @Override 342 | public void ping() throws TException { 343 | try { 344 | Thread.sleep(3100); 345 | } catch (InterruptedException e) { 346 | // ignore 347 | } 348 | } 349 | }); 350 | Args thriftArgs = new Args(serverTransport); 351 | thriftArgs.processor(processor); 352 | thriftArgs.protocolFactory(proFactory); 353 | TServer tserver = new TThreadPoolServer(thriftArgs); 354 | servers.add(tserver); 355 | logger.info("启动测试服务监听:" + port); 356 | tserver.serve(); 357 | } catch (TTransportException e) { 358 | logger.error("thrift服务器启动失败", e); 359 | ex.set(e); 360 | } 361 | } 362 | }; 363 | 364 | runner.start(); 365 | 366 | Throwable throwable = ex.get(); 367 | if (throwable != null) { 368 | throw throwable; 369 | } 370 | // 等待服务器启动 371 | Thread.sleep(1000); 372 | return serverInfo; 373 | } 374 | 375 | protected ThriftServerInfo startMulitServiceServer() throws Throwable { 376 | // 获取一个监听端口 377 | final int port = choseListenPort(); 378 | ThriftServerInfo serverInfo = new ThriftServerInfo(LOACLHOST, port); 379 | final AtomicReference ex = new AtomicReference(); 380 | // TODO 381 | Thread runner = new Thread("thrift-server-starter") { 382 | @Override 383 | public void run() { 384 | try { 385 | TMultiplexedProcessor processor = new TMultiplexedProcessor(); 386 | TServerTransport serverTransport = new TServerSocket(port); 387 | Factory proFactory = new TBinaryProtocol.Factory(); 388 | 389 | processor.registerProcessor("example", new Example.Processor(new Example.Iface() { 390 | 391 | @Override 392 | public void pong() throws TException { 393 | logger.info("example pong"); 394 | } 395 | 396 | @Override 397 | public void ping() throws TException { 398 | logger.info("example ping"); 399 | } 400 | })); 401 | 402 | processor.registerProcessor("other", new Other.Processor(new Other.Iface() { 403 | 404 | @Override 405 | public void pong() throws TException { 406 | logger.info("other pong"); 407 | } 408 | 409 | @Override 410 | public void ping() throws TException { 411 | logger.info("other ping"); 412 | } 413 | })); 414 | Args thriftArgs = new Args(serverTransport); 415 | thriftArgs.processor(processor); 416 | thriftArgs.protocolFactory(proFactory); 417 | TServer tserver = new TThreadPoolServer(thriftArgs); 418 | servers.add(tserver); 419 | logger.info("启动测试服务监听:" + port); 420 | tserver.serve(); 421 | } catch (TTransportException e) { 422 | logger.error("thrift服务器启动失败", e); 423 | ex.set(e); 424 | } 425 | } 426 | }; 427 | 428 | runner.start(); 429 | 430 | Throwable throwable = ex.get(); 431 | if (throwable != null) { 432 | throw throwable; 433 | } 434 | // 等待服务器启动 435 | Thread.sleep(1000); 436 | return serverInfo; 437 | } 438 | 439 | /** 440 | * 获取一个可用端口的方法 return 可用的监听端口 441 | */ 442 | private int choseListenPort() { 443 | for (int port = MIN_PORT; port < MAX_PORT; port++) { 444 | if (checkPortNotInUse(port)) { 445 | return port; 446 | } 447 | } 448 | return 0; 449 | } 450 | 451 | /** 452 | * 判断端口是否未使用的方法 453 | * 454 | * @param port 455 | * 需要校验的端口 456 | * @return true为已使用 false为未使用 457 | */ 458 | private boolean checkPortNotInUse(int port) { 459 | try { 460 | new ServerSocket(port).close(); 461 | return true; 462 | } catch (IOException e) { 463 | return false; 464 | } 465 | } 466 | 467 | } 468 | -------------------------------------------------------------------------------- /src/test/java/com/wmz7year/thrift/pool/CheckConnectionAlive.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.List; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 23 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 24 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 25 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 26 | import com.wmz7year.thrift.pool.example.Example; 27 | import com.wmz7year.thrift.pool.example.Example.Client; 28 | 29 | /* 30 | * 检测连接是否可用 反射调用ping方法 31 | */ 32 | public class CheckConnectionAlive extends BasicAbstractTest { 33 | 34 | private List servers; 35 | 36 | @Override 37 | protected void beforeTest() throws Exception { 38 | this.servers = startServers(1); 39 | } 40 | 41 | @Override 42 | protected void afterTest() throws Exception { 43 | } 44 | 45 | public void testConnectionAlive() throws Exception { 46 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(); 47 | config.setConnectTimeout(3000); 48 | config.setThriftProtocol(TProtocolType.BINARY); 49 | config.setClientClass(Example.Client.class); 50 | for (ThriftServerInfo thriftServerInfo : servers) { 51 | config.addThriftServer(thriftServerInfo.getHost(), thriftServerInfo.getPort()); 52 | } 53 | config.setMaxConnectionPerServer(2); 54 | config.setMinConnectionPerServer(1); 55 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 56 | config.setMaxConnectionAge(2); 57 | config.setLazyInit(false); 58 | ThriftConnectionPool pool = new ThriftConnectionPool(config); 59 | 60 | ThriftConnection connection = pool.getConnection(); 61 | assertTrue(pool.isConnectionHandleAlive((ThriftConnectionHandle) connection)); 62 | 63 | pool.close(); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/com/wmz7year/thrift/pool/DynamicServersTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.List; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 23 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 24 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 25 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 26 | import com.wmz7year.thrift.pool.example.Example; 27 | import com.wmz7year.thrift.pool.example.Example.Client; 28 | 29 | /* 30 | * 测试动态添加或者删除服务器列表功能 31 | */ 32 | public class DynamicServersTest extends BasicAbstractTest { 33 | 34 | private List servers; 35 | 36 | @Override 37 | protected void beforeTest() throws Exception { 38 | this.servers = startServers(2); 39 | } 40 | 41 | public void testDynamicServer() throws Throwable { 42 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(); 43 | config.setConnectTimeout(3000); 44 | config.setThriftProtocol(TProtocolType.BINARY); 45 | config.setClientClass(Example.Client.class); 46 | for (ThriftServerInfo thriftServerInfo : servers) { 47 | config.addThriftServer(thriftServerInfo.getHost(), thriftServerInfo.getPort()); 48 | } 49 | config.setMaxConnectionPerServer(2); 50 | config.setMinConnectionPerServer(1); 51 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 52 | config.setMaxConnectionAge(2); 53 | config.setLazyInit(false); 54 | ThriftConnectionPool pool = new ThriftConnectionPool(config); 55 | 56 | int testCount = (servers.size() + 1) * config.getMaxConnectionPerServer() * 10; 57 | 58 | int currentServer = pool.getThriftServerCount(); 59 | ThriftServerInfo newServer = startServer(); 60 | for (int i = 0; i < testCount; i++) { 61 | if (i % 3 == 0) { 62 | logger.info("添加新服务器"); 63 | pool.addThriftServer(newServer); 64 | currentServer++; 65 | assertEquals(currentServer, pool.getThriftServerCount()); 66 | } else if (i % 7 == 0) { 67 | logger.info("移除服务器"); 68 | pool.removeThriftServer(newServer); 69 | currentServer--; 70 | assertEquals(currentServer, pool.getThriftServerCount()); 71 | } 72 | ThriftConnection connection = pool.getConnection(); 73 | connection.getClient().ping(); 74 | connection.close(); 75 | } 76 | 77 | pool.close(); 78 | } 79 | 80 | @Override 81 | protected void afterTest() throws Exception { 82 | // ignore 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/com/wmz7year/thrift/pool/GetConnectionFromServerFailedCounterTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.List; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 23 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 24 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 25 | import com.wmz7year.thrift.pool.example.Example; 26 | 27 | /* 28 | * 当连接池从服务器获取N次连接后依然无法获取连接时 应当删除服务器信息 29 | */ 30 | public class GetConnectionFromServerFailedCounterTest extends BasicAbstractTest { 31 | private List servers; 32 | 33 | @Override 34 | protected void beforeTest() throws Exception { 35 | servers = startServers(1); 36 | } 37 | 38 | @Override 39 | protected void afterTest() throws Exception { 40 | // TODO Auto-generated method stub 41 | 42 | } 43 | 44 | public void testConnectionFaildCounter() throws Exception { 45 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(); 46 | config.setConnectTimeout(3000); 47 | config.setThriftProtocol(TProtocolType.BINARY); 48 | config.setClientClass(Example.Client.class); 49 | // 该端口不存在 50 | config.addThriftServer(servers.get(0)); 51 | config.setMaxConnectionPerServer(2); 52 | config.setMinConnectionPerServer(1); 53 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 54 | config.setMaxConnectionAge(2); 55 | config.setLazyInit(false); 56 | config.setAcquireIncrement(2); 57 | config.setAcquireRetryDelay(2000); 58 | 59 | config.setAcquireRetryAttempts(1); 60 | config.setMaxConnectionCreateFailedCount(1); 61 | config.setConnectionTimeoutInMs(5000); 62 | 63 | ThriftConnectionPool pool = new ThriftConnectionPool(config); 64 | 65 | // 正常获取连接 66 | pool.getConnection().getClient().ping(); 67 | 68 | // 关闭服务器 69 | stopAllServers(); 70 | // 等待连接关闭 71 | TimeUnit.SECONDS.sleep(5); 72 | 73 | try { 74 | // 服务器失效的情况下获取连接 75 | pool.getConnection().getClient().ping(); 76 | } catch (Exception e) { 77 | // ignore 78 | } 79 | 80 | // 移除后服务器数量应该为0 81 | assertEquals(pool.getThriftServerCount(), 0); 82 | 83 | try { 84 | // 再次获取连接应该抛出无可用服务器的异常 85 | pool.getConnection().getClient().ping(); 86 | } catch (Exception e) { 87 | e.printStackTrace(); 88 | } 89 | 90 | 91 | pool.close(); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/test/java/com/wmz7year/thrift/pool/MultiplexedServiceTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import org.apache.thrift.TServiceClient; 24 | 25 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 26 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 27 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.ThriftServiceType; 28 | import com.wmz7year.thrift.pool.example.Example; 29 | import com.wmz7year.thrift.pool.example.Other; 30 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 31 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 32 | 33 | /* 34 | * 多服务测试 35 | */ 36 | public class MultiplexedServiceTest extends BasicAbstractTest { 37 | 38 | private List servers; 39 | 40 | @Override 41 | protected void beforeTest() throws Exception { 42 | this.servers = startMulitServiceServers(1); 43 | } 44 | 45 | @Override 46 | protected void afterTest() throws Exception { 47 | // TODO Auto-generated method stub 48 | 49 | } 50 | 51 | public void testMultiplexedService() throws Exception { 52 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(ThriftServiceType.MULTIPLEXED_INTERFACE); 53 | config.setConnectTimeout(3000); 54 | config.setThriftProtocol(TProtocolType.BINARY); 55 | // 该端口不存在 56 | for (ThriftServerInfo thriftServerInfo : servers) { 57 | config.addThriftServer(thriftServerInfo.getHost(), thriftServerInfo.getPort()); 58 | } 59 | config.addThriftClientClass("example", Example.Client.class); 60 | config.addThriftClientClass("other", Other.Client.class); 61 | 62 | config.setMaxConnectionPerServer(2); 63 | config.setMinConnectionPerServer(1); 64 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 65 | config.setMaxConnectionAge(2); 66 | config.setLazyInit(false); 67 | config.setAcquireIncrement(2); 68 | config.setAcquireRetryDelay(2000); 69 | 70 | config.setAcquireRetryAttempts(1); 71 | config.setMaxConnectionCreateFailedCount(1); 72 | config.setConnectionTimeoutInMs(5000); 73 | 74 | config.check(); 75 | 76 | ThriftConnectionPool pool = new ThriftConnectionPool(config); 77 | ThriftConnection connection = pool.getConnection(); 78 | // example service 79 | com.wmz7year.thrift.pool.example.Example.Client exampleServiceClient = connection.getClient("example", 80 | Example.Client.class); 81 | exampleServiceClient.ping(); 82 | 83 | // other service 84 | com.wmz7year.thrift.pool.example.Other.Client otherServiceClient = connection.getClient("other", 85 | Other.Client.class); 86 | otherServiceClient.ping(); 87 | pool.close(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/test/java/com/wmz7year/thrift/pool/NoThriftServerStartPoolTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.concurrent.TimeUnit; 20 | 21 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 22 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 23 | import com.wmz7year.thrift.pool.example.Example; 24 | 25 | /* 26 | * 没有服务器时启动连接连接池的测试 27 | */ 28 | public class NoThriftServerStartPoolTest extends BasicAbstractTest { 29 | 30 | /* 31 | * @see com.wmz7year.thrift.pool.BasicAbstractTest#beforeTest() 32 | */ 33 | @Override 34 | protected void beforeTest() throws Exception { 35 | 36 | } 37 | 38 | /* 39 | * @see com.wmz7year.thrift.pool.BasicAbstractTest#afterTest() 40 | */ 41 | @Override 42 | protected void afterTest() throws Exception { 43 | 44 | } 45 | 46 | public void testNoThriftServerStartPool() throws Exception { 47 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(); 48 | config.setConnectTimeout(3000); 49 | config.setThriftProtocol(TProtocolType.BINARY); 50 | config.setClientClass(Example.Client.class); 51 | config.setMaxConnectionPerServer(2); 52 | config.setMinConnectionPerServer(1); 53 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 54 | config.setMaxConnectionAge(2); 55 | config.setLazyInit(false); 56 | config.setAcquireIncrement(2); 57 | config.setAcquireRetryDelay(2000); 58 | config.setAcquireRetryAttempts(1); 59 | config.setMaxConnectionCreateFailedCount(1); 60 | config.setConnectionTimeoutInMs(5000); 61 | // 设置支持没有thrift服务器启动 62 | config.setNoServerStartUp(true); 63 | 64 | ThriftConnectionPool pool = new ThriftConnectionPool(config); 65 | assertEquals(0, pool.getThriftServerCount()); 66 | pool.close(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/com/wmz7year/thrift/pool/ThrfitConnectionPoolRemoveServerStopThreadTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.List; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import org.apache.thrift.TServiceClient; 23 | 24 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 25 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 26 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 27 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.ThriftServiceType; 28 | import com.wmz7year.thrift.pool.example.Example; 29 | import com.wmz7year.thrift.pool.example.Other; 30 | 31 | /* 32 | * 当服务器移除连接池时 需要及时停止对应的监控线程 33 | */ 34 | public class ThrfitConnectionPoolRemoveServerStopThreadTest extends BasicAbstractTest { 35 | 36 | private List servers; 37 | 38 | /* 39 | * @see com.wmz7year.thrift.pool.BasicAbstractTest#beforeTest() 40 | */ 41 | @Override 42 | protected void beforeTest() throws Exception { 43 | this.servers = startServers(1); 44 | } 45 | 46 | /* 47 | * @see com.wmz7year.thrift.pool.BasicAbstractTest#afterTest() 48 | */ 49 | @Override 50 | protected void afterTest() throws Exception { 51 | // ignore 52 | } 53 | 54 | public void testThriftConnectionPoolRemoveServerStopThread() throws Exception { 55 | 56 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(ThriftServiceType.MULTIPLEXED_INTERFACE); 57 | config.setConnectTimeout(3000); 58 | config.setThriftProtocol(TProtocolType.BINARY); 59 | // 该端口不存在 60 | ThriftServerInfo thriftServerInfo = servers.get(0); 61 | config.addThriftServer(thriftServerInfo); 62 | config.addThriftClientClass("example", Example.Client.class); 63 | config.addThriftClientClass("other", Other.Client.class); 64 | 65 | config.setMaxConnectionPerServer(2); 66 | config.setMinConnectionPerServer(1); 67 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 68 | config.setMaxConnectionAge(2); 69 | config.setLazyInit(false); 70 | config.setAcquireIncrement(2); 71 | config.setAcquireRetryDelay(2000); 72 | 73 | config.setAcquireRetryAttempts(1); 74 | config.setMaxConnectionCreateFailedCount(1); 75 | config.setConnectionTimeoutInMs(5000); 76 | 77 | ThriftConnectionPool pool = new ThriftConnectionPool(config); 78 | pool.getConnection().close(); 79 | 80 | // 移除并且停止服务 81 | pool.removeThriftServer(thriftServerInfo); 82 | stopAllServers(); 83 | pool.close(); 84 | 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/com/wmz7year/thrift/pool/ThriftServerBindIDTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.util.List; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 23 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 24 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 25 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 26 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.ThriftServiceType; 27 | import com.wmz7year.thrift.pool.example.Example; 28 | import com.wmz7year.thrift.pool.example.Example.Client; 29 | import com.wmz7year.thrift.pool.example.Other; 30 | 31 | /* 32 | * thrift服务器添加可选的ID表识别 可以根据ID获取对应的服务器 33 | */ 34 | public class ThriftServerBindIDTest extends BasicAbstractTest { 35 | 36 | private List servers; 37 | 38 | /* 39 | * @see com.wmz7year.thrift.pool.BasicAbstractTest#beforeTest() 40 | */ 41 | @Override 42 | protected void beforeTest() throws Exception { 43 | this.servers = startServers(2); 44 | } 45 | 46 | /* 47 | * @see com.wmz7year.thrift.pool.BasicAbstractTest#afterTest() 48 | */ 49 | @Override 50 | protected void afterTest() throws Exception { 51 | // ignore 52 | } 53 | 54 | public void testThriftServerBindID() throws Exception { 55 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(ThriftServiceType.MULTIPLEXED_INTERFACE); 56 | config.setConnectTimeout(3000); 57 | config.setThriftProtocol(TProtocolType.BINARY); 58 | // 该端口不存在 59 | ThriftServerInfo thriftServerInfo1 = servers.get(0); 60 | byte[] nodeID1 = String.format("%s%d", thriftServerInfo1.getHost(), thriftServerInfo1.getPort()).getBytes(); 61 | config.addThriftServer(thriftServerInfo1.getHost(), thriftServerInfo1.getPort(), nodeID1); 62 | 63 | ThriftServerInfo thriftServerInfo2 = servers.get(1); 64 | byte[] nodeID2 = String.format("%s%d", thriftServerInfo2.getHost(), thriftServerInfo2.getPort()).getBytes(); 65 | config.addThriftServer(thriftServerInfo2.getHost(), thriftServerInfo2.getPort(), nodeID2); 66 | 67 | config.addThriftClientClass("example", Example.Client.class); 68 | config.addThriftClientClass("other", Other.Client.class); 69 | 70 | config.setMaxConnectionPerServer(2); 71 | config.setMinConnectionPerServer(1); 72 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 73 | config.setMaxConnectionAge(2); 74 | config.setLazyInit(false); 75 | config.setAcquireIncrement(2); 76 | config.setAcquireRetryDelay(2000); 77 | 78 | config.setAcquireRetryAttempts(1); 79 | config.setMaxConnectionCreateFailedCount(1); 80 | config.setConnectionTimeoutInMs(5000); 81 | 82 | ThriftConnectionPool pool = new ThriftConnectionPool(config); 83 | ThriftConnection connection = pool.getConnection(nodeID1); 84 | assertNotNull(connection); 85 | connection.close(); 86 | connection = pool.getConnection(nodeID2); 87 | assertNotNull(connection); 88 | connection.close(); 89 | pool.close(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/com/wmz7year/thrift/pool/TimeoutConnectionTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Jiang Wei 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.wmz7year.thrift.pool; 18 | 19 | import java.io.IOException; 20 | import java.util.List; 21 | import java.util.concurrent.CountDownLatch; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | import org.apache.thrift.TException; 25 | import org.apache.thrift.TServiceClient; 26 | import org.apache.thrift.transport.TTransportException; 27 | 28 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig; 29 | import com.wmz7year.thrift.pool.config.ThriftConnectionPoolConfig.TProtocolType; 30 | import com.wmz7year.thrift.pool.config.ThriftServerInfo; 31 | import com.wmz7year.thrift.pool.connection.ThriftConnection; 32 | import com.wmz7year.thrift.pool.example.Example; 33 | import com.wmz7year.thrift.pool.exception.ThriftConnectionPoolException; 34 | 35 | /* 36 | * 服务端响应超时的单元测试 37 | */ 38 | public class TimeoutConnectionTest extends BasicAbstractTest { 39 | 40 | private List servers; 41 | 42 | /* 43 | * @see com.wmz7year.thrift.pool.BasicAbstractTest#beforeTest() 44 | */ 45 | @Override 46 | protected void beforeTest() throws Exception { 47 | servers = startTimeoutServers(1); 48 | // TODO 49 | 50 | } 51 | 52 | /* 53 | * @see com.wmz7year.thrift.pool.BasicAbstractTest#afterTest() 54 | */ 55 | @Override 56 | protected void afterTest() throws Exception { 57 | // ignore 58 | } 59 | 60 | public void testTimeoutService() throws Exception { 61 | ThriftConnectionPoolConfig config = new ThriftConnectionPoolConfig(); 62 | config.setConnectTimeout(3000); 63 | config.setThriftProtocol(TProtocolType.BINARY); 64 | // 该端口不存在 65 | for (ThriftServerInfo thriftServerInfo : servers) { 66 | config.addThriftServer(thriftServerInfo.getHost(), thriftServerInfo.getPort()); 67 | } 68 | config.setClientClass(Example.Client.class); 69 | 70 | config.setMaxConnectionPerServer(2); 71 | config.setMinConnectionPerServer(1); 72 | config.setIdleMaxAge(2, TimeUnit.SECONDS); 73 | config.setMaxConnectionAge(2); 74 | config.setLazyInit(false); 75 | config.setAcquireIncrement(2); 76 | config.setAcquireRetryDelay(2000); 77 | 78 | config.setAcquireRetryAttempts(1); 79 | config.setMaxConnectionCreateFailedCount(1); 80 | config.setConnectionTimeoutInMs(50000); 81 | 82 | config.check(); 83 | 84 | final ThriftConnectionPool pool = 85 | new ThriftConnectionPool(config); 86 | 87 | final int threadCount = 2; 88 | final CountDownLatch countDown = new CountDownLatch(threadCount); 89 | for (int i = 0; i < threadCount; i++) { 90 | new Thread(new Runnable() { 91 | 92 | @Override 93 | public void run() { 94 | for (int i = 0; i < threadCount; i++) { 95 | ThriftConnection connection = null; 96 | try { 97 | connection = pool.getConnection(); 98 | // example service 99 | com.wmz7year.thrift.pool.example.Example.Client exampleServiceClient = 100 | connection.getClient("example", 101 | Example.Client.class); 102 | exampleServiceClient.ping(); 103 | connection.close(); 104 | } catch (TTransportException e) { 105 | logger.info("set time out connection unavailable"); 106 | connection.setAvailable(false); 107 | } catch (ThriftConnectionPoolException e) { 108 | e.printStackTrace(); 109 | } catch (TException e) { 110 | e.printStackTrace(); 111 | } catch (IOException e) { 112 | e.printStackTrace(); 113 | } finally { 114 | if (connection != null) { 115 | try { 116 | connection.close(); 117 | } catch (IOException e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | } 122 | } 123 | countDown.countDown(); 124 | } 125 | }).start(); 126 | } 127 | 128 | countDown.await(); 129 | 130 | pool.close(); 131 | } 132 | 133 | } 134 | --------------------------------------------------------------------------------