剖析框架代码结构的系统方法

在日常开发过程中,我们经常会使用Dubbo、Spring Cloud、MyBatis等开源框架,这些框架非常主流,值得我们深入分析它们的源码。那么,当你拿到这些框架时,如何能够快速把握框架源码的组织结构呢?这就是今天我们要讨论的内容。

在本讲中,针对框架代码结构,我们给出四个维度的剖析方法,分别是架构演进过程、核心执行流程、基础架构组成和可扩展性设计。让我们一起来看一下。


架构演进过程与代码结构

我们先来回顾一下Dubbo框架,我们知道Dubbo源代码包含了common、remoting、rpc、cluster、registry、monitor、config和container等八大核心包结构。


RPC框架的基本组成

Dubbo是一个典型的RPC框架。在对Dubbo进行详细讨论之前,我们可以大致来想象一下设计和实现一个RPC的基本思路和步骤。


在上图中,我们看到首先要实现一个最基础的技术组件,用来完成一对一的服务提供者和消费者之间的远程访问。

有了远程访问组件之后,我们就能够实现远程通信了,但采用的是代码中嵌入远程服务信息的网络通信手段。这种做法比较直接,但对于一个服务框架而言显然是不利于系统开发和集成的,这时候我们就希望通过一定的手段来使远程调用本地化,即实现透明化远程调用。目前实现这一目标的主流做法就是代理。

现在服务访问已经非常简单了,但还只是一个一对一的模式,需要添加集群访问模式。集群能够为我们提供负载均衡、集群容错等机制。

最后,如果RPC中的服务提供者和消费者的数量逐渐增多,那么就需要添加服务治理机制,实现服务的自动发现和注册。同时,为了监控系统中服务的状态,一般还需要添加一定的服务监控措施。

Dubbo中的架构演进过程

Dubbo作为一款优秀的RPC框架,在设计整个代码结构中也基本采用了上述思路。同时,Dubbo也充分体现了功能演进思想,下图展示的就是Dubbo代码结构的逐步演进过程。


显然,以上代码结构体现了从简单到复杂、从核心功能到辅助机制逐步实现和完善的设计过程。

我们先来看位于最上面的Protocol层,该层是Dubbo中实现RPC架构的基础组件。我们可以通过Protocol层实现远程过程调用。

但是这种调用方式偏底层,开发人员需要指定远程服务器地址并手工触发远程调用,也就是无法实现透明化的调用过程。为了解决这个问题,Dubbo通过引入Proxy机制来封装透明化调用。

现在,远程调用已经实现了,接下来要考虑的问题就是追求性能和可用性,这时候就需要集成负载均衡和集群容错机制,Dubbo通过引入Cluster组件实现了这一目标。

对于Dubbo而言,为了追求高性能和高扩展性,还专门实现了一套Dubbo协议。这在普通RPC架构中是不常见的,Dubbo通过Remoting包做到了这一点。

最后,Dubbo也提供了Registry和Monitor组件来分别实现服务的注册和监控功能。

借助于上图所示的五个步骤,我们基于架构演进过程分析了Dubbo中各个代码包的定位和作用。作为总结,我们从一个视图出发对整个过程进行整合。


通过上图,我们可以把Dubbo中各个核心组件分成三大部分。最底部的Remoting部分主要完成网络通信,包含Exchange、Transport和Serialize组件,其中Exchange和Transport组件的功能用来完成远程通信,而Serialize则主要完成数据的序列化和反序列化过程。上图中的中间部分称之为内核部分,包含了Protocal、Proxy、Cluster、Registry、Monitor等组件。而位于最上面的Business部分代表的是业务服务。

最后,我们结合本讲内容再对Dubbo中包结构与代码层次之间的关系做简单梳理,我们在包结构中分别添加了对应的分层组件,如下图所示。


基于以上内容的分析,我们在阅读Dubbo框架源码时,就能够做到从易到难进行逐步拆解,才能够而避免陷入具体的代码细节中。

核心执行流程与代码结构

任何一个软件系统都存在核心执行流程,对大多数开源框架而言,这种核心主流程往往只有一条,所以只要抓住这条主流程,我们就能把握框架的整体代码结构。

那么,对于一个框架而言,什么才是它的主流程呢?这个问题实际上并不难回答。以前面提到的Dubbo框架为例,它的主流程就是一次RPC请求和响应过程。而对于接下来要讨论的MyBatis而言,显然我们应该关注它的一次SQL执行过程。

MyBatis中的主执行流程

接下来,我们将来到MyBatis这个ORM框架,尝试从主流程角度对该框架进行代码结构的解读。

在日常开发过程中,通常都是将MyBatis和Spring整合在一起使用,而这种整合在方便开发的同时也屏蔽了部分流程上的细节,一定程度上导致我们对MyBatis的执行过程缺少了全局的理解。因此,我们还是直接从MyBatis的原始使用方法入手分析SQL执行的主流程,如下所示。代码1。

//获取MyBatis配置文件

InputStream is = Resource.getResourceAsStream("mybatis_config.xml");

//构建SqlSessionFactory

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

//创建SqlSession

SqlSession sqlSession = sqlSessionFactory.openSession();

//获取Mapper

TestMapper testMapper = sqlSession.getMapper(TestMapper.class);

//获取和使用目标对象

TestObject testObject = testMapper.getObject("testCode1");

System.out.println(testObject.getCode());

以上几行代码是使用MyBatis这个框架的基本开发流程,这一流程取决于MyBatis为我们开放的接口。作为一个框架,MyBatis同样屏蔽了底层实现上的细节。但正因为有这些接口的存在,为我们深入框架内核提供了入口。显然,在上述代码中,MyBatis为我们提供了两个核心的入口,一个是SqlSession,另一个是Mapper,我们将从这两个入口开始切入。

我们先来讨论一个核心问题,即我们是如何获取SqlSession?下图通过时序图展示了整个流程。


获取了SqlSession之后,接下来就要讨论Mapper部分的内容,获取Mapper的过程如下图所示。


从上图中,我们注意到MyBatis是通过工厂类MapperProxyFactory获取到MapperProxy对象,而这个MapperProxy对象就是一种代理对象,可以通过它来获取Mapper类。而在通过代理获取到Mapper之后,整个执行过程还是会回到SqlSession。事实上,关于具体的SQL操作交给了执行器对象Executor去处理。

最后,我们同样试图把前面介绍的各个核心类整合在一起完整描述流程执行的过程,结果如下所示。


从上图中可以看到,Configuration对象的获取是通过对MyBatis配置文件的解析,SqlSession的获取使用了工厂方法。而用户自定义Mapper中方法的执行则是通过代理模式来实现的,对执行结果的处理也分别使用了缓存。

这里内容的讨论还是关注于整体的执行流程,上述过程中所涉及的各种设计模式、代理机制、缓存机制等都属于具体的技术实现点,而不是核心的主流程。

作为总结,今天的内容关注与两种剖析代码结构的方法,即架构演进过程和核心执行流程。同时,我们采用Dubbo和MyBatis这两个主流开源框架讲述如何应用这些主题来具体剖析框架的代码结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值