Sharding-Jdbc之读写分离导读 原

前言

      Sharding-JDBC是一个开源的分布式数据库中间件,它无需额外部署和依赖,完全兼容JDBC和各种ORM框架。Sharding-JDBC作为面向开发的微服务云原生基础类库,完整的实现了分库分表、读写分离和分布式主键功能,并初步实现了柔性事务。

以2.0.3版本为例maven包依赖如下

<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.0</modelVersion>

	<groupId>com.dongpeng</groupId>
	<artifactId>sharding-jdbc</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>sharding-jdbc</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>io.shardingjdbc</groupId>
			<artifactId>sharding-jdbc-core</artifactId>
			<version>2.0.3</version>
		</dependency> 
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>23.0</version>
		</dependency>
		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId> 
			<version>0.9.5.2</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.46</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.21</version>
		</dependency> 
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.21</version>
		</dependency>

	</dependencies>
	<build>
		<resources>
			<resource>
				<directory>src/main/java</directory>
			</resource>
			<resource>
				<directory>src/main/resources</directory>
			</resource>
		</resources>
		<plugins>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

demo实现如下

package com.dongpeng.sharding.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import io.shardingjdbc.core.api.MasterSlaveDataSourceFactory;
import io.shardingjdbc.core.api.config.MasterSlaveRuleConfiguration;
/**
 * sharding-jdbc读写分离
 * @author Admin
 *
 */
public class MasterSalveDemo {
	public static void main(String[] args) throws Exception {
		Map<String, DataSource> dataSourceMap = new HashMap<String, DataSource>();
		ComboPooledDataSource dataSource1 = new ComboPooledDataSource();
		dataSource1.setDriverClass("com.mysql.jdbc.Driver"); // loads the jdbc driver
		dataSource1.setJdbcUrl("jdbc:mysql://localhost:3306/db_0?useSSL=false");
		dataSource1.setUser("root");
		dataSource1.setPassword("root");
		dataSourceMap.put("ds_0", dataSource1);

		ComboPooledDataSource dataSource2 = new ComboPooledDataSource();
		dataSource2.setDriverClass("com.mysql.jdbc.Driver"); // loads the jdbc driver
		dataSource2.setJdbcUrl("jdbc:mysql://localhost:3306/db_1?useSSL=false");
		dataSource2.setUser("root");
		dataSource2.setPassword("root");
		dataSourceMap.put("ds_1", dataSource2);

		MasterSlaveRuleConfiguration masterSlaveRuleConfig = new MasterSlaveRuleConfiguration();
		masterSlaveRuleConfig.setName("ms_ds");
		masterSlaveRuleConfig.setMasterDataSourceName("ds_0");
		masterSlaveRuleConfig.getSlaveDataSourceNames().add("ds_1");

		DataSource dataSource = MasterSlaveDataSourceFactory.createDataSource(dataSourceMap, masterSlaveRuleConfig,
				new HashMap<String, Object>());
		Connection connection = dataSource.getConnection();
		Statement statement = connection.createStatement();
		ResultSet rs = statement.executeQuery("select * from t_order_0 where user_id=1");
		while (rs.next()) {
			System.out.println(rs.getString("order_id"));
		}

		statement.close();
		connection.close();
	}
}

源码解析

主要的几个类

    MasterSlaveRuleConfiguration

    MasterSlaveDataSourceFactory

    MasterSlaveConnection

    MasterSlaveDataSource

    MasterSlaveStatement

    MasterSlavePreparedStatement

   各个类的说明如下

    MasterSalveRuleConfiguration

  是一个配置类用于配置主从的一些参数,主要的配置参数如下

    private String name;
    
    private String masterDataSourceName;
    
    private Collection<String> slaveDataSourceNames = new LinkedList<>();
    
    private MasterSlaveLoadBalanceAlgorithmType loadBalanceAlgorithmType;
    
    private String loadBalanceAlgorithmClassName;

name在独立的读写分离模式中,目前在策略接口中有使用

masterDataSourceName配置主节点dataSource

slaveDataSourceNames 提供从节点dataSource配置

loadBalanceAlgorithmType配置默认的从节点选取策略,默认支持两种一是轮询(),二是随机(由类RandomMasterSlaveLoadBalanceAlgorithm实现)

