SlideShare une entreprise Scribd logo
1  sur  24
Exodus 的重构和向 Apollo 的迁移


 1: Apollo 在哪些环节切入的?

 2: 再谈 Webx 中容器的初始化 ( 重构过程的注意事项
 )

 3: Exodus2 如何迁移?

                    hongjiang 2009-11-14
Apollo 的切入点
 1: pipeline 的 valve
替换 PerformActionValve 和
           PerformScreenValve

                                                             CheckCSRF
                        Hessian                                 可选
Logging     Auth                       AnalyzeURL
                        Remote



              Screen                                         GreenChanel
                              Action                            Trace
             Template

              Screen          Action
                                                    Choose
Redirect                  TBStore
                         CheckCode

                         ExpiredUrl
1 ) DO 的 properties 由框架来自动装配
2 ) AO 自动注入到 Action 中
3 )新的 Action 不再走 CommandDispather
再来说说 webx 中 service 的初始化
         ( 重构过程需要注意 )

之前大部分的应用部署, web 和 biz 层的 service 容器都是分离的,中间
 通过 CommandDispather 方式来交互;但实际上我们没有用到分布式
 的方案(可能以后也不会需要), CommandDispather 没有发挥很大
 的作用,反而麻烦并且弱化了类型系统,显得很“鸡肋”。

Apollo 去除了它,在容器层面也比较“单纯”,都在同一个 Service 容器
  中。

但考虑对以前代码的兼容,又必须支持 commanddispather 模式,如果在
 现有系统上,使用 Apollo 的话,它的布局会很乱,看上去如下:
现有系统上 (Exodus2 重构前 ) 使用 Apollo 注释:
                                                              每个 car 的
                                                              pipeline 配置中
                                        Web 层全局 Spring 容器     用新的 valve 替
 ServiceA       MailService         Apollo 框架将从这里获取 AO/BO     代,判断请求类
                                                              型如果是旧的,
                                                              仍使用以前的
                                                              commanddispat
                                                              her 方式找 biz 层
                                                              ,新的方式则不
      cd                      cd                cd            需要
                                                              commanddispat
      ctx                     ctx               ctx           her ,从 web 层
                                                              的全局 spring 中
                                                              去寻找。这意味
                                                              着 biz 层的所有
                                                              对象需要在 web
                                                              容器中再配置一
                                                              份。

                                                                缩写 cd 表示
                                                              Commanddispat
           AO             BO             DAO                        her
                                                                缩写 ctx 表示
                                                              WebApplicationC
                                                              ontextUtilsBean(
     MailService          TemplateSvc          OtherService   见 web-tool.xml)
如果维护一个上图布局的 Exodus ,非常头大
 ,兼容性所带来的复杂性增加了犯错的可能
 性。所以要迁移的话,必须将 Exodus 的布局
 进行调整(当然,重构也不仅仅是为了向
 apollo 迁移,本来 web/biz 分离的方式对我
 们就已经没有意义。)

我们期望一个简单直接的方式
合并后, Apollo 框架所期望
       的
                     全局 Springx
OtherService   ctx
                          AO       BO      DAO
MailService    cd




   Car1                           Car2

  Spring                          Spring

 ServiceA                      ServiceA

 ServiceB                      ServiceB



注:红色和黄色表示兼容以前的,绿色表示新的请求方式
注释:
原先每个 car 里的 Spring 容器,不再配置任何对象, cd 和 ctx
 移到全局 spring 中,它们之间是级联的,所以 car 中访问 cd
 时会得到全局 spring 中的 cd 。

旧的请求仍走以前的 cd 方式,新的则可以直接访问全局
 spring 中的 AO ;(同样是因为 spring 容器级联的原因)

对于 cd 和 ctx 我们改变它的访问为全局的 spring 容器(也就
 是自身所在的容器)即可,甚至不必修改代码,只要不再配
 置 biz 层 service 容器,它就会访问它所在的 spring 容器。

注: cd 和 ctx 到 AO 之间的虚线不是表示它们有引用关系,表
 示旧的请求方式仍通过 cd 或 ctx 才能得到 AO 的
解惑
Web 层的 Service 分布在 main-instance( 全局 ) 和
 sub-instance(car 级别 )
把 Biz 层合并后,其中 biz 层的 service 要放到 main-
 instance 中

