分层架构设计(转)

1 互联网分层架构的本质: 1) 互联网分层架构的本质,是数据的移动 2) 互联网分层架构中,数据的传输格式(协议)与数据在各层次的形态很重

1. 互联网分层架构的本质:

    1).互联网分层架构的本质,是数据的移动

    2).互联网分层架构中,数据的传输格式(协议)与数据在各层次的形态很重要

    3).互联网分层架构演进的核心原则与方法:封装与复用

        a.让上游更高效的获取与处理数据,复用

        b.让下游能屏蔽数据的获取细节,封装

2. 互联网分层架构是一个很有意思的问题,服务化的引入,并不是越早越好:

    1).请求处理时间可能会增加

    2).运维可能会更加复杂

    3).定位问题可能会更加麻烦

3. 当业务越来越复杂,垂直拆分的系统越来越多,基础数据服务越来越多,底层数据获取复杂性成为通用痛点的时候,就应该抽象出通用业务服务,简化数据获取过程,提高数据获取效率,向上游屏蔽底层的复杂性。

    这样的好处是:

    复杂的从基础服务获取数据代码,只有在通用业务service处写了一次,没有代码拷贝

    底层基础数据service接口发生变化,只有通用业务service一处需要升级修改

    如果有bug,不管是底层基础数据service的bug,还是通用业务service的bug,都只有一处需要升级修改

    业务web-server获取数据更便捷,获取所有数据,只需一个RPC接口调用

4. 为啥要前后端分离

    1).一点点展现的改动,需要Java工程师们重新编译,打包,上线,重启tomcat,效率极低

    2).原先Java工程师负责所有MVC的研发工作,现在分为Java和FE两块,需要等前端和后端都完成研发,才能一起调试整体效果,不仅增加了沟通成本,任何一块出问题,都可能导致项目延期

        当业务越来越复杂,端上的产品越来越多,展现层的变化越来越快越来越多,站点层存在大量代码拷贝,数据获取复杂性成为通用痛点的时候,就应该进行前后端分离分层抽象,简化数据获取过程,提高数据获取效率,向上游屏蔽底层的复杂性。

        这样的好处是:

        复杂的业务逻辑与数据生成,只有在站点数据层处写了一次,没有代码拷贝

        底层service接口发生变化,只有站点数据层一处需要升级修改

        底层service如果有bug,只有站点数据层一处需要升级修改

        站点展现层可以根据产品的不同形态,传入不同的参数,调用不同的站点数据层接口

        除此之外:

        产品追求绚丽的效果,并对设备兼容性要求高,不再困扰Java工程师,由更专业的FE对接

        一点点展现的改动,不再需要Java工程师们重新编译,打包,上线,重启tomcat

       约定好json接口后,Java和FE分开开发,FE可以用mock的接口自测,不再等待一起联调

5. 分库需求

    高并发大流量的互联网架构,一般通过服务层来访问数据库,随着数据量的增大,数据库需要进行水平切分,分库后将数据分布到不同的数据库实例(甚至物理机器)上,以达到降低数据量,增加实例数的扩容目的。

    一旦涉及分库,逃不开“分库依据”patition key的概念,使用哪一个字段来水平切分数据库呢:大部分的业务场景,会使用业务主键id。

    确定了分库依据patition key后,接下来要确定的是分库算法:大部分的业务场景,会使用业务主键id取模的算法来分库,这样即能够保证每个库的数据分布是均匀的,又能够保证每个库的请求分布是均匀的,

     实在是简单实现负载均衡的好方法,此法在互联网架构中应颇多。

6. 究竟为什么要引入数据库中间件

     1). partition key上的单行查询

          典型场景:通过uid查询user

          场景特点:通过patition key查询;每次只返回一行记录

          解决方案:base-service层通过patition key来进行库路由

      2).非patition key上的单行查询

          典型场景:通过login_name查询user

          场景特点:通过非patition key查询;每次只返回一行记录

          解决方案1:base-service层访问所有库

          解决方案2:base-service先查mapping库,再通过patition key路由

                              a.新建mapping库,记录login_name到uid的映射关系

                              b.当有非 patition key的查询时,先通过login_name查询uid

                              c.再通过patition key进行路由,最终返回一条记录

          解决方案3:基因法 

      3).patition key上的批量查询:

           典型场景:用户列表uid上的IN查询

           场景特点:通过patition key查询;每次返回多行记录

           解决方案1:base-service层访问所有库,结果集到base-service合并

           解决方案2:base-service分析路由规则,按需访问

                               a.base-service根据路由规则分析,判断出有些数据落在库1,有些数据落在库2

                               b.base-service按需访问相关库,而不是访问全库

                               c.base-service合并结果集,返回列表数据

      4).非patition key上的跨库分页需求

          关于分库后,跨库分页的查询需求,详见《业界难题,跨库分页的四种方案》。

      5).本文写到这里,上述一、二、三、四、五其实都不是重点,base-service层通过各种各样的奇技淫巧,能够解决db水平切分后的数据访问问题,只不过:

           base-service层的复杂度提高了; 数据的获取效率降低了

           底层的复杂性会扩散到各个base-service,所有的base-service都要关注:

           patition key路由;非patition key查询,先mapping,再路由;先全库,再合并;先分析,再按需路由;跨库分页处理…

           这个架构图是不是看上去很别扭?如何让数据的获取更加高效快捷呢?

           数据库中间件的引入,势在必行。

           这是“基于服务端”的数据库中间件架构图:

            base-service层,就像访问db一样,访问db-proxy,高效获取数据

            所有底层的复杂性,都屏蔽在db-proxy这一层

            这是“基于客户端”的数据库中间件架构图:

            base-service层,通过db-proxy.jar,高效获取数据

            所有底层的复杂性,都屏蔽在db-proxy.jar这一层

            结论:

           当数据库水平切分,base-service层获取db数据过于复杂,成为通用痛点的时候,就应该抽象出数据库中间件,简化数据获取过程,提高数据获取效率,向上游屏蔽底层的复杂性。