springboot 2.2.1.RELEASE
Zookeeper 3.7.1
ShardingSphere 4.1.1
注意:不要使用集成了springboot的数据源,比如:druid-spring-boot-starter
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.testgroupId>
<artifactId>sharding-jdbcartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.1.RELEASEversion>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.39version>
dependency>
<dependency>
<groupId>com.zaxxergroupId>
<artifactId>HikariCPartifactId>
<version>3.4.5version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>1.2.5version>
dependency>
<dependency>
<groupId>tk.mybatisgroupId>
<artifactId>mapper-spring-boot-starterartifactId>
<version>2.1.5version>
dependency>
<dependency>
<artifactId>guavaartifactId>
<groupId>com.google.guavagroupId>
<version>20.0version>
dependency>
<dependency>
<groupId>org.apache.shardingspheregroupId>
<artifactId>sharding-jdbc-orchestration-spring-boot-starterartifactId>
<version>4.1.1version>
<exclusions>
<exclusion>
<artifactId>guavaartifactId>
<groupId>com.google.guavagroupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.apache.shardingspheregroupId>
<artifactId>sharding-orchestration-center-zookeeper-curatorartifactId>
<version>4.1.1version>
<exclusions>
<exclusion>
<artifactId>guavaartifactId>
<groupId>com.google.guavagroupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger-uiartifactId>
<version>2.9.2version>
<exclusions>
<exclusion>
<artifactId>guavaartifactId>
<groupId>com.google.guavagroupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-swagger2artifactId>
<version>2.9.2version>
<exclusions>
<exclusion>
<artifactId>guavaartifactId>
<groupId>com.google.guavagroupId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.16version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
server:
port: 18000
spring:
shardingsphere:
orchestration:
orchestration_ds:
orchestrationType: registry_center,config_center
instanceType: zookeeper
serverLists: localhost:2181
namespace: safety_v1
props:
overwrite: true
# digest: root:root
mybatis:
type-aliases-package: com.test.domain
mapper-locations: classpath:mapper/**/*.xml
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 分页配置
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countSql
pageSizeZero: true #pageSize=0 or RowBounds.Limit = 0的时候就不适用分页,但是返回对象还是PageInfo
datasource节点数据
# 租户1的数据库
ds_1: !!org.apache.shardingsphere.orchestration.core.configuration.YamlDataSourceConfiguration
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
properties:
jdbcUrl: jdbc:mysql://localhost:3306/test_v1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
maxPoolSize: 10
maintenanceIntervalMilliseconds: 30000
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
minPoolSize: 1
maxLifetimeMilliseconds: 1800000
# 租户2的数据库
ds_2: !!org.apache.shardingsphere.orchestration.core.configuration.YamlDataSourceConfiguration
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
properties:
jdbcUrl: jdbc:mysql://localhost:3306/test_v2?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
maxPoolSize: 10
maintenanceIntervalMilliseconds: 30000
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
minPoolSize: 1
maxLifetimeMilliseconds: 1800000
rule节点数据
CustomHintShardingAlgorithm
类,需要自己实现HintShardingAlgorithm
接口
下面配置中只配置了sys_user表,系统有多少张表就配置多少个logic table
已优化,不需要配置多个 logic table
,随便瞎写一个logic table就行。详情见最下面【优化说明】
tables:
sys_user:
actualDataNodes: ds_$->{1..2}.sys_user
defaultDatabaseStrategy:
hint:
algorithmClassName: com.test.config.sharding.CustomHintShardingAlgorithm
defaultTableStrategy:
none:
import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;
import java.util.ArrayList;
import java.util.Collection;
public class CustomHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
/**
* Sharding.
*
* sharding value injected by hint, not in SQL.
*
* @param availableTargetNames available data sources or tables's names
* @param shardingValue sharding value
* @return sharding result for data sources or tables's names
*/
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, HintShardingValue<Long> shardingValue) {
Collection<String> result = new ArrayList<>();
for (String targetName : availableTargetNames) {
for (Long value : shardingValue.getValues()) {
if (targetName.endsWith("_" + value)) {
result.add(targetName);
}
}
}
return result;
}
}
自定义拦截器,在拦截器内部指定该用户访问哪个数据库
HintManager.clear();
HintManager.getInstance().setDatabaseShardingValue(2L);
zooinspector
README_packaging.md
文件说明,打包编译zookeeper-contrib/zookeeper-contrib-zooinspector
目录,运行 mvn install 命令不需要配置logic table(可以随便瞎写一个logic table)
修改框架的源码:ShardingUnicastRoutingEngine.java
package org.apache.shardingsphere.sharding.route.engine.type.unicast;
import cn.test.exception.BaseException;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
import org.apache.shardingsphere.api.hint.HintManager;
import org.apache.shardingsphere.underlying.common.rule.DataNode;
import org.apache.shardingsphere.core.rule.ShardingRule;
import org.apache.shardingsphere.core.rule.TableRule;
import org.apache.shardingsphere.sharding.route.engine.type.ShardingRouteEngine;
import org.apache.shardingsphere.underlying.common.config.exception.ShardingSphereConfigurationException;
import org.apache.shardingsphere.underlying.route.context.RouteResult;
import org.apache.shardingsphere.underlying.route.context.RouteUnit;
import org.apache.shardingsphere.underlying.route.context.RouteMapper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Sharding unicast routing engine.
*/
@RequiredArgsConstructor
public final class ShardingUnicastRoutingEngine implements ShardingRouteEngine {
private final Collection<String> logicTables;
@Override
public RouteResult route(final ShardingRule shardingRule) {
RouteResult result = new RouteResult();
Collection<Comparable<?>> databaseShardingValues = HintManager.getDatabaseShardingValues();
if (databaseShardingValues.size()!=1){
throw new BaseException("未指定租户");
}
// 修改部分:指定数据源 start
logicTables.clear();
String dataSourceName = "ds_"+databaseShardingValues.iterator().next();
// 修改部分:指定数据源 end
//String dataSourceName = shardingRule.getShardingDataSourceNames().getRandomDataSourceName();
RouteMapper dataSourceMapper = new RouteMapper(dataSourceName, dataSourceName);
if (shardingRule.isAllBroadcastTables(logicTables)) {
List<RouteMapper> tableMappers = new ArrayList<>(logicTables.size());
for (String each : logicTables) {
tableMappers.add(new RouteMapper(each, each));
}
result.getRouteUnits().add(new RouteUnit(dataSourceMapper, tableMappers));
} else if (logicTables.isEmpty()) {
result.getRouteUnits().add(new RouteUnit(dataSourceMapper, Collections.emptyList()));
} else if (1 == logicTables.size()) {
String logicTableName = logicTables.iterator().next();
if (!shardingRule.findTableRule(logicTableName).isPresent()) {
result.getRouteUnits().add(new RouteUnit(dataSourceMapper, Collections.emptyList()));
return result;
}
DataNode dataNode = shardingRule.getDataNode(logicTableName);
result.getRouteUnits().add(new RouteUnit(dataSourceMapper, Collections.singletonList(new RouteMapper(logicTableName, dataNode.getTableName()))));
} else {
List<RouteMapper> tableMappers = new ArrayList<>(logicTables.size());
Set<String> availableDatasourceNames = null;
boolean first = true;
for (String each : logicTables) {
TableRule tableRule = shardingRule.getTableRule(each);
DataNode dataNode = tableRule.getActualDataNodes().get(0);
tableMappers.add(new RouteMapper(each, dataNode.getTableName()));
Set<String> currentDataSourceNames = new HashSet<>(tableRule.getActualDatasourceNames().size());
for (DataNode eachDataNode : tableRule.getActualDataNodes()) {
currentDataSourceNames.add(eachDataNode.getDataSourceName());
}
if (first) {
availableDatasourceNames = currentDataSourceNames;
first = false;
} else {
availableDatasourceNames = Sets.intersection(availableDatasourceNames, currentDataSourceNames);
}
}
if (availableDatasourceNames.isEmpty()) {
throw new ShardingSphereConfigurationException("Cannot find actual datasource intersection for logic tables: %s", logicTables);
}
dataSourceName = shardingRule.getShardingDataSourceNames().getRandomDataSourceName(availableDatasourceNames);
result.getRouteUnits().add(new RouteUnit(new RouteMapper(dataSourceName, dataSourceName), tableMappers));
}
return result;
}
}
sql中含有通配符*
,会导致mybatis返回值字段未null(这个应该是ShardingSphere的bug)
重写 ShardingResultSetMetaData.java
package org.apache.shardingsphere.shardingjdbc.jdbc.core.resultset;
import lombok.RequiredArgsConstructor;
import org.apache.shardingsphere.core.rule.ShardingRule;
import org.apache.shardingsphere.shardingjdbc.jdbc.adapter.WrapperAdapter;
import org.apache.shardingsphere.shardingjdbc.jdbc.core.constant.SQLExceptionConstant;
import org.apache.shardingsphere.sql.parser.binder.segment.select.projection.Projection;
import org.apache.shardingsphere.sql.parser.binder.segment.select.projection.impl.ColumnProjection;
import org.apache.shardingsphere.sql.parser.binder.statement.SQLStatementContext;
import org.apache.shardingsphere.sql.parser.binder.statement.dml.SelectStatementContext;
import org.apache.shardingsphere.underlying.common.database.DefaultSchema;
import java.lang.reflect.Field;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
/**
* Sharding result set meta data.
*/
@RequiredArgsConstructor
public final class ShardingResultSetMetaData extends WrapperAdapter implements ResultSetMetaData {
private final ResultSetMetaData resultSetMetaData;
private final ShardingRule shardingRule;
private final SQLStatementContext sqlStatementContext;
// 修改这个方法
@Override
public int getColumnCount() {
if (sqlStatementContext instanceof SelectStatementContext) {
int size = ((SelectStatementContext) sqlStatementContext).getProjectionsContext().getExpandProjections().size();
if (size == 0) {
try {
return resultSetMetaData.getColumnCount();
} catch (SQLException e) {
e.printStackTrace();
}
}else {
try {
Field field = resultSetMetaData.getClass().getDeclaredField("fields");
field.setAccessible(true);
com.mysql.jdbc.Field[] fields = (com.mysql.jdbc.Field[]) field.get(resultSetMetaData);
return fields.length;
} catch (Exception e) {
e.printStackTrace();
}
return size;
}
}
return 0;
}
@Override
public boolean isAutoIncrement(final int column) throws SQLException {
return resultSetMetaData.isAutoIncrement(column);
}
@Override
public boolean isCaseSensitive(final int column) throws SQLException {
return resultSetMetaData.isCaseSensitive(column);
}
@Override
public boolean isSearchable(final int column) throws SQLException {
return resultSetMetaData.isSearchable(column);
}
@Override
public boolean isCurrency(final int column) throws SQLException {
return resultSetMetaData.isCurrency(column);
}
@Override
public int isNullable(final int column) throws SQLException {
return resultSetMetaData.isNullable(column);
}
@Override
public boolean isSigned(final int column) throws SQLException {
return resultSetMetaData.isSigned(column);
}
@Override
public int getColumnDisplaySize(final int column) throws SQLException {
return resultSetMetaData.getColumnDisplaySize(column);
}
@Override
public String getColumnLabel(final int column) throws SQLException {
return resultSetMetaData.getColumnLabel(column);
}
@Override
public String getColumnName(final int column) throws SQLException {
if (sqlStatementContext instanceof SelectStatementContext) {
List<Projection> actualProjections = ((SelectStatementContext) sqlStatementContext).getProjectionsContext().getExpandProjections();
if (column > actualProjections.size()) {
throw new SQLException(SQLExceptionConstant.COLUMN_INDEX_OUT_OF_RANGE, SQLExceptionConstant.OUT_OF_INDEX_SQL_STATE, 0);
}
Projection projection = ((SelectStatementContext) sqlStatementContext).getProjectionsContext().getExpandProjections().get(column - 1);
if (projection instanceof ColumnProjection) {
return ((ColumnProjection) projection).getName();
}
}
return resultSetMetaData.getColumnName(column);
}
@Override
public String getSchemaName(final int column) {
return DefaultSchema.LOGIC_NAME;
}
@Override
public int getPrecision(final int column) throws SQLException {
return resultSetMetaData.getPrecision(column);
}
@Override
public int getScale(final int column) throws SQLException {
return resultSetMetaData.getScale(column);
}
@Override
public String getTableName(final int column) throws SQLException {
String actualTableName = resultSetMetaData.getTableName(column);
return shardingRule.getLogicTableNames(actualTableName).isEmpty() ? actualTableName : shardingRule.getLogicTableNames(actualTableName).iterator().next();
}
@Override
public String getCatalogName(final int column) {
return DefaultSchema.LOGIC_NAME;
}
@Override
public int getColumnType(final int column) throws SQLException {
return resultSetMetaData.getColumnType(column);
}
@Override
public String getColumnTypeName(final int column) throws SQLException {
return resultSetMetaData.getColumnTypeName(column);
}
@Override
public boolean isReadOnly(final int column) throws SQLException {
return resultSetMetaData.isReadOnly(column);
}
@Override
public boolean isWritable(final int column) throws SQLException {
return resultSetMetaData.isWritable(column);
}
@Override
public boolean isDefinitelyWritable(final int column) throws SQLException {
return resultSetMetaData.isDefinitelyWritable(column);
}
@Override
public String getColumnClassName(final int column) throws SQLException {
return resultSetMetaData.getColumnClassName(column);
}
}
public static void main(String[] args) throws NoSuchAlgorithmException {
String digest = DigestAuthenticationProvider.generateDigest("admin:123456");
System.out.println(digest);
}
超级管理员账号密码:
admin:123456
启动脚本中添加:
"-Dzookeeper.DigestAuthenticationProvider.superDigest=admin:0uek/hZ/V9fgiM35b0Z2226acMQ="
登录:
addauth digest admin:123456
刷新权限:
setAcl -R / digest:admin:0uek/hZ/V9fgiM35b0Z2226acMQ=:cdrwa