同时考虑有些 sub-instance 的 service 是否也可以放
 到 main-instance 中,
要做这个调整,必须弄清楚 service 的创建过程
以前在《 Exodus2 大局观》中分享过,但说的不特别
 细,这次再详细的了解一下这里,先看一下 web 层
 Service 容器的布局 :
Web 层 Service 容器

                           全局: name=null


              Service 包装                   Service 包装


           Car1                  Car2                 Car3
        name=home             name=offer          name=member


        Service 包装            Service 包装            Service 包装


        Service 包装            Service 包装            Service 包装




                               注释:
每个 car(sub-instance) 是一个 ServiceInstanceContext ,它的 name 就是 car 的
                               name
 全局 (main-instance) 也是一个 ServiceInstanceContext ,它的 name=null
在 DefaultServiceManager.initMapping 时,如果
 全局配置了某些 Service ,局部没有配置(或者反
 之)都会把做一个补全的工作,将全局有,局部没
 有的 Service 也在局部创建一份该 Service 的包装
 (注意,不是直接创建一个 Service )
这个包装类的名字是 ServiceInstance ,个人感觉命
 名很不好,容易理解成其他意思,改为
 ServiceWrapper 更好(后续我会用 Wrapper 来指
 代)。
创建好这个包装的时候,还没有对其中真正的
 Service 进行初始化
后续再对 Service 进行初始化的时候,调用
 wrapper 中的 getInstance( 这个方法的命名
 也不好,不如直观的叫 getRealService 意思
 更清晰些 )
来获取真正的 Service 对象,得到它的 Class ,
 返回 class.newInstance ,才真正进行了初始
 化。
Main-instance?

                                        Sub-instance
                 全局
                                          sub 指定了 class?
      全局指定了 class?
                                        yes
     yes
                                     该 class 在全局             该 class 在全局
                                      也指定了 ?                  也指定了 ?
 返回 Class.     return
newInstance      null
                            全局有               全局无
                                                          全局有               全局无
                     该类实现了
                  MultiInstance 接口        创建一份
                                         Service 实例         该类实现了            return
                                                         MultiInstance 接口     null

              使用全局          创建一份
              Service      Service 实例
                                                       使用全局            创建一份
                                                       Service        Service 实例
►   如果这个 wrapper 是在全局( main-instance )中,
►   getInstance 直接根据全局配置的 Class ,通过反射创建实例对象返回。
►   如果这个 wrapper 是在局部( sub-instance )中,则:
►   1) 局部配置了 Class :
►   判断这个类是否在全局也配置过?
►   1.1) 全局有,并且该类实现了 MultiInstance 接口,则将全局的那个
    Service 对象再做一层包装返回给局部。
►   (可以理解成全局和局部共享 service )
►   1.1) 全局无,或者全局有但该 Service 类没有实现 MultiInstance 接口:
    局部创建一份 Service 类实例(通过 Class.newInstance )

► 2 )局部没有配置 Class :
► 判断这个类是否在全局也配置过?
► 2.1 )全局无,返回 null
► 2.2) 全局有,并且该类实现了 MultiInstance 接口,则将全局的那个
  Service 对象再做一层包装返回给局部。 ( 同 1.1)
► 2.3) 全局有,但该类没有实现 MultiInstance 接口: 局部创建一份
  Service 类实例(同 1.2 )
上面是大致的逻辑,全部的逻辑见 DefautlServiceManager 的内部类
  ServiceInstance.getInstance 方法。
假设我们为全局配置了 A,B 两个 Service ;
      为 car1 配置了 C Service ;为 car2 配置了 D Service 。
         A 实现了 MultiInstance 接口 , 它大致是这个样子



InstanceA              InstanceB       InstanceC          InstanceD
            A                      B               null            null



        Car1                               Car2


                 A                           A



                 B                           B



                 C                          null


                null                         D
现在解释清楚了 service 配置与创建问题,但每个 service 在配
 置文件中的 property 是如何注入到 service 中的呢?

这部分的实现是子 Service.init 方法中实现,对每个 service 要
 根据当前 ServiceInstanceContext 中的 Configuration 来获取
 它所需要的 properties.