loadBanceAlgorithmClassName 配置自定义实现的从节点选取策略,可根据自己的需求自定义实现,loadBalanceAlgorithmType配置优于自定义配置要这个类启作用,不要配置loadBalanceAlgorithmType

MasterSlaveDataSourceFactory

这个是个DataSource的工厂类实现,用于提供各种终端的配置载入方式,支持文件,自配置等等

  public static DataSource createDataSource(final Map<String, DataSource> dataSourceMap, final MasterSlaveRuleConfiguration masterSlaveRuleConfig, 
                                              final Map<String, Object> configMap) throws SQLException {
        return new MasterSlaveDataSource(masterSlaveRuleConfig.build(dataSourceMap), configMap);
    }
  public static DataSource createDataSource(final File yamlFile) throws SQLException, IOException {
        YamlMasterSlaveConfiguration config = unmarshal(yamlFile);
        return new MasterSlaveDataSource(config.getMasterSlaveRule(Collections.<String, DataSource>emptyMap()), config.getMasterSlaveRule().getConfigMap());
    }

MasterSlaveDataSource

主要用于构建dataSource的信息,同时提供一些dataSource的选取方法,主要的先取方法如下

 public NamedDataSource getDataSource(final SQLType sqlType) {
        if (isMasterRoute(sqlType)) {
            DML_FLAG.set(true);
            return new NamedDataSource(masterSlaveRule.getMasterDataSourceName(), masterSlaveRule.getMasterDataSource());
        }
        String selectedSourceName = masterSlaveRule.getStrategy().getDataSource(masterSlaveRule.getName(), 
                masterSlaveRule.getMasterDataSourceName(), new ArrayList<>(masterSlaveRule.getSlaveDataSourceMap().keySet()));
        DataSource selectedSource = selectedSourceName.equals(masterSlaveRule.getMasterDataSourceName())
                ? masterSlaveRule.getMasterDataSource() : masterSlaveRule.getSlaveDataSourceMap().get(selectedSourceName);
        Preconditions.checkNotNull(selectedSource, "");
        return new NamedDataSource(selectedSourceName, selectedSource);
    }

MasterSlaveConnection

MasterSlaveConnection是继承自AbstractConnectionAdapter的类,实现了connection接口,主要提供MasterSlaveStatement和MasterSlavePreparedStatement构建方式

如下几个方法

 @Override
    public Statement createStatement() throws SQLException {
        return new MasterSlaveStatement(this);
    }
  @Override
    public PreparedStatement prepareStatement(final String sql) throws SQLException {
        return new MasterSlavePreparedStatement(this, sql);
    }

同是提供connection的获取方式方法如下,在MasterSlaveStatement,MasterSlavePreparedStatement中有使用到

   public Collection<Connection> getConnections(final SQLType sqlType) throws SQLException {
        cachedSQLType = sqlType;
        Map<String, DataSource> dataSources = SQLType.DDL == sqlType ? masterSlaveDataSource.getMasterDataSource() : masterSlaveDataSource.getDataSource(sqlType).toMap();
        Collection<Connection> result = new LinkedList<>();
        for (Entry<String, DataSource> each : dataSources.entrySet()) {
            String dataSourceName = each.getKey();
            if (getCachedConnections().containsKey(dataSourceName)) {
                result.add(getCachedConnections().get(dataSourceName));
                continue;
            }
            Connection connection = each.getValue().getConnection();
            getCachedConnections().put(dataSourceName, connection);
            result.add(connection);
            replayMethodsInvocation(connection);
        }
        return result;
    }

MasterSlavePreparedStatement和MasterSlaveStatement

这两类都是对PreparedStatement和SlaveStatement的封装,提供了他们对应的sql执行方法,两类执行的方法都会调用Connection的获取方式如下两行代码,最终执行jdbc本身的实现,详情可以查看源码

  SQLStatement sqlStatement = new SQLJudgeEngine(sql).judge();
  Collection<Connection> connections = connection.getConnections(sqlStatement.getType());

SQLStatement是一个sql类型解析类,sharding-jdbc实现了自己的一套sql的解析规则代码如下

