├── .gitignore ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── README.md ├── pom.xml └── src ├── main ├── java │ └── io │ │ └── isharing │ │ └── springddal │ │ ├── datasource │ │ ├── DynamicDataSource.java │ │ └── DynamicDataSourceHolder.java │ │ └── route │ │ ├── MysqlRouteStrategy.java │ │ ├── RouteInterceptor.java │ │ ├── RouteStrategy.java │ │ ├── RouteUtils.java │ │ ├── annotation │ │ ├── Router.java │ │ └── RouterConst.java │ │ ├── exception │ │ ├── ConfigurationException.java │ │ ├── ObjectAccessException.java │ │ └── ParamsErrorException.java │ │ ├── keygen │ │ └── SnowflakeIdFactory.java │ │ └── rule │ │ ├── RuleAlgorithm.java │ │ ├── conf │ │ ├── DataNode.java │ │ ├── DataNodeSource.java │ │ ├── DataSourceNode.java │ │ ├── DbGlobal.java │ │ ├── ShardRule.java │ │ ├── TableRule.java │ │ ├── XMLDataNodesLoader.java │ │ ├── XMLLoader.java │ │ ├── XMLParserUtils.java │ │ └── XMLTableRulesLoader.java │ │ ├── function │ │ ├── AbstractPartitionAlgorithm.java │ │ ├── AutoPartitionByLong.java │ │ ├── NumberParseUtil.java │ │ ├── PartitionByDate.java │ │ ├── PartitionByLong.java │ │ ├── PartitionByMod.java │ │ ├── PartitionByMonth.java │ │ ├── PartitionByYear.java │ │ └── PartitionUtil.java │ │ └── utils │ │ ├── BeanObjectEntityConfig.java │ │ ├── ConfigUtil.java │ │ ├── FieldDictionary.java │ │ ├── Initializable.java │ │ ├── JVMInfo.java │ │ ├── ObjectUtil.java │ │ ├── OrderRetainingMap.java │ │ ├── ParameterMapping.java │ │ ├── ReflectionProvider.java │ │ ├── SplitUtil.java │ │ └── Visitor.java └── resources │ ├── applicationContext.xml_tpl │ ├── autopartition-long.txt │ ├── datanodes.dtd │ ├── datanodes.xml_tpl │ ├── log4j.properties │ ├── logback.xml │ ├── rules.dtd │ ├── rules.xml_tpl │ └── spring-ddal.xml_tpl └── test └── java └── io └── isharing └── springddal └── route └── keygen └── TestSnowflakeIdFactory.java /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .classpath 3 | .settings/ 4 | bin/ 5 | target/ 6 | logpath_IS_UNDEFINED/ 7 | spring_ddal.iml 8 | pom.xml 9 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/main/resources=UTF-8 4 | encoding//src/test/java=UTF-8 5 | encoding//src/test/resources=UTF-8 6 | encoding/=UTF-8 7 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.7 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 13 | org.eclipse.jdt.core.compiler.source=1.7 14 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## spring-ddal简介 2 | 3 | Spring DDAL是基于spring AOP和AbstractRoutingDataSource实现了读写分离和分库分表功能,是一款轻量级的插件,简单易用、轻耦合,使用注解即可完成读写分离、分库分表。 4 | 5 | Spring-DDAL的拆分方式与其他中间件不一样,是基于方法注解的方式实现,是一种轻量级的读写分离、分库分表实现。而其他中间件如Amoeba(已停止维护)、Cobar(已停止维护)、TDDL、Sharding-JDBC(推荐,开源,已入驻Apache)、dble(推荐,开源,专业/商业团队长期维护)、one-proxy(推荐,收费)以及MyCat(强烈不推荐)等等功能更完备、也更强大。 6 | 7 | 建议:由于是基于应用层做的分库分表,因此,虽然能进行拆分,但是在SQL路由方面还比较弱,尤其涉及分片路由、结果合并排序等方面,仍然存在不少问题。因此,建议仅使用该插件的读写分离或分库或大表的分表功能,不建议同时使用分库分表功能!!! 8 | 9 | 如果有条件,建议可以直接上Sharding-JDBC或dble等中间件。中小型项目前期可以使用Spring DDAL过渡,后期支持Amoeba、Cobar、dble、MyCat的平缓切换。 10 | 11 | 样例使用工程,请参见链接: 12 | 13 | 14 | ## Spring-DDAL十分钟快速上手 15 | 16 | ### Step 01:配置Spring-DDAL.xml文件 17 | 将Spring-DDAL.xml配置文件中dateSourcePointcut部分修改为自己项目的包名。如使用的是MyBatis,也请修改相应的包名。 18 | ```xml 19 |   20 | 21 | ``` 22 | 23 | ### Step 02:配置datanodes.xml文件 24 | 主要是datanodes.xml文件中的dataSource和dataNode部分。dataSources部分主要是配置数据源,dataNode部分主要是配置根据数据源配置读写库。看XML文件节点就知道怎么配置。 25 | 数据源配置: 26 | 27 | ```xml 28 | 29 | 30 | jdbc:mysql://localhost:3306/example?useUnicode=true&characterEncoding=UTF-8 31 | test 32 | 111111 33 | 34 | 35 | jdbc:mysql://localhost:3306/example?useUnicode=true&characterEncoding=UTF-8 36 | test 37 | 111111 38 | 39 | 40 | jdbc:mysql://localhost:3306/example?useUnicode=true&characterEncoding=UTF-8 41 | test 42 | 111111 43 | 44 | 45 | 46 | jdbc:mysql://192.168.2.3:3306/example?useUnicode=true&characterEncoding=UTF-8 47 | test 48 | 111111 49 | 50 | 51 | jdbc:mysql://192.168.2.3:3306/example?useUnicode=true&characterEncoding=UTF-8 52 | test 53 | 111111 54 | 55 | 56 | jdbc:mysql://192.168.2.3:3306/example?useUnicode=true&characterEncoding=UTF-8 57 | test 58 | 111111 59 | 60 | 61 | ``` 62 | 数据节点配置: 63 | ```xml 64 | 65 | 66 | ds0 67 | ds0_repl1, ds0_repl2 68 | 69 | 70 | ds0 71 | ds0_repl1, ds0_repl2 72 | 73 | 74 | ds1 75 | ds1_repl1, ds1_repl2 76 | 77 | 78 | ds2 79 | ds2_repl1, ds2_repl2 80 | 81 | 82 | ``` 83 | Step03:配置rule.xml文件 84 | 配置拆分规则rule.xml文件,这里可以使用规则模板直接复制修改相应的规则即可。 85 | ```xml 86 | 87 | userlt2000w 88 | id 89 | autopartition-long.txt 90 | 91 | 92 | waybilldb2017 93 | orderDate 94 | yyyy-MM-dd 95 | 2016-01-01 00:00:01 96 | 97 | 98 | ``` 99 | 主要配置tableRule的name属性和property的值即可。具体看文件中的规则说明。 100 | 101 | Step 04:其他配置文件 102 | 其他配置与常规的web工程和spring的配置一致,无须改变。 103 | 104 | Step05:项目中开发注解配置 105 | 按常规的项目,假设你的项目架构也是有DAO 层的,而DAO层对应的是一个SQL查询操作,如果是这样就简单了,你可以这样操作,在你的DAO层的实现类上使用@Router注解实现读写分离、分库分表,可定义在类名上也可定义在方法名上。如定义在方法名上的: 106 | 107 | ```java 108 | @Router(dataNode="dn1,dn2",ruleName="part-by-rang-long") 109 | public List getStudentByClassID(int classId){ 110 | …… 111 | } 112 | ``` 113 | 114 | 另外,如果定义有BaseDao的公用父类和方法,则需要在BaseDao的公用方法中定义@Transactional(org.springframework.transaction.annotation.Transactional)表明该方法为写操作,如果是写操作该注解不能定义readOnly为true。 115 | 116 | ## Router注解说明及示例 117 | Router注解主要有如下几个属性:isRoute、forceReadOnMaster、readOnly和ruleName。 118 | 其中(红色必须): 119 | * isRoute: #是否拆分,默认为拆分 120 | * forceReadOnMaster: #强制将读操作在写库中读,以避免写的时候从读库读不到数据(主从同步延迟导致的问题) 121 | * readOnly: #是否读操作,默认true 122 | * ruleName: #拆分规则名,对应rule.xml的tableRule的name属性 123 | * dataNode: #定义数据节点名称,对应datanode.xml文件部分定义 124 | * type: #拆分类型,普通规则拆分默认为空。全局库/表须定义为:global 125 | 126 | 使用举例: 127 | 示例一: 128 | ```java 129 | @Router(dataNode="dn1,dn2",ruleName="part-by-rang-long",forceReadOnMaster=true) 130 | public List getStudentByClassID(int classId){ 131 | …… 132 | } 133 | ``` 134 | 说明:示例一为强制从主库读,拆分规则为:part-by-rang-long。 135 | 136 | 示例二: 137 | ```java 138 | @Router(dataNode="dn1,dn2",ruleName="part-by-rang-long") 139 | public int saveStudent (Student stu){ 140 | …… 141 | } 142 | ``` 143 | 说明:示例二为按拆分规则写数据,拆分规则为:part-by-rang-long 144 | 145 | 示例三: 146 | ```java 147 | @Router(isRoute=false, dataNode="dn1,dn2,dn3",ruleName="part-by-rang-long") 148 | public List getStudentByClassID(int classId){ 149 | …… 150 | } 151 | ``` 152 | 说明:示例三为虽配置了Router,但是这里isRoute=false不配置拆分,默认会走part-by-rang-long规则中定义的defaultNode进行读操作。 153 | 154 | 示例四: 155 | ```java 156 | @Repository 157 | @Router(isRoute=true, dataNode="dn1,dn2",ruleName="part-by-year") 158 | public class StudentDaoImpl extends BaseDaoImpl implements StudentDao { 159 | …… 160 | } 161 | ``` 162 | 说明:示例四为基于类的注解配置,但是如果方法上也有注解配置,那么方法中的配置将会覆盖类上的配置信息,不重叠部分不变。比如例三和例四种,最后isRoute的值会是false,并且getStudentByClassID的Router注解值会变成:isRoute=false, dataNode="dn1,dn2,dn3",ruleName="part-by-rang-long"。 163 | 164 | 注意事项: 165 | 1. 同一个表,Router注解建议都定义是类名上; 166 | 2. 如果DAO层是MyBatis生成的,无实现方法,则须在类名上定义Router注解,方法上定义的无效; 167 | 3. Router定义只针对物理表对应的DAO,如果是一个DAO有多个表操作,则无法支持。 168 | 169 | 170 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.isharing.springddal 7 | spring_ddal 8 | ${springddal.version} 9 | jar 10 | 11 | fkh_ddal 12 | http://maven.apache.org 13 | 14 | 15 | UTF-8 16 | 1.0.1-SNAPSHOT 17 | 1.7 18 | 4.3.18.RELEASE 19 | 1.9.13 20 | 3.4.1 21 | 1.3.0 22 | 1.6.4 23 | 1.0.1 24 | 25 | 26 | 27 | 28 | release 29 | 30 | 1.0.0 31 | 32 | 33 | 34 | 35 | 36 | 37 | junit 38 | junit 39 | 4.12 40 | test 41 | 42 | 43 | 44 | 45 | net.coobird 46 | thumbnailator 47 | 0.4.7 48 | 49 | 50 | dom4j 51 | dom4j 52 | 1.6.1 53 | 54 | 55 | xml-apis 56 | xml-apis 57 | 58 | 59 | 60 | 61 | org.jdom 62 | jdom 63 | 2.0.2 64 | 65 | 66 | xml-apis 67 | xml-apis 68 | 69 | 70 | 71 | 72 | org.apache.commons 73 | commons-pool2 74 | 2.3 75 | 76 | 77 | org.apache.pdfbox 78 | pdfbox 79 | 2.0.12 80 | 81 | 82 | org.apache.httpcomponents 83 | httpclient 84 | 4.3.6 85 | 86 | 87 | org.apache.httpcomponents 88 | httpcore 89 | 4.3.6 90 | 91 | 92 | org.springframework.data 93 | spring-data-mongodb 94 | 1.9.1.RELEASE 95 | 96 | 97 | org.springframework.session 98 | spring-session-data-redis 99 | 1.1.1.RELEASE 100 | 101 | 102 | org.mybatis.caches 103 | mybatis-ehcache 104 | 1.0.3 105 | 106 | 107 | org.springframework 108 | spring-jms 109 | ${spring-version} 110 | 111 | 112 | org.apache.geronimo.specs 113 | geronimo-jms_1.1_spec 114 | 1.1.1 115 | 116 | 117 | org.apache.kafka 118 | kafka_2.11 119 | 0.8.2.1 120 | 121 | 122 | org.slf4j 123 | slf4j-log4j12 124 | 125 | 126 | org.apache.zookeeper 127 | zookeeper 128 | 129 | 130 | 131 | 132 | 133 | 134 | javassist 135 | javassist 136 | 3.12.1.GA 137 | 138 | 139 | 140 | commons-io 141 | commons-io 142 | 2.4 143 | 144 | 145 | commons-lang 146 | commons-lang 147 | 2.6 148 | 149 | 150 | commons-beanutils 151 | commons-beanutils 152 | 1.8.3 153 | 154 | 155 | com.isharing.commons 156 | commons-utils 157 | 2.0.0 158 | 159 | 160 | commons-collections 161 | commons-collections 162 | 3.2.1 163 | 164 | 165 | commons-logging 166 | commons-logging 167 | 1.2 168 | 169 | 170 | commons-configuration 171 | commons-configuration 172 | 1.10 173 | 174 | 175 | commons-codec 176 | commons-codec 177 | 1.10 178 | 179 | 180 | 181 | log4j 182 | log4j 183 | 1.2.17 184 | 185 | 186 | 187 | javax.servlet 188 | javax.servlet-api 189 | 3.0.1 190 | provided 191 | 192 | 193 | 194 | com.alibaba 195 | fastjson 196 | 1.1.36 197 | 198 | 199 | 200 | org.codehaus.jackson 201 | jackson-core-asl 202 | ${jackson-version} 203 | 204 | 205 | org.codehaus.jackson 206 | jackson-mapper-asl 207 | ${jackson-version} 208 | 209 | 210 | org.codehaus.jackson 211 | jackson-xc 212 | ${jackson-version} 213 | 214 | 215 | 216 | 217 | org.slf4j 218 | log4j-over-slf4j 219 | ${org.slf4j-version} 220 | 221 | 222 | org.slf4j 223 | slf4j-api 224 | ${org.slf4j-version} 225 | 226 | 227 | ch.qos.logback 228 | logback-classic 229 | ${org.logback-version} 230 | 231 | 232 | ch.qos.logback 233 | logback-core 234 | ${org.logback-version} 235 | 236 | 237 | 238 | cglib 239 | cglib 240 | 2.2.2 241 | 242 | 243 | mysql 244 | mysql-connector-java 245 | 5.1.21 246 | 247 | 248 | com.alibaba.druid 249 | druid-wrapper 250 | 0.2.9 251 | 252 | 253 | 254 | 255 | org.mybatis 256 | mybatis 257 | ${mybatis-version} 258 | 259 | 260 | org.mybatis 261 | mybatis-spring 262 | ${mybatis-spring.version} 263 | 264 | 265 | 266 | com.github.pagehelper 267 | pagehelper 268 | 4.1.2 269 | 270 | 271 | 272 | 273 | org.springframework 274 | spring-core 275 | ${spring-version} 276 | 277 | 278 | org.springframework 279 | spring-beans 280 | ${spring-version} 281 | 282 | 283 | org.springframework 284 | spring-context 285 | ${spring-version} 286 | 287 | 288 | 289 | org.springframework 290 | spring-aop 291 | ${spring-version} 292 | 293 | 294 | org.springframework 295 | spring-aspects 296 | ${spring-version} 297 | 298 | 299 | org.springframework 300 | spring-orm 301 | ${spring-version} 302 | 303 | 304 | org.springframework 305 | spring-tx 306 | ${spring-version} 307 | 308 | 309 | org.springframework 310 | spring-test 311 | ${spring-version} 312 | test 313 | 314 | 315 | 316 | 317 | 318 | 319 | nexus-releases 320 | Internal Releases 321 | 322 | https://dev.fkhwl.com/nexus/content/repositories/releases 323 | 324 | 325 | 326 | nexus-snapshots 327 | Internal Snapshots 328 | 329 | https://dev.fkhwl.com/nexus/content/repositories/snapshots 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | org.apache.maven.plugins 339 | maven-compiler-plugin 340 | 2.5.1 341 | 342 | 1.7 343 | 1.7 344 | UTF-8 345 | 346 | 347 | 348 | org.apache.maven.plugins 349 | maven-surefire-plugin 350 | 2.4.2 351 | 352 | true 353 | 354 | 355 | 356 | 357 | 358 | 359 | org.apache.maven.plugins 360 | maven-deploy-plugin 361 | 2.8.2 362 | 363 | 364 | 365 | 366 | 367 | 368 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/datasource/DynamicDataSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.datasource; 29 | 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | 33 | import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 34 | 35 | import io.isharing.springddal.route.rule.conf.XMLLoader; 36 | 37 | /** 38 | * 动态数据源 39 | * 40 | * @author Chen Fei 41 | * 42 | */ 43 | public class DynamicDataSource extends AbstractRoutingDataSource { 44 | 45 | // private Object writeDataSource; 46 | // private List readDataSources; 47 | // private Map readDataSources; 48 | // private int readDataSourceSize = 0; 49 | 50 | // private AtomicInteger readIndex = new AtomicInteger(0); 51 | 52 | /** 53 | * 数据源键名 54 | */ 55 | // private static final String DATASOURCE_KEY_WRITE = "write"; 56 | // private static final String DATASOURCE_KEY_READ = "read"; 57 | 58 | public DynamicDataSource() { 59 | super(); 60 | initDataSources(); 61 | } 62 | 63 | /** 64 | * 初始化数据库源 65 | * 66 | * 备注:数据库源配置于dbshardConfig.xml文件中 67 | * 68 | * @param dataSourceFactory 69 | */ 70 | private void initDataSources() { 71 | Object writeDataSource = XMLLoader.getDefaultWriteDataNode(); 72 | setDefaultTargetDataSource(writeDataSource); 73 | Map targetDataSources = new HashMap(); 74 | targetDataSources = XMLLoader.getDataSources(); 75 | // for (Map.Entry entry : targetDataSources.entrySet()) { 76 | // System.out.println(entry.getKey() + "--->" + entry.getValue()); 77 | // } 78 | setTargetDataSources(targetDataSources); 79 | } 80 | 81 | /* 82 | * (non-Javadoc) 83 | * 84 | * @see 85 | * org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource# 86 | * afterPropertiesSet() 87 | */ 88 | // @Override 89 | // public void afterPropertiesSet() { 90 | // if (this.writeDataSource == null) { 91 | // throw new IllegalArgumentException("Property 'writeDataSource' is required"); 92 | // } 93 | // setDefaultTargetDataSource(writeDataSource); 94 | // Map targetDataSources = new HashMap(); 95 | // targetDataSources.put(DATASOURCE_KEY_WRITE, writeDataSource); 96 | // if (this.readDataSources == null) { 97 | // readDataSourceSize = 0; 98 | // } else { 99 | // /*for (int i = 0; i < readDataSources.size(); i++) { 100 | // targetDataSources.put(DATASOURCE_KEY_READ + i, readDataSources.get(i)); 101 | // }*/ 102 | // int i = 0; 103 | // for(Entry e : readDataSources.entrySet()) { 104 | // targetDataSources.put(DATASOURCE_KEY_READ + i, e.getValue()); 105 | // i++; 106 | // } 107 | // readDataSourceSize = readDataSources.size(); 108 | // } 109 | // setTargetDataSources(targetDataSources); 110 | // super.afterPropertiesSet(); 111 | // } 112 | 113 | /* 114 | * (non-Javadoc) 115 | * 116 | * @see 117 | * org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource# 118 | * determineCurrentLookupKey() 119 | */ 120 | @Override 121 | protected Object determineCurrentLookupKey() { 122 | // if (DynamicDataSourceHolder.isChoiceNone() || DynamicDataSourceHolder.isChoiceWrite()) { 123 | // return DATASOURCE_KEY_WRITE; 124 | // } 125 | // int index = readIndex.incrementAndGet() % readDataSourceSize; 126 | // return DATASOURCE_KEY_READ + index; 127 | return DynamicDataSourceHolder.getDataSourceKey(); 128 | } 129 | 130 | // public DataSourceFactory getDataSourceFactory() { 131 | // return dataSourceFactory; 132 | // } 133 | // 134 | // public void setDataSourceFactory(DataSourceFactory dataSourceFactory) { 135 | // this.dataSourceFactory = dataSourceFactory; 136 | // } 137 | 138 | /** 139 | * @return the writeDataSource 140 | */ 141 | // public Object getWriteDataSource() { 142 | // return writeDataSource; 143 | // } 144 | 145 | /** 146 | * @param writeDataSource 147 | * the writeDataSource to set 148 | */ 149 | // public void setWriteDataSource(Object writeDataSource) { 150 | // this.writeDataSource = writeDataSource; 151 | // } 152 | // 153 | // public Map getReadDataSources() { 154 | // return readDataSources; 155 | // } 156 | // 157 | // public void setReadDataSources(Map readDataSources) { 158 | // this.readDataSources = readDataSources; 159 | // } 160 | 161 | 162 | /** 163 | * @return the readDataSources 164 | */ 165 | // public List getReadDataSources() { 166 | // return readDataSources; 167 | // } 168 | 169 | /** 170 | * @param readDataSources 171 | * the readDataSources to set 172 | */ 173 | // public void setReadDataSources(List readDataSources) { 174 | // this.readDataSources = readDataSources; 175 | // } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/datasource/DynamicDataSourceHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.datasource; 29 | 30 | /** 31 | * 数据源管理器 32 | * 33 | * @author Chen Fei 34 | * 35 | */ 36 | public class DynamicDataSourceHolder { 37 | 38 | private static final ThreadLocal rwholder = new ThreadLocal(); 39 | private static final ThreadLocal dataSourceHolder = new ThreadLocal(); 40 | private static final ThreadLocal tableIndexHolder= new ThreadLocal(); 41 | 42 | private static enum DataSourceType { 43 | write, read; 44 | } 45 | 46 | /** 47 | * 标记为写数据源 48 | */ 49 | public static void markWrite() { 50 | rwholder.set(DataSourceType.write); 51 | } 52 | 53 | /** 54 | * 标记为读数据源 55 | */ 56 | public static void markRead() { 57 | rwholder.set(DataSourceType.read); 58 | } 59 | 60 | /** 61 | * 重置 62 | */ 63 | public static void reset() { 64 | rwholder.set(null); 65 | } 66 | 67 | /** 68 | * 是否还未设置数据源 69 | * @return 70 | */ 71 | public static boolean isChoiceNone() { 72 | return null == rwholder.get(); 73 | } 74 | 75 | /** 76 | * 当前是否选择了写数据源 77 | * @return 78 | */ 79 | public static boolean isChoiceWrite() { 80 | return DataSourceType.write == rwholder.get(); 81 | } 82 | 83 | /** 84 | * 当前是否选择了读数据源 85 | * @return 86 | */ 87 | public static boolean isChoiceRead() { 88 | return DataSourceType.read == rwholder.get(); 89 | } 90 | 91 | public static void setDataSourceKey(String dbKey) { 92 | dataSourceHolder.set(dbKey); 93 | } 94 | 95 | public static String getDataSourceKey() { 96 | return (String) dataSourceHolder.get(); 97 | } 98 | 99 | public static void clearDbKey() { 100 | dataSourceHolder.remove(); 101 | } 102 | 103 | public static void setTableIndex(String tableIndex){ 104 | tableIndexHolder.set(tableIndex); 105 | } 106 | 107 | public static String getTableIndex(){ 108 | return (String) tableIndexHolder.get(); 109 | } 110 | 111 | public static void clearTableIndex(){ 112 | tableIndexHolder.remove(); 113 | } 114 | 115 | } 116 | 117 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/RouteStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route; 29 | 30 | import java.sql.SQLNonTransientException; 31 | 32 | import io.isharing.springddal.route.annotation.Router; 33 | 34 | /** 35 | * 路由接口 通过调用该接口来自动判断数据位于哪个服务器 36 | * 37 | * @author Chen Fei 38 | * 39 | */ 40 | public interface RouteStrategy { 41 | 42 | public final static int ROUTER_TYPE_DB = 0; /*仅分库*/ 43 | public final static int ROUTER_TYPE_TABLE = 1; /*仅分表*/ 44 | public final static int ROUTER_TYPE_DBANDTABLE = 2; /*分库分表*/ 45 | 46 | /** 47 | * 根据理由规则进行路由计算 48 | * 49 | * @param router Router注解对象 50 | * @param routeField 拆分字段 51 | * @param routeFieldValue 拆分字段的值 52 | * @return 通过DbContextHolder保存拆分库表后的index 53 | * @throws 54 | */ 55 | public void route(Router router, String routeField, String routeFieldValue) throws SQLNonTransientException; 56 | 57 | /** 58 | * 根据理由规则进行路由计算 59 | * 60 | * @param router Router注解对象 61 | * @param routeField 拆分字段 62 | * @param routeFieldValue 拆分字段的值 63 | * @param isRead 是否是读操作 64 | * @return 通过DbContextHolder保存拆分库表后的index 65 | * @throws 66 | */ 67 | public void route(Router router, String routeField, String routeFieldValue, boolean isRead) throws SQLNonTransientException; 68 | 69 | /** 70 | * 针对全局表或者无Router注解配置使用,默认路由到全局库中。 71 | */ 72 | public void routeToGlobalNode(boolean isRead, boolean forceReadOnMaster); 73 | 74 | /** 75 | * 如果没有配置拆分字段,则默认路由到规则配置(rules.xml)中的默认节点。 76 | * 如果还读不到写节点信息,则会再从dataNode节点中找默认写节点(相当于全局配置)。 77 | * 78 | * @param ruleName 规则名称 79 | */ 80 | public void routeToDefaultNode(String ruleName, boolean isRead, boolean forceReadOnMaster); 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/RouteUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route; 29 | 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | import sun.misc.BASE64Encoder; 34 | 35 | public class RouteUtils { 36 | 37 | private static final Logger log = LoggerFactory.getLogger(RouteUtils.class); 38 | 39 | /** 40 | * 默认编码 41 | */ 42 | private final static String encode = "utf-8"; 43 | 44 | /** 45 | * 最大资源数 46 | */ 47 | private final static int resourceMax = 10000; 48 | 49 | /** 50 | * 获取hashCode 51 | * 52 | * @param routeValue 53 | * @return 54 | */ 55 | public static int getHashCodeBase64(String routeValue) { 56 | int hashCode = 0; 57 | try { 58 | String pinBase64 = new BASE64Encoder().encode(routeValue.getBytes(encode)); 59 | hashCode = Math.abs(pinBase64.hashCode()); 60 | } catch (Exception e) { 61 | log.error("hashCode 失败", e); 62 | } 63 | return hashCode; 64 | } 65 | 66 | /** 67 | * 获取资源码 68 | * 69 | * @param routeValue 70 | * @return 71 | */ 72 | public static int getResourceCode(String routeValue) { 73 | int hashCode = RouteUtils.getHashCodeBase64(routeValue); 74 | int resourceCode = hashCode % resourceMax; 75 | return resourceCode; 76 | } 77 | 78 | public static void main(String args[]) { 79 | String payid = "140331160123935469773"; 80 | 81 | String resource = payid.substring(payid.length() - 4); 82 | 83 | int routeFieldInt = Integer.valueOf(resource); 84 | System.out.println("1# routeFieldInt="+routeFieldInt); 85 | routeFieldInt = getResourceCode(resource); 86 | System.out.println("2# routeFieldInt="+routeFieldInt); 87 | int mode = 6 * 200; 88 | int dbIndex = routeFieldInt % mode / 200; 89 | int tbIndex = routeFieldInt % 200; 90 | 91 | System.out.println(dbIndex + "-->" + tbIndex); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/annotation/Router.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.annotation; 29 | 30 | import java.lang.annotation.ElementType; 31 | import java.lang.annotation.Retention; 32 | import java.lang.annotation.RetentionPolicy; 33 | import java.lang.annotation.Target; 34 | 35 | /** 36 | * 分库分表的路由配置,可定义在类名上也可定义在类方法名上,如果同时配置了,那么方法名上的配置优先级大于类名上的。
37 | * 38 | * @author Chen Fei 39 | * 40 | */ 41 | @Retention(RetentionPolicy.RUNTIME) 42 | @Target({ElementType.TYPE,ElementType.METHOD}) 43 | public @interface Router { 44 | 45 | /** 46 | * 是否拆分,默认为拆分 47 | */ 48 | boolean isRoute() default true; 49 | 50 | /** 51 | * 强制将读操作在写库中读,以避免写的时候从读库读不到数据(主从同步延迟导致的问题) 52 | * TODO 未考虑事务传播的情况 53 | */ 54 | boolean forceReadOnMaster() default false; 55 | 56 | /** 57 | * 是否读操作,默认true 58 | */ 59 | boolean readOnly() default true; 60 | 61 | /** 62 | * 拆分字段 63 | */ 64 | // String routerField() default RouterConst.ROUTER_ROUTEFIELD_DEFAULT; 65 | 66 | /** 67 | * 拆分规则名 68 | */ 69 | String ruleName() default ""; 70 | 71 | /** 72 | * 数据库节点,对应datanodes.xml文件中的dataNodes节点。 73 | * 多个节点以英文逗号分隔,如果是连续节点可以定义为:dn$0-10,表示定义了dn0-dn10共10个节点。 74 | */ 75 | String dataNode() default ""; 76 | 77 | /** 78 | * 拆分类型,普通规则拆分默认为空。全局库/表须定义为:global 79 | */ 80 | String type() default ""; 81 | 82 | /** 83 | * 拆分表的后缀样式/风格 84 | */ 85 | String tableStyle() default RouterConst.ROUTER_TABLE_SUFFIX_DEFAULT; 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/annotation/RouterConst.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.annotation; 29 | 30 | /** 31 | * 拆分规则库引擎参数的默认属性名 32 | */ 33 | public class RouterConst { 34 | 35 | /** 36 | * 默认拆分字段,值为createDate 37 | */ 38 | // public static final String ROUTER_ROUTEFIELD_DEFAULT = "createDate"; 39 | // public static final String ROUTER_DBRULENAME_DEFAULT = "partitionByDate"; 40 | 41 | public static final String ROUTER_GLOBAL = "Global"; 42 | public static final String ROUTER_TBLRULENAME_DEFAULT = "AutoPartitionByLong"; 43 | public static final String ROUTER_TABLE_SUFFIX_DEFAULT = "_0000"; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/exception/ConfigurationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.exception; 29 | 30 | public class ConfigurationException extends RuntimeException { 31 | private static final long serialVersionUID = -180146385688342818L; 32 | 33 | public ConfigurationException() { 34 | super(); 35 | } 36 | 37 | public ConfigurationException(String message, Throwable cause) { 38 | super(message, cause); 39 | } 40 | 41 | public ConfigurationException(String message) { 42 | super(message); 43 | } 44 | 45 | public ConfigurationException(Throwable cause) { 46 | super(cause); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/exception/ObjectAccessException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.exception; 29 | 30 | public class ObjectAccessException extends RuntimeException { 31 | private static final long serialVersionUID = 1L; 32 | 33 | public ObjectAccessException(String message) { 34 | super(message); 35 | } 36 | 37 | public ObjectAccessException(String message, Throwable cause) { 38 | super(message, cause); 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/exception/ParamsErrorException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.exception; 29 | 30 | public class ParamsErrorException extends RuntimeException { 31 | 32 | private static final long serialVersionUID = 2296896317900237622L; 33 | 34 | public ParamsErrorException() { 35 | super(); 36 | } 37 | 38 | public ParamsErrorException(String message, Throwable cause) { 39 | super(message, cause); 40 | } 41 | 42 | public ParamsErrorException(String message) { 43 | super(message); 44 | } 45 | 46 | public ParamsErrorException(Throwable cause) { 47 | super(cause); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/keygen/SnowflakeIdFactory.java: -------------------------------------------------------------------------------- 1 | package io.isharing.springddal.route.keygen; 2 | 3 | import java.util.Calendar; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class SnowflakeIdFactory { 9 | 10 | private static final Logger log = LoggerFactory.getLogger(SnowflakeIdFactory.class); 11 | 12 | private static final long TWEPOCH; 13 | // 毫秒内自增位 14 | private final long SEQUENCE_BITS = 12L; 15 | // 机器标识位数 16 | private final long WORKER_ID_BITS = 5L; 17 | // 数据中心标识位数 18 | private final long DATA_CENTER_ID_BITS = 5L; 19 | 20 | private final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);//4095 21 | 22 | // 机器ID最大值 31 23 | private final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);//31 24 | // 数据中心ID最大值 31 25 | private final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);//31 26 | 27 | // 机器ID偏左移12位 28 | private final long WORKER_ID_SHIFT = SEQUENCE_BITS;//12 29 | private final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;//17 30 | private final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;//22 31 | 32 | private long workerId; 33 | private long datacenterId; 34 | private long sequence = 0L; 35 | private long lastTimestamp = -1L; 36 | 37 | static { 38 | Calendar calendar = Calendar.getInstance(); 39 | calendar.set(2017, Calendar.JANUARY, 1); 40 | calendar.set(Calendar.HOUR_OF_DAY, 0); 41 | calendar.set(Calendar.MINUTE, 0); 42 | calendar.set(Calendar.SECOND, 0); 43 | calendar.set(Calendar.MILLISECOND, 0); 44 | TWEPOCH = calendar.getTimeInMillis(); 45 | } 46 | 47 | public SnowflakeIdFactory(long workerId, long datacenterId) { 48 | if (workerId > MAX_WORKER_ID || workerId < 0) { 49 | throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID)); 50 | } 51 | if (datacenterId > MAX_DATA_CENTER_ID || datacenterId < 0) { 52 | throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", MAX_DATA_CENTER_ID)); 53 | } 54 | this.workerId = workerId; 55 | this.datacenterId = datacenterId; 56 | } 57 | 58 | public synchronized long generateKey() { 59 | long timestamp = timeGen(); 60 | if (timestamp < lastTimestamp) { 61 | //服务器时钟被调整了,ID生成器停止服务. 62 | throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 63 | } 64 | if (lastTimestamp == timestamp) { 65 | sequence = (sequence + 1) & SEQUENCE_MASK; 66 | if (sequence == 0) { 67 | timestamp = tilNextMillis(lastTimestamp); 68 | } 69 | } else { 70 | sequence = 0L; 71 | } 72 | 73 | lastTimestamp = timestamp; 74 | return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | (datacenterId << DATA_CENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT) | sequence; 75 | } 76 | 77 | protected long tilNextMillis(long lastTimestamp) { 78 | long timestamp = timeGen(); 79 | while (timestamp <= lastTimestamp) { 80 | timestamp = timeGen(); 81 | } 82 | return timestamp; 83 | } 84 | 85 | protected long timeGen() { 86 | return System.currentTimeMillis(); 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/RuleAlgorithm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule; 29 | 30 | public interface RuleAlgorithm { 31 | void init(); 32 | Integer calculate(String columnValue) ; 33 | Integer[] calculateRange(String beginValue,String endValue) ; 34 | int getPartitionNum(); 35 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/DataNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | public class DataNode { 31 | 32 | private String nodeName; 33 | private String writeNodes; 34 | private String readNodes; 35 | // private boolean defaultWriteNode; 36 | 37 | public String getNodeName() { 38 | return nodeName; 39 | } 40 | 41 | public void setNodeName(String nodeName) { 42 | this.nodeName = nodeName; 43 | } 44 | 45 | public String getWriteNodes() { 46 | return writeNodes; 47 | } 48 | 49 | public void setWriteNodes(String writeNodes) { 50 | this.writeNodes = writeNodes; 51 | } 52 | 53 | public String getReadNodes() { 54 | return readNodes; 55 | } 56 | 57 | public void setReadNodes(String readNodes) { 58 | this.readNodes = readNodes; 59 | } 60 | 61 | // public boolean isDefaultWriteNode() { 62 | // return defaultWriteNode; 63 | // } 64 | // 65 | // public void setDefaultWriteNode(boolean defaultWriteNode) { 66 | // this.defaultWriteNode = defaultWriteNode; 67 | // } 68 | 69 | @Override 70 | public String toString() { 71 | return "DataNode [writeNodes=" + writeNodes + ", readNodes=" + readNodes + "]"; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/DataNodeSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | public class DataNodeSource { 31 | 32 | private String name; 33 | private String url; 34 | private String userName; 35 | private String password; 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | 45 | public String getUrl() { 46 | return url; 47 | } 48 | 49 | public void setUrl(String url) { 50 | this.url = url; 51 | } 52 | 53 | public String getUserName() { 54 | return userName; 55 | } 56 | 57 | public void setUserName(String userName) { 58 | this.userName = userName; 59 | } 60 | 61 | public String getPassword() { 62 | return password; 63 | } 64 | 65 | public void setPassword(String password) { 66 | this.password = password; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "DataSource [name=" + name + ", url=" + url + ", userName=" + userName + ", password=" + password + "]"; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/DataSourceNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | import java.util.List; 31 | 32 | public class DataSourceNode { 33 | 34 | private List writeNodesNameList; 35 | private List readNodesNameList; 36 | 37 | // private List writeNodes; 38 | // private List readNodes; 39 | 40 | public DataSourceNode(List writeNodesNameList, List readNodesNameList) { 41 | this.writeNodesNameList = writeNodesNameList; 42 | this.readNodesNameList = readNodesNameList; 43 | } 44 | 45 | // public DataSourceNode(List writeNodes, List readNodes) { 46 | // this.writeNodes = writeNodes; 47 | // this.readNodes = readNodes; 48 | // } 49 | 50 | public List getWriteNodesNameList() { 51 | return writeNodesNameList; 52 | } 53 | 54 | public List getReadNodesNameList() { 55 | return readNodesNameList; 56 | } 57 | 58 | // public List getWriteNodes() { 59 | // return writeNodes; 60 | // } 61 | // 62 | // public List getReadNodes() { 63 | // return readNodes; 64 | // } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/DbGlobal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | public class DbGlobal { 31 | 32 | /** 数据库数量 */ 33 | // private int dbNumber; 34 | 35 | /** 数据表数量 */ 36 | private int tableNumber; 37 | 38 | /** 规则类型 */ 39 | // private int ruleType; 40 | 41 | /** 路由类型 */ 42 | private int routeType; 43 | 44 | /** 数据表index样式 */ 45 | private String tableIndexStyle; 46 | 47 | /** 默认主库写节点 */ 48 | // private String defaultWriteNode; 49 | 50 | // public int getDbNumber() { 51 | // return dbNumber; 52 | // } 53 | // 54 | // public void setDbNumber(int dbNumber) { 55 | // this.dbNumber = dbNumber; 56 | // } 57 | 58 | public int getTableNumber() { 59 | return tableNumber; 60 | } 61 | 62 | public void setTableNumber(int tableNumber) { 63 | this.tableNumber = tableNumber; 64 | } 65 | 66 | // public int getRuleType() { 67 | // return ruleType; 68 | // } 69 | // 70 | // public void setRuleType(int ruleType) { 71 | // this.ruleType = ruleType; 72 | // } 73 | 74 | public int getRouteType() { 75 | return routeType; 76 | } 77 | 78 | public void setRouteType(int routeType) { 79 | this.routeType = routeType; 80 | } 81 | 82 | public String getTableIndexStyle() { 83 | return tableIndexStyle; 84 | } 85 | 86 | public void setTableIndexStyle(String tableIndexStyle) { 87 | this.tableIndexStyle = tableIndexStyle; 88 | } 89 | 90 | // public String getDefaultWriteNode() { 91 | // return defaultWriteNode; 92 | // } 93 | // 94 | // public void setDefaultWriteNode(String defaultWriteNode) { 95 | // this.defaultWriteNode = defaultWriteNode; 96 | // } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/ShardRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | public class ShardRule { 31 | 32 | private String tableName; 33 | private String column; 34 | 35 | public String getTableName() { 36 | return tableName; 37 | } 38 | 39 | public void setTableName(String tableName) { 40 | this.tableName = tableName; 41 | } 42 | 43 | public String getColumn() { 44 | return column; 45 | } 46 | 47 | public void setColumn(String column) { 48 | this.column = column; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "ShardRule [tableName=" + tableName + ", column=" + column + "]"; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/TableRule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | import java.io.Serializable; 31 | 32 | public class TableRule implements Serializable { 33 | 34 | private static final long serialVersionUID = -4371474720936040919L; 35 | 36 | public static final String NAME_ALIAS = "name"; 37 | public static final String CLASS_ALIAS = "class"; 38 | public static final String DEFAULT_NODE_ALIAS = "defaultNode"; 39 | public static final String ROUTE_COLUMN_ALIAS = "routeColumn"; 40 | public static final String MAPFILE_ALIAS = "mapFile"; 41 | public static final String NODES_COUNT_ALIAS = "nodesCount"; 42 | public static final String PARTITION_COUNT_ALIAS = "partitionCount"; 43 | public static final String PARTITION_LENGTH_ALIAS = "partitionLength"; 44 | public static final String DATEFORMAT_ALIAS = "dateFormat"; 45 | public static final String SBEGINDATE_ALIAS = "sBeginDate"; 46 | public static final String SENDDATE_ALIAS = "sEndDate"; 47 | public static final String SPARTITIONDAY_ALIAS = "sPartionDay"; 48 | 49 | 50 | private String name; 51 | private String clazz; 52 | 53 | private String defaultNode; 54 | private String routeColumn; 55 | 56 | private String mapFile; 57 | 58 | private String nodesCount; 59 | 60 | private String partitionCount; 61 | private String partitionLength; 62 | 63 | private String dateFormat; 64 | private String sBeginDate; 65 | private String sEndDate; 66 | private String sPartionDay; 67 | 68 | public String getName() { 69 | return name; 70 | } 71 | 72 | public void setName(String name) { 73 | this.name = name; 74 | } 75 | 76 | public String getClazz() { 77 | return clazz; 78 | } 79 | 80 | public void setClazz(String clazz) { 81 | this.clazz = clazz; 82 | } 83 | 84 | public String getDefaultNode() { 85 | return defaultNode; 86 | } 87 | 88 | public void setDefaultNode(String defaultNode) { 89 | this.defaultNode = defaultNode; 90 | } 91 | 92 | public String getRouteColumn() { 93 | return routeColumn; 94 | } 95 | 96 | public void setRouteColumn(String routeColumn) { 97 | this.routeColumn = routeColumn; 98 | } 99 | 100 | public String getMapFile() { 101 | return mapFile; 102 | } 103 | 104 | public void setMapFile(String mapFile) { 105 | this.mapFile = mapFile; 106 | } 107 | 108 | public String getNodesCount() { 109 | return nodesCount; 110 | } 111 | 112 | public void setNodesCount(String nodesCount) { 113 | this.nodesCount = nodesCount; 114 | } 115 | 116 | public String getPartitionCount() { 117 | return partitionCount; 118 | } 119 | 120 | public void setPartitionCount(String partitionCount) { 121 | this.partitionCount = partitionCount; 122 | } 123 | 124 | public String getPartitionLength() { 125 | return partitionLength; 126 | } 127 | 128 | public void setPartitionLength(String partitionLength) { 129 | this.partitionLength = partitionLength; 130 | } 131 | 132 | public String getDateFormat() { 133 | return dateFormat; 134 | } 135 | 136 | public void setDateFormat(String dateFormat) { 137 | this.dateFormat = dateFormat; 138 | } 139 | 140 | public String getsBeginDate() { 141 | return sBeginDate; 142 | } 143 | 144 | public void setsBeginDate(String sBeginDate) { 145 | this.sBeginDate = sBeginDate; 146 | } 147 | 148 | public String getsEndDate() { 149 | return sEndDate; 150 | } 151 | 152 | public void setsEndDate(String sEndDate) { 153 | this.sEndDate = sEndDate; 154 | } 155 | 156 | public String getsPartionDay() { 157 | return sPartionDay; 158 | } 159 | 160 | public void setsPartionDay(String sPartionDay) { 161 | this.sPartionDay = sPartionDay; 162 | } 163 | 164 | @Override 165 | public String toString() { 166 | return "TableRule [name=" + name + ", clazz=" + clazz + ", defaultNode=" + defaultNode + ", routeColumn=" 167 | + routeColumn + ", mapFile=" + mapFile + ", nodesCount=" + nodesCount + ", partitionCount=" 168 | + partitionCount + ", partitionLength=" + partitionLength + ", dateFormat=" + dateFormat 169 | + ", sBeginDate=" + sBeginDate + ", sEndDate=" + sEndDate + ", sPartionDay=" + sPartionDay + "]"; 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/XMLDataNodesLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | import java.io.InputStream; 31 | import java.util.ArrayList; 32 | import java.util.HashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Properties; 36 | import java.util.concurrent.ConcurrentHashMap; 37 | 38 | import javax.sql.DataSource; 39 | 40 | import org.slf4j.Logger; 41 | import org.slf4j.LoggerFactory; 42 | import org.w3c.dom.Document; 43 | import org.w3c.dom.Element; 44 | import org.w3c.dom.Node; 45 | import org.w3c.dom.NodeList; 46 | 47 | import com.alibaba.druid.pool.DruidDataSourceFactory; 48 | 49 | 50 | public class XMLDataNodesLoader { 51 | 52 | private static final Logger log = LoggerFactory.getLogger(XMLDataNodesLoader.class); 53 | 54 | private final static String DataNodesDTD = "/datanodes.dtd"; 55 | private final static String DataNodesXML = "/datanodes.xml"; 56 | 57 | /** 58 | * dataNodes的数据源,以key-value存储,其中key为XML文件中dataNode的name属性,value为DataSourceNode对象。DataSourceNode对应XML文件中的dataNode节点,数据源以List保存。 59 | */ 60 | private final Map dataNodesMap = new ConcurrentHashMap(); 61 | /** 62 | * 所有数据源(包含读、写数据源),均以key-value的形式存储,其中key为XML文件中dataSource的name属性,value为转换后的DataSource对象 63 | */ 64 | private final Map dataSources = new ConcurrentHashMap(); 65 | 66 | /** 67 | * 数据库的全局配置 68 | */ 69 | private DbGlobal dbGlobal = new DbGlobal(); 70 | 71 | /** 72 | * 全局的默认写节点 73 | */ 74 | private DataSource defaultWriteDataNode; 75 | 76 | /** 77 | * 全局的默认写节点的key名称 78 | */ 79 | private String defaultWriteDataNodeName; 80 | 81 | 82 | public XMLDataNodesLoader() { 83 | super(); 84 | loadDataNodesConfig(); 85 | } 86 | 87 | public Map getDataNodesMap() { 88 | return dataNodesMap; 89 | } 90 | 91 | public Map getDataSources() { 92 | return dataSources; 93 | } 94 | 95 | public DbGlobal getDbGlobal() { 96 | return dbGlobal; 97 | } 98 | 99 | public DataSource getDefaultWriteDataNode() { 100 | return defaultWriteDataNode; 101 | } 102 | 103 | public String getDefaultWriteDataNodeName() { 104 | return defaultWriteDataNodeName; 105 | } 106 | 107 | private void loadDataNodesConfig() { 108 | Element root = null; 109 | try { 110 | InputStream isdtd = XMLDataNodesLoader.class.getResourceAsStream(DataNodesDTD); 111 | InputStream isxml = XMLDataNodesLoader.class.getResourceAsStream(DataNodesXML); 112 | Document xmldoc = XMLParserUtils.getDocument(isdtd, isxml); 113 | root = xmldoc.getDocumentElement(); 114 | //DataSource Configuration 115 | Map ds = parseDataSourceCfg(root); 116 | 117 | //dataNode Configuration 118 | List dataNodeCfgs = parseDataNodeCfg(root); 119 | 120 | //DB Global Configuration 121 | dbGlobal = parseDbGlobalCfg(root); 122 | 123 | Map dataSourceMap = initDataSources(ds); 124 | initDataNodes(dataSourceMap, dataNodeCfgs); 125 | 126 | } catch (Exception e) { 127 | e.printStackTrace(); 128 | } 129 | } 130 | 131 | private Map initDataSources(Map dataSourceCfgs) throws Exception{ 132 | Map dataSourceMap = new HashMap(); 133 | for (Map.Entry entry : dataSourceCfgs.entrySet()) { 134 | Properties p = new Properties(); 135 | p.setProperty("driverClassName", "com.mysql.jdbc.Driver"); 136 | p.setProperty("url", entry.getValue().getUrl()); 137 | p.setProperty("username", entry.getValue().getUserName()); 138 | p.setProperty("password", entry.getValue().getPassword()); 139 | 140 | p.setProperty("filters", "stat"); 141 | p.setProperty("maxActive", "200"); 142 | p.setProperty("initialSize", "30"); 143 | p.setProperty("maxWait", "60000"); 144 | p.setProperty("minIdle","1"); 145 | p.setProperty("timeBetweenEvictionRunsMillis","60000"); 146 | p.setProperty("minEvictableIdleTimeMillis","300000"); 147 | p.setProperty("validationQuery","SELECT 'x'"); 148 | p.setProperty("testWhileIdle","true"); 149 | p.setProperty("testOnBorrow","false"); 150 | p.setProperty("testOnReturn","false"); 151 | 152 | // DataSource ds = BasicDataSourceFactory.createDataSource(p); //dhcp 153 | DataSource ds = DruidDataSourceFactory.createDataSource(p); //druid 154 | 155 | dataSourceMap.put(entry.getValue().getName(), ds); 156 | //log.debug(">>> key=" + entry.getKey() + ", value=" + entry.getValue().getUrl()); 157 | } 158 | return dataSourceMap; 159 | } 160 | 161 | private void initDataNodes(Map dataSourceMap, List dataNodeCfgs) { 162 | for (DataNode cfg : dataNodeCfgs) { 163 | List writeNodesDsNameList = new ArrayList(); 164 | List readNodesDsNameList = new ArrayList(); 165 | String nodeName = cfg.getNodeName(); 166 | 167 | List writeNodesStr = split(cfg.getWriteNodes(), ","); 168 | for (String str : writeNodesStr) { 169 | if (dataSourceMap.get(str) != null) { 170 | writeNodesDsNameList.add(str); 171 | dataSources.put(str, dataSourceMap.get(str)); 172 | // if(cfg.isDefaultWriteNode()){ 173 | if(isGlobalNode(nodeName)){ 174 | defaultWriteDataNode = dataSourceMap.get(str); 175 | defaultWriteDataNodeName = str; 176 | } 177 | } 178 | } 179 | 180 | List readNodesStr = split(cfg.getReadNodes(), ","); 181 | for (String str : readNodesStr) { 182 | if (dataSourceMap.get(str) != null) { 183 | readNodesDsNameList.add(str); 184 | dataSources.put(str, dataSourceMap.get(str)); 185 | } 186 | } 187 | DataSourceNode dsNode = new DataSourceNode(writeNodesDsNameList, readNodesDsNameList); 188 | // if(cfg.isDefaultWriteNode()){ 189 | if(isGlobalNode(nodeName)){ 190 | dataNodesMap.put("default", dsNode); 191 | } 192 | dataNodesMap.put(cfg.getNodeName(), dsNode); 193 | } 194 | dataSourceMap = null; 195 | } 196 | 197 | private boolean isGlobalNode(String nodeName){ 198 | return (nodeName.equalsIgnoreCase("global") || nodeName.equalsIgnoreCase("default")); 199 | } 200 | 201 | /** 202 | * DataSource Configuration 203 | * @param root 204 | */ 205 | private Map parseDataSourceCfg(Element root){ 206 | Map dataSourcesMap = new ConcurrentHashMap(); 207 | NodeList nodeList = root.getElementsByTagName("dataSources").item(0).getChildNodes(); 208 | for (int i = 0; i < nodeList.getLength(); i++) { 209 | if (nodeList.item(i) == null || nodeList.item(i).getChildNodes().getLength() < 1) { 210 | continue; 211 | } 212 | DataNodeSource dscfg = new DataNodeSource(); 213 | 214 | String dsName = nodeList.item(i).getAttributes().getNamedItem("name").getNodeValue(); 215 | dscfg.setName(dsName); 216 | dataSourcesMap.put(dsName, dscfg); 217 | 218 | NodeList dsNodes = nodeList.item(i).getChildNodes(); 219 | for (int j = 0; j < dsNodes.getLength(); j++) { 220 | Node tmp = dsNodes.item(j); 221 | if (tmp == null) { 222 | continue; 223 | } 224 | if ("url".equals(tmp.getNodeName())) { 225 | dscfg.setUrl(tmp.getTextContent()); 226 | continue; 227 | } 228 | if ("userName".equals(tmp.getNodeName())) { 229 | dscfg.setUserName(tmp.getTextContent()); 230 | continue; 231 | } 232 | if ("password".equals(tmp.getNodeName())) { 233 | dscfg.setPassword(tmp.getTextContent()); 234 | continue; 235 | } 236 | } 237 | } 238 | return dataSourcesMap; 239 | } 240 | 241 | /** 242 | * DataNode configuration 243 | * 244 | * @param root 245 | */ 246 | private List parseDataNodeCfg(Element root){ 247 | List dataNodesList = new ArrayList(); 248 | NodeList dnList = root.getElementsByTagName("dataNodes").item(0).getChildNodes(); 249 | for (int i = 0; i < dnList.getLength(); i++) { 250 | if (dnList.item(i) == null || dnList.item(i).getChildNodes().getLength() < 1) { 251 | continue; 252 | } 253 | DataNode dataNodeCfg = new DataNode(); 254 | 255 | String nodeName = dnList.item(i).getAttributes().getNamedItem("name").getNodeValue(); 256 | dataNodeCfg.setNodeName(nodeName); 257 | 258 | /*try{ 259 | String isDefault = dnList.item(i).getAttributes().getNamedItem("default").getNodeValue(); 260 | dataNodeCfg.setDefaultWriteNode(Boolean.parseBoolean(isDefault)); 261 | }catch(NullPointerException ne){ 262 | dataNodeCfg.setDefaultWriteNode(false); 263 | }*/ 264 | 265 | NodeList dn = dnList.item(i).getChildNodes(); 266 | dataNodesList.add(dataNodeCfg); 267 | for (int k = 0; k < dn.getLength(); k++) { 268 | Node tmp = dn.item(k); 269 | if (tmp == null) { 270 | continue; 271 | } 272 | if ("writeNodes".equals(tmp.getNodeName())) { 273 | dataNodeCfg.setWriteNodes(tmp.getTextContent()); 274 | continue; 275 | } 276 | if ("readNodes".equals(tmp.getNodeName())) { 277 | dataNodeCfg.setReadNodes(tmp.getTextContent()); 278 | continue; 279 | } 280 | } 281 | } 282 | return dataNodesList; 283 | } 284 | 285 | /** 286 | * DB Global Configuration 287 | * @param root 288 | */ 289 | private DbGlobal parseDbGlobalCfg(Element root){ 290 | DbGlobal dbGlobalCfg = new DbGlobal(); 291 | NodeList dgnl = root.getElementsByTagName("global"); 292 | for (int i = 0; i < dgnl.getLength(); i++) { 293 | NodeList dsNodes = dgnl.item(i).getChildNodes(); 294 | for (int j = 0; j < dsNodes.getLength(); j++) { 295 | Node tmp = dsNodes.item(j); 296 | if (tmp == null) { 297 | continue; 298 | } 299 | // if ("dbNumber".equals(tmp.getNodeName())) { 300 | // dbGlobalCfg.setDbNumber(Integer.parseInt(tmp.getTextContent())); 301 | // continue; 302 | // } 303 | if ("tableNumber".equals(tmp.getNodeName())) { 304 | dbGlobalCfg.setTableNumber(Integer.parseInt(tmp.getTextContent())); 305 | continue; 306 | } 307 | if ("routeType".equals(tmp.getNodeName())) { 308 | dbGlobalCfg.setRouteType(Integer.parseInt(tmp.getTextContent())); 309 | continue; 310 | } 311 | // if ("ruleType".equals(tmp.getNodeName())) { 312 | // dbGlobalCfg.setRuleType(Integer.parseInt(tmp.getTextContent())); 313 | // continue; 314 | // } 315 | // if ("defaultWriteNode".equals(tmp.getNodeName())) { 316 | // dbGlobalCfg.setDefaultWriteNode(tmp.getTextContent()); 317 | // continue; 318 | // } 319 | if ("tableIndexStyle".equals(tmp.getNodeName())) { 320 | dbGlobalCfg.setTableIndexStyle(tmp.getTextContent()); 321 | continue; 322 | } 323 | } 324 | } 325 | return dbGlobalCfg; 326 | } 327 | 328 | private List split(String input, String s) { 329 | if (input == null) { 330 | return null; 331 | } 332 | List list = new ArrayList(); 333 | String[] tmp = input.split(s); 334 | for (String str : tmp) { 335 | if (str == null || str.matches("\\s*")) { 336 | continue; 337 | } 338 | list.add(str.trim()); 339 | } 340 | return list; 341 | } 342 | 343 | public static void main(String[] args) { 344 | new XMLDataNodesLoader(); 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/XMLLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | import java.util.Map; 31 | 32 | import javax.sql.DataSource; 33 | 34 | import org.apache.commons.lang.StringUtils; 35 | 36 | import io.isharing.springddal.route.rule.RuleAlgorithm; 37 | 38 | public class XMLLoader { 39 | 40 | // private static List dataNodes = null; 41 | private static Map dataNodesMap = null; 42 | private static Map dataSources = null; 43 | private static DbGlobal dbGlobal = null; 44 | private static DataSource defaultWriteDataNode; 45 | private static String defaultWriteDataNodeName; 46 | 47 | private static Map tableRules = null; 48 | private static Map partitionAlgorithm = null; 49 | 50 | private volatile static XMLDataNodesLoader ds; 51 | private volatile static XMLTableRulesLoader rl; 52 | 53 | private static XMLDataNodesLoader getXMLDataNodesLoader() { 54 | if (ds == null) { 55 | synchronized (XMLDataNodesLoader.class) { 56 | if (ds == null) { 57 | ds = new XMLDataNodesLoader(); 58 | } 59 | } 60 | } 61 | return ds; 62 | } 63 | 64 | private static XMLTableRulesLoader getXMLTableRulesLoader() { 65 | if (rl == null) { 66 | synchronized (XMLTableRulesLoader.class) { 67 | if (rl == null) { 68 | rl = new XMLTableRulesLoader(); 69 | } 70 | } 71 | } 72 | return rl; 73 | } 74 | 75 | // public static List getDataNodes() { 76 | // if(dataNodes == null) { 77 | // dataNodes = getXMLDataNodesLoader().getDataNodes(); 78 | // } 79 | // return dataNodes; 80 | // } 81 | 82 | public static Map getDataNodesMap() { 83 | if(dataNodesMap == null) { 84 | dataNodesMap = getXMLDataNodesLoader().getDataNodesMap(); 85 | } 86 | return dataNodesMap; 87 | } 88 | 89 | public static Map getDataSources() { 90 | if(dataSources == null) { 91 | dataSources = getXMLDataNodesLoader().getDataSources(); 92 | } 93 | return dataSources; 94 | } 95 | 96 | public static DataSource getDefaultWriteDataNode() { 97 | if(defaultWriteDataNode == null) { 98 | defaultWriteDataNode = getXMLDataNodesLoader().getDefaultWriteDataNode(); 99 | } 100 | return defaultWriteDataNode; 101 | } 102 | 103 | public static String getDefaultWriteDataNodeName() { 104 | if(StringUtils.isBlank(defaultWriteDataNodeName)){ 105 | defaultWriteDataNodeName = getXMLDataNodesLoader().getDefaultWriteDataNodeName(); 106 | } 107 | return defaultWriteDataNodeName; 108 | } 109 | 110 | public static DbGlobal getDbGlobal() { 111 | if(dbGlobal == null) { 112 | dbGlobal = getXMLDataNodesLoader().getDbGlobal(); 113 | } 114 | return dbGlobal; 115 | } 116 | 117 | public static Map getTableRules() { 118 | if(tableRules == null) { 119 | tableRules = getXMLTableRulesLoader().getTableRules(); 120 | } 121 | return tableRules; 122 | } 123 | 124 | public static Map getPartitionAlgorithm() { 125 | if(partitionAlgorithm == null) { 126 | partitionAlgorithm = getXMLTableRulesLoader().getPartitionAlgorithm(); 127 | } 128 | return partitionAlgorithm; 129 | } 130 | 131 | public static TableRule getTableRuleByRuleName(String ruleName){ 132 | if(StringUtils.isBlank(ruleName)){ 133 | return null; 134 | } 135 | Map tableRuleMap = getTableRules(); 136 | TableRule tableRule = tableRuleMap.get(ruleName); 137 | return tableRule; 138 | } 139 | 140 | public static void main(String[] args) { 141 | // XMLLoader xml = new XMLLoader(); 142 | // System.out.println(XMLLoader.getDataNodes()); 143 | // System.out.println(XMLLoader.getDataNodes()); 144 | Map m = XMLLoader.getDataNodesMap(); 145 | for (Map.Entry entry : m.entrySet()) { 146 | DataSourceNode dsn = entry.getValue(); 147 | System.out.println(entry.getKey() + "--->" + dsn.getWriteNodesNameList() +", "+dsn.getReadNodesNameList()); 148 | } 149 | // System.out.println(XMLLoader.getDataNodesMap()); 150 | // System.out.println(XMLLoader.getDataSources()); 151 | // System.out.println(XMLLoader.getDbGlobal()); 152 | // System.out.println(XMLLoader.getTableRules()); 153 | // System.out.println(XMLLoader.getDefaultWriteDataNodeName()); 154 | // System.out.println(XMLLoader.getDefaultWriteDataNode()); 155 | 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/XMLParserUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | import java.io.IOException; 31 | import java.io.InputStream; 32 | import java.util.HashMap; 33 | import java.util.Map; 34 | 35 | import javax.xml.parsers.DocumentBuilder; 36 | import javax.xml.parsers.DocumentBuilderFactory; 37 | import javax.xml.parsers.ParserConfigurationException; 38 | 39 | import org.apache.commons.lang.StringUtils; 40 | import org.w3c.dom.Attr; 41 | import org.w3c.dom.Document; 42 | import org.w3c.dom.Element; 43 | import org.w3c.dom.NamedNodeMap; 44 | import org.w3c.dom.Node; 45 | import org.w3c.dom.NodeList; 46 | import org.xml.sax.EntityResolver; 47 | import org.xml.sax.ErrorHandler; 48 | import org.xml.sax.InputSource; 49 | import org.xml.sax.SAXException; 50 | import org.xml.sax.SAXParseException; 51 | 52 | import io.isharing.springddal.route.exception.ConfigurationException; 53 | 54 | public class XMLParserUtils { 55 | 56 | public static Document getDocument(final InputStream dtd, InputStream xml) 57 | throws ParserConfigurationException, SAXException, IOException { 58 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 59 | factory.setValidating(true); 60 | factory.setNamespaceAware(false); 61 | DocumentBuilder builder = factory.newDocumentBuilder(); 62 | builder.setEntityResolver(new EntityResolver() { 63 | @Override 64 | public InputSource resolveEntity(String publicId, String systemId) { 65 | return new InputSource(dtd); 66 | } 67 | }); 68 | builder.setErrorHandler(new ErrorHandler() { 69 | @Override 70 | public void warning(SAXParseException e) { 71 | } 72 | 73 | @Override 74 | public void error(SAXParseException e) throws SAXException { 75 | throw e; 76 | } 77 | 78 | @Override 79 | public void fatalError(SAXParseException e) throws SAXException { 80 | throw e; 81 | } 82 | }); 83 | return builder.parse(xml); 84 | } 85 | 86 | public static Map loadAttributes(Element e) { 87 | Map map = new HashMap(); 88 | NamedNodeMap nm = e.getAttributes(); 89 | for (int j = 0; j < nm.getLength(); j++) { 90 | Node n = nm.item(j); 91 | if (n instanceof Attr) { 92 | Attr attr = (Attr) n; 93 | map.put(attr.getName(), attr.getNodeValue()); 94 | } 95 | } 96 | return map; 97 | } 98 | 99 | public static Element loadElement(Element parent, String tagName) { 100 | NodeList nodeList = parent.getElementsByTagName(tagName); 101 | if (nodeList.getLength() > 1) { 102 | throw new ConfigurationException(tagName + " elements length over one!"); 103 | } 104 | if (nodeList.getLength() == 1) { 105 | return (Element) nodeList.item(0); 106 | } else { 107 | return null; 108 | } 109 | } 110 | 111 | /** 112 | * 获取节点下所有property 113 | * 114 | * @param parent 115 | * @return key-value property键值对 116 | */ 117 | public static Map loadElements(Element parent) { 118 | Map map = new HashMap(); 119 | NodeList children = parent.getChildNodes(); 120 | for (int i = 0; i < children.getLength(); i++) { 121 | Node node = children.item(i); 122 | if (node instanceof Element) { 123 | Element e = (Element) node; 124 | String name = e.getNodeName(); 125 | // 获取property 126 | if ("property".equals(name)) { 127 | String key = e.getAttribute("name"); 128 | String value = e.getTextContent(); 129 | map.put(key, StringUtils.isEmpty(value) ? null : value.trim()); 130 | } 131 | } 132 | } 133 | return map; 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/conf/XMLTableRulesLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.conf; 29 | 30 | import java.io.InputStream; 31 | import java.lang.reflect.InvocationTargetException; 32 | import java.util.Map; 33 | import java.util.concurrent.ConcurrentHashMap; 34 | 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | import org.w3c.dom.Document; 38 | import org.w3c.dom.Element; 39 | import org.w3c.dom.Node; 40 | import org.w3c.dom.NodeList; 41 | 42 | import io.isharing.springddal.route.exception.ConfigurationException; 43 | import io.isharing.springddal.route.rule.RuleAlgorithm; 44 | import io.isharing.springddal.route.rule.function.AbstractPartitionAlgorithm; 45 | import io.isharing.springddal.route.rule.utils.ParameterMapping; 46 | 47 | public class XMLTableRulesLoader { 48 | 49 | private static final Logger log = LoggerFactory.getLogger(XMLTableRulesLoader.class); 50 | 51 | private final static String TableRulesDTD = "/rules.dtd"; 52 | private final static String TableRulesXML = "/rules.xml"; 53 | private final Map tableRules = new ConcurrentHashMap(); 54 | private final Map partitionAlgorithm = new ConcurrentHashMap(); 55 | 56 | public XMLTableRulesLoader() { 57 | super(); 58 | loadTableRules(); 59 | } 60 | 61 | public Map getTableRules() { 62 | return tableRules; 63 | } 64 | 65 | public Map getPartitionAlgorithm() { 66 | return partitionAlgorithm; 67 | } 68 | 69 | private void loadTableRules() { 70 | Element root = null; 71 | try { 72 | InputStream isdtd = XMLDataNodesLoader.class.getResourceAsStream(TableRulesDTD); 73 | InputStream isxml = XMLDataNodesLoader.class.getResourceAsStream(TableRulesXML); 74 | Document xmldoc = XMLParserUtils.getDocument(isdtd, isxml); 75 | root = xmldoc.getDocumentElement(); 76 | parseTableRulsXML(root); 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | 82 | private void parseTableRulsXML(Element root) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException { 83 | TableRule tableRule = null; 84 | NodeList list = root.getElementsByTagName("tableRules").item(0).getChildNodes(); 85 | for (int i = 0, n = list.getLength(); i < n; ++i) { 86 | Node node = list.item(i); 87 | if (node instanceof Element) { 88 | Element e = (Element) node; 89 | // 获取name标签 90 | String name = e.getAttribute("name"); 91 | // 如果Map已有,则function重复 92 | if (partitionAlgorithm.containsKey(name)) { 93 | throw new ConfigurationException("rule function " + name + " duplicated!"); 94 | } 95 | // 获取class标签 96 | String clazz = e.getAttribute("class"); 97 | // System.out.println(">>> "+name+", "+clazz); 98 | 99 | tableRule = new TableRule(); 100 | tableRule.setName(name); 101 | tableRule.setClazz(clazz); 102 | 103 | Map props = XMLParserUtils.loadElements(e); 104 | tableRule = initTables(props, tableRule); 105 | 106 | // 根据class利用反射新建分片算法 107 | AbstractPartitionAlgorithm algorithm = createPartitionAlgorithm(name, clazz); 108 | // 根据读取参数配置分片算法 109 | ParameterMapping.mapping(algorithm, props); 110 | // 每个AbstractPartitionAlgorithm可能会实现init来初始化 111 | algorithm.init(); 112 | // 放入functions map 113 | partitionAlgorithm.put(name, algorithm); 114 | tableRules.put(name, tableRule); 115 | } 116 | } 117 | // System.out.println("======================"); 118 | // for (Map.Entry entry : tableRules.entrySet()) { 119 | // System.out.println(entry.getKey()+", "+entry.getValue()); 120 | // } 121 | // System.out.println("----------------------"); 122 | // for (Map.Entry entry : partitionAlgorithm.entrySet()) { 123 | // System.out.println(entry.getKey()+", "+entry.getValue().getPartitionNum()); 124 | // } 125 | } 126 | 127 | private AbstractPartitionAlgorithm createPartitionAlgorithm(String name, String clazz) 128 | throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException { 129 | Class clz = Class.forName(clazz); 130 | // 判断是否继承AbstractPartitionAlgorithm 131 | if (!AbstractPartitionAlgorithm.class.isAssignableFrom(clz)) { 132 | throw new IllegalArgumentException( 133 | "rule function must implements " + AbstractPartitionAlgorithm.class.getName() + ", name=" + name); 134 | } 135 | return (AbstractPartitionAlgorithm) clz.newInstance(); 136 | } 137 | 138 | /** 139 | * 根据配置文件初始化TableRule对象。这里也可以用Map替代,更具通用性,需要与XML节点各名称对应即可。 140 | * 141 | * @param props 142 | * @param tableRule 143 | * @return 144 | */ 145 | private TableRule initTables(Map props, TableRule tableRule){ 146 | for (Map.Entry entry : props.entrySet()) { 147 | String key = entry.getKey(); 148 | Object value = entry.getValue(); 149 | if(key.equals(TableRule.NAME_ALIAS)){ 150 | tableRule.setName(value.toString()); 151 | } 152 | if(key.equals(TableRule.CLASS_ALIAS)){ 153 | tableRule.setClazz(value.toString()); 154 | } 155 | if(key.equals(TableRule.DEFAULT_NODE_ALIAS)){ 156 | tableRule.setDefaultNode(value.toString()); 157 | } 158 | if(key.equals(TableRule.ROUTE_COLUMN_ALIAS)){ 159 | tableRule.setRouteColumn(value.toString()); 160 | } 161 | if(key.equals(TableRule.MAPFILE_ALIAS)){ 162 | tableRule.setMapFile(value.toString()); 163 | } 164 | if(key.equals(TableRule.NODES_COUNT_ALIAS)){ 165 | tableRule.setNodesCount(value.toString()); 166 | } 167 | if(key.equals(TableRule.PARTITION_COUNT_ALIAS)){ 168 | tableRule.setPartitionCount(value.toString()); 169 | } 170 | if(key.equals(TableRule.PARTITION_LENGTH_ALIAS)){ 171 | tableRule.setPartitionLength(value.toString()); 172 | } 173 | if(key.equals(TableRule.DATEFORMAT_ALIAS)){ 174 | tableRule.setDateFormat(value.toString()); 175 | } 176 | if(key.equals(TableRule.SBEGINDATE_ALIAS)){ 177 | tableRule.setsBeginDate(value.toString()); 178 | } 179 | if(key.equals(TableRule.SENDDATE_ALIAS)){ 180 | tableRule.setsEndDate(value.toString()); 181 | } 182 | if(key.equals(TableRule.SPARTITIONDAY_ALIAS)){ 183 | tableRule.setsPartionDay(value.toString()); 184 | } 185 | } 186 | return tableRule; 187 | } 188 | 189 | public static void main(String[] args) { 190 | new XMLTableRulesLoader(); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/function/AbstractPartitionAlgorithm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.function; 29 | 30 | import java.io.Serializable; 31 | 32 | import io.isharing.springddal.route.rule.RuleAlgorithm; 33 | 34 | 35 | /** 36 | * 路由分片函数抽象类 37 | * 为了实现一个默认的支持范围分片的函数 calcualteRange 38 | * 重写它以实现自己的范围路由规则 39 | * 40 | */ 41 | public abstract class AbstractPartitionAlgorithm implements RuleAlgorithm ,Serializable { 42 | 43 | private static final long serialVersionUID = 1775487253161848486L; 44 | 45 | @Override 46 | public void init() { 47 | } 48 | 49 | /** 50 | * 返回所有被路由到的节点的编号 51 | * 返回长度为0的数组表示所有节点都被路由(默认) 52 | * 返回null表示没有节点被路由到 53 | */ 54 | @Override 55 | public Integer[] calculateRange(String beginValue, String endValue) { 56 | return new Integer[0]; 57 | } 58 | 59 | /** 60 | * 对于存储数据按顺序存放的字段做范围路由,可以使用这个函数 61 | * @param algorithm 62 | * @param beginValue 63 | * @param endValue 64 | * @return 65 | */ 66 | public static Integer[] calculateSequenceRange(AbstractPartitionAlgorithm algorithm, String beginValue, String endValue) { 67 | Integer begin = 0, end = 0; 68 | begin = algorithm.calculate(beginValue); 69 | end = algorithm.calculate(endValue); 70 | 71 | if(begin == null || end == null){ 72 | return new Integer[0]; 73 | } 74 | 75 | if (end >= begin) { 76 | int len = end-begin+1; 77 | Integer [] re = new Integer[len]; 78 | for(int i =0;i. 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.function; 29 | 30 | import java.io.BufferedReader; 31 | import java.io.InputStream; 32 | import java.io.InputStreamReader; 33 | import java.util.HashSet; 34 | import java.util.LinkedList; 35 | import java.util.Set; 36 | import java.util.regex.Matcher; 37 | import java.util.regex.Pattern; 38 | 39 | import org.apache.commons.lang.StringUtils; 40 | 41 | import io.isharing.springddal.route.rule.RuleAlgorithm; 42 | 43 | /** 44 | * 按Long值范围拆分,范围值可在mapFile(autopartition-long.txt)中定义。 45 | */ 46 | public class AutoPartitionByLong extends AbstractPartitionAlgorithm implements RuleAlgorithm { 47 | 48 | private String mapFile; 49 | private LongRange[] longRongs; 50 | private String defaultNode; 51 | 52 | private int _partDefaultNode = -1; 53 | 54 | @Override 55 | public void init() { 56 | initialize(); 57 | } 58 | 59 | public void setMapFile(String mapFile) { 60 | this.mapFile = mapFile; 61 | } 62 | 63 | @Override 64 | public Integer calculate(String columnValue) { 65 | // columnValue = NumberParseUtil.eliminateQoute(columnValue); 66 | try { 67 | long value = Long.parseLong(columnValue); 68 | Integer rst = null; 69 | for (LongRange longRang : this.longRongs) { 70 | if (value <= longRang.valueEnd && value >= longRang.valueStart) { 71 | return longRang.nodeIndx; 72 | } 73 | } 74 | // 数据超过范围,暂时使用配置的默认节点 75 | if (rst == null && _partDefaultNode >= 0) { 76 | return _partDefaultNode; 77 | } 78 | return rst; 79 | } catch (NumberFormatException e) { 80 | throw new IllegalArgumentException( 81 | new StringBuilder() 82 | .append("columnValue:") 83 | .append(columnValue) 84 | .append(" Please eliminate any quote and non number within it.") 85 | .toString(), e); 86 | } 87 | } 88 | 89 | @Override 90 | public Integer[] calculateRange(String beginValue, String endValue) { 91 | return AbstractPartitionAlgorithm.calculateSequenceRange(this, beginValue, endValue); 92 | } 93 | 94 | @Override 95 | public int getPartitionNum() { 96 | // int nPartition = longRongs.length; 97 | 98 | /* 99 | * fix #1284 这里的统计应该统计Range的nodeIndex的distinct总数 100 | */ 101 | Set distNodeIdxSet = new HashSet(); 102 | for (LongRange range : longRongs) { 103 | distNodeIdxSet.add(range.nodeIndx); 104 | } 105 | int nPartition = distNodeIdxSet.size(); 106 | return nPartition; 107 | } 108 | 109 | private void initialize() { 110 | BufferedReader in = null; 111 | try { 112 | if(StringUtils.isBlank(mapFile)) { 113 | setMapFile("autopartition-long.txt"); 114 | } 115 | InputStream fin = this.getClass().getClassLoader().getResourceAsStream(mapFile); 116 | if (fin == null) { 117 | throw new RuntimeException("can't find class resource file " + mapFile); 118 | } 119 | in = new BufferedReader(new InputStreamReader(fin)); 120 | LinkedList longRangeList = new LinkedList(); 121 | 122 | for (String line = null; (line = in.readLine()) != null;) { 123 | line = line.trim(); 124 | if (line.startsWith("#") || line.startsWith("//")) { 125 | continue; 126 | } 127 | int ind = line.indexOf('='); 128 | if (ind < 0) { 129 | System.out.println(" warn: bad line int " + mapFile + " :" + line); 130 | continue; 131 | } 132 | String pairs[] = line.substring(0, ind).trim().split("-"); 133 | long longStart = NumberParseUtil.parseLong(pairs[0].trim()); 134 | long longEnd = NumberParseUtil.parseLong(pairs[1].trim()); 135 | int nodeId = Integer.parseInt(line.substring(ind + 1).trim()); 136 | longRangeList.add(new LongRange(nodeId, longStart, longEnd)); 137 | } 138 | longRongs = longRangeList.toArray(new LongRange[longRangeList.size()]); 139 | _partDefaultNode = getDataNodeNoFromDNName(defaultNode); 140 | } catch (Exception e) { 141 | if (e instanceof RuntimeException) { 142 | throw (RuntimeException) e; 143 | } else { 144 | throw new RuntimeException(e); 145 | } 146 | 147 | } finally { 148 | try { 149 | in.close(); 150 | } catch (Exception e2) { 151 | } 152 | } 153 | } 154 | 155 | private static int getDataNodeNoFromDNName(String dnName){ 156 | String regEx="[^0-9]"; 157 | Pattern p = Pattern.compile(regEx); 158 | Matcher m = p.matcher(dnName); 159 | String result = m.replaceAll("").trim(); 160 | return Integer.parseInt(result); 161 | } 162 | 163 | public String getDefaultNode() { 164 | return defaultNode; 165 | } 166 | 167 | public void setDefaultNode(String defaultNode) { 168 | this.defaultNode = defaultNode; 169 | } 170 | 171 | public int get_partDefaultNode() { 172 | return _partDefaultNode; 173 | } 174 | 175 | public void set_partDefaultNode(int _partDefaultNode) { 176 | this._partDefaultNode = _partDefaultNode; 177 | } 178 | 179 | static class LongRange { 180 | public final int nodeIndx; 181 | public final long valueStart; 182 | public final long valueEnd; 183 | 184 | public LongRange(int nodeIndx, long valueStart, long valueEnd) { 185 | super(); 186 | this.nodeIndx = nodeIndx; 187 | this.valueStart = valueStart; 188 | this.valueEnd = valueEnd; 189 | } 190 | } 191 | 192 | public static void main(String[] args) { 193 | AutoPartitionByLong apl = new AutoPartitionByLong(); 194 | // apl.setMapFile("autopartition-long.txt"); 195 | apl.setDefaultNode("dn1"); 196 | apl.init(); 197 | int result = apl.calculate("20"); 198 | System.out.println(">>> Total Partition: "+apl.getPartitionNum()); 199 | System.out.println(">>> Current Partition: "+result); 200 | System.out.println(">>> Partition Info:"); 201 | Integer[] range = apl.calculateRange("1", "20"); 202 | for(Integer i : range){ 203 | System.out.println("\t> Partition " + i); 204 | } 205 | } 206 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/function/NumberParseUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.function; 29 | 30 | public class NumberParseUtil { 31 | /** 32 | * 只去除开头结尾的引号,而且是结对去除,语法不对的话通不过 33 | * @param number 34 | * @return 35 | */ 36 | public static String eliminateQoute(String number){ 37 | number = number.trim(); 38 | if(number.contains("\"")){ 39 | if(number.charAt(0)=='\"'){ 40 | number = number.substring(1); 41 | if(number.charAt(number.length()-1)=='\"'){ 42 | number = number.substring(0,number.length()-1); 43 | } 44 | } 45 | }else if(number.contains("\'")){ 46 | if(number.charAt(0)=='\''){ 47 | number = number.substring(1); 48 | if(number.charAt(number.length()-1)=='\''){ 49 | number = number.substring(0,number.length()-1); 50 | } 51 | } 52 | } 53 | return number; 54 | } 55 | 56 | /** 57 | * can parse values like 200M ,200K,200M1(2000001) 58 | * 59 | * @param val 60 | * @return 61 | */ 62 | public static long parseLong(String val) { 63 | val = val.toUpperCase(); 64 | int indx = val.indexOf("M"); 65 | 66 | int plus = 10000; 67 | if (indx < 0) { 68 | indx = val.indexOf("K"); 69 | plus = 1000; 70 | } 71 | if (indx > 0) { 72 | String longVal = val.substring(0, indx); 73 | 74 | long theVale = Long.parseLong(longVal) * plus; 75 | String remain = val.substring(indx + 1); 76 | if (remain.length() > 0) { 77 | theVale += Integer.parseInt(remain); 78 | } 79 | return theVale; 80 | } else { 81 | return Long.parseLong(val); 82 | } 83 | 84 | } 85 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/function/PartitionByDate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.function; 29 | 30 | import java.text.ParseException; 31 | import java.text.SimpleDateFormat; 32 | import java.util.ArrayList; 33 | import java.util.Calendar; 34 | import java.util.Collections; 35 | import java.util.Date; 36 | import java.util.List; 37 | 38 | import org.slf4j.Logger; 39 | import org.slf4j.LoggerFactory; 40 | 41 | import io.isharing.springddal.route.rule.RuleAlgorithm; 42 | 43 | /** 44 | * 例子 按日期列分区 格式 between操作解析的范例 45 | * 功能说明:按开始时间sBeginDate到结束时间sEndDate范围内,每sPartionDay天做一个分片。 46 | */ 47 | public class PartitionByDate extends AbstractPartitionAlgorithm implements RuleAlgorithm { 48 | 49 | private static final long serialVersionUID = -4646042281671936362L; 50 | private static final Logger LOGGER = LoggerFactory.getLogger(PartitionByDate.class); 51 | 52 | /** 53 | * 开始日期 54 | */ 55 | private String sBeginDate; 56 | /** 57 | * 结束日期, 如果配置了 sEndDate 则代表数据达到了这个日期的分片后后循环从开始分片插入; 如果没配置则按每sPartionDay天继续切分。 58 | */ 59 | private String sEndDate; 60 | /** 61 | * 每分片天数, 即默认从开始日期算起,分隔 x 天一个分区 62 | */ 63 | private String sPartionDay; 64 | /** 65 | * 日期格式 66 | */ 67 | private String dateFormat; 68 | 69 | private long beginDate; 70 | private long partionTime; 71 | private long endDate; 72 | private int nCount; 73 | 74 | private ThreadLocal formatter; 75 | 76 | private static final long oneDay = 86400000; 77 | 78 | @Override 79 | public void init() { 80 | try { 81 | partionTime = Integer.parseInt(sPartionDay) * oneDay; 82 | 83 | beginDate = new SimpleDateFormat(dateFormat).parse(sBeginDate).getTime(); 84 | 85 | if(sEndDate!=null&&!sEndDate.equals("")){ 86 | endDate = new SimpleDateFormat(dateFormat).parse(sEndDate).getTime(); 87 | nCount = (int) ((endDate - beginDate) / partionTime) + 1; 88 | } 89 | formatter = new ThreadLocal() { 90 | @Override 91 | protected SimpleDateFormat initialValue() { 92 | return new SimpleDateFormat(dateFormat); 93 | } 94 | }; 95 | } catch (ParseException e) { 96 | throw new java.lang.IllegalArgumentException(e); 97 | } 98 | } 99 | 100 | @Override 101 | public Integer calculate(String columnValue) { 102 | try { 103 | long targetTime = formatter.get().parse(columnValue).getTime(); 104 | int targetPartition = (int) ((targetTime - beginDate) / partionTime); 105 | if(targetTime>endDate && nCount!=0){ 106 | targetPartition = targetPartition%nCount; 107 | } 108 | return targetPartition; 109 | } catch (ParseException e) { 110 | throw new IllegalArgumentException( 111 | new StringBuilder() 112 | .append("columnValue:") 113 | .append(columnValue) 114 | .append(" Please check if the format satisfied.") 115 | .toString(),e); 116 | } 117 | } 118 | 119 | @Override 120 | public Integer[] calculateRange(String beginValue, String endValue) { 121 | SimpleDateFormat format = new SimpleDateFormat(this.dateFormat); 122 | try { 123 | Date beginDate = format.parse(beginValue); 124 | Date endDate = format.parse(endValue); 125 | Calendar cal = Calendar.getInstance(); 126 | List list = new ArrayList(); 127 | while(beginDate.getTime() <= endDate.getTime()){ 128 | Integer nodeValue = this.calculate(format.format(beginDate)); 129 | if(Collections.frequency(list, nodeValue) < 1) list.add(nodeValue); 130 | cal.setTime(beginDate); 131 | cal.add(Calendar.DATE, 1); 132 | beginDate = cal.getTime(); 133 | } 134 | Integer[] nodeArray = new Integer[list.size()]; 135 | for (int i=0;i 0 ? count : -1; 149 | } 150 | 151 | public void setsBeginDate(String sBeginDate) { 152 | this.sBeginDate = sBeginDate; 153 | } 154 | 155 | public void setsPartionDay(String sPartionDay) { 156 | this.sPartionDay = sPartionDay; 157 | } 158 | 159 | public void setDateFormat(String dateFormat) { 160 | this.dateFormat = dateFormat; 161 | } 162 | public String getsEndDate() { 163 | return this.sEndDate; 164 | } 165 | public void setsEndDate(String sEndDate) { 166 | this.sEndDate = sEndDate; 167 | } 168 | 169 | public static void main(String[] args) { 170 | String bd = "2017-08-01 13:33:12"; 171 | String ed = "2017-12-31 23:59:59"; 172 | PartitionByDate pd = new PartitionByDate(); 173 | pd.setsBeginDate(bd); 174 | pd.setsEndDate(ed); 175 | pd.setDateFormat("yyyy-MM-dd HH:mm:ss"); 176 | pd.setsPartionDay("30"); 177 | pd.init(); 178 | int result = pd.calculate("2018-08-31 13:33:12"); 179 | Integer[] range = pd.calculateRange(bd, ed); 180 | System.out.println(">>> Total Partition: "+pd.getPartitionNum()); 181 | System.out.println(">>> Current Partition: "+result); 182 | System.out.println(">>> Partition Info:"); 183 | for(Integer i : range){ 184 | System.out.println("\t> Partition " + i); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/function/PartitionByLong.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.function; 29 | 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | import com.isharing.commons.string.StringUtils; 34 | 35 | import io.isharing.springddal.route.rule.RuleAlgorithm; 36 | 37 | /** 38 | * partitionCount: 分片个数列表
39 | * partitionLength: 分片范围列表
40 | * 分区长度: 默认为最大2^n=1024 ,即最大支持1024分区
41 | * 约束 :
42 | * count,length两个数组的长度必须是一致的。
43 | * 1024 = sum((count[i]*length[i]))。
44 | * count和length两个向量的点积恒等于1024
45 | * 用法例子:
46 | * public void testPartitionByLong() {
47 | * // 本例的分区策略:希望将数据水平分成3份,前两份各占25%,第三份占50%。(故本例非均匀分区)
48 | * // |<---------------------1024------------------------>|
49 | * // |<----256--->|<----256--->|<----------512---------->|
50 | * // | partition0 | partition1 | partition2 |
51 | * // | 共2份,故count[0]=2 | 共1份,故count[1]=1 |
52 | * int[] count = new int[] { 2, 1 };
53 | * int[] length = new int[] { 256, 512 };
54 | * PartitionUtil pu = new PartitionUtil(count, length);

55 | * 56 | * // 下面代码演示分别以offerId字段或memberId字段根据上述分区策略拆分的分配结果
57 | * int DEFAULT_STR_HEAD_LEN = 8; // cobar默认会配置为此值
58 | * long offerId = 12345;
59 | * String memberId = "qiushuo";

60 | * 61 | * // 若根据offerId分配,partNo1将等于0,即按照上述分区策略,offerId为12345时将会被分配到partition0中
62 | * int partNo1 = pu.partition(offerId);

63 | * 64 | * // 若根据memberId分配,partNo2将等于2,即按照上述分区策略,memberId为qiushuo时将会被分到partition2中
65 | * int partNo2 = pu.partition(memberId, 0, DEFAULT_STR_HEAD_LEN);

66 | * 67 | * Assert.assertEquals(0, partNo1);
68 | * Assert.assertEquals(2, partNo2);
69 | * }
70 | */ 71 | public final class PartitionByLong extends AbstractPartitionAlgorithm implements RuleAlgorithm { 72 | 73 | private static final long serialVersionUID = 1043719201606373967L; 74 | private static final Logger LOGGER = LoggerFactory.getLogger(PartitionByLong.class); 75 | 76 | protected int[] count; 77 | protected int[] length; 78 | protected PartitionUtil partitionUtil; 79 | 80 | private static int[] toIntArray(String string) { 81 | String[] strs = StringUtils.split(string, ','); 82 | int[] ints = new int[strs.length]; 83 | for (int i = 0; i < strs.length; ++i) { 84 | ints[i] = Integer.parseInt(strs[i]); 85 | } 86 | return ints; 87 | } 88 | 89 | public void setPartitionCount(String partitionCount) { 90 | this.count = toIntArray(partitionCount); 91 | } 92 | 93 | public void setPartitionLength(String partitionLength) { 94 | this.length = toIntArray(partitionLength); 95 | } 96 | 97 | @Override 98 | public void init() { 99 | partitionUtil = new PartitionUtil(count, length); 100 | } 101 | 102 | @Override 103 | public Integer calculate(String columnValue) { 104 | // columnValue = NumberParseUtil.eliminateQoute(columnValue); 105 | try { 106 | long key = Long.parseLong(columnValue); 107 | return partitionUtil.partition(key); 108 | } catch (NumberFormatException e) { 109 | throw new IllegalArgumentException(new StringBuilder().append("columnValue:").append(columnValue) 110 | .append(" Please eliminate any quote and non number within it.").toString(), e); 111 | } 112 | } 113 | 114 | @Override 115 | public Integer[] calculateRange(String beginValue, String endValue) { 116 | return AbstractPartitionAlgorithm.calculateSequenceRange(this, beginValue, endValue); 117 | } 118 | 119 | // @Override 120 | // public int getPartitionCount() { 121 | // int nPartition = 0; 122 | // for(int i = 0; i < count.length; i++) { 123 | // nPartition += count[i]; 124 | // } 125 | // return nPartition; 126 | // } 127 | 128 | public static void main(String[] args) { 129 | PartitionByLong pl = new PartitionByLong(); 130 | pl.setPartitionLength("4"); 131 | pl.setPartitionCount("256"); 132 | pl.init(); 133 | int result = pl.calculate("128"); 134 | System.out.println(">>> Total Partition: "+pl.getPartitionNum()); 135 | System.out.println(">>> Current Partition: "+result); 136 | System.out.println(">>> Partition Info:"); 137 | Integer[] range = pl.calculateRange("1", "9"); 138 | for(Integer i : range){ 139 | System.out.println("\t> Partition " + i); 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/function/PartitionByMod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.function; 29 | 30 | import java.math.BigInteger; 31 | import java.util.ArrayList; 32 | import java.util.HashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | 36 | import io.isharing.springddal.route.rule.RuleAlgorithm; 37 | 38 | 39 | /** 40 | * 取模拆分。即根据columnValue与count(节点数)进行求模计算。 41 | * 比如count节点数为10,那么columnValue等于0时,拆分分区为0,columnValue等于21时,拆分分区位于1 (21 % 10 =1)。 42 | * 43 | */ 44 | public class PartitionByMod extends AbstractPartitionAlgorithm implements RuleAlgorithm { 45 | 46 | /** 47 | * 拆分的总节点数。 48 | */ 49 | private int count; 50 | 51 | @Override 52 | public void init() { 53 | 54 | } 55 | 56 | public void setCount(int count) { 57 | this.count = count; 58 | } 59 | 60 | @Override 61 | public Integer calculate(String columnValue) { 62 | // columnValue = NumberParseUtil.eliminateQoute(columnValue); 63 | try { 64 | BigInteger bigNum = new BigInteger(columnValue).abs(); 65 | return (bigNum.mod(BigInteger.valueOf(count))).intValue(); 66 | } catch (NumberFormatException e){ 67 | throw new IllegalArgumentException(new StringBuilder().append("columnValue:").append(columnValue).append(" Please eliminate any quote and non number within it.").toString(),e); 68 | } 69 | } 70 | 71 | @Override 72 | public int getPartitionNum() { 73 | int nPartition = this.count; 74 | return nPartition; 75 | } 76 | 77 | private static void hashTest() { 78 | PartitionByMod hash=new PartitionByMod(); 79 | hash.setCount(11); 80 | hash.init(); 81 | 82 | int[] bucket=new int[hash.count]; 83 | 84 | Map> hashed=new HashMap>(); 85 | 86 | int total=1000_0000;//数据量 87 | int c=0; 88 | for(int i=100_0000;i list=hashed.get(h); 93 | if(list==null){ 94 | list=new ArrayList<>(); 95 | hashed.put(h, list); 96 | } 97 | list.add(i); 98 | } 99 | System.out.println(c+" "+total); 100 | double d=0; 101 | c=0; 102 | int idx=0; 103 | System.out.println("index bucket ratio"); 104 | for(int i:bucket){ 105 | d+=i/(double)total; 106 | c+=i; 107 | System.out.println(idx+++" "+i+" "+(i/(double)total)); 108 | } 109 | System.out.println(d+" "+c); 110 | 111 | System.out.println("****************************************************"); 112 | rehashTest(hashed.get(0)); 113 | } 114 | 115 | private static void rehashTest(List partition) { 116 | PartitionByMod hash=new PartitionByMod(); 117 | hash.count=110;//分片数 118 | hash.init(); 119 | 120 | int[] bucket=new int[hash.count]; 121 | 122 | int total=partition.size();//数据量 123 | int c=0; 124 | for(int i:partition){//假设分片键从100万开始 125 | c++; 126 | int h=hash.calculate(Integer.toString(i)); 127 | bucket[h]++; 128 | } 129 | System.out.println(c+" "+total); 130 | c=0; 131 | int idx=0; 132 | System.out.println("index bucket ratio"); 133 | for(int i:bucket){ 134 | c+=i; 135 | System.out.println(idx+++" "+i+" "+(i/(double)total)); 136 | } 137 | } 138 | 139 | public static void main(String[] args) { 140 | // hashTest(); 141 | PartitionByMod partitionByMod = new PartitionByMod(); 142 | partitionByMod.count=8; 143 | // partitionByMod.calculate("\"6\""); 144 | // partitionByMod.calculate("\'6\'"); 145 | int result = partitionByMod.calculate("26"); 146 | System.out.println(result); 147 | } 148 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/function/PartitionByMonth.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.function; 29 | 30 | import java.text.ParseException; 31 | import java.text.SimpleDateFormat; 32 | import java.util.ArrayList; 33 | import java.util.Calendar; 34 | import java.util.Collections; 35 | import java.util.List; 36 | 37 | import org.apache.log4j.Logger; 38 | 39 | import io.isharing.springddal.route.rule.RuleAlgorithm; 40 | 41 | /** 42 | * 例子 按月份列分区 ,每个自然月一个分片,格式 between操作解析的范例 43 | * 每个月存入每个月单独的数据库中 44 | * 45 | */ 46 | public class PartitionByMonth extends AbstractPartitionAlgorithm implements RuleAlgorithm { 47 | 48 | private static final long serialVersionUID = 4208799852149537498L; 49 | private static final Logger LOGGER = Logger.getLogger(PartitionByDate.class); 50 | 51 | /** 52 | * 开始日期 53 | */ 54 | private String sBeginDate; 55 | /** 56 | * 结束日期, 如果配置了 sEndDate 则代表数据达到了这个日期的分片后后循环从开始分片插入; 57 | * 如果没配置则按每一年的月份继续切分(即如开始时间为2017年1月,传入的时间为2018年12月,则分区为23)。 58 | */ 59 | private String sEndDate; 60 | /** 61 | * 日期格式 62 | */ 63 | private String dateFormat; 64 | 65 | private Calendar beginDate; 66 | private Calendar endDate; 67 | private int nPartition; 68 | 69 | private ThreadLocal formatter; 70 | 71 | @Override 72 | public void init() { 73 | try { 74 | beginDate = Calendar.getInstance(); 75 | beginDate.setTime(new SimpleDateFormat(dateFormat) 76 | .parse(sBeginDate)); 77 | formatter = new ThreadLocal() { 78 | @Override 79 | protected SimpleDateFormat initialValue() { 80 | return new SimpleDateFormat(dateFormat); 81 | } 82 | }; 83 | if(sEndDate!=null&&!sEndDate.equals("")) { 84 | endDate = Calendar.getInstance(); 85 | endDate.setTime(new SimpleDateFormat(dateFormat).parse(sEndDate)); 86 | nPartition = ((endDate.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR)) * 12 87 | + endDate.get(Calendar.MONTH) - beginDate.get(Calendar.MONTH)) + 1; 88 | 89 | if (nPartition <= 0) { 90 | throw new java.lang.IllegalArgumentException("Incorrect time range for month partitioning!"); 91 | } 92 | } else { 93 | nPartition = -1; 94 | } 95 | } catch (ParseException e) { 96 | throw new java.lang.IllegalArgumentException(e); 97 | } 98 | } 99 | 100 | /** 101 | * For circulatory partition, calculated value of target partition needs to be 102 | * rotated to fit the partition range 103 | */ 104 | private int reCalculatePartition(int targetPartition) { 105 | /** 106 | * If target date is previous of start time of partition setting, shift 107 | * the delta range between target and start date to be positive value 108 | */ 109 | if (targetPartition < 0) { 110 | targetPartition = nPartition - (-targetPartition) % nPartition; 111 | } 112 | 113 | if (targetPartition >= nPartition) { 114 | targetPartition = targetPartition % nPartition; 115 | } 116 | 117 | return targetPartition; 118 | } 119 | 120 | @Override 121 | public Integer calculate(String columnValue) { 122 | try { 123 | int targetPartition; 124 | Calendar curTime = Calendar.getInstance(); 125 | curTime.setTime(formatter.get().parse(columnValue)); 126 | targetPartition = ((curTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR)) 127 | * 12 + curTime.get(Calendar.MONTH) 128 | - beginDate.get(Calendar.MONTH)); 129 | 130 | /** 131 | * For circulatory partition, calculated value of target partition needs to be 132 | * rotated to fit the partition range 133 | */ 134 | if (nPartition > 0) { 135 | targetPartition = reCalculatePartition(targetPartition); 136 | } 137 | return targetPartition; 138 | 139 | } catch (ParseException e) { 140 | throw new IllegalArgumentException(new StringBuilder().append("columnValue:").append(columnValue).append(" Please check if the format satisfied.").toString(),e); 141 | } 142 | } 143 | 144 | @Override 145 | public Integer[] calculateRange(String beginValue, String endValue) { 146 | try { 147 | int startPartition, endPartition; 148 | Calendar partitionTime = Calendar.getInstance(); 149 | SimpleDateFormat format = new SimpleDateFormat(dateFormat); 150 | partitionTime.setTime(format.parse(beginValue)); 151 | startPartition = ((partitionTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR)) 152 | * 12 + partitionTime.get(Calendar.MONTH) 153 | - beginDate.get(Calendar.MONTH)); 154 | partitionTime.setTime(format.parse(endValue)); 155 | endPartition = ((partitionTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR)) 156 | * 12 + partitionTime.get(Calendar.MONTH) 157 | - beginDate.get(Calendar.MONTH)); 158 | 159 | List list = new ArrayList(); 160 | 161 | while (startPartition <= endPartition) { 162 | Integer nodeValue = reCalculatePartition(startPartition); 163 | if (Collections.frequency(list, nodeValue) < 1) 164 | list.add(nodeValue); 165 | startPartition++; 166 | } 167 | int size = list.size(); 168 | return (list.toArray(new Integer[size])); 169 | } catch (ParseException e) { 170 | LOGGER.error("error",e); 171 | return new Integer[0]; 172 | } 173 | } 174 | 175 | @Override 176 | public int getPartitionNum() { 177 | int nPartition = this.nPartition; 178 | return nPartition; 179 | } 180 | 181 | public void setsBeginDate(String sBeginDate) { 182 | this.sBeginDate = sBeginDate; 183 | } 184 | 185 | public void setDateFormat(String dateFormat) { 186 | this.dateFormat = dateFormat; 187 | } 188 | 189 | public void setsEndDate(String sEndDate) { 190 | this.sEndDate = sEndDate; 191 | } 192 | 193 | public static void main(String[] args) { 194 | String bd = "2017-08-01 13:33:12"; 195 | String ed = "2017-12-31 23:59:59"; 196 | PartitionByMonth pm = new PartitionByMonth(); 197 | pm.setsBeginDate(bd); 198 | // pm.setsEndDate(ed); 199 | pm.setDateFormat("yyyy-MM-dd HH:mm:ss"); 200 | pm.init(); 201 | int result = pm.calculate("2017-12-01 13:33:12"); 202 | Integer[] range = pm.calculateRange(bd, ed); 203 | System.out.println(">>> Total Partition: "+pm.getPartitionNum()); 204 | System.out.println(">>> Current Partition: "+result); 205 | System.out.println(">>> Partition Info:"); 206 | for(Integer i : range){ 207 | System.out.println("\t> Partition " + i); 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/function/PartitionByYear.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.function; 29 | 30 | import java.text.ParseException; 31 | import java.text.SimpleDateFormat; 32 | import java.util.ArrayList; 33 | import java.util.Calendar; 34 | import java.util.Collections; 35 | import java.util.List; 36 | 37 | import org.apache.commons.lang.StringUtils; 38 | import org.apache.log4j.Logger; 39 | 40 | import io.isharing.springddal.route.rule.RuleAlgorithm; 41 | 42 | /** 43 | * 按年份列分区 ,每个自然年一个分片。每年的数据存入单独的数据库中。 44 | * 45 | */ 46 | public class PartitionByYear extends AbstractPartitionAlgorithm implements RuleAlgorithm { 47 | 48 | private static final long serialVersionUID = 4208799852149537498L; 49 | private static final Logger LOGGER = Logger.getLogger(PartitionByDate.class); 50 | 51 | /** 52 | * 开始日期 53 | */ 54 | private String sBeginDate; 55 | /** 56 | * 结束日期, 如果配置了 sEndDate 则代表数据达到了这个日期的分片后后循环从开始分片插入; 57 | * 如果没配置则按每一年的年份继续切分(即如开始时间为2010年1月,传入的时间为2018年12月,则分区为8)。 58 | */ 59 | private String sEndDate; 60 | /** 61 | * 日期格式,比如:yyyy-MM-dd HH:mm:ss 62 | */ 63 | private String dateFormat; 64 | 65 | private Calendar beginDate; 66 | private Calendar endDate; 67 | private int nPartition; 68 | 69 | private ThreadLocal formatter; 70 | 71 | @Override 72 | public void init() { 73 | try { 74 | beginDate = Calendar.getInstance(); 75 | beginDate.setTime(new SimpleDateFormat(dateFormat).parse(sBeginDate)); 76 | formatter = new ThreadLocal() { 77 | @Override 78 | protected SimpleDateFormat initialValue() { 79 | return new SimpleDateFormat(dateFormat); 80 | } 81 | }; 82 | if(StringUtils.isNotBlank(sEndDate)) { 83 | endDate = Calendar.getInstance(); 84 | endDate.setTime(new SimpleDateFormat(dateFormat).parse(sEndDate)); 85 | nPartition = ((endDate.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))) + 1; 86 | 87 | if (nPartition <= 0) { 88 | throw new java.lang.IllegalArgumentException("Incorrect time range for month partitioning!"); 89 | } 90 | } else { 91 | nPartition = -1; 92 | } 93 | } catch (ParseException e) { 94 | throw new java.lang.IllegalArgumentException(e); 95 | } 96 | } 97 | 98 | /** 99 | * For circulatory partition, calculated value of target partition needs to be 100 | * rotated to fit the partition range 101 | */ 102 | private int reCalculatePartition(int targetPartition) { 103 | /** 104 | * If target date is previous of start time of partition setting, shift 105 | * the delta range between target and start date to be positive value 106 | */ 107 | if (targetPartition < 0) { 108 | targetPartition = nPartition - (-targetPartition) % nPartition; 109 | } 110 | 111 | if (targetPartition >= nPartition) { 112 | targetPartition = targetPartition % nPartition; 113 | } 114 | 115 | return targetPartition; 116 | } 117 | 118 | @Override 119 | public Integer calculate(String columnValue) { 120 | try { 121 | int targetPartition; 122 | Calendar curTime = Calendar.getInstance(); 123 | curTime.setTime(formatter.get().parse(columnValue)); 124 | targetPartition = ((curTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))); 125 | 126 | /** 127 | * For circulatory partition, calculated value of target partition needs to be 128 | * rotated to fit the partition range 129 | */ 130 | if (nPartition > 0) { 131 | targetPartition = reCalculatePartition(targetPartition); 132 | } 133 | return targetPartition; 134 | 135 | } catch (ParseException e) { 136 | throw new IllegalArgumentException(new StringBuilder().append("columnValue:").append(columnValue).append(" Please check if the format satisfied.").toString(),e); 137 | } 138 | } 139 | 140 | @Override 141 | public Integer[] calculateRange(String beginValue, String endValue) { 142 | try { 143 | int startPartition, endPartition; 144 | Calendar partitionTime = Calendar.getInstance(); 145 | SimpleDateFormat format = new SimpleDateFormat(dateFormat); 146 | partitionTime.setTime(format.parse(beginValue)); 147 | startPartition = ((partitionTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))); 148 | partitionTime.setTime(format.parse(endValue)); 149 | endPartition = ((partitionTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR))); 150 | 151 | List list = new ArrayList(); 152 | 153 | while (startPartition <= endPartition) { 154 | Integer nodeValue = reCalculatePartition(startPartition); 155 | if (Collections.frequency(list, nodeValue) < 1){ 156 | list.add(nodeValue); 157 | } 158 | startPartition++; 159 | } 160 | int size = list.size(); 161 | return (list.toArray(new Integer[size])); 162 | } catch (ParseException e) { 163 | LOGGER.error("error",e); 164 | return new Integer[0]; 165 | } 166 | } 167 | 168 | @Override 169 | public int getPartitionNum() { 170 | int nPartition = this.nPartition; 171 | return nPartition; 172 | } 173 | 174 | public void setsBeginDate(String sBeginDate) { 175 | this.sBeginDate = sBeginDate; 176 | } 177 | 178 | public void setDateFormat(String dateFormat) { 179 | this.dateFormat = dateFormat; 180 | } 181 | 182 | public void setsEndDate(String sEndDate) { 183 | this.sEndDate = sEndDate; 184 | } 185 | 186 | public static void main(String[] args) { 187 | String bd = "2015-03-01 13:33:12"; 188 | String ed = "2017-12-31 23:59:59"; 189 | PartitionByYear pm = new PartitionByYear(); 190 | pm.setsBeginDate(bd); 191 | pm.setsEndDate(ed); 192 | pm.setDateFormat("yyyy-MM-dd HH:mm:ss"); 193 | pm.init(); 194 | int result = pm.calculate("2019-12-01 13:33:12"); 195 | Integer[] range = pm.calculateRange(bd, ed); 196 | System.out.println(">>> Total Partition: "+pm.getPartitionNum()); 197 | System.out.println(">>> Current Partition: "+result); 198 | System.out.println(">>> Partition Info:"); 199 | for(Integer i : range){ 200 | System.out.println("\t> Partition " + i); 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/function/PartitionUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.function; 29 | 30 | /** 31 | * 数据分区工具 32 | */ 33 | public final class PartitionUtil { 34 | 35 | // 分区长度:数据段分布定义,其中取模的数一定要是2^n, 因为这里使用x % 2^n == x & (2^n - 1)等式,来优化性能。 36 | private static final int PARTITION_LENGTH = 1024; 37 | 38 | // %转换为&操作的换算数值 39 | private static final long AND_VALUE = PARTITION_LENGTH - 1; 40 | 41 | // 分区线段 42 | private final int[] segment = new int[PARTITION_LENGTH]; 43 | 44 | /** 45 | *
 46 |      * @param count 表示定义的分区数
 47 |      * @param length 表示对应每个分区的取值长度
 48 |      * 注意:其中count,length两个数组的长度必须是一致的。
 49 |      * 约束:1024 = sum((count[i]*length[i]))。 count和length两个向量的点积恒等于1024
 50 |      * 
51 | */ 52 | public PartitionUtil(int[] count, int[] length) { 53 | if (count == null || length == null || (count.length != length.length)) { 54 | throw new RuntimeException("error,check your scope & scopeLength definition."); 55 | } 56 | int segmentLength = 0; 57 | for (int i = 0; i < count.length; i++) { 58 | segmentLength += count[i]; 59 | } 60 | int[] ai = new int[segmentLength + 1]; 61 | 62 | int index = 0; 63 | for (int i = 0; i < count.length; i++) { 64 | for (int j = 0; j < count[i]; j++) { 65 | ai[++index] = ai[index - 1] + length[i]; 66 | } 67 | } 68 | if (ai[ai.length - 1] != PARTITION_LENGTH) { 69 | throw new RuntimeException("error,check your partitionScope definition."); 70 | } 71 | 72 | // 数据映射操作 73 | for (int i = 1; i < ai.length; i++) { 74 | for (int j = ai[i - 1]; j < ai[i]; j++) { 75 | segment[j] = (i - 1); 76 | } 77 | } 78 | } 79 | 80 | public int partition(long hash) { 81 | return segment[(int) (hash & AND_VALUE)]; 82 | } 83 | 84 | public int partition(String key, int start, int end) { 85 | return partition(hash(key, start, end)); 86 | } 87 | 88 | /** 89 | * 字符串hash算法:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
90 | * 其中s[]为字符串的字符数组,换算成程序的表达式为:
91 | * h = 31*h + s.charAt(i); => h = (h << 5) - h + s.charAt(i);
92 | * 93 | * @param start 94 | * hash for s.substring(start, end) 95 | * @param end 96 | * hash for s.substring(start, end) 97 | */ 98 | public static long hash(String s, int start, int end) { 99 | if (start < 0) { 100 | start = 0; 101 | } 102 | if (end > s.length()) { 103 | end = s.length(); 104 | } 105 | long h = 0; 106 | for (int i = start; i < end; ++i) { 107 | h = (h << 5) - h + s.charAt(i); 108 | } 109 | return h; 110 | } 111 | 112 | public static void main(String[] args) { 113 | // 本例的分区策略:希望将数据水平分成3份,前两份各占25%,第三份占50%。(故本例非均匀分区) 114 | // |<---------------------1024------------------------>| 115 | // |<----256--->|<----256--->|<----------512---------->| 116 | // | partition0 | partition1 | partition2 | 117 | // | 共2份,故count[0]=2 | 共1份,故count[1]=1 | 118 | int[] count = new int[] { 2, 1 }; 119 | int[] length = new int[] { 256, 512 }; 120 | PartitionUtil pu = new PartitionUtil(count, length); 121 | 122 | // 下面代码演示分别以offerId字段或memberId字段根据上述分区策略拆分的分配结果 123 | int DEFAULT_STR_HEAD_LEN = 8; // cobar默认会配置为此值 124 | long offerId = 12345; 125 | String memberId = "qiushuo"; 126 | 127 | // 若根据offerId分配,partNo1将等于0,即按照上述分区策略,offerId为12345时将会被分配到partition0中 128 | int partNo1 = pu.partition(offerId); 129 | 130 | // 若根据memberId分配,partNo2将等于2,即按照上述分区策略,memberId为qiushuo时将会被分到partition2中 131 | int partNo2 = pu.partition(memberId, 0, DEFAULT_STR_HEAD_LEN); 132 | 133 | // Assert.assertEquals(0, partNo1); 134 | // Assert.assertEquals(2, partNo2); 135 | 136 | System.out.println(partNo1); 137 | System.out.println(partNo2); 138 | } 139 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/BeanObjectEntityConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | import java.lang.reflect.InvocationTargetException; 31 | import java.util.HashMap; 32 | import java.util.Map; 33 | 34 | import io.isharing.springddal.route.exception.ConfigurationException; 35 | 36 | 37 | public class BeanObjectEntityConfig implements Cloneable { 38 | 39 | private static final ReflectionProvider refProvider = new ReflectionProvider(); 40 | 41 | private String name; 42 | private String className; 43 | private Map params = new HashMap(); 44 | 45 | public String getName() { 46 | return name; 47 | } 48 | 49 | public void setName(String name) { 50 | this.name = name; 51 | } 52 | 53 | public String getClassName() { 54 | return className; 55 | } 56 | 57 | public void setClassName(String beanObject) { 58 | this.className = beanObject; 59 | } 60 | 61 | public Map getParams() { 62 | return params; 63 | } 64 | 65 | public void setParams(Map params) { 66 | this.params = params; 67 | } 68 | 69 | public Object create(boolean initEarly) throws IllegalAccessException, InvocationTargetException { 70 | Object obj = null; 71 | try { 72 | obj = refProvider.newInstance(Class.forName(className)); 73 | } catch (ClassNotFoundException e) { 74 | throw new ConfigurationException(e); 75 | } 76 | ParameterMapping.mapping(obj, params); 77 | if (initEarly && (obj instanceof Initializable)) { 78 | ((Initializable) obj).init(); 79 | } 80 | return obj; 81 | } 82 | 83 | @Override 84 | public Object clone() { 85 | try { 86 | super.clone(); 87 | } catch (CloneNotSupportedException e) { 88 | throw new ConfigurationException(e); 89 | } 90 | BeanObjectEntityConfig bc = null; 91 | try { 92 | bc = getClass().newInstance(); 93 | } catch (InstantiationException e) { 94 | throw new ConfigurationException(e); 95 | } catch (IllegalAccessException e) { 96 | throw new ConfigurationException(e); 97 | } 98 | // if (bc == null) { 99 | // return null; 100 | // } 101 | bc.className = className; 102 | bc.name = name; 103 | // Map params = new HashMap(); 104 | // params.putAll(params); 105 | return bc; 106 | } 107 | 108 | @Override 109 | public int hashCode() { 110 | int hashcode = 37; 111 | hashcode += (name == null ? 0 : name.hashCode()); 112 | hashcode += (className == null ? 0 : className.hashCode()); 113 | hashcode += (params == null ? 0 : params.hashCode()); 114 | return hashcode; 115 | } 116 | 117 | @Override 118 | public boolean equals(Object object) { 119 | if (object instanceof BeanObjectEntityConfig) { 120 | BeanObjectEntityConfig entity = (BeanObjectEntityConfig) object; 121 | boolean isEquals = equals(name, entity.name); 122 | isEquals = isEquals && equals(className, entity.getClassName()); 123 | isEquals = isEquals && (ObjectUtil.equals(params, entity.params)); 124 | return isEquals; 125 | } 126 | return false; 127 | } 128 | 129 | private static boolean equals(String str1, String str2) { 130 | if (str1 == null) { 131 | return str2 == null; 132 | } 133 | return str1.equals(str2); 134 | } 135 | 136 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/ConfigUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | import java.util.Properties; 31 | 32 | import io.isharing.springddal.route.exception.ConfigurationException; 33 | 34 | public class ConfigUtil { 35 | 36 | public static String filter(String text) { 37 | return filter(text, System.getProperties()); 38 | } 39 | 40 | public static String filter(String text, Properties properties) { 41 | StringBuilder s = new StringBuilder(); 42 | int cur = 0; 43 | int textLen = text.length(); 44 | int propStart = -1; 45 | int propStop = -1; 46 | String propName = null; 47 | String propValue = null; 48 | for (; cur < textLen; cur = propStop + 1) { 49 | propStart = text.indexOf("${", cur); 50 | if (propStart < 0) { 51 | break; 52 | } 53 | s.append(text.substring(cur, propStart)); 54 | propStop = text.indexOf("}", propStart); 55 | if (propStop < 0) { 56 | throw new ConfigurationException("Unterminated property: " + text.substring(propStart)); 57 | } 58 | propName = text.substring(propStart + 2, propStop); 59 | propValue = properties.getProperty(propName); 60 | if (propValue == null) { 61 | s.append("${").append(propName).append('}'); 62 | } else { 63 | s.append(propValue); 64 | } 65 | } 66 | return s.append(text.substring(cur)).toString(); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/FieldDictionary.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | import java.lang.reflect.Field; 31 | import java.util.Collections; 32 | import java.util.HashMap; 33 | import java.util.Iterator; 34 | import java.util.Map; 35 | 36 | import io.isharing.springddal.route.exception.ObjectAccessException; 37 | 38 | public class FieldDictionary { 39 | 40 | private final Map> nameCache = Collections.synchronizedMap(new HashMap>()); 41 | private final Map> keyCache = Collections.synchronizedMap(new HashMap>()); 42 | 43 | /** 44 | * Returns an iterator for all serializable fields for some class 45 | * 46 | * @param cls 47 | * the class you are interested on 48 | * @return an iterator for its serializable fields 49 | */ 50 | public Iterator serializableFieldsFor(Class cls) { 51 | return buildMap(cls, true).values().iterator(); 52 | } 53 | 54 | /** 55 | * Returns an specific field of some class. If definedIn is null, it searchs 56 | * for the field named 'name' inside the class cls. If definedIn is 57 | * different than null, tries to find the specified field name in the 58 | * specified class cls which should be defined in class definedIn (either 59 | * equals cls or a one of it's superclasses) 60 | * 61 | * @param cls 62 | * the class where the field is to be searched 63 | * @param name 64 | * the field name 65 | * @param definedIn 66 | * the superclass (or the class itself) of cls where the field 67 | * was defined 68 | * @return the field itself 69 | */ 70 | public Field field(Class cls, String name, Class definedIn) { 71 | Map fields = buildMap(cls, definedIn != null); 72 | Field field = fields.get(definedIn != null ? new FieldKey(name, definedIn, 0) : name); 73 | if (field == null) { 74 | throw new ObjectAccessException("No such field " + cls.getName() + "." + name); 75 | } else { 76 | return field; 77 | } 78 | } 79 | 80 | private Map buildMap(Class cls, boolean tupleKeyed) { 81 | final String clsName = cls.getName(); 82 | if (!nameCache.containsKey(clsName)) { 83 | synchronized (keyCache) { 84 | if (!nameCache.containsKey(clsName)) { // double check 85 | final Map keyedByFieldName = new HashMap(); 86 | final Map keyedByFieldKey = new OrderRetainingMap(); 87 | while (!Object.class.equals(cls)) { 88 | Field[] fields = cls.getDeclaredFields(); 89 | if (JVMInfo.reverseFieldDefinition()) { 90 | for (int i = fields.length >> 1; i-- > 0;) { 91 | final int idx = fields.length - i - 1; 92 | final Field field = fields[i]; 93 | fields[i] = fields[idx]; 94 | fields[idx] = field; 95 | } 96 | } 97 | for (int i = 0; i < fields.length; i++) { 98 | Field field = fields[i]; 99 | field.setAccessible(true); 100 | if (!keyedByFieldName.containsKey(field.getName())) { 101 | keyedByFieldName.put(field.getName(), field); 102 | } 103 | keyedByFieldKey.put(new FieldKey(field.getName(), field.getDeclaringClass(), i), field); 104 | } 105 | cls = cls.getSuperclass(); 106 | } 107 | nameCache.put(clsName, keyedByFieldName); 108 | keyCache.put(clsName, keyedByFieldKey); 109 | } 110 | } 111 | } 112 | return tupleKeyed ? keyCache.get(clsName) : nameCache.get(clsName); 113 | } 114 | 115 | private static class FieldKey { 116 | 117 | private String fieldName; 118 | private Class declaringClass; 119 | private Integer depth; 120 | private int order; 121 | 122 | public FieldKey(String fieldName, Class declaringClass, int order) { 123 | this.fieldName = fieldName; 124 | this.declaringClass = declaringClass; 125 | this.order = order; 126 | Class c = declaringClass; 127 | int i = 0; 128 | while (c.getSuperclass() != null) { 129 | i++; 130 | c = c.getSuperclass(); 131 | } 132 | //不用构造器创建Integer,用静态方法节省时间和空间,因为depth是不可变变量 133 | depth = Integer.valueOf(i); 134 | } 135 | 136 | @Override 137 | public boolean equals(Object o) { 138 | if (this == o) { 139 | return true; 140 | } 141 | if (!(o instanceof FieldKey)) { 142 | return false; 143 | } 144 | 145 | final FieldKey fieldKey = (FieldKey) o; 146 | 147 | if (declaringClass != null ? !declaringClass.equals(fieldKey.declaringClass) 148 | : fieldKey.declaringClass != null) { 149 | return false; 150 | } 151 | 152 | if (fieldName != null ? !fieldName.equals(fieldKey.fieldName) : fieldKey.fieldName != null) { 153 | return false; 154 | } 155 | 156 | return true; 157 | } 158 | 159 | @Override 160 | public int hashCode() { 161 | int result; 162 | result = (fieldName != null ? fieldName.hashCode() : 0); 163 | result = 29 * result + (declaringClass != null ? declaringClass.hashCode() : 0); 164 | return result; 165 | } 166 | 167 | @Override 168 | public String toString() { 169 | return "FieldKey{" + "order=" + order + ", writer=" + depth + ", declaringClass=" + declaringClass 170 | + ", fieldName='" + fieldName + "'" + "}"; 171 | } 172 | 173 | } 174 | 175 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/Initializable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | public interface Initializable { 31 | void init(); 32 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/JVMInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | import java.lang.reflect.Field; 31 | import java.text.AttributedString; 32 | import java.util.HashMap; 33 | import java.util.Map; 34 | 35 | public class JVMInfo { 36 | 37 | private static final float DEFAULT_JAVA_VERSION = 1.3f; 38 | private static final boolean reverseFieldOrder; 39 | private static final float majorJavaVersion = getMajorJavaVersion(System.getProperty("java.specification.version")); 40 | 41 | private ReflectionProvider reflectionProvider; 42 | private Map> loaderCache = new HashMap>(); 43 | 44 | static { 45 | boolean reverse = false; 46 | final Field[] fields = AttributedString.class.getDeclaredFields(); 47 | for (int i = 0; i < fields.length; i++) { 48 | if (fields[i].getName().equals("text")) { 49 | reverse = i > 3; 50 | } 51 | } 52 | reverseFieldOrder = reverse; 53 | } 54 | 55 | /** 56 | * Parses the java version system property to determine the major java 57 | * version, ie 1.x 58 | * 59 | * @param javaVersion 60 | * the system property 'java.specification.version' 61 | * @return A float of the form 1.x 62 | */ 63 | public static final float getMajorJavaVersion(String javaVersion) { 64 | try { 65 | return Float.parseFloat(javaVersion.substring(0, 3)); 66 | } catch (NumberFormatException e) { 67 | // Some JVMs may not conform to the x.y.z java.version format 68 | return DEFAULT_JAVA_VERSION; 69 | } 70 | } 71 | 72 | public static boolean is14() { 73 | return majorJavaVersion >= 1.4f; 74 | } 75 | 76 | public static boolean is15() { 77 | return majorJavaVersion >= 1.5f; 78 | } 79 | 80 | public static boolean is16() { 81 | return majorJavaVersion >= 1.6f; 82 | } 83 | 84 | private static boolean isSun() { 85 | return System.getProperty("java.vm.vendor").indexOf("Sun") != -1; 86 | } 87 | 88 | private static boolean isApple() { 89 | return System.getProperty("java.vm.vendor").indexOf("Apple") != -1; 90 | } 91 | 92 | private static boolean isHPUX() { 93 | return System.getProperty("java.vm.vendor").indexOf("Hewlett-Packard Company") != -1; 94 | } 95 | 96 | private static boolean isIBM() { 97 | return System.getProperty("java.vm.vendor").indexOf("IBM") != -1; 98 | } 99 | 100 | private static boolean isBlackdown() { 101 | return System.getProperty("java.vm.vendor").indexOf("Blackdown") != -1; 102 | } 103 | 104 | /* 105 | * Support for sun.misc.Unsafe and sun.reflect.ReflectionFactory is present 106 | * in JRockit versions R25.1.0 and later, both 1.4.2 and 5.0 (and in future 107 | * 6.0 builds). 108 | */ 109 | private static boolean isBEAWithUnsafeSupport() { 110 | // This property should be "BEA Systems, Inc." 111 | if (System.getProperty("java.vm.vendor").indexOf("BEA") != -1) { 112 | 113 | /* 114 | * Recent 1.4.2 and 5.0 versions of JRockit have a java.vm.version 115 | * string starting with the "R" JVM version number, i.e. 116 | * "R26.2.0-38-57237-1.5.0_06-20060209..." 117 | */ 118 | String vmVersion = System.getProperty("java.vm.version"); 119 | if (vmVersion.startsWith("R")) { 120 | /* 121 | * Wecould also check that it's R26 or later, but that is 122 | * implicitly true 123 | */ 124 | return true; 125 | } 126 | 127 | /* 128 | * For older JRockit versions we can check java.vm.info. JRockit 129 | * 1.4.2 R24 -> "Native Threads, GC strategy: parallel" and JRockit 130 | * 5.0 R25 -> "R25.2.0-28". 131 | */ 132 | String vmInfo = System.getProperty("java.vm.info"); 133 | if (vmInfo != null) { 134 | // R25.1 or R25.2 supports Unsafe, other versions do not 135 | return (vmInfo.startsWith("R25.1") || vmInfo.startsWith("R25.2")); 136 | } 137 | } 138 | // If non-BEA, or possibly some very old JRockit version 139 | return false; 140 | } 141 | 142 | public Class loadClass(String name) { 143 | try { 144 | Class clazz = loaderCache.get(name); 145 | if (clazz == null) { 146 | clazz = Class.forName(name, false, getClass().getClassLoader()); 147 | loaderCache.put(name, clazz); 148 | } 149 | return clazz; 150 | } catch (ClassNotFoundException e) { 151 | return null; 152 | } 153 | } 154 | 155 | public synchronized ReflectionProvider getReflectionProvider() { 156 | if (reflectionProvider == null) { 157 | reflectionProvider = new ReflectionProvider(); 158 | } 159 | return reflectionProvider; 160 | } 161 | 162 | protected boolean canUseSun14ReflectionProvider() { 163 | return (isSun() || isApple() || isHPUX() || isIBM() || isBlackdown() || isBEAWithUnsafeSupport()) && is14() 164 | && loadClass("sun.misc.Unsafe") != null; 165 | } 166 | 167 | public static boolean reverseFieldDefinition() { 168 | return reverseFieldOrder; 169 | } 170 | 171 | public static void main(String[] args) { 172 | System.out.println(majorJavaVersion); 173 | } 174 | 175 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/ObjectUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | import java.beans.BeanInfo; 31 | import java.beans.IntrospectionException; 32 | import java.beans.Introspector; 33 | import java.beans.PropertyDescriptor; 34 | import java.io.ByteArrayInputStream; 35 | import java.io.ByteArrayOutputStream; 36 | import java.io.IOException; 37 | import java.io.ObjectInputStream; 38 | import java.io.ObjectOutputStream; 39 | import java.lang.reflect.Field; 40 | import java.lang.reflect.InvocationTargetException; 41 | import java.util.Arrays; 42 | import java.util.List; 43 | 44 | import org.slf4j.Logger; 45 | import org.slf4j.LoggerFactory; 46 | 47 | public final class ObjectUtil { 48 | 49 | private static final Logger LOGGER = LoggerFactory.getLogger(ObjectUtil.class); 50 | 51 | public static Object getStaticFieldValue(String className, String fieldName) { 52 | Class clazz = null; 53 | try { 54 | clazz = Class.forName(className); 55 | Field field = clazz.getField(fieldName); 56 | if (field != null) { 57 | return field.get(null); 58 | } 59 | } catch (ClassNotFoundException e) { 60 | // LOGGER.error("getStaticFieldValue", e); 61 | } catch (NoSuchFieldException e) { 62 | // LOGGER.error("getStaticFieldValue", e); 63 | } catch (IllegalAccessException e) { 64 | // LOGGER.error("getStaticFieldValue", e); 65 | } 66 | return null; 67 | } 68 | 69 | public static Object copyObject(Object object) { 70 | ByteArrayOutputStream b = new ByteArrayOutputStream(); 71 | ObjectOutputStream s = null; 72 | try { 73 | s = new ObjectOutputStream(b); 74 | s.writeObject(object); 75 | ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(b.toByteArray())); 76 | return ois.readObject(); 77 | } catch (IOException e) { 78 | throw new RuntimeException(e); 79 | } catch (ClassNotFoundException e) { 80 | throw new RuntimeException(e); 81 | } 82 | 83 | } 84 | 85 | /** 86 | * 递归地比较两个数组是否相同,支持多维数组。 87 | *

88 | * 如果比较的对象不是数组,则此方法的结果同ObjectUtil.equals。 89 | *

90 | * 91 | * @param array1 92 | * 数组1 93 | * @param array2 94 | * 数组2 95 | * @return 如果相等, 则返回true 96 | */ 97 | public static boolean equals(Object array1, Object array2) { 98 | if (array1 == array2) { 99 | return true; 100 | } 101 | 102 | if ((array1 == null) || (array2 == null)) { 103 | return false; 104 | } 105 | 106 | Class clazz = array1.getClass(); 107 | 108 | if (!clazz.equals(array2.getClass())) { 109 | return false; 110 | } 111 | 112 | if (!clazz.isArray()) { 113 | return array1.equals(array2); 114 | } 115 | 116 | // array1和array2为同类型的数组 117 | if (array1 instanceof long[]) { 118 | long[] longArray1 = (long[]) array1; 119 | long[] longArray2 = (long[]) array2; 120 | 121 | if (longArray1.length != longArray2.length) { 122 | return false; 123 | } 124 | 125 | for (int i = 0; i < longArray1.length; i++) { 126 | if (longArray1[i] != longArray2[i]) { 127 | return false; 128 | } 129 | } 130 | 131 | return true; 132 | } else if (array1 instanceof int[]) { 133 | int[] intArray1 = (int[]) array1; 134 | int[] intArray2 = (int[]) array2; 135 | 136 | if (intArray1.length != intArray2.length) { 137 | return false; 138 | } 139 | 140 | for (int i = 0; i < intArray1.length; i++) { 141 | if (intArray1[i] != intArray2[i]) { 142 | return false; 143 | } 144 | } 145 | 146 | return true; 147 | } else if (array1 instanceof short[]) { 148 | short[] shortArray1 = (short[]) array1; 149 | short[] shortArray2 = (short[]) array2; 150 | 151 | if (shortArray1.length != shortArray2.length) { 152 | return false; 153 | } 154 | 155 | for (int i = 0; i < shortArray1.length; i++) { 156 | if (shortArray1[i] != shortArray2[i]) { 157 | return false; 158 | } 159 | } 160 | 161 | return true; 162 | } else if (array1 instanceof byte[]) { 163 | byte[] byteArray1 = (byte[]) array1; 164 | byte[] byteArray2 = (byte[]) array2; 165 | 166 | if (byteArray1.length != byteArray2.length) { 167 | return false; 168 | } 169 | 170 | for (int i = 0; i < byteArray1.length; i++) { 171 | if (byteArray1[i] != byteArray2[i]) { 172 | return false; 173 | } 174 | } 175 | 176 | return true; 177 | } else if (array1 instanceof double[]) { 178 | double[] doubleArray1 = (double[]) array1; 179 | double[] doubleArray2 = (double[]) array2; 180 | 181 | if (doubleArray1.length != doubleArray2.length) { 182 | return false; 183 | } 184 | 185 | for (int i = 0; i < doubleArray1.length; i++) { 186 | if (Double.doubleToLongBits(doubleArray1[i]) != Double.doubleToLongBits(doubleArray2[i])) { 187 | return false; 188 | } 189 | } 190 | 191 | return true; 192 | } else if (array1 instanceof float[]) { 193 | float[] floatArray1 = (float[]) array1; 194 | float[] floatArray2 = (float[]) array2; 195 | 196 | if (floatArray1.length != floatArray2.length) { 197 | return false; 198 | } 199 | 200 | for (int i = 0; i < floatArray1.length; i++) { 201 | if (Float.floatToIntBits(floatArray1[i]) != Float.floatToIntBits(floatArray2[i])) { 202 | return false; 203 | } 204 | } 205 | 206 | return true; 207 | } else if (array1 instanceof boolean[]) { 208 | boolean[] booleanArray1 = (boolean[]) array1; 209 | boolean[] booleanArray2 = (boolean[]) array2; 210 | 211 | if (booleanArray1.length != booleanArray2.length) { 212 | return false; 213 | } 214 | 215 | for (int i = 0; i < booleanArray1.length; i++) { 216 | if (booleanArray1[i] != booleanArray2[i]) { 217 | return false; 218 | } 219 | } 220 | 221 | return true; 222 | } else if (array1 instanceof char[]) { 223 | char[] charArray1 = (char[]) array1; 224 | char[] charArray2 = (char[]) array2; 225 | 226 | if (charArray1.length != charArray2.length) { 227 | return false; 228 | } 229 | 230 | for (int i = 0; i < charArray1.length; i++) { 231 | if (charArray1[i] != charArray2[i]) { 232 | return false; 233 | } 234 | } 235 | 236 | return true; 237 | } else { 238 | Object[] objectArray1 = (Object[]) array1; 239 | Object[] objectArray2 = (Object[]) array2; 240 | 241 | if (objectArray1.length != objectArray2.length) { 242 | return false; 243 | } 244 | 245 | for (int i = 0; i < objectArray1.length; i++) { 246 | if (!equals(objectArray1[i], objectArray2[i])) { 247 | return false; 248 | } 249 | } 250 | 251 | return true; 252 | } 253 | } 254 | 255 | public static void copyProperties(Object fromObj, Object toObj) { 256 | Class fromClass = fromObj.getClass(); 257 | Class toClass = toObj.getClass(); 258 | try { 259 | BeanInfo fromBean = Introspector.getBeanInfo(fromClass); 260 | BeanInfo toBean = Introspector.getBeanInfo(toClass); 261 | 262 | PropertyDescriptor[] toPd = toBean.getPropertyDescriptors(); 263 | List fromPd = Arrays.asList(fromBean.getPropertyDescriptors()); 264 | 265 | for (PropertyDescriptor propertyDescriptor : toPd) { 266 | propertyDescriptor.getDisplayName(); 267 | PropertyDescriptor pd = fromPd.get(fromPd.indexOf(propertyDescriptor)); 268 | if (pd.getDisplayName().equals(propertyDescriptor.getDisplayName()) 269 | && !pd.getDisplayName().equals("class") && propertyDescriptor.getWriteMethod() != null) { 270 | propertyDescriptor.getWriteMethod().invoke(toObj, pd.getReadMethod().invoke(fromObj, null)); 271 | } 272 | } 273 | } catch (IntrospectionException e) { 274 | throw new RuntimeException(e); 275 | } catch (IllegalArgumentException e) { 276 | throw new RuntimeException(e); 277 | } catch (IllegalAccessException e) { 278 | throw new RuntimeException(e); 279 | } catch (InvocationTargetException e) { 280 | throw new RuntimeException(e); 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/OrderRetainingMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | import java.util.ArrayList; 31 | import java.util.Arrays; 32 | import java.util.Collection; 33 | import java.util.Collections; 34 | import java.util.HashMap; 35 | import java.util.Iterator; 36 | import java.util.List; 37 | import java.util.Map; 38 | import java.util.Map.Entry; 39 | import java.util.Set; 40 | 41 | public class OrderRetainingMap extends HashMap { 42 | 43 | private static final long serialVersionUID = 8538722653898458547L; 44 | private ArraySet keyOrder = new ArraySet(); 45 | private List valueOrder = new ArrayList(); 46 | 47 | public OrderRetainingMap() { 48 | super(); 49 | } 50 | 51 | public OrderRetainingMap(Map m) { 52 | super(); 53 | putAll(m); 54 | } 55 | 56 | public V put(K key, V value) { 57 | int idx = keyOrder.lastIndexOf(key); 58 | if (idx < 0) { 59 | keyOrder.add(key); 60 | valueOrder.add(value); 61 | } else { 62 | valueOrder.set(idx, value); 63 | } 64 | return super.put(key, value); 65 | } 66 | 67 | public V remove(Object key) { 68 | int idx = keyOrder.lastIndexOf(key); 69 | if (idx != 0) { 70 | keyOrder.remove(idx); 71 | valueOrder.remove(idx); 72 | } 73 | return super.remove(key); 74 | } 75 | 76 | public void clear() { 77 | keyOrder.clear(); 78 | valueOrder.clear(); 79 | super.clear(); 80 | } 81 | 82 | public Collection values() { 83 | return Collections.unmodifiableList(valueOrder); 84 | } 85 | 86 | public Set keySet() { 87 | return Collections.unmodifiableSet(keyOrder); 88 | } 89 | 90 | public Set> entrySet() { 91 | Map.Entry[] entries = new Map.Entry[size()]; 92 | for (Iterator> iter = super.entrySet().iterator(); iter.hasNext();) { 93 | Map.Entry entry = (Map.Entry)iter.next(); 94 | entries[keyOrder.indexOf(entry.getKey())] = entry; 95 | } 96 | Set> set = new ArraySet>(); 97 | set.addAll(Arrays.asList(entries)); 98 | return Collections.unmodifiableSet(set); 99 | } 100 | 101 | private static class ArraySet extends ArrayList implements Set { 102 | private static final long serialVersionUID = 3153152900548049706L; 103 | } 104 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/ParameterMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | import java.beans.BeanInfo; 31 | import java.beans.IntrospectionException; 32 | import java.beans.Introspector; 33 | import java.beans.PropertyDescriptor; 34 | import java.lang.reflect.InvocationTargetException; 35 | import java.lang.reflect.Method; 36 | import java.math.BigDecimal; 37 | import java.math.BigInteger; 38 | import java.util.ArrayList; 39 | import java.util.Date; 40 | import java.util.HashMap; 41 | import java.util.List; 42 | import java.util.Map; 43 | 44 | import org.apache.commons.lang.StringUtils; 45 | import org.slf4j.Logger; 46 | import org.slf4j.LoggerFactory; 47 | 48 | import io.isharing.springddal.route.exception.ConfigurationException; 49 | 50 | public class ParameterMapping { 51 | 52 | private static final Logger LOGGER = LoggerFactory.getLogger(ParameterMapping.class); 53 | private static final Map, PropertyDescriptor[]> descriptors = new HashMap, PropertyDescriptor[]>(); 54 | 55 | /** 56 | * 将property键值对赋值组装到object中 57 | * @param object 目标反射对象 58 | * @param parameter property的键值对 59 | * @throws IllegalAccessException 60 | * @throws InvocationTargetException 61 | */ 62 | public static void mapping(Object object, Map parameter) throws IllegalAccessException, 63 | InvocationTargetException { 64 | //获取用于导出clazz这个JavaBean的所有属性的PropertyDescriptor 65 | PropertyDescriptor[] pds = getDescriptors(object.getClass()); 66 | for (int i = 0; i < pds.length; i++) { 67 | PropertyDescriptor pd = pds[i]; 68 | Object obj = parameter.get(pd.getName()); 69 | Object value = obj; 70 | Class cls = pd.getPropertyType(); 71 | //类型转换 72 | if (obj instanceof String) { 73 | String string = (String) obj; 74 | if (!StringUtils.isEmpty(string)) { 75 | string = ConfigUtil.filter(string); 76 | } 77 | if (isPrimitiveType(cls)) { 78 | value = convert(cls, string); 79 | } 80 | } else if (obj instanceof BeanObjectEntityConfig) { 81 | value = createBean((BeanObjectEntityConfig) obj); 82 | } else if (obj instanceof BeanObjectEntityConfig[]) { 83 | List list = new ArrayList(); 84 | for (BeanObjectEntityConfig beanconfig : (BeanObjectEntityConfig[]) obj) { 85 | list.add(createBean(beanconfig)); 86 | } 87 | value = list.toArray(); 88 | } 89 | //赋值 90 | if (cls != null 91 | && value != null) { 92 | Method method = pd.getWriteMethod(); 93 | if (method != null) { 94 | method.invoke(object, new Object[] { value }); 95 | } 96 | } 97 | } 98 | } 99 | 100 | @SuppressWarnings("unchecked") 101 | public static Object createBean(BeanObjectEntityConfig config) throws IllegalAccessException, InvocationTargetException { 102 | Object bean = config.create(true); 103 | if (bean instanceof Map) { 104 | Map map = (Map) bean; 105 | for (Map.Entry entry : config.getParams().entrySet()) { 106 | String key = entry.getKey(); 107 | Object value = entry.getValue(); 108 | if (value instanceof BeanObjectEntityConfig) { 109 | BeanObjectEntityConfig mapBeanConfig = (BeanObjectEntityConfig) entry.getValue(); 110 | value = mapBeanConfig.create(true); 111 | mapping(value, mapBeanConfig.getParams()); 112 | } 113 | map.put(key, value); 114 | } 115 | } else if (bean instanceof List) { 116 | } else { 117 | mapping(bean, config.getParams()); 118 | } 119 | return bean; 120 | } 121 | 122 | /** 123 | * 用于导出clazz这个JavaBean的所有属性的PropertyDescriptor 124 | * @param clazz 125 | * @return 126 | */ 127 | private static PropertyDescriptor[] getDescriptors(Class clazz) { 128 | //PropertyDescriptor类表示JavaBean类通过存储器导出一个属性 129 | PropertyDescriptor[] pds; 130 | List list; 131 | PropertyDescriptor[] pds2 = descriptors.get(clazz); 132 | //该clazz是否第一次加载 133 | if (null == pds2) { 134 | try { 135 | BeanInfo beanInfo = Introspector.getBeanInfo(clazz); 136 | pds = beanInfo.getPropertyDescriptors(); 137 | list = new ArrayList(); 138 | //加载每一个类型不为空的property 139 | for (int i = 0; i < pds.length; i++) { 140 | if (null != pds[i].getPropertyType()) { 141 | list.add(pds[i]); 142 | } 143 | } 144 | pds2 = new PropertyDescriptor[list.size()]; 145 | list.toArray(pds2); 146 | } catch (IntrospectionException ie) { 147 | LOGGER.error("ParameterMappingError", ie); 148 | pds2 = new PropertyDescriptor[0]; 149 | } 150 | } 151 | descriptors.put(clazz, pds2); 152 | return (pds2); 153 | } 154 | 155 | private static Object convert(Class cls, String string) { 156 | Method method = null; 157 | Object value = null; 158 | if (cls.equals(String.class)) { 159 | value = string; 160 | } else if (cls.equals(Boolean.TYPE)) { 161 | value = Boolean.valueOf(string); 162 | } else if (cls.equals(Byte.TYPE)) { 163 | value = Byte.valueOf(string); 164 | } else if (cls.equals(Short.TYPE)) { 165 | value = Short.valueOf(string); 166 | } else if (cls.equals(Integer.TYPE)) { 167 | value = Integer.valueOf(string); 168 | } else if (cls.equals(Long.TYPE)) { 169 | value = Long.valueOf(string); 170 | } else if (cls.equals(Double.TYPE)) { 171 | value = Double.valueOf(string); 172 | } else if (cls.equals(Float.TYPE)) { 173 | value = Float.valueOf(string); 174 | } else if ((cls.equals(Boolean.class)) || (cls.equals(Byte.class)) || (cls.equals(Short.class)) 175 | || (cls.equals(Integer.class)) || (cls.equals(Long.class)) || (cls.equals(Float.class)) 176 | || (cls.equals(Double.class))) { 177 | try { 178 | method = cls.getMethod("valueOf", new Class[] { String.class }); 179 | value = method.invoke(null, new Object[] { string }); 180 | } catch (Exception t) { 181 | LOGGER.error("valueofError", t); 182 | value = null; 183 | } 184 | } else if (cls.equals(Class.class)) { 185 | try { 186 | value = Class.forName(string); 187 | } catch (ClassNotFoundException e) { 188 | throw new ConfigurationException(e); 189 | } 190 | } else { 191 | value = null; 192 | } 193 | return (value); 194 | } 195 | 196 | /** 197 | * 判断一个类是否为基本数据类型。 198 | * @param clazz 要判断的类。 199 | * @return true 表示为基本数据类型。 200 | */ 201 | public static boolean isPrimitiveType(Class cls) { 202 | if (cls.equals(String.class) || cls.equals(Boolean.TYPE) || cls.equals(Byte.TYPE) || cls.equals(Short.TYPE) 203 | || cls.equals(Integer.TYPE) || cls.equals(Long.TYPE) || cls.equals(Double.TYPE) 204 | || cls.equals(Float.TYPE) || cls.equals(Boolean.class) || cls.equals(Byte.class) 205 | || cls.equals(Short.class) || cls.equals(Integer.class) || cls.equals(Long.class) 206 | || cls.equals(Float.class) || cls.equals(Double.class) || cls.equals(Class.class) 207 | || cls.equals(BigDecimal.class) || cls.equals(BigInteger.class) || cls.equals(Date.class) 208 | || cls.equals(Character.class) 209 | ) { 210 | return true; 211 | } else { 212 | return false; 213 | } 214 | } 215 | 216 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/ReflectionProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | import java.io.ByteArrayInputStream; 31 | import java.io.ByteArrayOutputStream; 32 | import java.io.DataOutputStream; 33 | import java.io.IOException; 34 | import java.io.ObjectInputStream; 35 | import java.io.ObjectStreamClass; 36 | import java.io.ObjectStreamConstants; 37 | import java.io.Serializable; 38 | import java.lang.reflect.Constructor; 39 | import java.lang.reflect.Field; 40 | import java.lang.reflect.InvocationTargetException; 41 | import java.lang.reflect.Method; 42 | import java.lang.reflect.Modifier; 43 | import java.util.Collections; 44 | import java.util.HashMap; 45 | import java.util.Iterator; 46 | import java.util.Map; 47 | 48 | import io.isharing.springddal.route.exception.ObjectAccessException; 49 | 50 | public class ReflectionProvider { 51 | 52 | private transient Map, byte[]> serializedDataCache = Collections.synchronizedMap(new HashMap, byte[]>()); 53 | private transient FieldDictionary fieldDictionary = new FieldDictionary(); 54 | 55 | public Object newInstance(Class type) { 56 | try { 57 | Constructor[] c = type.getDeclaredConstructors(); 58 | for (int i = 0; i < c.length; i++) { 59 | if (c[i].getParameterTypes().length == 0) { 60 | if (!Modifier.isPublic(c[i].getModifiers())) { 61 | c[i].setAccessible(true); 62 | } 63 | return c[i].newInstance(new Object[0]); 64 | } 65 | } 66 | if (Serializable.class.isAssignableFrom(type)) { 67 | return instantiateUsingSerialization(type); 68 | } else { 69 | throw new ObjectAccessException("Cannot construct " + type.getName() 70 | + " as it does not have a no-args constructor"); 71 | } 72 | } catch (InstantiationException e) { 73 | throw new ObjectAccessException("Cannot construct " + type.getName(), e); 74 | } catch (IllegalAccessException e) { 75 | throw new ObjectAccessException("Cannot construct " + type.getName(), e); 76 | } catch (InvocationTargetException e) { 77 | if (e.getTargetException() instanceof RuntimeException) { 78 | throw (RuntimeException) e.getTargetException(); 79 | } else if (e.getTargetException() instanceof Error) { 80 | throw (Error) e.getTargetException(); 81 | } else { 82 | throw new ObjectAccessException("Constructor for " + type.getName() + " threw an exception", e.getTargetException()); 83 | } 84 | } 85 | } 86 | 87 | public void visitSerializableFields(Object object, Visitor visitor) { 88 | for (Iterator iterator = fieldDictionary.serializableFieldsFor(object.getClass()); iterator.hasNext();) { 89 | Field field = iterator.next(); 90 | if (!fieldModifiersSupported(field)) { 91 | continue; 92 | } 93 | validateFieldAccess(field); 94 | try { 95 | Object value = field.get(object); 96 | visitor.visit(field.getName(), field.getType(), field.getDeclaringClass(), value); 97 | } catch (IllegalArgumentException e) { 98 | throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e); 99 | } catch (IllegalAccessException e) { 100 | throw new ObjectAccessException("Could not get field " + field.getClass() + "." + field.getName(), e); 101 | } 102 | } 103 | } 104 | 105 | public void writeField(Object object, String fieldName, Object value, Class definedIn) { 106 | Field field = fieldDictionary.field(object.getClass(), fieldName, definedIn); 107 | validateFieldAccess(field); 108 | try { 109 | field.set(object, value); 110 | } catch (IllegalArgumentException e) { 111 | throw new ObjectAccessException("Could not set field " + field.getName() + "@" + object.getClass(), e); 112 | } catch (IllegalAccessException e) { 113 | throw new ObjectAccessException("Could not set field " + field.getName() + "@" + object.getClass(), e); 114 | } 115 | } 116 | 117 | public void invokeMethod(Object object, String methodName, Object value, Class definedIn) { 118 | try { 119 | Method method = object.getClass().getMethod(methodName, new Class[] { value.getClass() }); 120 | method.invoke(object, new Object[] { value }); 121 | } catch (Exception e) { 122 | throw new ObjectAccessException("Could not invoke " + object.getClass() + "." + methodName, e); 123 | } 124 | } 125 | 126 | public Class getFieldType(Object object, String fieldName, Class definedIn) { 127 | return fieldDictionary.field(object.getClass(), fieldName, definedIn).getType(); 128 | } 129 | 130 | public boolean fieldDefinedInClass(String fieldName, Class type) { 131 | try { 132 | Field field = fieldDictionary.field(type, fieldName, null); 133 | return fieldModifiersSupported(field); 134 | } catch (ObjectAccessException e) { 135 | return false; 136 | } 137 | } 138 | 139 | public Field getField(Class definedIn, String fieldName) { 140 | return fieldDictionary.field(definedIn, fieldName, null); 141 | } 142 | 143 | private Object instantiateUsingSerialization(Class type) { 144 | try { 145 | byte[] data; 146 | if (serializedDataCache.containsKey(type)) { 147 | data = serializedDataCache.get(type); 148 | } else { 149 | ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 150 | DataOutputStream stream = new DataOutputStream(bytes); 151 | stream.writeShort(ObjectStreamConstants.STREAM_MAGIC); 152 | stream.writeShort(ObjectStreamConstants.STREAM_VERSION); 153 | stream.writeByte(ObjectStreamConstants.TC_OBJECT); 154 | stream.writeByte(ObjectStreamConstants.TC_CLASSDESC); 155 | stream.writeUTF(type.getName()); 156 | stream.writeLong(ObjectStreamClass.lookup(type).getSerialVersionUID()); 157 | stream.writeByte(2); // classDescFlags (2 = Serializable) 158 | stream.writeShort(0); // field count 159 | stream.writeByte(ObjectStreamConstants.TC_ENDBLOCKDATA); 160 | stream.writeByte(ObjectStreamConstants.TC_NULL); 161 | data = bytes.toByteArray(); 162 | serializedDataCache.put(type, data); 163 | } 164 | 165 | ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data)); 166 | return in.readObject(); 167 | } catch (IOException e) { 168 | throw new ObjectAccessException("Cannot create " + type.getName() + " by JDK serialization", e); 169 | } catch (ClassNotFoundException e) { 170 | throw new ObjectAccessException("Cannot find class " + e.getMessage()); 171 | } 172 | } 173 | 174 | private boolean fieldModifiersSupported(Field field) { 175 | return !(Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())); 176 | } 177 | 178 | private void validateFieldAccess(Field field) { 179 | if (Modifier.isFinal(field.getModifiers())) { 180 | if (JVMInfo.is15()) { 181 | field.setAccessible(true); 182 | } else { 183 | throw new ObjectAccessException("Invalid final field " + field.getDeclaringClass().getName() + "." 184 | + field.getName()); 185 | } 186 | } 187 | } 188 | 189 | private Object readResolve() { 190 | serializedDataCache = Collections.synchronizedMap(new HashMap, byte[]>()); 191 | fieldDictionary = new FieldDictionary(); 192 | return this; 193 | } 194 | 195 | } -------------------------------------------------------------------------------- /src/main/java/io/isharing/springddal/route/rule/utils/Visitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 ChenFei, All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 3 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, see . 16 | * 17 | * This code is available under licenses for commercial use. Please contact 18 | * ChenFei for more information. 19 | * 20 | * http://www.gplgpu.com 21 | * http://www.chenfei.me 22 | * 23 | * Title : Spring DDAL 24 | * Author : Chen Fei 25 | * Email : cn.fei.chen@qq.com 26 | * 27 | */ 28 | package io.isharing.springddal.route.rule.utils; 29 | 30 | public interface Visitor { 31 | void visit(String name, Class type, Class definedIn, Object value); 32 | } -------------------------------------------------------------------------------- /src/main/resources/applicationContext.xml_tpl: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/resources/autopartition-long.txt: -------------------------------------------------------------------------------- 1 | # range start-end ,data node index 2 | # K=1000,M=10000. 3 | 0-10=0 4 | 10-20=1 5 | 20-30=2 6 | 30-40=3 -------------------------------------------------------------------------------- /src/main/resources/datanodes.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/resources/datanodes.xml_tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 42 | 43 | 44 | jdbc:mysql://192.168.2.3:3306/fkh_dev?useUnicode=true&characterEncoding=utf8 45 | test 46 | 111111 47 | 48 | 49 | jdbc:mysql://192.168.2.3:3306/fkh_dev?useUnicode=true&characterEncoding=utf8 50 | test 51 | 111111 52 | 53 | 54 | jdbc:mysql://192.168.2.3:3306/fkh_dev?useUnicode=true&characterEncoding=utf8 55 | test 56 | 111111 57 | 58 | 59 | 60 | jdbc:mysql://192.168.2.3:3306/fkh_dev?useUnicode=true&characterEncoding=utf8 61 | test 62 | 111111 63 | 64 | 65 | jdbc:mysql://192.168.2.3:3306/fkh_dev?useUnicode=true&characterEncoding=utf8 66 | test 67 | 111111 68 | 69 | 70 | jdbc:mysql://192.168.2.3:3306/fkh_dev?useUnicode=true&characterEncoding=utf8 71 | test 72 | 111111 73 | 74 | 75 | 76 | jdbc:mysql://192.168.2.3:3306/fkh_dev?useUnicode=true&characterEncoding=utf8 77 | test 78 | 111111 79 | 80 | 81 | jdbc:mysql://192.168.2.3:3306/fkh_dev?useUnicode=true&characterEncoding=utf8 82 | test 83 | 111111 84 | 85 | 86 | jdbc:mysql://192.168.2.3:3306/fkh_dev?useUnicode=true&characterEncoding=utf8 87 | test 88 | 111111 89 | 90 | 91 | 92 | 99 | 100 | 101 | ds0 102 | ds0_repl1, ds0_repl2 103 | 104 | 105 | ds0 106 | ds0_repl1, ds0_repl2 107 | 108 | 109 | ds1 110 | ds1_repl1, ds1_repl2 111 | 112 | 113 | ds2 114 | ds2_repl1, ds2_repl2 115 | 116 | 117 | 118 | 123 | 124 | 2 125 | 5 126 | 00 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.logger.com.ibatis=debug 2 | log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug 3 | log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug 4 | log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug 5 | log4j.logger.java.sql.Connection=debug 6 | log4j.logger.java.sql.Statement=debug 7 | log4j.logger.java.sql.PreparedStatement=debug 8 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ${logpath}/springddal/${env}_%d{yyyy-MM-dd}.log 12 | 30 13 | 14 | 15 | ${ENCODER_PATTERN} 16 | 17 | 18 | 19 | 20 | 21 | 22 | %d{MM/dd HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/resources/rules.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/rules.xml_tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | dn1 8 | id 9 | autopartition-long.txt 10 | 11 | 12 | 13 | dn1 14 | id 15 | 3 16 | 17 | 18 | 19 | dn1 20 | id 21 | 8 22 | 128 23 | 24 | 25 | 26 | dn1 27 | createTime 28 | yyyy-MM-dd HH:mm:ss 29 | 2015-01-01 00:00:01 30 | 2016-08-01 23:59:59 31 | 32 | 33 | 34 | dn1 35 | startDate 36 | yyyy-MM-dd 37 | 2015-01-01 00:00:01 38 | 39 | 40 | 41 | 42 | dn1 43 | createTime 44 | yyyy-MM-dd 45 | 2015-01-01 00:00:01 46 | 47 | 48 | 49 | 50 | dn1 51 | createTime 52 | yyyy-MM-dd HH:mm:ss 53 | 2015-01-01 00:00:01 54 | 55 | 2016-01-01 23:59:59 56 | 10 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/resources/spring-ddal.xml_tpl: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/test/java/io/isharing/springddal/route/keygen/TestSnowflakeIdFactory.java: -------------------------------------------------------------------------------- 1 | package io.isharing.springddal.route.keygen; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.concurrent.CountDownLatch; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | /** 15 | * 16 | * @author 来源自:常乐_smile(http://blog.csdn.net/li396864285/article/details/54668031) 17 | * 18 | */ 19 | public class TestSnowflakeIdFactory { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(TestSnowflakeIdFactory.class); 22 | 23 | public static void testProductIdByMoreThread(int dataCenterId, int workerId, final int n) throws InterruptedException { 24 | List tlist = new ArrayList<>(); 25 | final Set setAll = new HashSet<>(); 26 | final CountDownLatch cdLatch = new CountDownLatch(10); 27 | long start = System.currentTimeMillis(); 28 | int threadNo = dataCenterId; 29 | final Map idFactories = new HashMap<>(); 30 | for(int i=0;i<10;i++){ 31 | //用线程名称做map key. 32 | idFactories.put("snowflake"+i,new SnowflakeIdFactory(workerId, threadNo++)); 33 | } 34 | for(int i=0;i<10;i++){ 35 | Thread temp =new Thread(new Runnable() { 36 | @Override 37 | public void run() { 38 | Set setId = new HashSet<>(); 39 | SnowflakeIdFactory idWorker = idFactories.get(Thread.currentThread().getName()); 40 | for(int j=0;j setOne = new HashSet<>(); 67 | Set setTow = new HashSet<>(); 68 | long start = System.currentTimeMillis(); 69 | for (int i = 0; i < n; i++) { 70 | setOne.add(idWorker.generateKey());//加入set 71 | } 72 | long end1 = System.currentTimeMillis() - start; 73 | log.info("第一批ID预计生成"+n+"个,实际生成"+setOne.size()+"个<<<<*>>>>共耗时: "+end1); 74 | 75 | for (int i = 0; i < n; i++) { 76 | setTow.add(idWorker2.generateKey());//加入set 77 | } 78 | long end2 = System.currentTimeMillis() - start; 79 | log.info("第二批ID预计生成"+n+"个, 实际生成"+setTow.size()+"个<<<<*>>>>共耗时: "+end2); 80 | 81 | setOne.addAll(setTow); 82 | log.info("合并总计生成ID个数:"+setOne.size()); 83 | 84 | } 85 | 86 | public static void testPerSecondProductIdNums(){ 87 | SnowflakeIdFactory idWorker = new SnowflakeIdFactory(1, 2); 88 | long start = System.currentTimeMillis(); 89 | int count = 0; 90 | for (int i = 0; System.currentTimeMillis()-start<1000; i++,count=i) { 91 | /** 测试方法一: 此用法纯粹的生产ID,每秒生产ID个数为300w+ */ 92 | idWorker.generateKey(); 93 | /** 测试方法二: 在log中打印,同时获取ID,此用法生产ID的能力受限于log.error()的吞吐能力. 94 | * 每秒徘徊在10万左右. */ 95 | //log.error("{}",idWorker.nextId()); 96 | } 97 | long end = System.currentTimeMillis()-start; 98 | System.out.println("耗时:"+end); 99 | System.out.println("生成ID个数:"+count); 100 | } 101 | 102 | public static void main(String[] args) { 103 | /** case1: 测试每秒生产id个数? 104 | * 结论: 每秒生产id个数300w+ */ 105 | // testPerSecondProductIdNums(); 106 | 107 | /** case2: 单线程-测试多个生产者同时生产N个id,验证id是否有重复? 108 | * 结论: 验证通过,没有重复. */ 109 | // testProductId(1,2,10000);//验证通过! 110 | // testProductId(1,2,20000);//验证通过! 111 | 112 | /** case3: 多线程-测试多个生产者同时生产N个id, 全部id在全局范围内是否会重复? 113 | * 结论: 验证通过,没有重复. */ 114 | // try { 115 | // testProductIdByMoreThread(1,2,1000000);//单机测试此场景,性能损失至少折半! 116 | // } catch (InterruptedException e) { 117 | // e.printStackTrace(); 118 | // } 119 | 120 | SnowflakeIdFactory idWorker = new SnowflakeIdFactory(1, 2); 121 | System.out.println(idWorker.generateKey()); 122 | } 123 | } 124 | --------------------------------------------------------------------------------