框架初始化时,把所有的配置文件 : webx-
 default.xml(webroot 下面 ), webx-turbine-
 default.xml(TurbineScheme 的 ), webx-
 default.xml(DefaultScheme 的 ) ,以及每个 car 下面的
 webx.xml 都合并起来。(当然里面做了去重工作)
Configuration 类用 key-value 方式来存储

对于全局的 Service ,它的 key 命名是这样的 :
……
services.RunDataService.class = …
services.RunDataService.earlyInit = …
services.RunDataService.rundata.class = ..
services.RunDataService.request.buffered.class = …
……
services.PullService.class = ..
……

而对于某个 car ,比如 offer 的,它会用自己的 name 作为 key 的前缀,命名是这样的:
……
offer.services.ResourceLoaderService.class      =…
offer.services.ResourceLoaderService.earlyInit = …
offer.services.ResourceLoaderService.resource.descriptors = …
offer.services.PullService.class = …
……
还是具体的来看 DefaultBeanFactoryService 的 init() 过程中对 properties 的加载:

见代码:
String[] descriptors = getConfiguration().getStringArray(BEAN_DESCRIPTORS);

1) 如果是在全局
从 Configuration 中获取 services.BeanFactoryService.bean.descriptors 这个 key 的值。
因为全局对 BeanFactoryService 配置了 bean.descriptors ,则能够得到这些 properties ,放入
   descriptors 数组。
( 对于 BeanFactoryService 它的 properties 通常是另一个或多个 xml ,在此 xml 里面描述了要
   加载的 bean)

2) 如果是在某个 car 下,比如 offer 下
从 Configuration 中获取 offer.services.BeanFactoryService.bean.descriptors 这个 key 的值
   。
如果我们没有在局部设定 BeanFactoryService ,当然找不到上面的 key 了,得到的
   descriptors 是一个空数组。它后续也不会设置任何 property 。

所以如果我们考虑将某个 sub-instance 的 service 移到 main 中,如果此 service 不是
  MultiInstance 接口实现者,要考虑其中的 properties 问题
Exodus2 的现状及重构
  三层 service 容器的合并
Exodus2 的现状 , 有 3 个 service 容器
Web 层 service 容器

                          ViewCache
                            Service



    Car1           Car2        Car3        Viewcache 层
                                           Service 容器

                                           ResourceLoaderSvc



                                           ViewCacheSupport
                                                Service

Biz 层 service 容器

             AO/BO/DAO ...

                               ViewCache
  Mail     Template   Others     Service
ViewCache 主要是访问我们的类目信息,存放
  在 vc.xml.china 文件中,加载到内存中大约
  30 多 M
Web 和 biz 层都要访问到,所以把它独立提取
  出来了。

现在要把这 3 层 service 容器合并为一个容器
 ,使得后续更容易迁移到 apollo 框架
合并后
                     全局 Spring
OtherService   ctx
                         AO       BO      DAO
ViewCache      cd




   Car1                          Car2

  Spring                         Spring

 ServiceA                     ServiceA

 ServiceB                     ServiceB
更新:
提交测试时发现 headquarters 二方库对
 ViewCacheService 接口新增了方法,经交流
 虽然上层可以补充实现此方法返回 null (我
 们不会用到),但不排除后续 headquarters
 仍有修改的可能,所以对 ViewCache 最终放
 弃了合并,仍独立使用一个 service 容器

Contenu connexe

Tendances

Java线上应用问题排查方法和工具(空望)
Java线上应用问题排查方法和工具(空望)Java线上应用问题排查方法和工具(空望)
Java线上应用问题排查方法和工具(空望)
ykdsg
 
Java cpu
Java cpuJava cpu
Java cpu
ykdsg
 
线程与并发
线程与并发线程与并发
线程与并发
Tony Deng
 
深入理解Andorid重难点
深入理解Andorid重难点深入理解Andorid重难点
深入理解Andorid重难点
Bin Shao
 
Java华为面试题
Java华为面试题Java华为面试题
Java华为面试题
yiditushe
 
Btrace intro(撒迦)
Btrace intro(撒迦)Btrace intro(撒迦)
Btrace intro(撒迦)
ykdsg
 
Java垃圾收集原理
Java垃圾收集原理Java垃圾收集原理
Java垃圾收集原理
yin gong
 
