分布式访问框架halo-dal设计思想及原理

 


halo-dal原帖 地址 http://www.iteye.com/topic/1123284


近期刚刚完成halo-dal的代码,一些iteye的朋友通过qq问我设计思路。由于在qq上写的语言组织不是很完整,特此开一帖,详细写一下halo-dal的一些心得与技巧,希望大家能从中获得灵感,或者能指出我设计的不足之处。


halo-dal的雏形在2009年中旬的时候就已经存在了,当时看了手机之家 许超前 设计的 dal 相关的ppt,那时对此很好奇,工作之余设计了 hkframe。这个是建立在jdbc基础之上的一个分布式访问框架以及一个快速开发的web框架,当时没有开源的原因在于自认为没有做到很好,不是我理想的代码。后来一直在我自己的项目上使用。没有再进行更新。


到2011年时,又由于工作需要,再次捡起以前的代码翻看了2天,决定重新设计hkframe,用了2个星期完成了halo 框架,使用全新的思想进行设计。为什么叫halo,由于当时在玩一个游戏 halo,非常喜欢,在玩完游戏之余,非常兴奋的状态下完成的代码,所以起名的时候就写了halo。halo中还是包括一个jdbc访问框架和web框架。当时的框架是一个分布式访问和轻量级orm的结合体,进行开发,效率非常高。由于使用了asm来解决使用反射的问题,所以性能也很不错。唯一缺憾的地方就是只能用jdbc来写,而且必须是我提供的api。


设计halo-dal的原因为公司开发需要,发现没有合适的解决方案,公司内部jdbc hibernate ibatis 都在使用,而且很多设计方案代码入侵性较强,耦合度高。搜索发现hibernate share 只解决了分表问题,无法解决分库问题。ibatis没有多加搜索。后来就有一个想法,能不能设计一种简单的框架能支持各种现有的数据库访问框架呢。由此出现了halo-dal的设计。主要利用五一假期时间完成了代码。



进入正题:::


halo-dal 分布式数据访问设计 


功能主要解决的问题是 :

如何能方便的选择 datasource 和真正的 tablename


顺便要做到:

尽量使用jdbc原有接口,做到最小化的代码入侵。(旨在能使用大家各自喜欢的hibernate ibatis mybatis 也能解决分布式访问问题)


那么所有设计的入口就只能在Connection以下。


为了简化第一个版本的开发,我只想支持PreparedStatement方式,不支持普通的Statement 以及批处理使用的BatchPreparedStatement


什么是我需要的信息?

我的关注点放到了sql上,因为根据jdbc使用经验来说,操作数据库离不开2个最主要的参数sql ,以及sql中赋值的参数 Object[] values。也就是说我只需要拦截到sql以及alues就可以进行分析。sql中包含最显而易见的一个信息,那就是tablename,那么datasource从哪里获得?没有别的东西,只能是靠sql 与values。


每个模块完全独立设计,只靠接口传递信息:

第一个要解决的问题就是解析sql与values获得我需要的信息,对于sql解析,我使用的就是最普通的字符串indexof substring这些api。解析后,我需要的信息就清楚了tablename、条件表达式、赋值表达式。 


以下解释用到的例子

sql: select * from user where level=?

Object[] values =new Object[]{1};


tablename是什么?

在此的table name 不是真正的表名称。而是一个逻辑表明称:我称他为logicTableName,这个逻辑表名。最终会把sql中的tablename 替换成真正的表名。


条件表达式、赋值表达式是什么:

level=? 真正的值是1 那么表达式 就是 level、=、1,组合就成为了SQLExpression。


分表分库的路由解析器:

分析完sql,那就该进行分表分库信息的获取了,这个信息的解析,就在PartitionParser中,在此类的方法中传入tablename 以及表达式等有效信息。


例如level,这个是对user表进行分区的条件,那么我们只需要对level以及他的值进行相关运算即可。这个时候就用到了SQLExpression的数据.

通过解析,最终会获得 dsKey(datasource对应的key) 和 真正的表名称 realTableName,返回的对象PartitionTableInfo包含了这些信息。


如何使用解析的结果?

上面的步骤完成了sql解析以及获得真正datasource key和真正的表名,那么接下来就是如何组织上面2段程序。

既然要解析sql,那么就要找到能获得sql的位置。在Connection中与sql有关的几个主要接口就是


public PreparedStatement prepareStatement(String sql) throws SQLException;
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
            throws SQLException;
......

 


因此,我只能通过自定义DALConnection implements Connection 来实现一个虚拟的Connection,这样就可以截取sql信息。


DALDataSource DALConnection DALPreparedStatement

DataSource.getConnection方法是获得链接的入口,为了获得DALConnection,我就需要自定义DALDataSource implements DataSource.所有真实的DataSource全部由DALDataSource来掌管,这些真实的DataSource全部都通过一个map来保存key为 dsKey(程序中会用到),value就是DataSource。


使用了DALDataSource DALConnection的好处就是能延迟开启真正的DataSource,这样才能保证先解析,再定位。例如,在使用spring 进行事务管理时,

假设 service.methodTX这个方法是需要开启事务,通常只有获得了Connection,才能setAutoCommit(false);,但是我们在这个步骤还没有获得sql,就无法进行解析,只有通过DALDataSource来获得一个DALConnection,这个DALConnection在此时是与任何真实的Connection无关的。因此并不会开启真正的Connection,以及真正的执行setAutoCommit(false);,所有对Connection进行的设置,都会通过DALConnection进行记录,直到获取真正的Connection时,才全部设置到真身。


通过DALConnection.prepareStatement,我们也无法获取真正的PreparedStatement,只有继续造假,创建了DALPreparedStatement.代码的重点就是DALPreparedStatement,因为通过PreparedSatement才可以设置sql需要的参数。DALPreparedStatement,会记录所有对本身的操作,直到获得了真正的PreparedStatement,才把记录的操作设置到真身。

在执行PreparedStatement.execute*()系列方法之前,我们都已经传递了sql,并进行参数赋值,那么我们就可以进行真正的分析了。通过调用prepare()方法就可以进行进行数据访问路由分析。



public boolean execute() throws SQLException {
        this.prepare();
        return ps.execute();
    }

 


prepare这个方法会调用上面讲述的sql分析器 访问路由器,获得dsKey 、realTableName接下来 再次通过sql解析器替换sql中对应的表名。dsKey会存储到一个ThreadLocal类型的对象中,通过DALConnection.getCurrentConnection就可以获得真正的Connection,并开启PreparedStatement,最终执行的execute就落到了指定的数据库上。


整个halo-dal的设计思想就是以上这些了,有不妥之处请大家指出。


希望大家能做出更好的框架供我参考。





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值