细数软件架构中的解耦( 二 )


细数软件架构中的解耦

文章插图
 
在 JAVA 面向对象的语言里,使用方通过 Provider 接口 Response doService(Request r) 来对外刻画它的招标文件 。然后三个供应方,LocalProvider、RemoteProvider 和 AsyncProvider 来应标 。使用方只使用 Provider 接口,至于它跟哪个具体的 Provider 绑定,完全可以在“采购”时刻动态替换 。
面向接口动态绑定的解耦,体现在使用方把依赖的服务抽象为一个接口,依赖这个抽象的接口,而不依赖于具体的服务提供者,以便应对服务提供者变化的可能性 。
架构层 -Naming 解析动态绑定
细数软件架构中的解耦

文章插图
 
上图是域名服务 DNS 的示意流程 。客户端并不直接通过 IP 地址来访问 Provider#A 或 B,而是先询问 Naming 服务,并依据返回的服务列表,再访问 Provider#A 或 B 。如果某个 Provider 故障了,可以替换转移到其他的 Provider 。出于性能考虑,也可以在客户端把 Naming 的结果缓存起来,并配个缓存更新机制 。
基于 ZooKeeper 的应用层名字服务,思想上类似 DNS 。不同的是,它基于 TCP 长链接来实现 Server Push,可及时刷新服务列表 。
Naming 解析动态绑定的解耦,体现在使用方把依赖的对象或网络进程,抽象为一个名字,名字代表的具体服务提供者则通过 Lookup 机制返回,进而做到如果提供者有变化,只要改变 Lookup 的结果,无需改变使用方代码 。
前后节植入
前后节植入的设计理念是服务器是流程的集合,流程是环节的序列 。改变一个流程的行为,可以通过在其前后植入一个新环节来实现 。前后节植入,在应用层表现为 Chain 拦截模式,在架构层表现为 Proxy 代理模式 。
应用层 -Chain 拦截模式
细数软件架构中的解耦

文章插图
 
上图是 Strtus2 的架构,每个 Action 的执行,都会被包裹在一系列 Interceptor 里面,形成一条处理链 Chain,每个 Interceptor 会进行 PreHandler 和 PostHandler 处理 。这里的 Interceptor 可以增加、删除或替换,以此实现可拓展性 。比如可以在 Interceptor 里做鉴权、日志、性能统计、限流等 。
Chain 插拔的动态绑定,通过增删替 Interceptor,把过去 URL 与 Action 的 1:1 的处理关系,转变成了 M:N 的处理链 。一类请求(某个 URL),可以被多个 Interceptor 处理;一个 Interceptor 也可以处理多类请求 。
顺便说一下,Strtus2 这里说的“动态绑定”,是配置相对硬编码而言的 。严格意义上,这里的绑定是编译期的,不是运行期的,是静态的绑定 。类似的架构还有 Spring AOP 和 Servlet Filters 机制 。
架构层 -Proxy 代理模式
细数软件架构中的解耦

文章插图
 
上图是一个 Proxy 架构模式,这个应用极其广泛 。比如 HTTP 的 Nginx,SQL 的 Apache Calcite,memcached 和 redis 的 twitter/twemproxy 。为什么?因为 Proxy 对于 Backend 而言就是流量入口,是中间人,能扮演架构层面的 AOP 机制,可拓展性非常强 。
当一个请求过来后,刚开始 Proxy 转发给 Backend#A 。但是业务发展了,Proxy 也可以转发给 Backend#B 以实现负载均衡,更重要的是 A 和 B 还可以不同的版本,以实现灰度发布 。还可以植入 PrePlugin 和 PostPlugin:
在 PrePlugin 里可以做权限控制、流量控制、请求改写、缓存加速、恶意流量拦截、PV 统计、性能 Profile、ChaosMonkey 混沌事件植入等等 。
在 PostPlugin 里,还可以做响应报文改写,安全加密(后端不用考虑数据安全,对外时统一加密处理)、压缩加速等等 。
两者融合的实例 -CNAME 别名
细数软件架构中的解耦

文章插图
 
上图是一种混合模式:既有 Naming 解析,又有 Proxy 代理 。而且 Naming 服务,为了支持可拓展,还引入了父子层级,末端的 Naming 服务,完全可以委托给上一层级的 Naming 服务 。
在 DNS 里面,我们经常会看到 www.example.org 的域名解析,CNAME 别名到 www.example.org.cdnprovider.com (它是 cdnprovider.com 的子域名),这样客户端不用修改,依然访问的是 www.example.org ,但是对应的后端服务,却不再是直接访问 Provider#A 或 B,而是中间植入了 CNAME Proxy,再由 Proxy 依据 Plugin 的决策,是否转发给问 Provider#A 或 B 。
这个设计太棒了!它使得商业公司 cdnprovider.com 给 www.example.org 提供 CDN 服务时,完全是零侵入,不需要修改任何一段代码,只需要在域名服务商那修改 www.example.org 的域名解析,这个操作代表 www.example.org 同意 cdnprovider.com 为他们提供 CDN 服务,代表授权 。这一切,都源于基于 Naming 解析的动态绑定实现的解耦 。同样的,除了 CDN,我们的恶意流量清洗、灰度发布、性能分析等都可以采用这种方式,实现零侵入插拔 。


推荐阅读