Sun jdk 1.6内存管理 -使用篇
Sun jdk 1.6内存管理 -使用篇Sun jdk 1.6内存管理 -使用篇
Sun jdk 1.6内存管理 -使用篇
bluedavy lin
 

Tendances (20)

JVM内容管理和垃圾回收
JVM内容管理和垃圾回收JVM内容管理和垃圾回收
JVM内容管理和垃圾回收
 
Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析
 
Effective linux.1.(commandline)
Effective linux.1.(commandline)Effective linux.1.(commandline)
Effective linux.1.(commandline)
 
Java线上应用问题排查方法和工具(空望)
Java线上应用问题排查方法和工具(空望)Java线上应用问题排查方法和工具(空望)
Java线上应用问题排查方法和工具(空望)
 
并发编程交流
并发编程交流并发编程交流
并发编程交流
 
Rpc原理与实现
Rpc原理与实现Rpc原理与实现
Rpc原理与实现
 
IOS入门分享
IOS入门分享IOS入门分享
IOS入门分享
 
Java cpu
Java cpuJava cpu
Java cpu
 
线程与并发
线程与并发线程与并发
线程与并发
 
Java Crash分析(2012-05-10)
Java Crash分析(2012-05-10)Java Crash分析(2012-05-10)
Java Crash分析(2012-05-10)
 
为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)
 
深入理解Andorid重难点
深入理解Andorid重难点深入理解Andorid重难点
深入理解Andorid重难点
 
Java华为面试题
Java华为面试题Java华为面试题
Java华为面试题
 
千呼萬喚始出來的 Java SE 7
千呼萬喚始出來的 Java SE 7千呼萬喚始出來的 Java SE 7
千呼萬喚始出來的 Java SE 7
 
180518 ntut js and node
180518 ntut js and node180518 ntut js and node
180518 ntut js and node
 
Btrace intro(撒迦)
Btrace intro(撒迦)Btrace intro(撒迦)
Btrace intro(撒迦)
 
Java垃圾收集原理
Java垃圾收集原理Java垃圾收集原理
Java垃圾收集原理
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析
 
高性能的Java代码编写及常见问题排查
高性能的Java代码编写及常见问题排查高性能的Java代码编写及常见问题排查
高性能的Java代码编写及常见问题排查
 
Sun jdk 1.6内存管理 -使用篇
Sun jdk 1.6内存管理 -使用篇Sun jdk 1.6内存管理 -使用篇
Sun jdk 1.6内存管理 -使用篇
 

En vedette

深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)
wang hongjiang
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closures
wang hongjiang
 

En vedette (20)

Enum开锁
Enum开锁Enum开锁
Enum开锁
 
善用工具
善用工具善用工具
善用工具
 
Effective linux.2.(tools)
Effective linux.2.(tools)Effective linux.2.(tools)
Effective linux.2.(tools)
 
聊一些电影
聊一些电影聊一些电影
聊一些电影
 
深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)
 
Real world akka recepies v3
Real world akka recepies v3Real world akka recepies v3
Real world akka recepies v3
 
中等创业公司后端技术选型
中等创业公司后端技术选型中等创业公司后端技术选型
中等创业公司后端技术选型
 
Automatic Scaling Iterative Computations
Automatic Scaling Iterative ComputationsAutomatic Scaling Iterative Computations
Automatic Scaling Iterative Computations
 
Behavioral Simulations in MapReduce
Behavioral Simulations in MapReduceBehavioral Simulations in MapReduce
Behavioral Simulations in MapReduce
 
Apache Kafka, and the Rise of Stream Processing
Apache Kafka, and the Rise of Stream ProcessingApache Kafka, and the Rise of Stream Processing
Apache Kafka, and the Rise of Stream Processing
 
Apache Kafka at LinkedIn
Apache Kafka at LinkedInApache Kafka at LinkedIn
Apache Kafka at LinkedIn
 
Building Realtim Data Pipelines with Kafka Connect and Spark Streaming
Building Realtim Data Pipelines with Kafka Connect and Spark StreamingBuilding Realtim Data Pipelines with Kafka Connect and Spark Streaming
Building Realtim Data Pipelines with Kafka Connect and Spark Streaming
 
Building a Replicated Logging System with Apache Kafka
Building a Replicated Logging System with Apache KafkaBuilding a Replicated Logging System with Apache Kafka
Building a Replicated Logging System with Apache Kafka
 