/*
 * Copyright 1999-2015 dangdang.com.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * </p>
 */

package io.shardingjdbc.core.parsing;

import io.shardingjdbc.core.parsing.lexer.Lexer;
import io.shardingjdbc.core.parsing.lexer.analyzer.Dictionary;
import io.shardingjdbc.core.parsing.lexer.token.Assist;
import io.shardingjdbc.core.parsing.lexer.token.DefaultKeyword;
import io.shardingjdbc.core.parsing.lexer.token.Keyword;
import io.shardingjdbc.core.parsing.lexer.token.TokenType;
import io.shardingjdbc.core.parsing.parser.exception.SQLParsingException;
import io.shardingjdbc.core.parsing.parser.sql.SQLStatement;
import io.shardingjdbc.core.parsing.parser.sql.ddl.DDLStatement;
import io.shardingjdbc.core.parsing.parser.sql.dml.DMLStatement;
import io.shardingjdbc.core.parsing.parser.sql.dql.select.SelectStatement;
import lombok.RequiredArgsConstructor;

/**
 * SQL judge engine.
 *
 * @author zhangliang
 */
@RequiredArgsConstructor
public final class SQLJudgeEngine {
    
    private final String sql;
    
    /**
     * judge SQL Type only.
     *
     * @return SQL statement
     */
    public SQLStatement judge() {
        Lexer lexer = new Lexer(sql, new Dictionary());
        lexer.nextToken();
        while (true) {
            TokenType tokenType = lexer.getCurrentToken().getType();
            if (tokenType instanceof Keyword) {
                if (DefaultKeyword.SELECT == tokenType) {
                    return new SelectStatement();
                }
                if (DefaultKeyword.INSERT == tokenType || DefaultKeyword.UPDATE == tokenType || DefaultKeyword.DELETE == tokenType) {
                    return new DMLStatement();
                }
                if (DefaultKeyword.CREATE == tokenType || DefaultKeyword.ALTER == tokenType || DefaultKeyword.DROP == tokenType || DefaultKeyword.TRUNCATE == tokenType) {
                    return new DDLStatement();
                }
            }
            if (tokenType instanceof Assist && Assist.END == tokenType) {
                throw new SQLParsingException("Unsupported SQL statement: [%s]", sql);
            }
            lexer.nextToken();
        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏有趣的django

Django REST framework+Vue 打造生鲜超市(四)

五、商品列表页 5.1.django的view实现商品列表页 (1)goods/view_base.py 在goods文件夹下面新建view_base.py,为...

1.9K9
来自专栏Android 研究

APK安装流程详解14——PMS中的新安装流程上(拷贝)补充

mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACK...

2261
来自专栏风中追风

redis 实现分布式锁的演进

比如说:每分钟要执行关闭未支付订单的定时任务,在集群的环境下,如果不做处理,每台服务器都会去执行这个定时任务,显然每个时间段的定时任务只需要执行一次,并不需要每...

7026
来自专栏【转载】DRF+Vue+Mysql_生鲜超市系统

五、商品列表页

在goods文件夹下面新建view_base.py,为了区分django和django rest framework的view

1700
来自专栏MasiMaro 的技术博文

枚举进程中的模块

在Windows中枚举进程中的模块主要是其中加载的dll,在VC上主要有2种方式,一种是解析PE文件中导入表,从导入表中获取它将要静态加载的dll,一种是利用查...

1392
来自专栏lgp20151222

学习sharding-jdbc 分库分表扩展框架

https://gitee.com/a247292980/sharding-jdbc

1163
来自专栏别先生

Hibernate之关联关系映射(一对一主键映射和一对一外键映射)

1:Hibernate的关联关系映射的一对一外键映射:   1.1:第一首先引包,省略   1.2:第二创建实体类:     这里使用用户信息和身份证信息的关系...

2117
来自专栏闻道于事

JavaWeb(四)JDBC操作Oracle

JDBC:Java DataBase Connectivity(java数据库连接) SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范...

4024
来自专栏我是攻城师

在Lucene或Solr中实现高亮的策略

3755
来自专栏ml

HDUOJ-------2149Public Sale

Public Sale Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 ...

2908

扫码关注云+社区