Building Stream Infrastructure across Multiple Data Centers with Apache Kafka
Building Stream Infrastructure across Multiple Data Centers with Apache KafkaBuilding Stream Infrastructure across Multiple Data Centers with Apache Kafka
Building Stream Infrastructure across Multiple Data Centers with Apache Kafka
 
Building Reactive Distributed Systems For Streaming Big Data, Analytics & Mac...
Building Reactive Distributed Systems For Streaming Big Data, Analytics & Mac...Building Reactive Distributed Systems For Streaming Big Data, Analytics & Mac...
Building Reactive Distributed Systems For Streaming Big Data, Analytics & Mac...
 
Introduction to Kafka Streams
Introduction to Kafka StreamsIntroduction to Kafka Streams
Introduction to Kafka Streams
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closures
 
functional-scala
functional-scalafunctional-scala
functional-scala
 
泛型总结
泛型总结泛型总结
泛型总结
 
Scala类型系统
Scala类型系统Scala类型系统
Scala类型系统
 

Similaire à Exodus重构和向apollo迁移

Hadoop学习总结
Hadoop学习总结Hadoop学习总结
Hadoop学习总结
ordinary2012
 
Huangjing renren
Huangjing renrenHuangjing renren
Huangjing renren
d0nn9n
 
Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储 Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储
zhen chen
 
Cassandra的初步使用及一些简单的操作
Cassandra的初步使用及一些简单的操作Cassandra的初步使用及一些简单的操作
Cassandra的初步使用及一些简单的操作
zhubin885
 
Cassandra运维之道(office2003)
Cassandra运维之道(office2003)Cassandra运维之道(office2003)
Cassandra运维之道(office2003)
haiyuan ning
 
为10g rac cluster添加节点
为10g rac cluster添加节点为10g rac cluster添加节点
为10g rac cluster添加节点
maclean liu
 
中心教员J2 Ee面试题
中心教员J2 Ee面试题中心教员J2 Ee面试题
中心教员J2 Ee面试题
yiditushe
 
Scrum gathering 2012 Shanghai_精益与持续改进分会场演讲话题: 大型企业ci平台建设和实施分享(陈小光)
Scrum gathering 2012 Shanghai_精益与持续改进分会场演讲话题: 大型企业ci平台建设和实施分享(陈小光)Scrum gathering 2012 Shanghai_精益与持续改进分会场演讲话题: 大型企业ci平台建设和实施分享(陈小光)
Scrum gathering 2012 Shanghai_精益与持续改进分会场演讲话题: 大型企业ci平台建设和实施分享(陈小光)
JoXuZi
 
Cassandra简介.ppt
Cassandra简介.pptCassandra简介.ppt
Cassandra简介.ppt
james tong
 

Similaire à Exodus重构和向apollo迁移 (20)

Hadoop学习总结
Hadoop学习总结Hadoop学习总结
Hadoop学习总结
 
Huangjing renren
Huangjing renrenHuangjing renren
Huangjing renren
 
Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储 Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储
 
Cap 理论与实践
Cap 理论与实践Cap 理论与实践
Cap 理论与实践
 
Cassandra的初步使用及一些简单的操作
Cassandra的初步使用及一些简单的操作Cassandra的初步使用及一些简单的操作
Cassandra的初步使用及一些简单的操作
 
Cassandra运维之道(office2003)
Cassandra运维之道(office2003)Cassandra运维之道(office2003)
Cassandra运维之道(office2003)
 
为10g rac cluster添加节点
为10g rac cluster添加节点为10g rac cluster添加节点
为10g rac cluster添加节点
 
Vulkan introduction
Vulkan introductionVulkan introduction
Vulkan introduction
 
Spring 2.0 技術手冊第十章 - 專案:線上書籤
Spring 2.0 技術手冊第十章 - 專案:線上書籤Spring 2.0 技術手冊第十章 - 專案:線上書籤
Spring 2.0 技術手冊第十章 - 專案:線上書籤
 
中心教员J2 Ee面试题
中心教员J2 Ee面试题中心教员J2 Ee面试题
中心教员J2 Ee面试题
 
Thinking in React by Deot
Thinking in React by Deot Thinking in React by Deot
Thinking in React by Deot
 
Intro to svn
Intro to svnIntro to svn
Intro to svn
 
Spring 2.0 技術手冊第五章 - JDBC、交易支援
Spring 2.0 技術手冊第五章 - JDBC、交易支援Spring 2.0 技術手冊第五章 - JDBC、交易支援
Spring 2.0 技術手冊第五章 - JDBC、交易支援
 
No sql数据库笔谈
No sql数据库笔谈No sql数据库笔谈
No sql数据库笔谈
 
Nae client(using Node.js to create shell cmd)
Nae client(using Node.js to create shell cmd)Nae client(using Node.js to create shell cmd)
Nae client(using Node.js to create shell cmd)
 
容器驅動開發 - .NET Conf 2017 @ 台中
容器驅動開發 - .NET Conf 2017 @ 台中容器驅動開發 - .NET Conf 2017 @ 台中
容器驅動開發 - .NET Conf 2017 @ 台中
 
Scrum gathering 2012 Shanghai_精益与持续改进分会场演讲话题: 大型企业ci平台建设和实施分享(陈小光)
Scrum gathering 2012 Shanghai_精益与持续改进分会场演讲话题: 大型企业ci平台建设和实施分享(陈小光)Scrum gathering 2012 Shanghai_精益与持续改进分会场演讲话题: 大型企业ci平台建设和实施分享(陈小光)
Scrum gathering 2012 Shanghai_精益与持续改进分会场演讲话题: 大型企业ci平台建设和实施分享(陈小光)
 
Cfengine培训文档 刘天斯
Cfengine培训文档 刘天斯Cfengine培训文档 刘天斯
Cfengine培训文档 刘天斯
 
Make talk-cn
Make talk-cnMake talk-cn
Make talk-cn
 
Cassandra简介.ppt
Cassandra简介.pptCassandra简介.ppt
Cassandra简介.ppt
 

Exodus重构和向apollo迁移

  • 1. Exodus 的重构和向 Apollo 的迁移 1: Apollo 在哪些环节切入的? 2: 再谈 Webx 中容器的初始化 ( 重构过程的注意事项 ) 3: Exodus2 如何迁移? hongjiang 2009-11-14
  • 2. Apollo 的切入点 1: pipeline 的 valve
  • 3. 替换 PerformActionValve 和 PerformScreenValve CheckCSRF Hessian 可选 Logging Auth AnalyzeURL Remote Screen GreenChanel Action Trace Template Screen Action Choose Redirect TBStore CheckCode ExpiredUrl
  • 4. 1 ) DO 的 properties 由框架来自动装配 2 ) AO 自动注入到 Action 中 3 )新的 Action 不再走 CommandDispather
  • 5. 再来说说 webx 中 service 的初始化 ( 重构过程需要注意 ) 之前大部分的应用部署, web 和 biz 层的 service 容器都是分离的,中间 通过 CommandDispather 方式来交互;但实际上我们没有用到分布式 的方案(可能以后也不会需要), CommandDispather 没有发挥很大 的作用,反而麻烦并且弱化了类型系统,显得很“鸡肋”。 Apollo 去除了它,在容器层面也比较“单纯”,都在同一个 Service 容器 中。 但考虑对以前代码的兼容,又必须支持 commanddispather 模式,如果在 现有系统上,使用 Apollo 的话,它的布局会很乱,看上去如下:
  • 6. 现有系统上 (Exodus2 重构前 ) 使用 Apollo 注释: 每个 car 的 pipeline 配置中 Web 层全局 Spring 容器 用新的 valve 替 ServiceA MailService Apollo 框架将从这里获取 AO/BO 代,判断请求类 型如果是旧的, 仍使用以前的 commanddispat her 方式找 biz 层 ,新的方式则不 cd cd cd 需要 commanddispat ctx ctx ctx her ,从 web 层 的全局 spring 中 去寻找。这意味 着 biz 层的所有 对象需要在 web 容器中再配置一 份。 缩写 cd 表示 Commanddispat AO BO DAO her 缩写 ctx 表示 WebApplicationC ontextUtilsBean( MailService TemplateSvc OtherService 见 web-tool.xml)
  • 7. 如果维护一个上图布局的 Exodus ,非常头大 ,兼容性所带来的复杂性增加了犯错的可能 性。所以要迁移的话,必须将 Exodus 的布局 进行调整(当然,重构也不仅仅是为了向 apollo 迁移,本来 web/biz 分离的方式对我 们就已经没有意义。) 我们期望一个简单直接的方式
  • 8. 合并后, Apollo 框架所期望 的 全局 Springx OtherService ctx AO BO DAO MailService cd Car1 Car2 Spring Spring ServiceA ServiceA ServiceB ServiceB 注:红色和黄色表示兼容以前的,绿色表示新的请求方式
  • 9. 注释: 原先每个 car 里的 Spring 容器,不再配置任何对象, cd 和 ctx 移到全局 spring 中,它们之间是级联的,所以 car 中访问 cd 时会得到全局 spring 中的 cd 。 旧的请求仍走以前的 cd 方式,新的则可以直接访问全局 spring 中的 AO ;(同样是因为 spring 容器级联的原因) 对于 cd 和 ctx 我们改变它的访问为全局的 spring 容器(也就 是自身所在的容器)即可,甚至不必修改代码,只要不再配 置 biz 层 service 容器,它就会访问它所在的 spring 容器。 注: cd 和 ctx 到 AO 之间的虚线不是表示它们有引用关系,表 示旧的请求方式仍通过 cd 或 ctx 才能得到 AO 的
  • 10. 解惑 Web 层的 Service 分布在 main-instance( 全局 ) 和 sub-instance(car 级别 ) 把 Biz 层合并后,其中 biz 层的 service 要放到 main- instance 中 同时考虑有些 sub-instance 的 service 是否也可以放 到 main-instance 中, 要做这个调整,必须弄清楚 service 的创建过程 以前在《 Exodus2 大局观》中分享过,但说的不特别 细,这次再详细的了解一下这里,先看一下 web 层 Service 容器的布局 :
  • 11. Web 层 Service 容器 全局: name=null Service 包装 Service 包装 Car1 Car2 Car3 name=home name=offer name=member Service 包装 Service 包装 Service 包装 Service 包装 Service 包装 Service 包装 注释: 每个 car(sub-instance) 是一个 ServiceInstanceContext ,它的 name 就是 car 的 name 全局 (main-instance) 也是一个 ServiceInstanceContext ,它的 name=null
  • 12. 在 DefaultServiceManager.initMapping 时,如果 全局配置了某些 Service ,局部没有配置(或者反 之)都会把做一个补全的工作,将全局有,局部没 有的 Service 也在局部创建一份该 Service 的包装 (注意,不是直接创建一个 Service ) 这个包装类的名字是 ServiceInstance ,个人感觉命 名很不好,容易理解成其他意思,改为 ServiceWrapper 更好(后续我会用 Wrapper 来指 代)。
  • 13. 创建好这个包装的时候,还没有对其中真正的 Service 进行初始化 后续再对 Service 进行初始化的时候,调用 wrapper 中的 getInstance( 这个方法的命名 也不好,不如直观的叫 getRealService 意思 更清晰些 ) 来获取真正的 Service 对象,得到它的 Class , 返回 class.newInstance ,才真正进行了初始 化。
  • 14. Main-instance? Sub-instance 全局 sub 指定了 class? 全局指定了 class? yes yes 该 class 在全局 该 class 在全局 也指定了 ? 也指定了 ? 返回 Class. return newInstance null 全局有 全局无 全局有 全局无 该类实现了 MultiInstance 接口 创建一份 Service 实例 该类实现了 return MultiInstance 接口 null 使用全局 创建一份 Service Service 实例 使用全局 创建一份 Service Service 实例
  • 15. 如果这个 wrapper 是在全局( main-instance )中, ► getInstance 直接根据全局配置的 Class ,通过反射创建实例对象返回。 ► 如果这个 wrapper 是在局部( sub-instance )中,则: ► 1) 局部配置了 Class : ► 判断这个类是否在全局也配置过? ► 1.1) 全局有,并且该类实现了 MultiInstance 接口,则将全局的那个 Service 对象再做一层包装返回给局部。 ► (可以理解成全局和局部共享 service ) ► 1.1) 全局无,或者全局有但该 Service 类没有实现 MultiInstance 接口: 局部创建一份 Service 类实例(通过 Class.newInstance ) ► 2 )局部没有配置 Class : ► 判断这个类是否在全局也配置过? ► 2.1 )全局无,返回 null ► 2.2) 全局有,并且该类实现了 MultiInstance 接口,则将全局的那个 Service 对象再做一层包装返回给局部。 ( 同 1.1) ► 2.3) 全局有,但该类没有实现 MultiInstance 接口: 局部创建一份 Service 类实例(同 1.2 ) 上面是大致的逻辑,全部的逻辑见 DefautlServiceManager 的内部类 ServiceInstance.getInstance 方法。
  • 16. 假设我们为全局配置了 A,B 两个 Service ; 为 car1 配置了 C Service ;为 car2 配置了 D Service 。 A 实现了 MultiInstance 接口 , 它大致是这个样子 InstanceA InstanceB InstanceC InstanceD A B null null Car1 Car2 A A B B C null null D
  • 17. 现在解释清楚了 service 配置与创建问题,但每个 service 在配 置文件中的 property 是如何注入到 service 中的呢? 这部分的实现是子 Service.init 方法中实现,对每个 service 要 根据当前 ServiceInstanceContext 中的 Configuration 来获取 它所需要的 properties. 框架初始化时,把所有的配置文件 : webx- default.xml(webroot 下面 ), webx-turbine- default.xml(TurbineScheme 的 ), webx- default.xml(DefaultScheme 的 ) ,以及每个 car 下面的 webx.xml 都合并起来。(当然里面做了去重工作)
  • 18. Configuration 类用 key-value 方式来存储 对于全局的 Service ,它的 key 命名是这样的 : …… services.RunDataService.class = … services.RunDataService.earlyInit = … services.RunDataService.rundata.class = .. services.RunDataService.request.buffered.class = … …… services.PullService.class = .. …… 而对于某个 car ,比如 offer 的,它会用自己的 name 作为 key 的前缀,命名是这样的: …… offer.services.ResourceLoaderService.class =… offer.services.ResourceLoaderService.earlyInit = … offer.services.ResourceLoaderService.resource.descriptors = … offer.services.PullService.class = … ……
  • 19. 还是具体的来看 DefaultBeanFactoryService 的 init() 过程中对 properties 的加载: 见代码: String[] descriptors = getConfiguration().getStringArray(BEAN_DESCRIPTORS); 1) 如果是在全局 从 Configuration 中获取 services.BeanFactoryService.bean.descriptors 这个 key 的值。 因为全局对 BeanFactoryService 配置了 bean.descriptors ,则能够得到这些 properties ,放入 descriptors 数组。 ( 对于 BeanFactoryService 它的 properties 通常是另一个或多个 xml ,在此 xml 里面描述了要 加载的 bean) 2) 如果是在某个 car 下,比如 offer 下 从 Configuration 中获取 offer.services.BeanFactoryService.bean.descriptors 这个 key 的值 。 如果我们没有在局部设定 BeanFactoryService ,当然找不到上面的 key 了,得到的 descriptors 是一个空数组。它后续也不会设置任何 property 。 所以如果我们考虑将某个 sub-instance 的 service 移到 main 中,如果此 service 不是 MultiInstance 接口实现者,要考虑其中的 properties 问题
  • 20. Exodus2 的现状及重构 三层 service 容器的合并
  • 21. Exodus2 的现状 , 有 3 个 service 容器 Web 层 service 容器 ViewCache Service Car1 Car2 Car3 Viewcache 层 Service 容器 ResourceLoaderSvc ViewCacheSupport Service Biz 层 service 容器 AO/BO/DAO ... ViewCache Mail Template Others Service
  • 22. ViewCache 主要是访问我们的类目信息,存放 在 vc.xml.china 文件中,加载到内存中大约 30 多 M Web 和 biz 层都要访问到,所以把它独立提取 出来了。 现在要把这 3 层 service 容器合并为一个容器 ,使得后续更容易迁移到 apollo 框架
  • 23. 合并后 全局 Spring OtherService ctx AO BO DAO ViewCache cd Car1 Car2 Spring Spring ServiceA ServiceA ServiceB ServiceB
  • 24. 更新: 提交测试时发现 headquarters 二方库对 ViewCacheService 接口新增了方法,经交流 虽然上层可以补充实现此方法返回 null (我 们不会用到),但不排除后续 headquarters 仍有修改的可能,所以对 ViewCache 最终放 弃了合并,仍独立使用一个 service 容器