
Martin Fowler最近非常推崇的serverless架构模式,是BaaS云架构实现的核心架构模式。 Martin Fowler在2016.6.17号发表了一篇博客: 《Serverless Architectures》,引起业界广泛关注:在这篇博客里,他介绍了serverless架构,以及FaaS,Microservice,Docker等流行的架构和概念,描述了Amazon AWS lambda的价值, 进一步将这种云时代的架构清晰的展现在大家的视野里。 本文很多内容来自这篇博客,让大家清晰了解Martin对serverless架构的概念和价值的阐述。 在此基础上我按照模式的讲解思路将进一步阐述我个人对Serverless架构和BaaS之间的关系,以及后续的发展。 概念(Concept) Serverless是最新兴起的架构模式,中文意思是“无服务器”架构。跟很多其它软件类似,对Serverless还没有清晰定义,但是肯定有两个互相有重叠的定义: 1. Serverless最初是用于描述依赖第三方服务(‘云端’)实现对逻辑和状态进行管理的应用。典型的包括“富客户端”(例如单页Web应用、移动应用), 他们一般都使用基于云端的数据库(例如Parse、Firebase),认证服务(Auth0、AWS congnito)等。 这类服务以前被称为“(Mobile) backend as a Service (https://en.wikipedia.org/wiki/Mobile_backend_as_a_service)”,我将在本文中称他们为“BaaS”。 2. Serverless也可以指这样的应用,一部分服务逻辑由应用实现,但是跟传统架构不同在于,他们运行于无状态的容器中,可以由事件触发,短暂的,完全被第三方管理。 (感谢ThoughtWorks在最近Tech Radar中做出的定义)。这种思路是‘Functions as a Service / FaaS’,AWS Lambda是目前最佳的FaaS实现之一, 本文后续介绍中将使用FaaS作为这种架构的缩写。当开发Baas shaped应用,特别当开发一个富Web应用,而不是移动应用时,你会需要一些服务器端定制功能, Faas功能也许对于这种情况是一种好的解决方案,特别是如果他们和你使用的BaaS服务集成到一定程度时,这样功能案例包括数据校验和计算敏感的处理,比如图片和视频的制作。 但是我更愿意讨论的是本领域第二种方式,相比来说技术架构更新,引领了Serverless的很多创新。然而,这两个领域开始融合,一个例子是Auth0(https://auth0.com/),一开始Auth0是作为Baas(‘Authentication as a Service’),但是随着Auth0 webtask的发布,演变成了FaaS应用。 更多场合,当开发基于BaaS的应用时,特别当开发基于web的应用而不是移动应用时,更需要一部分服务端功能。特别当跟正在使用的BaaS整合在一起时,FaaS模式可以作为最佳实践。这类功能包括数据认可(以免客户端恶意攻击)和计算敏感进程(例如图像或者视频篡改)。 问题(Problem): 在目前主流云计算IaaS和PaaS中,开发者进行业务开发时, 仍然需要关心很多和服务器相关的服务端开发, 比如缓存,消息服务, web应用服务器, 数据库, 需要对服务器进行性能优化,考虑存储和计算资源, 考虑负载和扩展,考虑服务器容灾稳定性等非业务逻辑的开发。 这些服务器的运维和开发,知识和经验极大的限制了开发者进行业务开发的效率。 如何让开发者无需在服务器实现和部署服务,而直接租用服务或者开发服务而无需关注如何在服务器中运行部署服务,对开发者来说,这是一种去服务器而直接使用服务的架构。 背景(Context) 服务端开发在云计算时代基本是如下趋势: Real Server->Virtual Server-> Server with Middlewares -> Service __ 云计算的发展从IaaS,PaaS,SaaS,到最新的BaaS,在这个趋势中serverless(去服务器化)越来越明显。 IaaS将真实的物理机变成了虚拟机, PaaS进一步将虚拟机变成了包含基础设施的中间件服务。 BaaS&SaaS将中间件服务扩展到更基础的后端能力。 这些是云计算解决效率很成本的重要体现。 Serverless这种无服务器架构,用服务代替服务器,无需了解落实服务进一步提高云计算的成本和效率,从而为BaaS这种新时代云计算提供架构基础__ 限制(Force) BaaS要被开发者广泛接受,需要在云端解决以下的限制: BaaS服务的治理 BaaS服务需要提供逻辑定制扩展 BaaS服务能力独立部署,快速启动 BaaS服务可以弹性扩展,满足大并发需要 BaaS服务可以被监控,计费 BaaS服务要解决DevOps相关的问题 ... 解决(Solution) 要实现serverless架构, 需要利用以下技术和方案, 实现BaaS中的云代码特性, 开发者可以直接开发在云端业务代码,实现Functions as a Service。 实现API网关,对用API代表服务的入口,并对所有服务进行治理。 微服务架构技术,用微服务的概念来实施服务的开发。 利用docker容器技术部署运行微服务 典型场景 UI驱动应用 先讨论一个带有服务功能逻辑的传统面向客户端的三层应用,一个典型的电子商务应用(例如在线宠物商店)。 一般架构如图所示,假如服务端用Java开发完成,客户端用HTML/Javascript:这种架构中,因为有不少系统逻辑,例如认证、页面导航、搜索、交易等在服务端完成,客户端显得相对不太智能。采用Serverless架构,开起来如图二所示:这仅是最简单的视图,但是即使如此,还是有不少改变。请注意并不是建议架构迁移,而是使用这个工具来解释某些Serverless概念。 1. 删除了认证逻辑,而用第三方BaaS服务取代。 2. 使用另外一个BaaS,允许客户端直接访问架构与第三方(例如AWS Dynamo)上的数据子库。通过这种方式提供给客户更安全的访问数据库模式。 3. 前两点中包含着很重要的第三点,也就是以前运行在宠物商店服务端的逻辑现在都转移到客户端中,例如跟踪用户访问,理解应用的UX架构(例如页面导航),读取数据库转化为可视视图等。客户端则慢慢转化为单页面应用。 4. 某些我们想保留在服务端的UX相关功能,例如,计算敏感或者需要访问大量数据,比如搜索这类应用。对于搜索这类需求,我们不需要运行一个专用服务,而是通过FaaS模块,通过API Gateway对http访问提供响应。 这样可以使得客户端和服务端都从同一个数据库读取相关数据。 由于原始服务使用Java开发,AWS Lambda(FaaS提供者)支持Java功能,因此可以直接从宠物商店服务端将代码直接移植到宠物商店搜索功能,而不用重写代码。 5. 最后,可以将‘purchase’功能用另外一个FaaS功能取代,因为安全原因放在服务端还不如重新在客户端重新实现,当然前端还是APIGateway。 消息驱动应用 一个不同的例子是后台数据处理服务。例如正在写一个面向用户的应用,需要对UI请求快速响应,但是同时还想获取所有发生的行为。我们设想一个在线广告系统,当用户点击一个广告时,希望快速导向目标,但是同时,需要搜集点击量以便向广告商收取费用。 传统的架构,‘Ad Server’同步地响应客户,但是同时还会向异步处理‘点击量’的应用发送一个消息更新,以便以后向广告商收费的数据库。而Serverless架构则如下: 架构跟我们第一个例子有些许不同,这里我们用FaaS功能取代了一个一直运行的应用。此FaaS运行于方案提供商提供的消息驱动上下文之间。需要注意的是供应商提供了消息代理和FaaS,两者之间更加紧密地合作在一起。FaaS环境通过复制出若干实例也可以并行处理这些点击,这对开发无疑带来全新概念。 BaaS云代码解决方案 - FaaS(Function as a Service) FaaS可以作为最细粒度的BaaS, 就像一个接口实现可能有多个Function来完成一样。 一个BaaS服务可以有一个FaaS来实现,也可能是多个FaaS一起成一个chain来完成。 在这里我们将考虑FaaS的概念和实现。 下面的架构图来自IBM OpenWhisk, 非常贴切的描述了BaaS云代码的架构: 我们已经多次提过FaaS概念,现在我们来看看其真正含义。我们先看看Amazon Lambda产品的公开说明。我自己添加了标记,将会逐一展开说明。 AWS Lambda使得用户不需部署或者管理服务就可运行代码(1)…通过Lambda,可以虚拟化地运行任何类型应用和后台服务(2)—都是免管理的。只需上载代码,Lambda会管理其他一切(3)并且以高可用模式扩展应用(4)可以配置自动从其他AWS服务激活代码(5)或者从任何移动应用中调用。(6) 功能上FaaS就是不需要关心后台服务器或者应用服务,只需关心自己的代码即可。与现代其他架构相比(例如容器和PaaS),这是最大的不同。 如果回到上面所说的点击案例,FaaS代替了点击处理服务器(至少是一台物理服务器,但是绝对是一个特定应用),因为这种架构不需要一台指定服务器,甚至不需要一个一直运行的应用模块来处理。 FaaS并不需要特定框架或者库,从编程语言和环境角度更像是一个普通应用。例如AWS Lambda功能可以采用JavaScript、Python和任何其他JVM语言(Java、Clojure、Scala等)。Lambda功能可以运行任何其他绑定部署的代码,因此可以用任何可以编译成Unix进程的语言。FaaS功能也有一些架构上的限制,特别当面对状态(state)或者执行区间(execution duration)的问题,后面我们会提到。回到上面说的点击系统,转到FaaS架构唯一需要更改的代码就是‘main method/startup’代码(示例中被删除了),开始来代码应该是在顶层消息处理器中(‘message listener interface’实现),但却可能只是在方法签名(method signature)的一个小小改变。所有其他代码(例如写入数据库的代码)都没有任何改变。 因为没有应用服务需要部署因此FaaS跟传统架构差别很大,只需要上载代码到FaaS提供者就足够了。现在这也就意味着上载一个代码包(例如以zip或者JAR形式),然后调用特定API初始话此更新。 水平扩展是完全自动、弹性,由提供者来管理。如果应用需要并发处理100个请求,提供者将会处理后台所有需求。‘计算容器’只是短暂运行应用代码,运行完毕后就销毁这些需求。仍然回到点击案例,假如今天运气不错,客户点击了日常点击量的十倍。点击进程能处理这些变化吗?例如,我们设计的代码可以同时处理多条消息吗?即使可以,一个进程可以处理这么多负载吗?是否可以动态自动扩展进程还是需要手工重新配置?有了FaaS,代码只需要处理并发,而其他自扩展功能则由提供者自动处理。 FaaS功能是由提供者定义的消息类型触发的。对于Amazon AWS,这些出发包括 S3(文件)更新,时间(调度任务)和添加到消息总线上的消息(例如kinesis)。代码一般都会提供消息源所需的参数。点击案例中,已经假定我们使用了支持FaaS的消息代理。如果还没有的话,就需要一个,对消息生产者也有同样的要求。 许多提供者允许FaaS功能作为http响应来出发,一般是API网关。例如在宠物商店案例中,‘search’和‘purchase’功能。 以下是FaaS,云代码实施中解决的关键问题: State(状态) FaaS功能如果使用本地状态,会有很多严格限制。简单说需要假设任何进程间或者主机状态对子进程都不可见,包括在RAM和写到本地盘上的状态。换句话说,从一个部署单元来看FaaS功能是无状态的。这一点对应用架构来说影响很大,同样‘Twelve-Factor App’概念对架构也有细致的限制。 考虑到限制,应该如何解决呢?一般来说FaaS功能要么是自然无状态,也就是提供纯功能调用,要么会使用数据库,跨进程见cache(例如Redis),或者共享文件系统(或者S3)来存放状态或者提供处理请求所需的更多输入信息。 Execution Duration(执行区间) FaaS功能一般会限制每个功能允许运行多长。目前AWS Lambda功能允许最多运行5分钟,如果超出就被强行退出。这意味着某些长时间运行的任务如果转到FaaS架构,就需要重新设计,也就是说需要多创建几个不同FaaS功能协调器,而在传统架构中,只需有一个就可以了。 Startup Latency(启动延迟) 目前FaaS功能多长时间会响应跟很多因素有关,其延迟可能从10ms到2分钟。听起来不太好,我们用AWS Lambda举个例子。加入你的功能用Javascript或者Python开发,也不大(例如小于几千行代码),此时延迟一般不会超过10-100ms。更大的功能有可能会有更长的响应时间。如果Lambda功能运行在JVM上,当JVM运行时,偶尔会看到响应时间更长(例如大于10秒)的情况,然而对于以下情况,高延迟则会很显著: * 应用并不是很活跃,大概每十分种处理一次事件。 * 突然流量爆发。例如从每秒10次濡染增长到每秒100次。 前者一般可以通过提高响应频次的方法来解决。 这些问题需要考虑吗?的确需要看应用的具体情况。我以前团队用Java开发过一个异步消息处理Lambda应用,每天处理上亿条消息,而且没有启动延迟问题。也就是说不管采用什么开发语言,如果你想写一个低延时交易应用,现在可能并不适合采用FaaS架构。 为了确认应用是否有问题,最好是用生产类型负载测试一下。如果场景不适合,可以考虑转向FaaS提供商。 BaaS服务治理解决方案 - API网关(API Gateway) BaaS云架构中需要将一个后端服务以一个服务点(Restful API)的方式暴露给使用着,同是需要对这些服务点进行管理,提供文档,测试等信息,提高开发者体验。 当一个服务被实现后,提供一个API 网关的东西,将这个服务和服务点API关联起来。 API Gateway是一个服务器,也可以说是进入系统的唯一节点。这跟面向对象设计模式中的Facet模式很像。API Gateway封装内部系统的架构,并且提供API给各个客户端。它还可能有其他功能,如授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等。 API Gateway负责请求转发、合成和协议转换。所有来自客户端的请求都要先经过API Gateway,然后路由这些请求到对应的微服务。API Gateway将经常通过调用多个微服务来处理一个请求以及聚合多个服务的结果。它可以在web协议与内部使用的非Web友好型协议间进行转换,如 HTTP协议、WebSocket协议。 关于FaaS我们之前讨论过‘API Gateway’,一个API Gateway是一个http服务器,路由和服务点都在配置里定义,每个路由都跟一个FaaS功能有联系。当一个API Gateway接受请求,找到提供请求服务的路径,然后调用相关FaaS功能。一般API Gateway允许将http参数映射成FaaS功能需要的输入参数。API Gateway将FaaS功能结果转换为http响应,返回调用者。 Amazon Web Services等云服务提供商都各自提供自己的API Gateway。 除了API Gateway路由请求需要认证,输入验证,相应代码映射等,有时候可能还会想是否这是个好主意,那么让我们更深入论证一下。APIGateway+FaaS的一个应用场景就是用Serverless方式创建http前端微服务,充分利用扩展,管理和其它与FaaS功能有关的其它优点。目前API Gateway开发工具并不太成熟,因此定义API Gateway应用时候最好不是非常核心的引用。 BaaS服务实施方案 - Microservice 微服务用于解决如何分解巨大单体式应用为多个服务方法解决了复杂性问题。在功能不变的情况下,应用被分解为多个可管理的分支或服务。每个服务都有一个用RPC-或者消息驱动API定义清楚的边界。微服务架构模式给采用单体式编码方式很难实现的功能提供了模块化的解决方案,由此,单个服务很容易开发、理解和维护。 对于Serverless架构里,最关键的就是用将代表复杂答题时应用的服务器消除,取而代之用一些列轻量,简单,易开发维护扩展,相互松耦合,原子性的服务来代替。 MicroService架构也是BaaS种很重要的架构模式, 我们会在后续详细介绍。 BaaS服务DevOps方案 - Docker 在BaaS开发架构中,服务的需要能够动态灵活按需的发布在一个轻量级无状态的容器里,这个容器就是目前流行的Docker,Docker对实施服务的DevOps提供了革命性的方案,后续会详细介绍Docker在BaaS架构体系中的重要性。 Case(案例) Beehive云开发平台 - 阿里内部自己的BaaS开发平台 Beehive是按照BaaS架构理念构建的云开发平台, 在这个平台我们预先实现了对社交内容类开发必要的多种功能,包括数据,社交,权限,事件,计数,文件储存,地理位置等。 提供给开发者快速业务创新的平台。 目前已经有2个内部项目通过Beehive云开发平台开发。 该平台在服务器端实现了API Gateway, Data as a Service(通用数据存储)等架构特性。 下个版本,将重点实现Cloud Code(FaaS), 微服务docker化部署, 能力市场等特性。 打造云时代的新一代开发者平台。 对标Amazon APIGateway & lambda, Google FireBase, IBM BlueMix + OpenWhisk. Amazon APIGateway & lambda IBM OpenWhiskIBM Bluemix OpenWhisk平台让广大开发人员能够迅速构建微服务,从而可以响应诸多事件,比如鼠标点击或收到来自监视摄像头的传感器数据,执行软件代码。事件发生后,代码会自动执行。因而,开发人员不需要为预先配置基础设施之类的事情(比如服务器或系统运行)而操心――他们只需专注于代码,因而显著加快了工作流程。 Google FireBase 《2016 Google I/O Firebase, Google在BaaS上又前进了一步》 -这里有对Google Firebase BaaS开发平台详细介绍。 Auth0一开始Auth0是作为Baas(‘Authentication as a Service’),但是随着Auth0 webtask的发布,演变成了FaaS应用。 Contentful 在BaaS云开发平台基础上,可以很容易开发出各种垂直类的SaaS平台。 Contentful就是一个很好的示例。 Contenful可以理解为基于BaaS基础上的一个CMS SaaS平台, 虽然是个内容管理平台,对充分体现了BaaS,Serverless, 定制化SaaS等理念。 有兴趣的同学可以深入研究下。 这是一个几乎不用服务器Serverless的内容管理系统网站架构,相比于传统使用WordPress和CraftCMS等内容管理系统可以节省网站的运营管理,相比于维护传统的LAMP架构,Serverless几乎可以没有DevOps。 有时很多客户只是需要一个静态网站,有一个漂亮的图形首页和一些其他静态页面即可,网站一旦发布,几乎很少需要运营维护。那么使用什么技术构建这种静态网站?无疑是Serverless架构,亚马逊的Web服务已经罗列出优点: 1.不需要纠结于操作系统的选择以及安全与管理 2.没有服务器需要配置 监控或扩展 3.没有成本超额造成的风险 4.没有性能考量造成的风险 Serverless更多思考 什么不是Serverless? 目前我们定义Serverless主要意味着’Backend as a Service’ 和 ‘Functions as a Service’。针对他们我们也深入探讨了各种因素。开始讨论有缺点之前,我们再来看看定义,或者至少定义一下什么不是‘Serverless’。很多人会很迷惘,因此讨论一下还是很值得的。 跟PaaS比较 考虑到Serverless FaaS功能很像12-Factor applications(译者注:是一种创建现代,可扩展,可维护SaaS应用的方法论),它们只是像Heroku的另外一种PaaS吗?下面引用Adrian Cockcroft的一段论述: 如果你的PaaS可以将以前半秒启动的应用在20ms内启动,就叫它Serverless。——Adrian Cockcroft 换句话说,许多PaaS应用不会每次请求来了启动,请求结束则关闭。而FaaS平台是这样的。 好吧,然并卵。如果我是一个很好地12-Factor App开发者,并不会太多开发上的不同吧?这是真的。但是在运维应用上却又很大不同。因为所有好的DevOps-savvy工程师都会同时考虑开发和运维,对吧?FaaS和PaaS在运维方面最大不同来自于可扩展性(scaling)。许多PaaS架构,用户必须考虑scale,例如杜宇Heroku来说需要运行多少Dynos;而对于FaaS应用来说,这是完全透明的。即使配置PaaS应用自扩展,也不会将它设置为请求级别的(除非负载访问情况很特殊),而对FaaS应用来说,从投入角度更加有效。 考虑到这些优势,为什么还是用PaaS架构?有一些原因,但是工具,API Gateway成熟度应该是最大原因。另外,12-Factor Apps为了优化,实现PaaS时会采用App内部只读cache,而对于FaaS来说则没有这个选项。 NoOps Serverless并不意味着‘免维护’。或许根据你对Serverless有多适应,应该意味着‘不需定时维护’,有很重要的两点需要考虑。首先,‘Ops’意味着比服务器维护更多的内容。至少还包括监控,部署,安全,网络,以及产品排错和系统扩展。这些问题对于Serverless应用来说仍然还在,需要一种策略来处理。某种程度上,在Serverless世界Ops有些困难,因为一切都是新的。 第二,即使系统管理还在发生,其实我们是他们外包给了Serverless。这并不是件坏事,我们实际上外包了很多事情。但是根据你真正想做什么,可能是件好事也可能时间坏事,有时候这种抽象会出现问题,你需要确认到底是谁在支持你的应用。 Stored Procedures as a Service 关于Serverless FaaS另外一个论点在于‘Stored Precedures as a Service’,我认为这一论点来自于很多FaaS的实现(包括本文中用到的例子)都是小段代码访问数据库。如果这就是所有要求,用FaaS当然是可以的。但是因为这仅仅是FaaS的一小部分功能,用这种想法考虑FaaS是以偏概全了。 还有人说,考虑FaaS是否会带来跟stored procedures同样的问题是值得的,包括技术方面的问题(Camile在tweet上提到这些问题)。使用stored procs值得审视FaaS上下文,这里有很多教训。例如: 经常需要用特定语言,或者是特定的框架、某种语言的扩展。 因为执行时必须要有数据库上下文,测试很困难。 版本控制需要技巧 注意,以上所述并不能涵盖所有stored procs,但是确实是我实际中遇到的问题。我们看看是否也适用于FaaS: 第一点对FaaS实施绝对不使用,因此可以毫不犹豫将它划去。 因为这里我们只讨论编码。整合测试是我们需要后续讨论的另外一个问题。 应为FaaS功能对于代码版本控制足够了,但是对于应用打包来说并没有成熟模板。Serverless框架自己提供了一套,AWS也宣称在2016年五月Serverless大会上发布一套,但是至少现在是一个值得担心的问题。 总结 Serverless代表无服务器计算技术崛起, 是新一代云服务和开发架构的实践。 BaaS是云端一体的开发架构,而serverless是BaaS的服务器端实现的主要架构方式。 Serverless架构是BaaS实现的精髓,是BaaS进一步的解读,FaaS(Function as a service)是BaaS中云代码的实现方式。 Amazon的Lambda是FaaS的实现之一,是很好的参考。 Amazon的BaaS战略通过Amazon API Gateway, Amazon Lambda来实现,在后端服务上已经处于领跑地位,真正把微服务,docker,BaaS等思想落地。 下面有一些简单公示,方便大家理解: BaaS = 云API + 端API。 云API = service with Serverless(API Gateway + Mirocroserivce + Docker) + ... 端API= 多端SDKs + Component(前端组件) 参考资料 Beehive云开发平台 - 阿里自己的Serverless架构的BaaS云开发平台 AMA社区-Beehive开发实例 - 基于Beehive开发,没写一行后端代码 Beehive云开发平台ATA圈 - 更多Beehive资料,请关注该圈子 《Serverless Architectures》- Martin Fowler最新Serverless架构大作 《The Twelve-Factor App》 - SaaS类应用开发人员必读 《BaaS后端即服务 - 通往中台架构之路》- 介绍BaaS的一些总体思考。 《BaaS后端即服务 - 概念篇》- 介绍BaaS的概念,在云架构体系中的定位。 《BaaS后端即服务 - 分析篇》- 介绍当前BaaS发展的趋势和主流BaaS平台功能的对比。
BusinessWorks目前以二方包的形式提供给已有业务系统用来满足平台化建设,适应业务快速开发需要。在这边ATA中,我们会介绍我们代码的组织结构和基本实现, 然后会针对交易领域简化开发一个订单下单的例子,来阐述如何进行业务的模块化开发,实现业务隔离。 1. 代码结构 businessworks-platform 是业务基础集成的平台实现 platform-common 提供平台的一些底层基础类库实现,包括对guice的扩展实现 platform-core 提供平台的一些核心功能实现,比如配置,jndi注册等。 platform-integration 提供apache camel的集成功能 platform-runtime 平台的运行期容器,负责启动平台容器,对容器内被管理对象进行生命周期管理 platform-web 提供平台的元数据已经运行监控的web功能。 businessworks-trade 是交易业务领域的demo实现 trade-domain 提供交易对象的demo实现,显现订单对象。 trade-toc 对订单提供超时功能扩展 trade-ump 对订单提供优惠功能扩展 businessworks-extension是业务方的扩展实现 trade-lifeservice-rest 提供生活服务业务的rest扩展 trade-lifeservice-toc 提供生活服务业务的超时实现 trade-lifeservice-ump 提供生活服务业务的优惠实现 目前businessworks-trade和businessworks-extension是业务开发的一个demo实现,用来阐述如果利用businessworks进行业务的扩展隔离开发。 2. 核心功能实现介绍 2.1 IAdaptable 接口扩展实现 这三个接口: IAdapabable, IAdapatableFactory, IAdaptableManager 《从Eclipse平台看交易平台化》设计到的接口扩展实现。 2.2 Guice Module的定制实现 在Guice module基础上增加了模块的元数据信息,以及模块组装的一些实现。 比如ModuleBuiler,提供了模块组织在一起构建的builder类。 package com.taobao.businessworks.common.inject; import com.google.common.collect.Lists; import java.util.Iterator; import java.util.List; public class ModulesBuilder implements Iterable<Module> { private final List<Module> modules = Lists.newArrayList(); public ModulesBuilder add(Module... modules) { for (Module module : modules) { add(module); } return this; } public ModulesBuilder add(Module module) { modules.add(module); if (module instanceof SpawnModules) { Iterable<? extends Module> spawned = ((SpawnModules) module).spawnModules(); for (Module spawn : spawned) { add(spawn); } } return this; } @Override public Iterator<Module> iterator() { return modules.iterator(); } public Injector createInjector() { Modules.processModules(modules); Injector injector = Guice.createInjector(modules); Injectors.cleanCaches(injector); // in ES, we always create all instances as if they are eager singletons // this allows for considerable memory savings (no need to store construction info) as well as cycles ((InjectorImpl) injector).readOnlyAllSingletons(); return injector; } public Injector createChildInjector(Injector injector) { Modules.processModules(modules); Injector childInjector = injector.createChildInjector(modules); Injectors.cleanCaches(childInjector); // in ES, we always create all instances as if they are eager singletons // this allows for considerable memory savings (no need to store construction info) as well as cycles ((InjectorImpl) childInjector).readOnlyAllSingletons(); return childInjector; } } 2.3 Camel的guice集成 通过guice集成camel提供的流程编排功能,并且可以以guice模块的形式注册新的流程实现。 2.4 平台的启动 Bootstrap会启动一个Node作为运行时容器, 在这个node容器内,所有的模块会被组装运行起来,包括通过插件方式提供的模块实现。 ModulesBuilder modules = Platform.getSystemModulesBuilder(); modules.add(new Version.Module(version)); modules.add(new PageCacheRecyclerModule(settings)); modules.add(new PluginsModule(settings, pluginsService)); //插件管理模块 modules.add(new SettingsModule(settings)); //配置模块 modules.add(new NodeModule(this));//运行node模块 modules.add(new NetworkModule()); //网络模块 modules.add(new MonitorModule(settings));//监控模块 modules.add(new JolokiaServerModule(settings));//Jolokia JMX模块 modules.add(new EnvironmentModule(environment));//环境模块 modules.add(new NodeEnvironmentModule(nodeEnvironment));//node环境模块 modules.add(new ThreadPoolModule(settings));//线程池模块 modules.add(new RestModule(settings)); //REST模块 modules.add(new TransportModule(settings));//传输层实现模块 if (settings.getAsBoolean(HTTP_ENABLED, true)) { modules.add(new HttpServerModule(settings)); //http服务模块 } modules.add(new RegistryModule(settings)); //注册模块 modules.add(new CamelRoutesModule()); //camel route管理模块 modules.add(new RoutesModule()); modules.add(new ActionModule(false)); modules.add(new NodeClientModule()); modules.add(new ExtensionsModule(settings)); injector = modules.createInjector(); 在 modules.add(new PluginsModule(settings, pluginsService)); //插件管理模块中, 这个模块会按照一定的规范把所有的第三方插件提供的模块加入到这个模块组里。从而达到系统功能,业务功能扩展的目的。 具体如何实现可以在后续实例。 3. Businessworks接入已有系统 对于目前存在的业务系统,如果想使用Businessworks平台提供的平台化功能,需要做一下步骤: 引入二方包依赖 <dependency> <groupId>com.taobao.businessworks</groupId> <artifactId>platform-runtime</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> 如果是个web系统,可以增加一个ServletContextListener,用来在web启动时,启动Businessworks容器。public class BusinessworksListener implements ServletContextListener{ /** 配置Businessworks*/ @Overridepublic void contextInitialized(ServletContextEvent sce) { String home = sce.getServletContext().getRealPath("/"); System.setProperty("user.dir", home); System.setProperty("bw.foreground", "yes"); Bootstrap.main(null); } @Overridepublic void contextDestroyed(ServletContextEvent sce) { Bootstrap.close(null); }} Businessworks对guava,log4j有版本要求,需要注意升级到对应版本。这样,你的业务系统就具有平台化的功能了。 4. 简单交易下单业务开发示例 首先描述下我们要开发的业务场景, 我们首先会在Businessworks的平台上开发一个简易下单功能。 然后我们会逐步扩展这个下单功能,增加订单超时,订单优惠功能,最后我们会以生活服务lifeservice业务方为例,提供lifeservice的特定实现。 4.1 交易领域开发 trade-domain 插件: 首先开发业务功能,我们需要定义业务领域对象, 这里我们会用IOrder接口来实现。 package com.taobao.businessworks.domain.order; import java.util.List; import com.taobao.businessworks.common.adaptable.IAdaptable; public interface IOrder extends IAdaptable { public void addItem(DrinkType drinkType, int shots, boolean iced); public List<OrderItem> getItems(); public void putAttribute(String key, Object value); public String getType(); public Object getAttribute(String key); } 可以看到这个IOrder接口扩展了一个IAdaptable接口,后续我们可以看到这个接口的功能作用。 在看IOrder的具体实现类,我们可以发现他集成了PlatformObject这个抽象类,在这个类中有IAdaptable接口的实现。 这样Order对象就自动实现了IAdaptable。 package com.taobao.businessworks.domain.order; import java.util.ArrayList; import java.util.List; import java.util.Map; import com.google.common.collect.Maps; import com.taobao.businessworks.common.adaptable.PlatformObject; public class Order extends PlatformObject implements IOrder{ private List<OrderItem> orderItems = new ArrayList<OrderItem>(); private int number; private String type; private float price; private final Map<String, Object> attributes = Maps.newHashMap(); public Order(String type, int number) { this.type= type; this.number = number; } public String getType() { return type; } public void putAttribute(String key, Object value) { this.attributes.put(key, value); } public Object getAttribute(String key) { return this.attributes.get(key); } public void addItem(DrinkType drinkType, int shots, boolean iced) { this.orderItems.add(new OrderItem(this, drinkType, shots, iced)); } public int getNumber() { return number; } public List<OrderItem> getItems() { return this.orderItems; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } } PlatformObject: public abstract class PlatformObject implements IAdaptable { /** * Constructs a new platform object. */ public PlatformObject() { super(); } /** * Returns an object which is an instance of the given class * associated with this object. Returns <code>null</code> if * no such object can be found. * <p> * This implementation of the method declared by <code>IAdaptable</code> * passes the request along to the platform's adapter manager; roughly * <code>Platform.getAdapterManager().getAdapter(this, adapter)</code>. * Subclasses may override this method (however, if they do so, they * should invoke the method on their superclass to ensure that the * Platform's adapter manager is consulted). * </p> * * @param adapter the class to adapt to * @return the adapted object or <code>null</code> * @see IAdaptable#getAdapter(Class) */ public Object getAdapter(Class adapter) { return AdapterManager.getDefault().getAdapter(this, adapter); } } 将这个简单的订单模型加入到平台很简单,在实现一个插件类: package com.taobao.businessworks.domain; import java.util.ArrayList; import java.util.Collection; import com.taobao.businessworks.common.inject.Module; import com.taobao.businessworks.plugins.AbstractPlugin; public class DomainPlugin extends AbstractPlugin { public DomainPlugin() { } @Override public String name() { return "trade-domain"; } @Override public String description() { return "trade domain Plugin"; } @Override public Collection<Class<? extends Module>> modules() { Collection<Class<? extends Module>> modules = new ArrayList<Class<? extends Module>>(); modules.add(DomainModule.class); return modules; } } 这个plugin会被平台调用,通过modules()方法,将整个jar和包括的模块引入businessworks平台。这样平台就具有了IOrder等模型的实现。 4.2 如何扩展开发领域功能 建设我们的简易下单系统需要增加超时功能和优惠功能。 按照我们以前的做法,我们会去IOrder接口里增加和超时或者 优惠相关的接口方法。这样带来的问题是我们的IOrder接口会随着业务功能的增加而膨胀,而且接口作为协议,已经对外暴露了,对基础接口的修改会导致依赖这个接口的上层代码的修改。 我们都知道作为核心类,应该尽可能保持它的稳定,这对业务系统的稳定和健壮也有很大的好处。 IAdaptable提供了一个优雅的实现让我们可以不改变IOrder接口,却可以给IOrder接口增加扩展的功能。 我们会以trade-toc这个项目为示例实现,来演示如果给交易这个领域增加超时功能。 首先我们会定义一个Toc接口,定义超时相关的功能: public interface Toc extends TocComponent { void setOrderConsignmentTimeout(IOrder order); void setOrderConfirmTimeout(IOrder order); int getOrderConsignmentTimeout(IOrder order); int getOrderConfirmTimeout(IOrder order); } 很简单,就是定义对订单设置确认收货超时和发货超时。 然后我们实现一个TocAdapterFactory工厂类,用来向平台注册IOrder的TOC扩展 package com.taobao.businessworks.toc; import com.taobao.businessworks.common.adaptable.IAdapterFactory; import com.taobao.businessworks.common.adaptable.IAdapterManager; import com.taobao.businessworks.common.inject.Inject; import com.taobao.businessworks.core.config.Platform; import com.taobao.businessworks.domain.order.IOrder; public class TocAdapterFactory implements IAdapterFactory { private final TocsService tocsService; @Inject public TocAdapterFactory(TocsService tocsService) { this.tocsService = tocsService; IAdapterManager manager = Platform.getAdapterManager(); manager.registerAdapters(this, IOrder.class); } @Override public Object getAdapter(Object adaptableObject, Class adapterType) { if (adapterType == Toc.class) { IOrder order = (IOrder) adaptableObject; Toc toc = tocsService.toc(order.getType()); if (toc== null) { toc = tocsService.toc("general"); } return toc; } return null; } @Override public Class[] getAdapterList() { return new Class[] { Toc.class }; } } 这个类会将Toc接口注册到AdapterManger里,这样IOrder就可以通过getAdapter方法得到toc相关的功能。 接着我们将继续把toc相关的功能都在TOC模块中封装实现:TocsModule: public class TocsModule extends AbstractModule { private final Settings settings; private Map<String, Class<? extends Module>> tocTypes = Maps.newHashMap(); @Inject public TocsModule(Settings settings) { this.settings = settings; registerToc("general", GeneralTocModule.class); registerToc("virtual", VirtualTocModule.class); } /** * Registers a custom river type name against a module. * * @param type The type * @param module The module */ public void registerToc(String type, Class<? extends Module> module) { tocTypes.put(type, module); } @Override protected void configure() { bind(TocsService.class).asEagerSingleton(); bind(TocAdapterFactory.class).asEagerSingleton(); bind(TocsTypesRegistry.class).toInstance(new TocsTypesRegistry(ImmutableMap.copyOf(tocTypes))); } } 这个类有如下功能: 绑定了TocAdapterFactory实现,注册toc对IOrder的扩展。 提供了一个toc超时业务子模块的注册机制。 从代码可以看到,我们默认提供了GeneralTocModule, VirtualTocModule两个缺省实现。 提供了一个TocsService辅助服务类,用来将toc模块的功能以service方式提供。 这种可扩展的模块机制,是我们业务扩展和隔离的基础,新的业务对应的接口实现,可以通过提供业务超时子模块的方式注册到超时富模块。 我们可以看一个GeneralTocModule的实现: public class GeneralTocModule extends AbstractModule { public GeneralTocModule() { super(); } @Override protected void configure() { bind(Toc.class).to(GeneralToc.class).asEagerSingleton(); } } 在这里, 平台提供了超时的一个缺省实现GernalToc, public class GeneralToc extends AbstractTocComponent implements Toc{ final TocSettings settings; @Inject public GeneralToc(TocName tocName, TocSettings settings) { super(tocName, settings); this.settings = settings; } @Override public void setOrderConsignmentTimeout(IOrder order) { order.putAttribute("consignment_timeout", 100); } @Override public int getOrderConsignmentTimeout(IOrder order) { return 100; } @Override public void setOrderConfirmTimeout(IOrder order) { order.putAttribute("confirm_timeout", 200); } @Override public int getOrderConfirmTimeout(IOrder order) { return 200; } 最后通过TocPlugin这个类,将TocsModule注册到平台内,从而让订单具有了超时功能扩展。 public class TocPlugin extends AbstractPlugin { public TocPlugin() { } @Override public String name() { return "trade-toc"; } @Override public String description() { return "trade toc Plugin"; } @Override public Collection<Class<? extends Module>> modules() { Collection<Class<? extends Module>> modules = new ArrayList<Class<? extends Module>>(); modules.add(TocsModule.class); return modules; } } 在extension-route这个插件里,我们注册扩展了两个restful api用来模拟下单的入口和实现:http://localhost:9200/buy_general 代表生成一个general的订单, http://localhost:9200/buy_virtual 代表生成一个virtual的订单, public class RestBuyNowAction extends BaseRestHandler { private final Version version; private final CamelContext camelContext; @Inject public RestBuyNowAction(Settings settings, Version version, RestController controller, Client client, CamelContext camelContext) { super(settings, controller, client); this.version = version; this.camelContext = camelContext; controller.registerHandler(GET, "/buy_general", this); controller.registerHandler(HEAD, "/buy_general", this); } @Override public void handleRequest(final RestRequest request, RestChannel channel, final Client client) throws Exception { RestStatus status = RestStatus.OK; if (request.method() == RestRequest.Method.HEAD) { channel.sendResponse(new BytesRestResponse(status)); return; } XContentBuilder builder = channel.newBuilder(); // Default to pretty printing, but allow ?pretty=false to disable if (!request.hasParam("pretty")) { builder.prettyPrint().lfAtEnd(); } Order order = new Order("general",1); Toc toc = (Toc)order.getAdapter(Toc.class); if (toc!=null) { toc.setOrderConfirmTimeout(order); toc.setOrderConsignmentTimeout(order); } Ump ump = (Ump)order.getAdapter(Ump.class); if (ump!=null) { ump.setOrderPromation(order); } int consignment_timeout = toc.getOrderConsignmentTimeout(order); int confirm_timeout = toc.getOrderConfirmTimeout(order); float promotion = ump.getOrderPromation(order); builder.startObject(); if (settings.get("name") != null) { builder.field("name", settings.get("name")); } builder.startObject("version") .field("confignment_timeout", consignment_timeout) .field("confirm_timeout",confirm_timeout) .field("promotion",promotion) .endObject(); builder.endObject(); channel.sendResponse(new BytesRestResponse(status, builder)); } } 这个代码里: Toc toc = (Toc)order.getAdapter(Toc.class); toc接口功过order的getAdapter的方式返回,在上面toc模快里TocAdapterFactory里, 解释了会根据order的type类型,返回对应的实现,这里会返回GenrealToc的实现。 我们可以通过在浏览器里输入url得到对应业务的toc和ump实现。 另外一乐buy_virtual的实现在类RestBuyVirtualAction中, 通过这两个实现对比,我们可以根据业务类型驱动平台调用对应的业务的超时和ump实现。 4.3 业务开发 下面我们再假设有个生活服务业务,需要对下单进行定制扩展, 生活服务订单会有自己特定的超时和ump优惠实现。 我们如何支持这个行业务呢: 第一步,实现一个extension-lifeservice-toc插件,里面会提供一个新的超时模块,注册对应的业务类型是“lifeservice”。 生活服务业务方会实现一个自己的Toc超时接口实现, 然后通过模块的形式封装,最后提供一个plugin实现类,将这个模块注册成为TocsModule的业务子模块: public class LifeserviceTocPlugin extends AbstractPlugin { public LifeserviceTocPlugin() { } @Override public String name() { return "lifeservice toc"; } @Override public String description() { return "lifeservice toc extension Plugin"; } public void onModule(TocsModule module) { module.registerToc("lifeservice", LifeserviceTocModule.class); } } 通过这个方式,生活服务对Toc超时的实现就已经注册到平台里,可以被使用了。 类似的方式我们可以开发生活服务的Ump模块。 最后我们开发一个restful API用来测试生活服务下单http://localhost:9200/buy_lifeservice public class RestLifeserviceBuyNowAction extends BaseRestHandler { private final Version version; private final CamelContext camelContext; @Inject public RestLifeserviceBuyNowAction(Settings settings, Version version, RestController controller, Client client, CamelContext camelContext) { super(settings, controller, client); this.version = version; this.camelContext = camelContext; controller.registerHandler(GET, "/buy_lifeservice", this); controller.registerHandler(HEAD, "/buy_lifeservice", this); } @Override public void handleRequest(final RestRequest request, RestChannel channel, final Client client) throws Exception { RestStatus status = RestStatus.OK; if (request.method() == RestRequest.Method.HEAD) { channel.sendResponse(new BytesRestResponse(status)); return; } XContentBuilder builder = channel.newBuilder(); // Default to pretty printing, but allow ?pretty=false to disable if (!request.hasParam("pretty")) { builder.prettyPrint().lfAtEnd(); } Order order = new Order("lifeservice",1); Toc toc = (Toc)order.getAdapter(Toc.class); toc.setOrderConfirmTimeout(order); toc.setOrderConsignmentTimeout(order); Ump ump = (Ump)order.getAdapter(Ump.class); ump.setOrderPromation(order); int consignment_timeout = toc.getOrderConsignmentTimeout(order); int confirm_timeout = toc.getOrderConfirmTimeout(order); float promotion = ump.getOrderPromation(order); builder.startObject(); if (settings.get("name") != null) { builder.field("name", settings.get("name")); } builder.startObject("version") .field("confignment_timeout", consignment_timeout) .field("confirm_timeout",confirm_timeout) .field("promotion",promotion) .endObject(); builder.endObject(); channel.sendResponse(new BytesRestResponse(status, builder)); } } 5. 总结 这边主要是针对业务开发和接入的实例性文档,在下一篇,会继续介绍业务流程的注册开发。同时也会对平台的一些基础功能模块做进一步介绍。
Businesworks的设计目标是为复杂业务系统提供平台化的底层支持,所谓平台化,就是对业务开发能以扩展,隔离的方式推进,驱动业务快速支持。 目前阿里很多的业务系统随着业务支持的增加,慢慢发展成为一个庞大的铁板一块式monolithic(铁板一块式) 风格的强耦合系统,系统本身可能经历一些重构和优化,满足新业务发展。但整体上还是为了快速的满足业务需求,在主流程上打补丁的方式,对业务的响应能力越来越差。于是平台化被提上日程,希望重新审视系统的架构设计,使架构不成为业务快速发展的瓶颈,并且进一步促进业务的快速开展。 这类业务系统的平台化对架构的需求有一些基本共性,Ali-Businessworks目标就是为了给业务系统提供平台化的基础框架,专门为复杂业务系统而设计,达到以下设计目标: 业务和平台分离。 平台将不关心具体的业务,只通过抽象统一的模型去完成业务逻辑。平台提供定制扩展机制,方便业务方通过定制扩展开发实现自己的业务需求。 业务隔离业务方将作为平台的ISV(Independent Software Vendors),通过交易平台,设计开发自己的业务。各业务方完全隔离,如果需要对方提供的服务,该业务方可以通过平台注册提供服务。对于调用者来着,这互相彼此完全透明。业务方只和平台打交道。 变化和实现分离在复杂业务平台系统中,业务变动需求频繁,比如大促期间,招商平台需要对玩法,招商流程等进行快速调整。因此我们需要把业务变化通过规则引擎管理起来,实现变化与实现分离,通过规则引擎去快速响应需求变化而不是硬编码实现,从而提高业务服务能力和系统稳定性。 职责功能分离系统实现通过plugin形式实现,通过在平台的注册提供服务,形成微服务架构,减少系统之间耦合,使系统实现简明规范,并且系统之间易于通过事件驱动方式协调,提高系统性能和稳定。 Businessworks的设计思想基于一下三篇ATA: 《从Eclipse平台看交易平台化》,强调微内核和扩展机制实现 《Google Guice平台模块化开发的果汁》,讨论平台的模块化开发,强调业务隔离,松耦合。 《平台化是舞台,流程编排就是导演一场戏》 ,讨论平台的服务流程编排 Businessworks就是基于这三篇文章的一个full story的实现, 定位复杂业务平台的基础架构,后续会以内部开源的方式进行迭代开发。 1. 平台架构 平台架构将采用微内核和插件扩展的架构,通过模块化技术,将平台的能力分层次透出。首先这个平台需要一个稳定的微内核Runtime,提供平台核心的能力,如扩展机制,插件模块管理等。在这个内核之上,平台提供一个基础集成平台,用于协调平台各组件协作,流程管理等。这个基础平台和微内核将构成平台的基石,然后在这些基础上,我们将构建业务领域平台,在这一层,定义业务领域相关的模型,功能点,业务原型等。最后业务方可以方便的利用平台的能力,构建自己特定的业务,满足业务需要。 平台自身成为一种运行时容器,为组件提供底层服务。而平台本身不具备任何面向用户的业务功能。 这个架构平台类似J2EE应用服务器,平台会定义类似J2EE规范的基础平台规范,基本功能组件用来定义扩展容器的能力,同时这些基本组件可以方便的扩展开发。各个业务方可以在平台规范下开发自己的业务模块,以类似J2EE应用包的形式部署在平台容器内,最终各个业务方在平台基础上构建一个灵活强大的业务系统。 同时通过分层设计,使得平台的建设开发和业务的开发独立开来,避免业务方代码影响平台,也是业务方以扩展开发的方式推进,保证业务之间的隔离,从而保障整个平台的稳定,更有效的提供对业务开发的支持效率。 2. 平台设计 在这一节,我们将分成讨论上面架构中的设计细节。主要分为基础平台设计,基础集成平台设计,业务领域平台设计,业务子系统设计,以及元数据和运维平台的设计。 2.1 基础平台 作为平台的微内核,主要提供接口扩展,模块,插件等基础功能。微内核的实现必须精简,小而美,对第三方依赖尽量减少,开发一个稳定轻量级的实现。 一个微内核意味着在这一层只提供核心的功能,作为一个可扩展的架构,这一层的目标是为提供扩展机制,同时对上层功能开发提供组件模块化支持,提供容器管理注册能力。 2.1.1 对接口的扩展能力(IAdaptable) 当平台的核心接口以API服务的形式暴露给上层应用时,某种程度就是对依赖应用的一种契约,这些接口必须保证能稳定提供服务, 对这些接口的修改不应该导致强迫依赖应用去修改, 但同时,接口因为平台的发展,需要提供更多的能力。为了满足平台能力扩展和对外提供稳定服务的双重要求,我们需要一种介质能对接口的行为进行动态的扩展,在不修改核心接口的前提下,利用扩展机制,扩展接口的行为,满足平台发展的需要。 为此,我们将设计一个IAdapatable接口, 用来满足接口扩展的需要。 同时平台需要对扩展接口提供注册管理功能,接口扩展通过平台注册后,平台会将对应扩展通过扩展查询机制,返回给调用者。 IAdapable接口来自Eclipse的实现方式,当一个接口声明实现IAdapter接口时,就代表这个接口可被扩展。 当一个接口需要扩展新的行为时,我们为新的行为定义一个新的接口。 然后通过IAdapterManger进行注册管理。 平台通过一个IAdapterManger的唯一实例会扩展提供一个类似注册表的实现,保存被扩展接口和扩展接口的注册信,从而给上层调用完成注册扩展,同时平台通过该注册信息,代理返回调用方期望的接口扩展实现。 IAdapterFactory是IAdapter的工厂方法,在这个工厂方法实现里,可以对多个接口进行扩展配置。 这个单例类作为平台的入口实现,用来管理平台的全局信息。AdapterManger是平台维护的一个关于接口扩展管理的实现。所有的Adapter实现通过AdapterManger提供的方法注册,查询,从而提供给平台基础的接口行为扩展能力。 2.1.2 提供模块化功能(Module) 对于平台来说,将系统的功能通过模块来定义功能的边界,模块之间相对独立。模块作为平台功能的组成的基本形式,通过预定义配置和运行时配置来完成平台的组装。 模块的具体实现将利用Guice的Module功能,完成配置信息和绑定各接口的特定实现。平台启动后,通过各种途径将相关模块整合起来,构建Guice Injector, 来完成将模块定义的实通过依赖注入的方式,快速构建起来,从而创建一个复合的模块系统。 平台利用Guice实现的模块模块系统中会涉及到以下概念: 2.1.2.1 模块元数据(Module Metadata) ModuleMetaData:{nameapace(命名), type(类型), setting(配置)} 平台会利用模块元数据对模块进行管理。模块作为平台的基本功能单位,我们将为此设计一个多维度管理方式。 模块的命名空间用来表示模块在复合模块系统中的唯一标识定位,通过name可以快速知道他所属的模块层级,命名格式为:module://{system|integration|trade|business}/{parent_module}/{module_name}模块类型用来标明模块的类型,比如系统模块,领域模块,还是业务扩展模块,每种不同类型模块,平台可以给予不同的能力控制。 Setting是模块的配置信息,读取自预定义配置或者运行时候配置,可以动态的对模块进行控制管理。 2.1.2.2 模块命名空间(Module Namespace) 多个模块基于他们在源代码树或者代码中的调用情况被放进了不同的命名空间中。不同的侧面,诸如插件,监控,环境设置服务是分割的功能实体,他们之间没有模块层面的依赖。模块可能已经太过庞大或者难以复合使用的将会被进一步分割成嵌套命名空间中的稍小的模块。 这样平台的功能和结构便可以通过: {Namespace:Module Name: Bound Class}清晰管理起来。 2.1.3 提供插件机制(Plugin) 在前面接口扩展,模块化基础上,平台已经具有了灵活扩展的能力,但是这种扩展能力是在平台代码的基础上通过增加新的类来实现的。对应一个平台来说,我们需要更加灵活的开发方式,让第三方团队可以跟方便的开发平台业务功能,这需要提供一种插件的方式,我们希望能够实现类似eclipse的插件机制。业务方提供一个压缩包,这个插件包有约定的目录组织方式,有一个描述插件的plugin定义文件(plugin.xml)。平台启动后,通过扫描classpath下的plugin.xml文件,得到plugin的实现类名,然后去启动执行,将plugin的功能加入到平台里。 插件的定义 插件主要提供模块的扩展和配置的扩展,如plugin接口定义 插件文件结构 平台默认一个目录为插件的安装目录,第三方插件放在该目录下,平台启动时,会加载扫描这些插件执行里面的plugin接口实现类。 插件扫描加载 平台会实现一个PluginService,用来在Classpath中扫描加载plugin插件,并提供插件的注册管理功能。 插件生命周期管理 平台会提供插件的安装,卸载,测试等功能。方便新功能的开发效率。 同时我们也会定义插件的元数据格式,用来管理插件。 插件开发 插件开发以maven项目形式,平台提供插件开发所需的二方包。插件的开发和平台开发独立,各业务系统可以独立开发功能插件 2.2 集成平台(Integration) 首先我们要明确为什么需要这个集成平台: 对于交易这样的复杂业务系统,数据庞大并不是平台增长的唯一方式。业务流程也可能在复杂性方面不断增长。交易处理的信息可能会是各种数量的并且任意数量组合的传输类型、过滤、增强以及路由等。复杂的问题会在任何领域都出现,但是解决它们的总体策略通常是一样的:分而治之。我们会将问题拆分为更容易解决的子问题。然后这些方案再按照与分解相反的方式组合在一起形成整体的解决方案。通过观察会发现这样的问题是经常发生的;借助于经验,能够识别出最优的方案。我所讨论的就是模式。这些模式被命名为企业集成模式(Enterprise Integration patterns), 由Gregor Hohpe和Bobby Woolf进行了分类和总结。 2.2.1 集成平台设计目标 在平台Runtime成品当平台的功能已插件,模块的形式接入平台后,我们需要一组模块一起完成一些复杂的业务功能。比如在交易系统中,下单这样一个流程,就可能设计到多个模块提供的功能和服务。如何方便对这些流程以及服务进行编排是体现平台能力的重要标志。当一些业务需求需要对流程进行少量修改或者重新定制某些服务,而大部分流程功能保持不变是,平台提供简单方便的编排能力就可以满足对业务的快速响应。 首先在这样的流程体系中,一个复杂的业务流程需要和多种外部业务系统(比如交易的下单系统,退款系统,支付宝),中间件,数据库系统(比如Tair,Notify,MetaQ)连接,调用本地服务或者远程服务才能完成,如果我们希望平台能够以简洁的方式对流程进行编排,平台必须达到以下要求: 简化外部系统的连接调用方式,消除不同系统调用方式的差异,将系统的调用细节由平台完成,对调用者透明,平台提供一致的调用模式,同时该调用方式简单有效。 同时,对流程的描述提供DSL语言支持,简化流程的设计,方便通过可视化流程设计工具提高开发者流程设计能力。 提供对流程的运行监控,实时追踪业务运行情况。 对流程的生命周期进行管理,可控制流程的启动停止。 高效的流程执行引擎,能针对流程进行流量控制。 完善的流程错误处理报警机制。 为了达到以上要求,系统通过集成平台提供对满足对流程编排管理的需要。这些要求在EIP模式里有很好的实践和总结,在金融领域,传统中间件厂商利用这些模式实现的中间件方案为复杂的金融交易业务提供了解决方案。 目前业务系统涉及的业务和流程也变得越来越复杂,流程,定制需要对不同的业务提供不同的支持。涉及到多个系统之间的交互,比如交易里一个订单从生成到完成,需要UIC,IC,UMP,库存,保险,风控,限购,积分,toc,buy,tp等多个系统合作,不同类型的业务订单设计的流程也很不相同,集成模式为我们解决这些业务的复杂性提供了很好的问题解决方案,让我们对于业务和流程能更清晰的管理。 集成平台设计的目的就是引入企业集成模式解决方案,为平台提供对复杂业务的流程的隔离和编排。 2.2.2 集成平台实现 集成平台会利用Apache Camel提供的EIP开源实现,完成集成和流程编排的功能. Apache Camel是Apache基金会下的一个开源项目,它是一个基于规则路由和处理的引擎,提供企业集成模式的Java对象的实现,通过应用程序接口 或称为陈述式的Java领域特定语言(DSL)来配置路由和处理的规则。其核心的思想就是从一个from源头得到数据,通过processor处理,再发 到一个to目的的.我们将在Apache Camel的基础上,为交易业务平台提供集成和编排能力。 2.3 业务领域平台(Domain) 业务领域平台主要定义业务的领域模型,包括功能,流程安排等。所有这些业务的基础元素会以元数据的方式定义和管理起来。 以交易为例: 2.3.1 业务领域模型 业务领域平台是建立在基础集成的平台基础上,通过设计业务相关的领域模型,来完成业务相关的基本功能和流程设计。 比如对于交易这个领域,我们需要定义出交易涉及的订单等领域对象模型,设计超时,优惠,物流等功能模块,以及下单等流程。 对于招商之类的业务平台,需要定义活动领域对象,设计玩法,招商流程等相关的功能和流程。 这些业务领域相关的实现会通过模块的方式实现并向基础平台注册。 2.3 业务平台(Business) 业务方根据特定的业务场景,开发符合自己需求的业务系统。 业务系统和业务领域平台的关系就像是策略模式的一种实现。业务领域平台提供业务的各种抽象的策略定义,比如定义了各种业务原型,定义了业务领域功能 业务系统则基于业务自身提供具体的策略定义, 如确定本类业务的交易原型,各种功能点的特定实现。 2.4 元数据平台(Metadata) 在这个平台,平台开发者可以通过平台元数据和集成元数据定制扩展平台的功能。 领域和业务开发者可以注册新的业务类型,设计业务流程等。 对于整个系统,我们将平台的能力通过元数据定义出来。 作为基于领域对象模型设计的平台系统, 根据系统的分成设计,会有以下元数据定义 平台元数据元数据 定义 基础平台 Adapter 定义接口扩展Module 定义模块 集成元数据 元数据 定义 集成平台 Component 定义端口组件Processor 定义处理器Router 定义流程 业务领域元数据... 2.5 运营开放平台 在业务系统里,一定是运行着大量业务的复杂系统,需要对业务的各个环节进行监控,比如我们需要知道订单从生成到关闭所经过的处理链路,需要知道不同的服务被调用的统计等等,所有这些将以不同视角通过运维开发平台提供给使用者,使交易业务更加透明,可控,方便问题排查,方便交易性能调优等。 运营的各方面能力也可以从平台各个层面进行维护监控。业务团队会关心本身业务的运行情况,领域团队可以关注领域相关能力的运行情况。 3 研发模式 开发团队分为平台开发团队,领域开发团队和业务开发团队,对平台的开发进程处理基础平台外,其他都以插件开发的形式推进。保证平台开发和业务开发独立,基于不同的代码管理。 平台开发团队的职责 a) 基础集成平台的功能模块开发,提供平台的基础能力,也包括性能优化,错误追踪隔离等功能。b) 开发辅助工具开发,包括流程设计,插件开发工具,平台命令行等。c) 发布流程设计和维护d) 测试机制设计e) 问题排查流程设计和辅助工具开发 领域开发团队职责 a) 负责开发该领域内的领域模型,相关功能点定义和实现,也包括流程原型设计,扩展点设计。b) 提供业务类型的审批流程设计c) 提供业务部署设计规范 业务开发团队职责a) 在交易平台基础上,定制扩展开发自身业务 b) 制定自身发布流程c) 制定自身监控报警设置 4 现状 目前Businessworks基础集成平台第一版本已经开发完成,并在招商平台中开始试用。 平台以二方包的实行提供给业务团队使用, 在下一篇中,《基础业务集成开发平台(BusinessWorks) - 业务开发篇》 我们会介绍如何在业务系统中接入Businessworks,从而具有平台化的一些底层支持。
该文章来自阿里巴巴技术协会(ATA)精选集 BaaS代表第二代云服务,相对于AWS、阿里云等公有云(IaaS,PaaS)是第一代云服务,通过广泛部署云数据中心解决了开发和运维系统不需要管理服务器的问题,BaaS则在第一代公有云数据中心基础之上,对云计算资源进一步封装、简化与优化,提供开发、运维和服务的一站式云服务。 这就是所谓BaaS(后端即服务)模式的兴起,BaaS将公有云数据中心资源根据前端应用场景打包,通过简化的调用接口提供给开发者使用。通过减负,开发者得以集中精力于用户的研究、APP软件的创意与设计以及移动端的应用开发即可,能大幅简化开发过程、周期、人员与资金投入,从而降低成本,并能把移动APP应用快速推向市场。 以我的理解,BaaS架构的发展趋势是解决业务的开发效率问题。 所以本系列文章主要介绍BaaS的发展和架构特点。 本文是BaaS相关架构实践的起始篇,主要介绍目前软件开发模式的发展趋势,目前BaaS的生态等综合性内容,然后用几个篇章详细介绍BaaS的概念,发展趋势,如何实践BaaS架构。 最后我们在探讨如何用BaaS的思路来,结合阿里的技术体系来构建实施中台。 《BaaS后端即服务 - 通往中台架构之路》 - 本篇会介绍相关的背景和总体思路。 《BaaS后端即服务 - 概念篇》- 介绍BaaS的概念,在云架构体系中的定位。 《BaaS后端即服务 - 分析篇》- 介绍当前BaaS发展的趋势和主流BaaS平台功能的对比。 《BaaS后端即服务 - 中台篇》- 介绍如何用BaaS的思路来构建阿里的中台,是否可以成为中台建设的基础架构之一。 1. 外面的世界很精彩 如果放在整个互联网来看,在这个移动互联网时代,火热的创业潮兴起,O2O,物联网,互联网金融, 一个想法从产生到实施,也许只有几个月的时间,否则就会有类似的产品推出, 在短暂时间内推出产品,去市场检验,从而占据市场,对创业者来说,都是一场要快速执行的战役。可以说这是一个激烈竞争的市场,在这样的市场,只有高效,有效的模式会成功的存留下来,而BaaS的架构就是诞生发展于移动互联网这个熔炉中并快速发展起来,成为云架构体系中重要的架构思想之一。 对创业开发者来说,成本和效率是最需要关注的两个方面,第一他们缺少技术的积累,第二他们需要很低的成本去验证他们的模式,从而成长起来。这些云服务提供者成为他们最好的选择。 当创业技术团队去实现他们的技术架构实现时,会欣喜的发现,他们需要的一些通用功能有专门的技术创业型公司为他们提供服务,这些服务以云服务的形式对开发者开放,他们或者提供某个领域的功能,或者提供全面的功能,可以说正在慢慢形成一个云服务生态,为创业者提供各种技术服务。 让我们假设自己是一个O2O的创业的CTO,准备就一个O2O的想法,开发出一个业务平台。 在这个移动互联网时代,基本会需要移动APP和web的功能,那么一下的问题我们需要解决: 服务器部署维护 APP和Web网站开发 后端服务开发 平台功能: 认证和授权 文件存储 推送和通讯 地图功能 支付功能 社会化分享 验证和安全 智能识别 搜索 用户行为分析 ... 业务功能 活动管理 最新动态 ... 当这些问题被规划出来后,作为CTO,你就需要招聘后端工程师,前端工程师,IOS,Android工程师,运维工程师等,然后针对平台的功能,会发现每一个都是需要去设计,实现,其中有些功能在技术上挑战很大,比如搜索,地图,支付这样的功能,独自快速实现这些功能对创业公司来说是需要很大成本(时间和开发资源)才能完成。 但同时,随着在技术市场上调研, 会发现这些基础的服务,有专门的公司提供解决方案,这些方案可以被集成进来,从而节省开发成本。比如: 推送通讯领域,有极光推送,个推,环迅,融云,UDesk,Bmob,meChat等等 地图功能,有百度地图LBS,高德地图 支付领域, 除了支付宝和微信支付自己提供的服务外,Ping++,现在支付等提供了更集成易用的整体支付解决方案。 验证和安全, mob,云之讯提供了短信验证等功能。 社会化分享,友盟,jiathis等 ... 基本在每个常用的功能领域,都有技术创业公司为此提供专门的服务,这些技术公司通过敏锐的商业触觉,快速响应技术市场的需求,从而也推动了新的技术架构服务的诞生。通过给创业者更多的方案选择,由此创业者可以直接使用这些服务来实现自己的业务需求,从而能快速的将产品平台开发出来给最终用户使用。 可以说这些业务和技术创业公司鱼水情深, 形成了生态互补,通过吸引创业公司使用它们的服务,技术平台公司获得更多的数据,创业公司则获得了免费的越来越好的服务。技术平台公司通过竞争,会越来越完善他们的服务,给开发者更好的开发体验,从而自己在领域中获得龙头地位。 这些技术平台公司在这个移动互联网,云计算的时代,通过将某个领域的功能封装成服务来获取开发者用户,他们的这种模式,就是云架构最近几年飞速发展起来的BaaS架构。 2. 我们的世界有点无奈 2.1 系统繁多,不能轻装上阵 淘宝技术部目前所有开发人员与系统相比太少,为什么有这么多系统,都起什么作用, 于是我们决定用领域模型分层的方式去梳理这些系统: 然后我们又发现,好像我们没做太多事情啊,支持的业务可以数的过来,这些居然需要上百系统来支持。 这后面存在的问题肯定让人深思,然后随着我们梳理的深入,会发现有些最主要的问题是: 1. 重复建设 2. 缺乏规划 这里对开发资源和物理资源的浪费将显而易见。 这些系统也成为了一个庞大的负担,我们需要花很大的精力去维护,升级,开发,同时也要花很多公司的资源去运行,监控。 那么我们在审视下,如果我们需要开发一个新的业务应用,是否能想外面创业公司一样,得到很好的服务资源呢,作为国内最强的技术公司,我们的开发实力毋庸置疑,可是我们的效率呢? 如果一个软件变得复杂难以维护需要去做代码级别的重构,那么我们这种系统级别的复杂就需要架构级别的重构了。 2.2 对开发人员技术要求很高,不利于集中精兵强将做关键系统。 作为阿里的技术开发人员,技术实力相对外部强很多,一方面原因是我们拥有很好的商业场景,需要我们用技术去保障,另一方面确实被高强度业务开发给淬炼出来的。 另外我们发现,如果我们想利用外包做一些阿里的系统,基本只能是 当我们需要开发一个新的业务系统, 开发阶段: 开发人员就需要考虑如何设计数据库,分库分表,安全,高并发,性能,需要使用到, 数据库(mysql,Hbase),消息中间件(notify,metaq), 缓存(Tair),分布式调用(HSF), ...J2EE里的那些技术阿里的开发人员都很熟悉了。 维护阶段: 我们都知道我们队系统稳定性的要求可以说是: 稳定压倒一切。在这个大促期间,大部分开发团队的工作重心都是在围绕稳定性做准备,梳理系统架构,梳理系统强弱依赖,设计限流预案等等。 于是高并发,系统性能调优,JVM等也成为阿里开发人员的强项了。 可以说,这样做几年业务系统下来,阿里的开发人员的技术能力得到很大的提升,通过呕心沥血,熬夜加班奋战来维护支持业务发展。 可是反过来想想,为什么我们做个业务系统,除了业务逻辑实现外,还需要每个开发人员掌握如此多的技能。 作为业务系统的开发人员,不是应该专注于业务逻辑的开发吗。 系统的稳定性,后端的高并发性能不是应该有更底层,更专业的团队去做吗,为什么每个开发团队都要求去做,这些不合理是由什么造成的,是分工?规划?还是技术架构? 2.3. 业务系统和平台技术系统界限不明确 阿里内部经常有个这样的问题, 做技术系统的会在逐渐去做业务, 做业务系统的会想沉淀做平台, 这种现象反映大家因为KPI驱动想做出成绩,当却也是分工不明确导致大家对自己的目标不明确。 业务开发团队需要去服务业务的需求,和技术开发团队需要为业务开发提供各种能力,做好各种底层支持服务, 这种架构上的明确会带来职责上的明确。 2.4. 系统之间缺乏集成协作标准,难以协同解决业务复杂性 这一点我们需要企业中间件软件学习, 如在传统银行业务中,他们内服的各种系统可以在企业集中模式(EIP),企业总线之类的标准协同起来完成复杂的业务。 3.我们可以做什么 软件工程很多是借鉴了建筑工程, 在建筑领域,有一个公司很引人注目,远大集团,在2010年上海世博会,他们用一天建成了远大馆而大出风头。 最近他们的一个新闻是: 远大集团19天建成57层高楼 建筑方式的革新成就中国速度”. 他们取得的成就是革新性的建筑方式,通过**标准化建筑模块**来建筑房屋。 如果我们能把集团多年来的技术能力积累更加标准化,模块化,让业务团队可以对这些能力快速组装使用,就是我们说期待的“中台”能力了。 阿里这样的集团,我们的技术积累已经成一个完整体系, 从云基础建设,中间件,电商系统基本覆盖了所有技术领域,通过这些积累,我们支持了全球最大的电商业务。我们需要梳理我们的能力,把能力”建筑模块化“,同时能力之间通过”标准化“协同。 这样我们的开发团队可以很好的分工开发各种能力模块,同时业务系统可以标准化的使用这些模块。 3.1 构建能力超市,利用精细市场管理, 将开发模式由农贸市场转化为现代化超市模式 我们知道, 城市化发展中,超市取代农贸市场是个趋势。 原因是: 超市与农贸市场最本质的区别在于超市是连锁经营,连锁经营的特点是统一进货、统一配送和统一管理。连锁超市以连锁制为轴心,以众多的门店网络为市场依托,以中央采购制开发销售利润,以现代化的配送中心获取物流利润,将市场信息向加工制造业渗透,发展定牌商品,甚至形成供应链,开发生产利润。可以说,连锁经营是将工业生产中的标准化、程序化、规模化向流通领域延伸,在大幅度降低成本的同时也为消费者带来了看得见的实惠。 这种通过统一管理,标准化的方式可以获得规模上,效率上的最大优势。 我们的业务开发模式也应该学习现代化超市模式,用统一管理,标准化来提高效率。 目前,集团由于业务快速发展和技术规划之间的差距,如果审视我们内部的开发生态,各开发团队由各自部门业务独自发展,很多资源存在重复建设情况,技术和业务不能很好的支持互补,对彼此或对市场整体能力不清晰,这种重复造轮子就像农贸市场重复进货一样。 我们更需要现代化超市的模式,布局,库存,能力等有总体的管理和规划,各开发团队的职责和能力能清晰的管理,彼此之间能更好的协作。 能力成为这个超市的商品,不存在但是需要的能力会被采购, 没有销量的能力会逐渐废弃,销量大的能力会被增强改进, 重复的能力会被避免。能力的种类会被统一布局,能力的使用可以被监控计费 这样我们就可以形成一种有效机制,当某个能力被开发形成后,就可以在类似超市中被所有业务团队看到使用,由于业务团队对这种能力的需求,他可以被得以进一步发展来满足这些业务方需要。 而不是目前这样各业务团队需要类似的能力,下面的开发团队重复性的去开发类似功能而形成资源浪费。(比如我们想想我们内部有多少规则系统....) 能力的开发者和能力的消费者可以在能力超市的市场化规划运作下,可以形成良性高效的生态,避免资源的浪费。能力的使用者利用“超市“可以知道方便的了解内部的所有能力,能力的开发者可以响应市场的需求,开发出真正需要的能力来填补空白。 3.2 建立系统之间的类企业集成标准,让多系统之间的数据交换更加便利 传统企业中间件利用企业集成系统来协调多系统解决复杂业务问题。 阿里的业务发展也需要我们的系统跟需要协调起来快速响应完成复杂业务的支持。 阿里内部有很多场景需要数据之间数据同步,比如业务系统和搜索系统数据同步。 ODPS离线数据和在线数据之间数据交互等。 目前这些系统之间数据交互是通过特定系统自己定义的API或者脚本实现。 这种标准的确实,需要开发人员掌握每个系统的特定API,如果能指定一定的标准,就像企业集成一样,也将大大降低开发人员的开发门槛,提高开发的效率。 3.3 开发从J2EE时代升级到云开发时代,构建移动互联时代的快速创新能力 J2EE时代的三层架构(表现层,中间层,数据服务层)将被移动互联网云计算时代的三层架构(UI,MBaaS,平台)取代。 复杂业务系统可以在新架构基础上变得简单,松耦合,更易和内部,外部分享数据和接口。 如上图说是,目前我们的开发架构大部分还只是在第一种重度耦合的状态或部分进入SOA架构,我们需要达到第三种开发架构状态,让系统之间可以简单,规范,互通的完成复杂业务。 并且,对业务开发团队来说,他们的开发能力更专注前端,交互,需要掌握的技术栈里就只需要javascript和Restful API就够了,他们可以跟专注去理解业务模型和逻辑,快速构建业务系统,进行业务创新。 而对于后端团队,将跟专注做平台和服务,后者需要他们将J2EE时代的开发架构,比如MVC, RPC等架构向向微服务,EDA,CQRS等云时代的架构升级,更好的将系统复杂性解构,利用服务化来构建满足业务团队的需要。 在架构升级改变下,开发团队的分工也将更加明确, 比如: 业务团队-> 前端,交互,业务逻辑 后端团队-> 平台,服务,稳定性 3.4. 在集团内部建立开发者生态 让应用(业务方),开发者,平台围绕以数据形成一个良性生态,在这样一个平台,所有开发者的知识和经验可以很好的被共享,对各种业务的开发,架构的设计从设计到实现都能被大家说review,关注。同时建立开发的扩展和模块标注,让所有开发者都可以主动提交能力模块并可能被中台“能力超市”采购。
该文章来自阿里巴巴技术协会(ATA)精选集 作为一个java程序员,Maven是再熟悉不过的工具了, 它提供了构建项目的一个框架, 在默认情况下为我们提供了许多常用的Plugin,其中便包括构建Java项目的Plugin,还有War,Ear等。除此之外还提供内建的项目生命周期管理。 以上是我们最熟悉的maven的一面。 下面我们要从领域平台设计的角度,分析Maven的优美设计,为我们做领域平台化提供参考,最后我们再来思考如何做招商的领域平台。 Maven是领域驱动设计实现的,作为一个强大的项目管理构建平台而实现 我们先会介绍maven的基本模型,概念以及功能。 然后我们在分析这个领域平台在领域设计上的一些亮点。最后我们再延伸思考,如果用maven的思路我们可以在阿里的业务系统平台化中得到什么启示。 Maven的用途,核心概念,用法,扩展等 Maven的基本介绍 Maven 是 Apache 组织下的一个通用项目管理工具,它主要用来帮助实现项目的构建、测试、打包和部署。**Maven 提供了标准的软件生命周期模型和构建模型,通过配置就能对项目进行全面的管理。它Maven 将构建的过程抽象成一个个的生命周期过程,在不同的阶段使用不同的已实现插件来完成相应的实际工作,这种设计方法极大的避免了设计和脚本编码的重复,极大的实现了复用。** Maven的核心概念 POM 这是Maven最重要的一个概念, pom是指project object Model。pom是一个xml,在maven2里为pom.xml。是maven工作的基础,在执行task或者goal时,maven会去项目根目录下读取pom.xml获得需要的配置信息 这个POM,就是maven平台的领域对象。 Maven就是围绕这个POM领域对象构建起来的领域平台。 pom文件中包含了项目的信息和maven build项目所需的配置信息,通常有项目信息(如版本、成员)、项目的依赖、插件和goal、build选项等等 pom是可以继承的,通常对于一个大型的项目或是多个module的情况,子模块的pom需要指定父模块的pom pom文件中节点含义如下: project pom文件的顶级元素 modelVersion 所使用的object model版本,为了确保稳定的使用,这个元素是强制性的。 除非maven开发者升级模板,否则不需要修改 groupId 是项目创建团体或组织的唯一标志符,通常是域名倒写, 如groupId org.apache.maven.plugins就是为所有maven插件预留的 artifactId 是项目artifact唯一的基地址名 packaging artifact打包的方式,如jar、war、ear等等。默认为jar。 这个不仅表示项目最终产生何种后缀的文件,也表示build过程使用什么样的lifecycle。 version artifact的版本,通常能看见为类似0.0.1-SNAPSHOT,其中SNAPSHOT表示项目开发中,为开发版本 name 表示项目的展现名,在maven生成的文档中使用 url 表示项目的地址,在maven生成的文档中使用 description 表示项目的描述,在maven生成的文档中使用 dependencies 表示依赖,在子节点dependencies中添加具体依赖的groupId artifactId和version build 表示build配置 parent 表示父pom groupId:artifactId:version唯一确定了一个artifact Artifact 一个项目将要产生的文件,可以是jar文件,源文件,二进制文件,war文件,甚至是pom文件。每个artifact都由groupId:artifactId:version组成的标识符唯一识别。需要被使用(依赖)的artifact都要放在仓库 Repositories Repositories是用来存储Artifact的。如果说我们的项目产生的Artifact是一个个小工具,那么Repositories就是一个仓库,里面有我们自己创建的工具,也可以储存别人造的工具,我们在项目中需要使用某种工具时,在pom中声明dependency,编译代码时就会根据dependency去下载工具(Artifact),供自己使用。 对于自己的项目完成后可以通过mvn install命令将项目放到仓库(Repositories)中 仓库分为本地仓库和远程仓库,远程仓库是指远程服务器上用于存储Artifact的仓库,本地仓库是指本机存储Artifact的仓库,对于windows机器本地仓库地址为系统用户的.m2/repository下面。 对于需要的依赖,在pom中添加dependency即可,可以在maven的仓库中搜索:http://mvnrepository.com/ Build(Lifecycle -> Phase -> Goal) Lifecycle(生命周期),是指一个项目build的过程。这是maven最高级别的的控制单元,它是一系列的phase组成,也就是说,一个生命周期,就是一个大任务的总称,不管它里面分成多少个子任务,反正就是运行一个lifecycle,就是交待了一个任务,运行完后,就得到了一个结果,中间的过程,是phase完成的,自己可以定义自己的lifecycle,包含自己想要的phase maven的Build Lifecycle分为三种,分别为default(处理项目的部署)、clean(处理项目的清理)、site(处理项目的文档生成)。他们都包含不同的lifecycle。 可以理解为任务单元,lifecycle是总任务,phase就是总任务分出来的一个个子任务,但是这些子任务是被规格化的,它可以同时被多个lifecycle所包含,一个lifecycle可以包含任意个phase,phase的执行是按顺序的,一个phase可以绑定很多个goal,至少为一个,没有goal的phase是没有意义的 下面重点介绍default Build Lifecycle几个重要的phase: validate 验证项目是否正确以及必须的信息是否可用 compile 编译源代码 test 测试编译后的代码,即执行单元测试代码 package 打包编译后的代码,在target目录下生成package文件 integration-test 处理package以便需要时可以部署到集成测试环境 verify 检验package是否有效并且达到质量标准 install 安装package到本地仓库,方便本地其它项目使用 deploy 部署,拷贝最终的package到远程仓库和替他开发这或项目共享,在集成或发布环境完成 以上的phase是有序的(注意实际两个相邻phase之间还有其他phase被省略,完整phase见lifecycle),下面一个phase的执行必须在上一个phase完成后 若直接以某一个phase为goal,将先执行完它之前的phase,如mvn install 将会先validate -> compile -> test -> package -> integration-test ->verify最后再执行install phase goal代表一个特定任务 A goal represents a specific task (finer than a build phase) which contributes to the building and managing of a project. mvn package表示打包的任务,通过上面的介绍我们知道,这个任务的执行会先执行package phase之前的phase mvn deploy表示部署的任务 mven clean install则表示先执行clean的phase(包含其他子phase),再执行install的phase。 mojo: lifecycle与phase与goal都是概念上的东西,mojo才是做具体事情的, 可以简单理解mojo为goal的实现类,它继承于AbstractMojo, 有一个execute方法,goal等的定义都是通过在mojo里定义一些 注释的anotation来实现的,maven会在打包时,自动根据这些anotation 生成一些xml文件,放在plugin的jar包里 Archetype 原型对于项目的作用就相当于模具对于工具的作用,我们想做一个锤子,将铁水倒入模具成型后,稍加修改就可以了。 类似我们可以根据项目类型的需要使用不同的Archetype创建项目。通过Archetype我们可以快速标准的创建项目。利用Archetype创建完项目后都有标准的文件夹目录结构 既然Archetype相当于模具,那么当然可以自己再造模具了啊,创建Archetype 下面介绍利用maven自带的集中Archetype创建项目。创建项目的goal为mvn archetype:generate,并且指定archetypeArtifactId,其中archetypeArtifactId见maven自带的archetypeArtifactId Plugin maven的核心仅仅定义了抽象的生命周期,具体的任务是交由插件完成的,插件以独立的形式存在。 对于插件本身,为了能够复用代码,它往往能够完成多个任务。如maven-dependency-plugin有十多个目标,每个目标对应了一个功能,如 dependency:analyze、 dependency:tree和dependency:list。这是一种通用的写法,冒号前面是插件前缀,后面是该插件的目标。 maven的生命周期与插件相互绑定,用以完成实际的构建任务。具体而言,是生命周期的阶段与插件的目标相互绑定,已完成某个具体的构建任务。例如项目编译这一任务,它对应了default生命周期的compile阶段,而maven-compiler-plugin这一插件的compile目标能够完成该任务,因此将他们绑定。 比如maven缺省的三个生命周期: clean, default, site和插件绑定如下: Clean clean clean:clean Site site site:site site-deploy site:deploy Default process-resources resources:resources compile compiler:compile process-test-resources resources:testResources test-compile compiler:testCompile test surefire:test package ejb:ejb or ejb3:ejb3 or jar:jar or par:par or rar:rar or war:war install install:install deploy deploy:deploy 用户还能够自己选择奖某个插件目标绑定到生命周期的某个阶段以执行更多更特色的任务。 比如: <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.1</version> <executions> <execution> <id>copy</id> <phase>install</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>lib</outputDirectory> </configuration> </execution> </executions> </plugin> 定义了一个id为copy的任务,利用插件maven-dependency-plugin的copy-dependencies目标绑定到default生命周期的install阶段,来实现项目依赖的jar包的自动复制。 当插件目标被绑定到不同的生命周期阶段时候,其执行顺序会有生命周期阶段的先后顺序决定的。如果多个目标被绑定到同一个阶段,他们的执行顺序是由插件声明的先后顺序决定目标的执行顺序。 profile 一个优秀的构建系统必须足够灵活,应该能够让项目在不同的环境下都能成功构建。maven为了支持构建的灵活性,内置了三大特性,即:属性、profile和资源过滤。 profile定义了一系列的profile变量,在具体构建时可用使用其中的某个profile去变量替换资源文件。 Maven设计特点 在上面的介绍里我们花了很长的篇幅来介绍maven的模型,概念,功能,和运作方式。大家也跟了解**maven是基于project这个领域对象模型上,以项目周期管理为目标而构建的领域平台。** 设计了领域模型,所有的功能围绕项目Project这个领域模型构建 设计了领域模型的生命周期和执行方式(lifecycle-phase-goal) 设计了项目的原型模式(architype) 设计了可扩展的插件体系(plugin-mojo) 设计了依赖管理(dependencies) 设计了动态配置机制(profile) 设计了artiface仓库模型(repositories) 从这7个设计点可以看出,maven这个平台设计得很清晰,对需要解决的问题(项目管理构建)设计了一系列的概念, 功能,流程,方便maven的使用者明确的掌握,同时又可以针对自己的个性化需要去定制扩展平台的功能。 构建maven式领域平台 前面我们都是在介绍maven本身和他的设计特点。 现在我们要发散思考,这种设计在我们用来做自己业务系统的领域平台时有什么可以借鉴。 **Maven是基于领域模型驱动设计的平台,围绕这个业务模型,从他的起始状态到结束状态有一个明显的生命周期概念, 在这个生命周期里,不同的功能点独立实现同时又被复用形成多个顺序执行的任务单元推动周期的执行。 ** 在我们的业务系统中也有很多类似的需要。 比如下面我们用招商和交易下单为例。 招商平台和Maven平台类比 什么是招商 每年大促前期最重要的准备工作就是招商,招商就是淘宝小二创建了一个活动项目,设计这个活动的玩法, 然后吸引一些商家带着他们商品参加这个活动,然后小二通过各种资质验证保证报名商家的资质,并通过各种条件在这些商家中选择出最后参加活动的结果。然后这个项目活动在参与者,玩法等确定下,去执行一些必要的动作,保障这个活动项目在活动期间能够按照设计的完成。 招商的领域模型(domain) 招商中最重要的领域模型是活动,我们也可以更规范的成为活动项目(project),每一次小二开始一个招商活动,其实就是设计一个招商活动项目。项目里需要明确项目的参与者,项目的时间(报名期,审核期,活动期),项目的玩法,项目的产出和需要执行的任务。 活动项目的生命周期(lifecycle) 很直观,我们就可以确定活动项目是有一个生命周期的。从小二创建活动项目开始到活动结束,基本会经历一下周期,创建->报名->审核->启动->运行->结束->收尾。 每个周期可以分多个阶段,每个阶段都有多个目标需要被执行。这些任务可以被灵活复用。 如何设计这些周期,阶段和目标对招商平台化是否成功有决定性作用。 活动项目的原型模板(architype) 每次小二设计一个活动项目,从资质,玩法,到运作方式等都需要做很多配置,管理。资深运营小二如何把他们的经验沉淀起来。避免下次类似的活动项目必须从头开始,新手小二也很难设计出一个成功的活动。这些就需要把这些活动按照一定的模式沉淀成原型(项目的模板),方便下一次即使是新手也可以快速的构建高效的活动项目。那么招商平台就也需要设计类似maven 原型类似的功能。 可扩展的插件体系(plugin-mojo) 招商可以说是根据业务方需求,不停的响应提供新的功能支持新的需求等。当新的需求来的时候,我们需要分析出平台不足或者需要定制的功能点,然后以插件扩展的方式完成开发,同时能把这些功能很好的注册沉淀到平台,平台的功.能随着业务的发展越来越强大。比如支持更多的资质校验,支持更多的算法模型,对已有流程如何进行定制扩展,加入新的流程点等等。都需要平台提供一个类maven的可扩展的插件体系。 依赖管理(dependencies) 招商活动会有很多的依赖,比如IC,UIC,报名的商家和商品资源,搜索算法等等。如何把这些依赖类似maven一样管理起来,对招商活动项目的运作和监控也就更加直观。 动态配置机制(profile) 很明显,这个机制对招商活动项目也很有用处,比如每次活动对商品的打标,活动时间之类都需要类似的功能机制去保障。方便小二的使用调整。比如小二可以预先准备一些配置,然后根据实际情况,切换不同的配置。 artiface仓库模型(repositories) 活动项目依赖的各种插件实现, 活动结束后生成的案例资源,都可以以案例库的方式存储起来。方便更进一步的知识积累和业务扩展。 这些只是对招商这个复杂系统的初步想法,具体招商如何设计模型,生命周期等都需要结合目前招商的功能,按照这种思路,我相信我们的招商平台会有一个很好的架构,以后也许我们可以给小二提供类似maven命令的方式来构建活动了,:) mvn create 创建活动 mvn enroll 报名 mvn review 审核 mvn start 启动 mvn close 结束 mvn clear 扫尾 招商平台化会包括以下功能: 活动项目对象模型 活动项目构建引擎 开放平台 原型管理 业务识别 ... 通过这些我们打造一个强大的招商平台。 交易下单平台(Buy)和Maven平台类比 另一个很类似的就是buy下单了,基于订单模型,下单是个典型的生命周期,也很适合用maven的思路来设计。 大家也可以按照类似的思路分析下。 此处省略 1000字,:)。 mvn buy mvn promotion mvn ... mvn order 总结 在工具软件里,有很多很好的设计,这些设计对我们做业务系统是个很好的参考。 比如eclipse, maven(还可以关注gradle,这个设计更好),我们可以用这些平台系统来作为我们平台化的参考类比设计,开拓我们的思路。
在过去的2014年, 前端开发因为大量前端框架的出现开发模式有了巨大的改变,MVC这个web服务器端开发的模式,由于angularjs们的出现,变成了前端MVVM+后端RestAPI的模式,使得web开发效率有了极大的提升,前端工程师基于angularjs等前端框架利用ajax技术结合后端Restful API,可以达到前后端分离,UI和模型分离。于是一个web页面在angularjs等框架的武装下,变成了具有丰富功能的单页应用,基本可以达到类似window客户端,flex等程序的交互能力。 可以说web开发由于angularjs,bootrap等前端框架下变得美好起来,不用为了跨浏览器兼容,为了实现UI的操作些大段的css,js代码。 基于angularjs等框架的开发已经大范围普及开来。可是开发者们在使用AngularJS将web程序从php,springmvc等服务器端渲染改成目前的前端渲染+ajax通过restful API请求数据的纯客户端程序后,发现对于搜索引擎来说,页面里的数据不能被爬虫搜索索引了。这就是需要去探讨的前端AJAX单页应用的SEO问题。 AJAX页面的SEO问题 搜索引擎爬虫(又叫机器人)最初是被设计用来抓取网页的HTML内容的。随着web以及网站技术的进化,JavaScript变成了web的主要语言。AJAX允许我们在web上做异步的操作。AngularJS们充分使用了异步模型,带给web页面很好的交互性,但这也给Google的爬虫带来了问题。对于angularjs页面来说,你的整个网站可能就仅仅是一个页面,利用angularjs的视图功能通过异步请求填充数据,所有的页面路由,视图,和交互逻辑都是在客户端完成。不论是你发表一个评论,写一个邮件,创建一个客户资料都是在一个页面完成。和你的web页面在没有ajax异步请求数据下,只是一个基本的空壳,没有实际的内容数据。所有的数据都是在浏览器端通过异步加载得到的。当你查看一个angularjs的网页源代码是,你可能看到是主体部分是: <data-ng-view></data-ng-view> 然后发现数据都不在了。 这也是爬虫所看到的,如果没有做特别的SEO设置的话。 怎么解决 Angularjs的好处太多,一切很美好,就是SEO这个问题成为开发者的唯一痛苦,在习惯angularjs的便利后,不用他就像是买了辆车,却被SEO问题限号了,不能开。 于是为了angularJS们的AJAX SEO优化支持成为开发者们努力的目标,目前AJAX SEO优化已有一些不错的解决方案,我们将在下面一一探讨: GOOGLE的AJAX爬虫方案 GOOGLE对这类AJAX页面有一套解决方案,可以让angularjs页面的数据像传统页面一样被爬虫抓取,不过需要按照google的方式对你的程序做一定的改造, 具体可以参考:oogle抓取AJAX内容的指南请参看 Google’s Webmaster AJAX Crawling Guidelines. 简单说,他基本的方式是: 当一个搜索引擎的爬虫访问你的应用程序并且看到时,它会在你的URL中添加一个?_escaped_fragment_=tag。 你的服务器将会拦截这个请求,并把它发送给一个用来处理这个特殊的爬虫请求的中间件。 可是GOOGLE只是搜索引擎的一种,其他的引擎对ajax页面还不能很好支持,对于国内站点来说,baidu等国内搜索引擎的SEO支持更为重要。所以我们还是需要考虑其他方案。 其次,需要针对性的按照google的方式对程序进行调整,也包括专门处理爬虫请求的中间件开发工作,都有不少的开发量。 Prerender.io方案 可是说这是上面google方案的第三方解决方案。 Prerender.io,是一个兼容多种不同平台(包括Node,PHP和Ruby)的一个服务。该服务是完全开源的,但是如果你不想搭建一个你自己的SEO服务器的话,你可以使用他们提供的解决方案。Prerender的人们认为,SEO是一件正确的事,并不是一个特权,他们已经做了一些了不起的工作来扩展他们的解决方案,添加了很多自定义的功能和插件。通过引导爬虫到prerender建立的代理页面服务器,相当于给爬虫单独建立了一个单独的通道,将网页快照喂给爬虫。 这种方式麻烦在于需要建立一个prerender服务器,需要给页面建立快照,还是存在很多工作量。 PhontomJS方案 PhantomJS 是一个基于WebKit的服务器端 JavaScript API。它全面支持web而不需浏览器支持,其快速,原生支持各种Web标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG。PhantomJS可以用于页面自动化,网络监测,网页截屏,以及无界面测试等。 利用PhontomJS这个框架,我们可以找到一种简单方式,通过phonetomJS代理将ajax页面的数据在ajax数据完成后,将整个完整页面传给爬虫,从而使得angularjs页面对爬虫来说,和传统页面一样,包含了数据的完整页面,从而达到SEO优化的目的。 这个方案可以说是一种非常简单可行的方式,可以通过在你的web程序中增加一个filter来实现对爬虫请求,通过PhontomJS取得完整页面后在传递给爬虫,基本算比较简洁的解决了SEO的问题。如果想对angularjs做SEO,这是一个值得考虑的方案。 javascript的服务器端渲染方案 这类方案出现后,我们看到一个很有意思的现象,原来的web页面从PHP,JSP等纯服务器端渲染方式,变成angularjs们的javascript的客户端渲染方式后,由于SEO问题需要解决, 于是javascript的服务器端渲染方案出现了,这是一种权衡两种模式后的一种改进方案,结合服务器端渲染,javascript客户端渲染两种方式的优点,而出现的一种混合模式,这种模式和普通使用Jsp或PHP/ASP等服务器端渲染区别所在,后者每切换一个页面实际是从服务器端再拉取一个新的页面内容,而新式的JS服务器端渲染技术是第一页如同JS/PHP/ASP,一旦输出渲染成功,页面各种效果包括切换到下一页都是由第一页的JS全面掌管,这时如同AngularJS等客户端Javascript驱动渲染页面一样,由AngularJS实时修改当前页面的DOM。 这种模式出现成为解决angularjs们SEO问题的一种解决方案,除此之外,还能带来更好的用户体验,加载页面更快。 想了解这种方案可以参考一下文章: Serverside React Rendering: Isomorphic JavaScript INSIDE FASTBOOT: FAKING THE DOM IN NODE.JS。 目前这类方案还存在探索阶段,如果angularjs们能够很好的将这种思想结合在框架本身,是可以解决大部分的SEO问题。 总结 就目前来说,angularJS们的SEO问题,对开发者来说还是一件比较麻烦的事情,需要考虑较多因素。目前存在的方案也没有非常成熟,不过在angularjs等越来越普及的情况下,相信SEO支持也会越来越好,拭目以待。
前言 RESTful架构,是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。后端通过提供一套标准的RESTful API,让网站,移动端和第三方系统都可以基于API进行数据交互和对接,极大的提高系统的开发效率,也使得前后端分离架构成为可能。 因此,不同的测试,开发团队(前端,移动端,第三方接入者等)都需要围绕API进行开发工作,API的规范和文档对于团队开发,测试变得越来越重要。除了一份标准的文档,我们还希望API能够在线测试使用,从而有更直观的API使用体验,降低API的学习成本。这些对于团队的开发协作都会事半功倍。 本文将介绍一些API文档和开发测试方面的一些实践,使用typeson,docson,swagger-ui等开源工具,建立一个API的集设计,实现,测试,文档的一体化可视平台,让API的开发和使用更加高效。 概述 首先我们会通过一个简单系统的RESTful API的开发,介绍如果利用typeson,docson,swagger-ui等工具辅助API的设计和开发,掌握这些工具的使用,提高API的开发效率,质量。 在这个实例的开发中,会涉及到以下规范和工具: Swagger *Swagger是一种和语言无关的规范和框架,用于定义服务接口,主要用于描述RESTful的API。它专注于为API创建优秀的文档和客户端库。支持Swagger的API可以为API方法生成交互式的文档,让用户可以通过以可视化的方式试验,查看请求和响应、头文件和返回代码,从而发现API的功能。它本身就非常强大,但是Swagger框架还支持为多种流行的语言——包括JavaScript、Python、Ruby、Java、Scala等等——生成客户端代码。* *网址: https://helloreverb.com/developers/swagger* Swagger-UI *为基于Swagger规范的API生成基于基于纯粹HTML,javascript,css的在线可视化文档,同时也能成为API的在线测试工具。* *网址: https://github.com/wordnik/swagger-ui* JSON Schema *类似于XSD对比与XML的关系,JSON schema是用来描述JSON数据类型的schema。* *网址:http://json-schema.org/* DOCSON *一个json数据的文档化工具,通过json schema生成对应的在线JSON可视化文档。* *网址:https://github.com/lbovet/docson* TypeScript *TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。* *网址:http://www.typescriptlang.org/* Typeson *利用typescript这种面向对象的javascipt超集,生成json schema。* *网址:https://github.com/lbovet/typson* 在这个API的开发实践中,我们会贯彻两个理念: Design-First 设计先行 TDD 测试驱动 从一个基本应用开始 这部分我们假设开发团队接到一个新的开发需求,开发一个客户邮件营销系统,注册用户可以管理自己的客户,并通过邮件对客户进行营销活动。所有的后台功能通过RESTful API形式提供服务,网站开发,移动端开发,和后端同时进行开发。 首先团队之间需要沟通协调设计API接口设计,形成规范和文档。这就是我们常说的设计先行理念。 数据模型设计 第一步,安装docson,从网址https://github.com/lbovet/docson,按照介绍,下载docson并且在web服务器里启动。可以用node.js或者部署到tomcat上。假设在tomcat上部署启动后,访问:http://localhost:8080/docson/index.html 可以看到如下页面: 说明docson已经准备好了 第二步,根据业务需要设计json schema: 我们可以直接手写json schema,入将以下文件保存为/examples/user.json: "$schema":"http://json-schema.org/draft-04/schema#", "title":"User", "description":"用户schema", "type":"object", "properties":{ "id":{ "description":"id", "type":"number" }, "username":{ "description":"用户名", "type":"string" }, "password":{ "description":"密码", "type":"string" }, "fullname":{ "description":"全名", "type":"string" }, "telphone":{ "description":"电话号码", "type":"string" }, "email":{ "description":"Email", "type":"string" }, "company":{ "description":"公司名", "$ref":"#/definitions/Company" }, "customers":{ "description":"客户", "type":"array", "items":{ "$ref":"#/definitions/Customer" }, "minItems":1, "uniqueItems":true }, "mailTemplates":{ "description":"模板", "type":"array", 将对应的地址http://localhost:8080/docson/examples/user.json 输入docson,回车后将看到图形化的schema: 点击Customer等按钮可以查看对应的跟详细的数据类型信息: 这样所有人员都可以直接查看数据模型的定义,协同设计。 除了手写外,我们也可以利用Typescript/typeson来辅助我们生成json schema具体可以参考网站和demo:http://lbovet.github.io/typson-demo/ 需要稍微熟悉下typescript API接口的设计 在数据模型定义好后,我们接着可以定义具体需要的API列表和每个API的接口形式,请求方法,输入输出参数等具体信息。在此我们利用前面设计好的json schema,结合swagger-ui工具,就可以定义设计API列表。 第一步 准备好swagger-ui,部署并启动。参考网址: https://github.com/wordnik/swagger-ui这里我们将用swagger-ui的离线版本,先行设计API。 第二步 设计每个API,按功能组织分类, 将设计好的json schema引入,同时设计如下API "apis":[ { "path":"/accounts/", "operations":[ { "parameters":[ { "name":"account", "description":"Create a new account", "required":true, "dataType":"Account", "paramType":"body" } ], "responseMessages":[ { "code":401, "message":"Unauthorized" }, { "code":500, "message":"Internal Application Error" } ], "httpMethod":"POST", "notes":null, "responseClass":"Account", "nickname":"createAccount", "summary":"Create a new account", "produces":[ "application/json" ] } ] }, 完成后,我们将可以得到如下的API在线文档: 如某个具体AP,获取账户的具体信息 如果API已经准备好,或者后端提供了一个mock实现,那么直接点击“try it out”,就可以直接调用该API,输入测试数据,测试返回数据和错误信息。 同时根据数据模型的json schema,swagger-ui 会自动生成对应json数据的form表单,无论结构多复杂,这极大方便API使用者对API的上手和测试。 API的开发测试 到目前阶段,我们可以看到,及时API完全没有编码实现,我们也有一个很清晰的API文档和测试环境。各个开发测试团队可以在这个在线文档和测试平台上,协同各自开发测试。比如: API开发团队根据这个文档,开发实现具体的API功能 前端团队或者移动端,根据这个API文档,分别独自开发各自功能,mock API的实现。 测试团队可以直接根据这个API的测试平台,利用selenium工具录制测试脚本和准备测试数据,在开发团队实现前就可以把测试准备好,用测试驱动开发 API使用阶段 在API开发测试完成后,需要提供API给外部系统使用,我们可以直接把这个swagger-ui建立的在线文档和测试平台提供给开发使用者,作为标准的文档和测试工具。这样也不需要格外写一份静态的接口文档给用户,我们提供的是一份标准的live文档和测试工具,可以极大的方便使用着了解API,减低使用者的学习成本。 总结 本文只是简单的介绍了RESTful API开发中利用一些工具进行辅助开发,希望docson,typeson,swagger-ui 等纯粹的基于html,javascript的前端工具能够方便大家的API开发,建立一个API的在线文档和测试中心。这对于需要提供API服务给第三方的平台开发者具有较大的价值。关于更多相关规范和工具的使用,希望大家参考对应网站提供的文档,深入掌握,也何以和作者交流
在上两篇ATA中,第一篇讨论了平台的扩展性(《从Eclipse平台看交易平台化》),强调微内核和扩展机制实现,第二篇讨论平台的模块化开发(《Google Guice平台模块化开发的果汁》),强调业务隔离,松耦合。这这第三篇ATA中,想分享下平台化中另一个重要方面,平台的服务流程编排 (备注:本文以下提到交易系统,只是举例,可以扩散为业务平台系统) 像本文标题一样,我们想象下,在舞台上,有各种角色,导演根据剧本的设计的场景,让这些角色,在他的编排下,完成不同剧情。 这和我们的业务平台有很多共通之处。在我们的交易业务系统中,也有很多“角色”,比如各种中间件服务,各种业务系统,我们设计的各种业务,简单类比来说就是编排这些“角色”一起完成一个特定的“剧情”。产品经理就是编剧,我们平台化需要实现了一个“智能导演“,能快速响应产品经理的这个编剧的设计,编排出不同的业务场景流程。 而且我们的业务舞台里,剧情不同,但是角色都是基本一样的,其实就是用同样的演员拍不同的剧情。 每一集的生产只是需要导演实现不同的编排,而且我们希望实现业务的时间和成本尽可能的低。大家都喜欢看美剧,美剧的制作可以说是流水化工业生产,编剧、导演、演员、 摄像、剪辑师等各工种分工合作,环环相扣,高效率生产,而且大家会注意到美剧的导演不重要,经常一个剧每集可能由不同的导演来导演。这是因为美剧的运作机制本身就完成了大部分导演的功能,这个机制里有很多的规范和套路,从而不依赖导演的个人能力就从而可以快速完成各种剧集的制作,导演只需要按照规范套路来导演。 如果我们的业务系统有一套类似美剧制作体系里的“导演”机制,我们的业务系统开发就可以真正围绕业务本身快速构建,流水化生产了。 这个"导演"机制的功能换成我们的技术术语,就是"流程编排"的能力,是在业务系统驱动各种服务按照不同的流程完成某项业务需求。 "导演"的KPI 假设在平台中我们要实现一个“导演”机制,那对这个导演的KPI应该怎么设定呢,导演需要什么素质和功能才是业务发展需求急需的。 导演的KPI: 一个好的“导演”应该能很好的协调舞台上的各种角色,指挥他们做正确的事情。 能够根据剧情设计,将各种角色资源按一定的流程完成各自的任务。 第一个KPI反应导演和不同的角色,不同的资源打交道,对资源进行调用的能力。 第二个KPI考核需要导演能按需而变,将复杂剧情分解,流水线一样的完成各种资源的调配,生产新的剧集。 导演能做好1.2 也离不开配套机制,每个角色分工都各司其责,能够响应请求完成任务。 在我们平台化中,第一个KPI对应的就是平台能够对不同的系统,包括中间件,服务,业务系统调用的能力,在交易这种复杂系统中,平台需要把各种调用隐藏在平台内部,对外提供一个简单一致的集成模型,从而可以保证平台能和众多其他系统一致沟通,简化各种异构系统带来的复杂性,这样我们就可以说平台实现的导演具有很好的协调能力。 第二个KPI对应的是平台具有快速响应业务需求,快速调整业务处理流程,用流程抽象具体的业务处理过程,完成业务请求。 那么我们来设计这个“导演机制”的实现,我们用“集成平台”来实现这个“导演机制”。 集成平台的背景 对于交易这样的复杂业务系统,数据庞大并不是平台增长的唯一方式。业务流程也可能在复杂性方面不断增长。交易处理的信息可能会是各种数量的并且任意数量组合的传输类型、过滤、增强以及路由等。 复杂的问题会在任何领域都出现,但是解决它们的总体策略通常是一样的:分而治之。我们会将问题拆分为更容易解决的子问题。然后这些方案再按照与分解相反的方式组合在一起形成整体的解决方案。通过观察会发现这样的问题是经常发生的;借助于经验,能够识别出最优的方案。我所讨论的就是模式 这些模式被命名为企业集成模式(Enterprise Integration patterns), 由Gregor Hohpe和Bobby Woolf进行了分类和总结,他对我们进行复杂业务的解决方案提供里整套的总结,为我们对复杂业务处理流程进行最优化实践指导。 在这里我们花点时间多了解下企业集成模式(Enterprise Integration patterns),这些企业集成模式就是我们“导演机制”里需要的套路,通过这些模式,我们可以针对复杂问题进行解耦组合,快速应对需求的变化。 企业集成模式 首先大家可以通过EIP的网站了解详细信息,EIPs对复杂业务系统开发有深远影响,IBM,TIBCO等中间件提供者利用EIP商业实现为解决复杂业务开发提供了整套解决方案,在金融等领域取得很大成功。 EIP本质上可以非常简单,通常表现为基本的操作如传输或过滤。最为重要的是,它们可以组合起来形成复杂的解决方案。这种组合本身也可以是模式。这种能力来源于所有的EAI模式具有相同的“接口(interface)”:信息可以进出模式。这样,模式就可以组合起来,这是通过接受一个模式的输出,并将这个输出作为另一个模块的输入来实现的。熟悉linux命令的人对linux的命令管道的功能便利强大印象深刻,EIP很类似linux的pipe,但是他更强大。 首先,EIP模式背后的设计思想是基于松耦合,面向服务的架构,数据包装成消息在传递,以消息驱动的方式协调各个服务,从而达到以标准化方式实现协议和服务。 EIP覆盖以下的一些内容: Messaging system Messaging channel Messaging custruction Messaging routing Messaging transformation Messaging endpoint System Management 广义地来说,这说明复杂业务问题就是模式的组合问题。这意味着解决问题,即便是很复杂的问题,将会简化成寻找满足需求的组合。实现自己的模式当前也会有大量的复杂性,但是这已经进行了隔离并且是可管理的。 上面的分析总结来说,就是将复杂性进行隔离和管理。通过EIPs模式,复杂的交易相关业务的实现的重点就是理解问题并识别出正确的模式,这有利于提高整体解决方案的质量。 企业集成模式或者EIPS对我们是有帮助的,因为它不仅给我们提供了问题的解决方案,并且也帮我们定义了我们所交流问题的本身。有了模式问题交流起来更容易。使用模式语言比描述需要解决的问题容易的多,就好像学习汉语比学习甲骨文要容易的多。 当我们用EIP来解决交易中涉及的复杂常见时,我们也将有一个更方便的语言在业务方,产品经理,开发团队中沟通。 然后我们就可以在对交易,业务的各种功能进行解耦并通过模块化方式接入平台基础上,利用这些功能模块形成了交易流程中各个功能节点,将他们连接起来连起来,完成真正的复杂的业务流程。 集成平台设计目标 当平台的功能已插件,模块的形式接入平台后,我们需要一组模块一起完成一些复杂的业务功能。比如在交易系统中,下单这样一个流程,就可能设计到多个模块提供的功能和服务。如何方便对这些流程以及服务进行编排是体现平台能力的重要标志。当一些业务需求需要对流程进行少量修改或者重新定制某些服务,而大部分流程功能保持不变是,平台提供简单方便的编排能力就可以满足对业务的快速响应。 首先在这样的流程体系中,一个复杂的业务流程需要和多种外部业务系统,中间件,数据库系统,连接,调用本地服务或者远程服务才能完成,如果我们希望平台能够以简洁的方式对流程进行编排,平台必须达到以下要求: 简化外部系统的连接调用方式,消除不同系统调用方式的差异,将系统的调用细节由平台完成,对调用者透明,平台提供一致的调用模式,同时该调用方式简单有效。 同时,对流程的描述提供DSL语言支持,简化流程的设计,方便通过可视化流程设计工具提高开发者流程设计能力. 提供对流程的运行监控,实时追踪业务运行情况。 对流程的生命周期进行管理,可控制流程的启动停止。 高效的流程执行引擎,能针对流程进行计算能力调配控制。 完善的流程错误处理报警机制。 目前交易系统涉及的业务和流程也变得越来越复杂,正向交易流程,逆向交易流程,生活服务预约流程等等。设计到多个交易系统之间的交互,一个订单从生成到完成,需要多个系统合作,不同类型的业务订单设计的流程也很不相同,集成模式为我们解决这些业务的复杂性提供了很好的问题解决方案,让我们对于业务和流程能更清晰的管理。 集成平台设计的目的就是引入企业集成模式解决方案,为平台提供对复杂业务的流程的隔离和编排。 集成平台的实现 企业集成模式的实现有很多,大型中间件厂商如TIBCO,IBM都有自己企业集成模式实现,在开源领域,Spring Integration, Apache Camel都是其中的佼佼者。 这两者对比中,本人更倾向Apache Camel,更成熟,有众多的应用,活跃的社区和完备的官方文档支持 Camel是什么 Apache Camel是Apache基金会下的一个开源项目,它是一个基于规则路由和处理的引擎,提供企业集成模式的Java对象的实现,通过应用程序接口 或称为陈述式的Java领域特定语言(DSL)来配置路由和处理的规则。其核心的思想就是从一个from源头得到数据,通过processor处理,再发 到一个to目的的. Camel框架的核心是一个路由引擎,它允许你定义自己的路由规则,决定接受哪些消息,做出决定如何处理,发送这些消息给其他目标。Camel用这种集成语言允许你定义复杂的路由规则。 Camel的基本原则之一是不会假设任何你需要处理的数据,这是很重要的一点,因为它给你们开发者一个集成任何系统的一个机会,不需要转换你的数据为另外的一种公认格式。 Camel 提供了高水平的抽象,它允许你根据相同的api协议或者系统的数据类型集成各种各样的系统。Camel的组件提供了特殊实现的接口api,其目的是给不同的协议和数据类型服务。Camel打破了传统模式,它支持190多种不同的协议和数据类型,它的扩展性和模块性允许你实现你自己专有协议的无缝插件。这些体系结构的选择淘汰没有必要的转换,从而使camel更加的高效,易学。结果证明,camel是适合嵌入到其他项目中的,因为它提供了充足的处理能力。其他开源的项目,例如Apache ServiceMix和ActiveMQ已经使用camel作为企业集成的一种处理方式。 我们应该提问camel不是什么,camel不是ESB,有些人称camel是个轻量级的ESB,因为它支持路由、事务、监控、编制等。Camel 没有一个容器或者一个可靠的消息总线,但是它可以依赖例如开源的ESB或者前面提到的ServiceMix,由于以上原因,我们更喜欢把camel称作超越一个ESB的集成框架。 Apache Camel的基本概念 端点(Endpoint) 是Camel中的一个基本概念,Endpoint作为Camel系统中一个通道的端点,可以发送或者接受消息。在Camel中Endpoint使用URI来配置。在运行时Camel通过URI来查找端点。端点的功能强大、全面而且又可维护。 组件(Component) Component是一些Endpoints URI的集合。他们通过Schema来连接(例如file:,jms:),而且作为一个endpoint的工厂。 路由(Route) 顾名思义,Route,就是路由,它定义了Message如何在一个系统中传输的真实路径或者通道。路由引擎自身并不暴露给开发者,但是开发者可以 自己定义路由,并且需要信任引擎可以完成复杂的传输工作。每个路由都有一个唯一的标识符,用来记录日志、调试、监控,以及启动或者停止路由。路由也有一个输入的Message,因此他们也有效的链接到一个输入端点。路由定义了一种领域特有的语言(DSL)。Camel提供了java、scala和基于XM的Route-DSL, 路由可以使用过滤器、多播、接收列表、并行处理来定义,从而变得非常灵活。 处理器(Processor) Processor是router中的一个组成元素,用来消息转换和处理。 Apache Camel的主要功能 路由引擎 路由引擎是camel的核心功能。路由引擎基于路由配置有选择的对消息进行路由。 企业集成模式 针对复杂业务处理,Gregor Hohpe 和Bobby Woolf 发现了很多问题,并给出了响应的解决方案。他们编写一本《Enterprise Integration Patterns》书,这本书对那些专业集成的人来说是必读的书。接下来,它将帮助你理解又快又容易地理解camel。。 Domain-speclfic language(DSL) Camel的domain-specific language(DSL) 对集成领域来说是一个主要的贡献。Camel提供了多种DSLs。例如java、Scala,Groovy,它还允许以XML特定方式的路由规则。 可拓展组件库 Camle提供了超过190个组件的拓展库。这些组件使camel用APIs连接传送和理解数据的格式。 模块化、组件式体系结构 Camel 有一个模块化体系结构,它允许任何组件加载到camel中对camel的功能进行扩展开发。 数据类型自动转换 Camel已经嵌入了150多种自动转换的机制。你不在需要配置类型转换规则,例如把byte arrays 转换为 Strings。如果你需要一种camel没有的转换类型,你可以自己去创建属于自己的Converter。 轻量核心 Camel核心被认为是轻量级的,总共自由包加起来大约也就1.6MB,它只依赖Apache Commons Logging,资源的通用管理。这样使camel更容易在你喜欢的任何地方嵌入或者部署。例如在标准的应用、web应用、spring应用、J2EE应用、JBI容器、OSGI bundle、java web start或者Google Appengine。Camel没有被设计成一个server或者ESB,取而代之的是可以嵌入到你选择的任何平台中。 测试驱动 Camel提供了测试工具箱,使测试你自己的camel应用更容易。这些测试工具在测试camel本身中广泛应用。例如他们可以帮助你虚拟真正的endpoints。 在Camel提供的功能基础上,我们可以打造一个基于阿里技术体系和业务系统的集成平台,为复杂业务提供更高效的解决方案。 期望的集成平台功能 目前阿里内部有众多系统为整个交易相关提供各种服务,整个交易就是讲这些系统联合起来, 提供统一的集成模型 当集成平台就绪时,我们期望他能够将各个业务系统通过集成方式连接起来,作为平台的能力扩展对外输出。从这个平台出发,业务开发者将不需要直接面对各种复杂的业务系统,然后开发对这些系统的服务调用代码,平台会完成这些基础部分,然后提供一个抽象的一致的访问形式,完全消除系统之间的差异。所有的外部系统成为了平台的服务。平台对这些集成的系统提供了更简化的访问形式,方便流程脚本的设计编写。比如利用Camel的component功能,实现对业务系统,中间件系统的集成封装,从而使每个服务,每个业务都可以以URI寻址的方式完成调用,举例来说:对TOC增加超时可能就是将数据传给如下的地址:.to("toc://insertTimeout?type=13") 这样,所有外部系统,都可以用URI这个简单格式表达出来,所有复杂的细节实现都由集成平台自身完成。 这种在http中广泛使用的URL形式将极大的简化我们队服务的调用形式,从而为DSL化流程提供可能。 集成平台期望能够对阿里内部所有的系统提供这种标注化的URI访问形式。 这样对开发的好处显而易见: 每一个开发新人在阿里开发都要去熟悉各种中间件系统,这些都有一定的学习成本。及时在熟悉后,开发人员也会碰到各种问题,比如HSF调用ar包依赖,需要维护版本升级等因素。 各个业务系统之间由于架构不同,提供服务的形式多种多样,用一个标准的规范去统一起来,对后续的业务开发效率极有好处。 更明确的分工,中间件,业务系统能跟规范自己的能力透出,实现面向服务的架构,系统之间更加解耦。开发团队可以分为提供基础设计,提供业务能力和开发业务等不同维度,协作开发,避免各自缺乏沟通或者深度耦合。 简化后的服务URI可以为DSL形式的流程脚本设计提供基础,形成业务人员,产品经理和开发人员都能够沟通的逻辑。 DSL快速流程设计 对流程的设计和实现可以很好的体现软件的现代化程度,从编程语言的发展趋势来看,都是为了能跟高效的完成流程的实现,比如目前我们是用java等编程语言来实现一个业务流程,最近领域语言的发展可以更高效抽象,贴近自然语言的方式来实现逻辑。我们经常在真正编写程序写一些伪代码,这些伪代码跟接近问题本身,处理业务逻辑。 将流程以DSL语言的方式来设计和实现,可以快速的响应需求变化。 以前要编写大量代码的业务逻辑可以转化成用DSL设计一些子流程,并将子流程组合起来形成一个大的流程。 Camel的DSL语言为我们的流程设计提供了很好的工具。 让我们对流程编排有一个语言级别的支持。 我们只需要描述流程从开始到结束需要执行的路径。 同时这种DSL语言可以很好的以图形化的实现表现和设计,我们可以让非程序开发人员有可能成为流程的开发着。 我们来看一个DSL流程的例子: from("direct:buyNow") .split().method("orderSplitter").to("direct:orderAssigment"); from("direct:orderAssignment").recipientList().method("orderRouter"); from("seda:bizType_200?concurrentConsumers=80") .to("bean:biztype200Handler?method=prepareOrder").to("direct:orderCreated"); from("seda:biztype_1001?concurrentConsumers=3") .to("bean:bieztype1001handler?method=prepareOrder").to("direct:orderCreated"); from("direct:orderCreated") .aggregate(new OrderAggregationStrategy()) .method("orderPayment", "oderPay").completionTimeout(5 * 1000L); 这段DSL有一些功能,根据业务类型进行识别路由,对每个业务类型,执行不同的流程,然后都执行到订单创建成功。 每个业务流程需要的机选能力也可以调配。 订单创建成功后,可以继续驱动走向订单付款的流程。 从这段例子可以看到,业务逻辑的实现被极大的简化,业务开发人员根本就不需要了解什么是HSF等该如何调用。 如果这样一段DSL基本可以完成一个下单的流程,那么我们维护和开发新的业务的代价也就可想而知有极大的提高。 DSL的学习成本很低,新的业务开发只需要参考老的业务DSL实现,做一些更改即可。 业务开发辅助工具 当集成平台通过EIP模式建立起来,同时具有DSL流程描述语言,那么我们就而已通过开发一些业务辅助工具更高效的开发业务。 比如: 可视化业务流程设计工具 提供给业务和开发人员类似Visio的辅助可视化开发工具,让业务设计人员能够可视化的设计业务流程。 业务流程模拟测试工具 流程设计完成后,可以对流程的输入和输出进行mock测试,输入不同的条件,检测不同的结果,从而保证流程开发的质量。 集成平台运行监控工具 集成平台完成了一个数据接入,数据处理,数据流出的数据网,就像铁路网一样,数据通过设计好的流程路径被处理分流,这些标准的方式为我们的实施可视化监控提供了可能。我们可以实施的跟踪数据处理过程,当有错误,某些点压力增大时能及时调整(比如限流,引流等),为业务稳健运行提供了更高层的保障。 总结 目前阿里的电商系统已经形成了一个庞大的生态,各种新老系统,中间件在一起完成业务功能。 我们在维护系统,开发新业务的过程中,感受到了越来越大的压力。旧的系统必须维护改造,新的系统业务能够快速发展,新旧系统之间都需要互联互通,同时我们又期望维护和开发成本都可以降低,企业集成模式为我们提供一种解决方案,如果我们能有效的在阿里实施EIP,将会对我们系统的架构升级,服务能力,业务快速发展提供保障。 最后期望发起一个项目,对apache camel做阿里相关技术的扩展开发,然后contribute回到apache社区,希望有兴趣的团队可以一起合作, Let's ride the camel。
该文章来自阿里巴巴技术协会(ATA)精选集 在前文《从Eclipse平台看交易平台化》中,主要探讨平台的扩展机制。 本文将继续探讨平台化开发中另一个重要方面: 模块机制。在阿里系统开发中,大家都有自己的模块化开发方式。比如目前交易中的TMF框架(Trade Module Framwork)也是从模块化开发解决业务隔离扩展。 Detail 2.0平台化项目中定义了一套自己的模块化方式。本文想通过介绍Guice Module的模块机制,介绍一种简单强大的模块开发解决方案,Guice在java开源软件中被广泛使用,也证明他的生命力。 希望给有开发需求的开发者可以直接拿来主义,在开发中提高效率。 Guice是什么 Guice(读作Juice)是Google开发的一套注入框架,目前最新版本是4.0Beta。注入的好处是将使用者与实现者、提供者分离,降低代码间的依赖,提高模块化程度。 Google的java开源产品,一般都具有良好的口碑,简洁而强悍,比如guava类库。 本文会只涉及Guice的DI基本功能,更主要是介绍利用Guice来组织平台的模块开发,在这点,Guice提供了非常好的解决方式,个人想通过这篇文章给需要模块化开发的同学推荐Guice的Module解决方案。 Guice的基本DI功能 在java开发方面,Spring是大部分系统采用的基本框架,我们利用Spring的IOC,DI等功能组织代码。 Spring的成功和优势无可置否,但是Google的Guice提供了更轻量级,更简洁高效的DI框架,并且更高程度抽象出模块的概念,为代码开发提供了强大的工具。 他的基本功能可以通过Guice wiki中了解, 摘抄一些优点介绍: IoC中Bean的注释:其实实现细节很是让人不得不佩服,因此,很多的其它框架也开发模仿; 通过“prodivers”和“modules”实现编程配置:这相对于其它语言的实现方式而言,显得更加的优美,至少认人觉得是一种比较实际可能的方法; 快速的“prototype”场景:可以通过CGLib快速的构建对象,这点让我很激动。Guice的出现让我们看到了其实prototype的bean和动态创建的bean其实也可以很容易的管理; Modules:module可以将应用程序分割成几大块,或是将应用程序组件化,尤其是对于大型的应用程序; Type safety:类型安全,它能够对构造函数、属性、方法(包含任意个参数的任意方法,而不仅仅是setter方法)进行注入; 快速启动; 简单、强大、快速的学习曲线; Guice的思想在一定程度上积极的影响着Spring和WebBeans; Guice的头Bob Lee(http://crazybob.org/)不愧为IoC大师; Guice DI基本原理: 创建一个GuiceInjector(一种DI依赖注入的实现方式)。 Injector将会使用它的配置完的模块来定位所有请求的依赖,并以一种拓扑顺序来为我们创建出这些实例 如果在新项目中,也建议用Guice作为DI解决方案。 Guice的模块概念 Guice定义Module这个接口用来确定各个接口和他对应的实现的绑定。 通过bind("interface").to("implementation")的方式来完成接口实现的组装。目前很多的开源框架都采用Guice进行模块化开发,最典型的例子是**ElasticSearch**这个著名的分布式搜索引擎。 本文将大量通过ES的模块化开发实践来说明Guice是模块开发的利器。 除了ES外,Apache Shiro, Apache Shindig等也也采用了Guice,越来越多的开源实开始将Spring DI换成Guice。 ok,从头开始,所有的Guice模块都实现一个通用接口Module, 这个接口定义很简单,注释里也跟精确的解释了模块的定义和功能: A module contributes configuration information, typically interface bindings, which will be used to create an {@link Injector}. A Guice-based application is ultimately composed of little more than a set of {@code Module}s and some bootstrapping code. /** * A module contributes configuration information, typically interface * bindings, which will be used to create an {@link Injector}. A Guice-based * application is ultimately composed of little more than a set of * {@code Module}s and some bootstrapping code. * <p/> * <p>Your Module classes can use a more streamlined syntax by extending * {@link AbstractModule} rather than implementing this interface directly. * <p/> * <p>In addition to the bindings configured via {@link #configure}, bindings * will be created for all methods annotated with {@literal @}{@link Provides}. * Use scope and binding annotations on these methods to configure the * bindings. */ public interface Module { /** * Contributes bindings and other configurations for this module to {@code binder}. * <p/> * <p><strong>Do not invoke this method directly</strong> to install submodules. Instead use * {@link Binder#install(Module)}, which ensures that {@link Provides provider methods} are * discovered. */ void configure(Binder binder); } 在configure方法里,我们将接口的具体实现通过模块绑定起来。 当这个模块被使用时,他定义的接口实现就会被调用。 同时Guide提供了Module接口的一个抽象类AbstractModule,提供了一些模块绑定的便利支持,同时推荐开发者的模块都继承这个抽象类。 Guice模块开发实践 对于平台来说,将系统的功能通过模块来定义功能的边界,模块之间相对独立。模块作为平台功能的组成的基本形式,通过预定义配置和运行时配置来完成平台的组装。 模块的具体实现将利用Guice的Module功能,完成配置信息和绑定各接口的特定实现。平台启动后,通过各种途径将相关模块整合起来,构建Guice Injector, 来完成将模块定义的实通过依赖注入的方式,快速构建起来,从而创建一个复合的模块系统。 给大家一个直观的印象,看看下面这个Elasticsearch的模块图: 点这里可以看到完整的图。 可以看到具体模块的组织。 可以看到这么一个复杂的分布式搜索引擎实现通过guice的模块化处理,有了一个清晰的结构。 下面我们将跟深入模块实现的细节和一些实践,用来更好的开发我们的业务平台。 Guice组织多业务可扩展平台开发 对于阿里内部常见的多业务平台来说,模块可能会分为系统模块,领域模块,业务模块这三个类型。 系统模块是用来建设平台本身需要的功能组织,比如平台的日志模块,监控模块等。 领域模块是某个领域的功能组件,比如交易领域里的物流模块,优惠模块等。 业务模块是业务方业务功能的实现,比如航旅开发出机票查询模块。 这样的模块化组织设计,为我们系统的分层,降低耦合度也非常有好处。 模块元数据(Metadata) 我们可以给模块定义元数据,用来描述模块的基本信息,从而可以方便平台对模块的管理和监控。 ModuleMetaData:{name(名称), type(类型), setting(配置)} 名称是模块的基本命名,类型可以是模块命名空间,setting保存模块的更多配置信息。 模块命名空间 多个模块基于他们在源代码树或者代码中的调用情况被放进了不同的命名空间中。不同的侧面,诸如插件,监控,环境设置服务是分割的功能实体,他们之间没有模块层面的依赖。模块可能已经太过庞大或者难以复合使用的将会被进一步分割成嵌套命名空间中的稍小的模块。 如果把模块系统类比成文件系统,模块是一个文件,那么命名空间就像是文件夹,相关的模块通过命名空间归类管理。命名空间和模块形成一个树状结构。 通过命名空间+模块名可以唯一的定位到一个模块。 这样平台的功能和结构便可以通过: {Namespace:Module Name: Bound Class}清晰管理起来。 模块扩展和替换 大多数模块仅仅会提供一个或者多个类或接口,但是某些模块可以产生它们需要的新模块。这些模块通常依赖当前配置或输入参数动态产生。这样我们便可能通过插件写出替代或者扩展平台的内置功能,然后通过一定的条件来开启和配置这些插件。 比如我们有个ScriptModule模块提供脚本功能,平台预先定义了Groovy和Python两种脚本,平台可以根据配置或运行时根据输入参数告诉平台动态采用某个Groovy或者Python,同时也可以使自定义脚本,开发平台的插件提供新的脚本模块,并通过注册到平台来满足平台对脚本功能的扩展。 这种类似ScriptModule的模块我们把他定义为可扩展模块,模块维护一个模块内功能的注册扩展机制,新的script实现可以通过该模块registerScript方法注册。平台在运行时,根据配置或者输入参数,动态决定某个脚本被调用。 public class ScriptModule extends AbstractModule { private final Map<String, Class<? extends NativeScriptFactory>> scripts = Maps.newHashMap(); public void registerScript(String name, Class<? extends NativeScriptFactory> script) { scripts.put(name, script); } @Override protected void configure() { MapBinder<String, NativeScriptFactory> scriptsBinder = MapBinder.newMapBinder(binder(), String.class, NativeScriptFactory.class); for (Map.Entry<String, Class<? extends NativeScriptFactory>> entry : scripts.entrySet()) { scriptsBinder.addBinding(entry.getKey()).to(entry.getValue()).asEagerSingleton(); } } } 需要更复杂灵活实现的话,可以在上面的的基础上跟进一步扩展,维护一个子模块的注册扩展机制,首先定义一个ScriptsModule: public class ScriptsModule extends AbstractModule { private final Settings settings; private Map<String, Class<? extends Module>> scriptTypes = Maps.newHashMap(); public ScriptsModule(Settings settings) { this.settings = settings; } /** * Registers a custom script type name against a module. * * @param type The type * @param module The module */ public void registerScript(String type, Class<? extends Module> module) { scriptTypes.put(type, module); } @Override protected void configure() { bind(ScriptsTypesRegistry.class).toInstance(new ScriptsTypesRegistry(ImmutableMap.copyOf(scriptTypes))); } } 然后我们提供具体的ScriptModule实现,比如通过配置文件来配置新的脚本,也可以开发一个插件将新的CustomSciptModule注册进去。从而跟灵活的扩展功能。 从这里大家可以看到,**我们可以利用这种可扩展模块的注册机制,实现对多业务的支持**: 比如交易平台提供了优惠类型模块, 每个业务可以把自己的优惠实现模块注册进去,平台会识别出业务类型后,自动找到对应业务的优惠实现,完成业务对优惠功能的定制需求。 { "ticket":"TicketDiscountModule",//电影票优惠模块 "flight":"FilghtDiscountModule",//机票优惠模块 ... } 模块构建使用 在了解模块的功能和扩展方式后,平台需要构建和使用模块。 平台启动时,首先你的启动类里,可以指定需要加载的模块,这个可以通过ModulesBuilder这个类来快速完成: ModulesBuilder modules = new ModulesBuilder(); modules.add(new PluginsModule(settings, pluginsService)); modules.add(new EnvironmentModule(environment)); … injector = modules.createInjector(); 模块加载完成后通过创建Guice injector, 就可以完成所有依赖实现的组装。 这是,你就可以通过 client = injector.getInstance(Client.class); 得到需要的实现,完成相应的逻辑。 ModuleBuider有些很酷的特性,如果一个Module同时实现了SpawnModules接口, public interface SpawnModules { Iterable<? extends Module> spawnModules(); } 以为这这个模块具有创建子模块的能力, 那么ModulesBuilder在加入这种类型的模块是,会递归的创建出所有的子模块。 public ModulesBuilder add(Module module) { modules.add(module); if (module instanceof SpawnModules) { Iterable<? extends Module> spawned = ((SpawnModules) module).spawnModules(); for (Module spawn : spawned) { add(spawn);//这是一个递归调用 } } return this; } 总结 模块化是对复杂业务平台分而治之的方式,通过模块化开发,可以使我们的平台建设更高效更灵活。 Guice的Module功能再开源软件中被广泛使用,在本人自己过去使用经历中,发现确实是个模块化的利器,希望更多的人了解使用。
该文章来自阿里巴巴技术协会(ATA)精选集 从Eclipse平台看交易平台化 淘宝网的交易平台伴随着互联网,网络购物的蓬勃发展,支持淘宝网成为全球最大的在线交易平台。各种业务方和他们新的交易类型对交易平台提出各种各样的需求,让交易系统的响应和业务支持在现有系统基础上越来越显露出其系统架构上的缺陷,架构缺乏平台化定制扩展的功能,在快速支持新业务,扩展业务功能方面越发捉襟见肘,只能通过加大开发团队力量的投入来满足业务方的需求。 最近交易开始"平台化",希望通过的业务模型,业务流程的重构,能够快速支持业务发展。本文我们将讨论平台化建设,以及就Eclipse这个平台化的典型,借鉴其中平台化的设计理念来探讨交易平台化。 平台化的目标 平台的开发,一般包括两方面的内容,一个是可复用性,一个是可配置型。Eclipse作为成功的开发工具集成平台,这这两方面提供了很好的典范,我们可以借鉴Eclipse平台设计的理念. Eclipse是一个怎样的平台 Eclipse是一种可扩展的开放源代码IDE。2001年11月,IBM公司捐出价值4,000万美元的源代码组建了Eclipse联盟,并由该联盟负责这种工具的后续开发。。集成开发环境(IDE)经常将其应用范围限定在“开发、构建和调试”的周期之中。为了帮助集成开发环境(IDE)克服目前的局限性,业界厂商合作创建了Eclipse平台。 Eclipse允许在同一IDE中集成来自不同供应商的工具,并实现了工具之间的互操作性,从而显著改变了项目工作流程,使开发者可以专注在实际的嵌入式目标上。Eclipse框架的这种灵活性来源于其扩展点。它们是在XML中定义的已知接口,并充当插件的耦合点。扩展点的范围包括从用在常规表述过滤器中的简单字符串,到一个Java类的描述。任何Eclipse插件定义的扩展点都能够被其它插件使用,反之,任何Eclipse插件也可以遵从其它插件定义的扩展点。 除了解由扩展点定义的接口外,插件不知道它们通过扩展点提供的服务将如何被使用。利用Eclipse,我们可以将高级设计(也许是采用UML)与低级开发工具(如应用调试器等)结合在一起。如果这些互相补充的独立工具采用Eclipse扩展点彼此连接。 Eclipse从设计的开始,就以可扩展为目标,为此Eclipse的架构师们设计了优美的平台基础架构,从而保障开发工具开发者可以为Eclipse提供多种多样的插件满足各种开发功能的需要。而Eclipse后来的成功也证明了这种架构在扩展和定制方面的强大生命力。 也就是说Eclipse是为了满足多种业务需求(不同的开发工具,开发语言,从设计开始就做了IDE的平台化设计,可以为后续的不同开发工具需求提供快速的接入和实现机制。 那么接着我们以平台化的视角看看Eclipse的平台化(“平台+插件”)有什么独到之处,以及我们可以学到什么。 Eclipse平台核心功能 Eclipse采用“平台+插件”的体系结构,平台仅仅作为一个容器,所有的业务功能都封装在插件中,通过插件组件构建开发环境。 Platform Runtime平台运行库是内核,Eclipse所有的功能就是通过这个runtime和插件一起完成。 在这个runtime里,定义Eclipse的核心功能。在下面的章节,会介绍Eclipse Runtime扩展实现的机制。通过学习这些机制,我们可以学到以下经验: Eclipse的核心类是怎样做到保持稳定,同时又怎样支持以后得各种功能扩展。 Eclipse如何对多业务的支持和新业务的快速接入。 首先我们来认识一个非常重要的接口: IAdapable 在Eclipse SDK版本下,用F4查看IAdaptable的类继承关系,你会发现IAdaplabe接口使用的是多么广泛,基本上Eclipse的核心类都继承这个接口。如果你想了解Eclipse的插件开发或者他的扩展机制,那么你需要深刻理解IAdaptable接口,这个接口是Eclipse的核心模式,是Eclipse扩展机制的核心,就是这个简单的IAdaptable接口奠定了整个Eclipse扩展平台的基础,对Eclipse开发者来说,这个接口就像Java的Exception,Object一样,无处不在。 什么是IAdaptable接口 直接看源代码: public interface IAdaptable { /** * Returns an object which is an instance of the given class * associated with this object. Returns <code>null</code> if * no such object can be found. * * @param adapter the adapter class to look up * @return a object castable to the given class, * or <code>null</code> if this object does not * have an adapter for the given class */ public Object getAdapter(Class adapter); } 这个接口只提供了唯一的一个方法getAdapter,从注释中可以了解到,他提供了一种将现有接口适配到给定类的方式,通过这种方式,从而对现有类的功能进行了扩展。 为什么需要这个接口,它有什么特别之处 当我们要增加一些新特性时,这些新特性可能需要用到已有接口提供的特性,那我们要怎样在新接口中使用这些特性。 我们通过一个Eclipse里的实例来说明IAdaptable的特别之处。Eclipse的Properteis View用来显示选中对象的Properteis。在Project View中,如果你选中一个项目,一个文件,选中对象的属性都会显示在Properties View中。那么这是如何实现的。 Properties View需要一个接口获取对象的属性并显示他们,在Eclipse中,这个接口是IPropertySource。 Eclipse的Resource 插件提供了IFile接口,用来代表Project View中对应的文件。如果按照常用的做法:让新接口继承已有接口。 public interface IFile extends IPropertySource 这种方式有两个缺陷: IFIle实现中必须增加IPropertySource的实现,意味着这对核心类的修改 如果还需要实现其他更多的接口,这个方式会导致接口的膨胀。 如果IFile通过实现IAdaptable接口的方式来实现对IPropertySouce的支持,如下所示: public Object getAdapter(Class adapter){ if(adapter.equals(IPropertySource.class)){ return new PropertySourceAdapter(this); } return null; } PropertySouceAdapter: class PropertySourceAdapter implements IPropertySource{ private final Object item; public PropertySourceAdapter(Object item){ this.item = item; } //required methods of IPropertySource ... } 可以看到,这种方式可以灵活的实现对特定接口的支持。在上面代码中,IFile实现也被修改,但在Eclipse真正的实现中,IFile实现时不需要修改的,Adapter实现类通过在plugin.xml中配置完成。 <plugin> <extension point="org.eclipse.core.runtime.adapters"> <factory adaptableType="org.eclipse.core.resources.IFile" class="org.eclipse.core.adapters.properties.FilePropertiesSourceAdapterFactory"> <adapter type="org.eclipse.ui.views.properties.IPropertySource"> </adapter> </factory> </extension> </plugin> 通过这种方式,我们发现这些核心类的实现在保持不变的情况下,可以通过IAdaptable接口以及Eclipse平台提供的注册机制,能够优雅的将现有类扩展支持其他的接口。 这种方式对于平台化来说十分重要, 首先,保持核心类的稳定,避免因为新功能的加入,需要对核心实现进行修改,这很容易导业务模型因为功能的增加和膨胀,不时的修改核心代码也很容易降低平台的稳定性。 其次, 新功能的引入和已有功能实现了隔离,他们在各自的实现类中完成功能逻辑,方便维护和测试 IAdapatable对交易平台的思考 Eclipse的核心代码设计非常简洁优美,发展10多年来,核心代码变动不大,但Eclipse平台支持的功能在Eclipse开源社区和各厂家的支持下,成倍的增加。 能够让各种工具在这些核心代码的基础上实现各自的功能,确不需要更改核心代码,相互之间又有很好的隔离性,这些核心代码的质量和背后的设计思想的强大能力让人赞叹。 目前交易平台的核心代码需要学习Eclipse的这一点,**对核心接口和实现应该达到不因为新业务新功能的引入就需要更改接口的目标。** 这样保持核心接口稳定,避免代码膨胀,对平台的生命力有很大的好处。 举例来说,下面代码是Buy里商品的基本接口 public interface ItemSDO extends ExtensibleSDO { .... boolean isHotel(); boolean isInsurance(); boolean isOfferItemNoPaid(); boolean isQrCodeSend(); .... } 有大量类似的接口方法,用来判断商品的业务类型,也就是说,如果需要增加一个新的业务,那么这个接口就会多一个方法。对这样核心的类库果各个业务开发团队都可以去修改,其风险以及以后得代码维护性都可想而知。 这样的例子在交易的代码中很普遍,我们需要在平台化中去改进这一点,设计核心代码可以满足以后业务的发展需要,让业务开发团队没必要去修改这些核心代码。 再比如, 打标这个操作可以说是交易中最常见的业务能力。达标就是对订单或者商品加上新的属性值。这其实很类似Eclipse的IFile和IPropertySource的功能。在Eclipse的Properties View中,你可以对选中文件的一些属性进行修改或者增加属性。 建设订单接口设计好后,需要增加达标的能力,并且把标记通过查询接口显示出来。 如果按照Eclipse的方式, 订单,商品都可以先继承IAdaptable接口: public interface ItemSDO extends IAdaptable { ... } public interface OrderLineSDO extends IAdaptable { ... } 而打标相应的接口封装在一个独立的类中:(直接用Eclipse的IProertySource做参考,和打标的设计目的基本一致: public interface IPropertySource { ... public void setPropertyValue(Object id, Object value); ... } ... 如果商品和订单需要打标的能力,那么只需要简单配置下: <plugin> <extension point="org.eclipse.core.runtime.adapters"> <factory adaptableType="org.eclipse.core.resources.ItemSDO" class="org.taobao.core.adapters.properties.ItemSDOPropertiesSourceAdapterFactory"> <adapter type="com.taobao.buy.properties.IPropertySource"> </adapter> </factory> </extension> </plugin> 生成一个新的类: ItemSDOPropertiesSourceAdapterFactory在这个类中去实现对商品的打标扩展: public class ItemSDOPropertiesSourceAdapterFactory implements IAdapterFactory { public Object getAdapter(Object adaptableObject, Class adapterType) { if (adapterType == IPropertySource.class) return new ItemSDOPropertySource((ItemSDO)adaptableObject); return null; } public Class[] getAdapterList() { return new Class[] {IPropertySource.class}; } } 可以看到,增加了打标这个能力后,商品或者订单接口都不需要修改。而且打标这个能力被抽象出来,可以被订单,商品等等扩展。 交易平台可以说是目前阿里中设计业务方最多的平台,B2B,航旅,O2O,虚拟商品,生活服务等新的业务快速增加,对业务的支持能力是交易平台化最核心和头痛的问题。目前交易的代码里到处是和业务相关的代码。 对比Eclipse,可以说Eclipse也是支持很多业务方的平台,在Eclipse的集成开发环境里,可以支持Java,Web,Scala等各种功能工具。 那么Eclipse是怎样解决业务隔离和快速支持新业务呢,我们可以做个类比, 交易平台需要创建各种不同业务类型的订单,如航旅订单,生活服务订单,酒店订单。 Eclipse需要创建不同业务类型的项目,比如Java Project,Maven Project, Scala Project, J2EE Project。 每种项目就和订单一样,有各自不同的特性和对应的行为能力。 我们首先介绍Eclipse的项目模型以及如果对项目支持不同的业务扩展,然后我们在对比下目前交易平台TMF的实现,看看交易平台化对业务支持可以从Eclipse中学到什么。 Project项目是Eclipse中最基本的组织单元。 使用Eclipse做开发,都是从创建一个项目开始,在Eclipse中,点击File->New Project...你可以发现Eclipse支持各种各样的Project类型,如下图所示: 选择不同的project,你会发先创建项目的流程,项目的特性都会有所不同。比如选择创建Maven Project,在项目创建中,会提示你设置项目的Maven artifact信息,项目生成后, 项目中会创建POM.xml文件,鼠标选中项目,右键菜单会出现Maven相关菜单。 这一切背后是怎么实现的? 在Eclipse里,项目一般是IProject的实例,我们先简单创建一个最基本的Eclipse Project,File-> New General Project: 项目创建后,只有一个文件.project, 这个文件时eclipse项目的元数据,打开这个文件: <?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>Test</name> <comment></comment> <projects> </projects> <buildSpec> </buildSpec> <natures> </natures> </projectDescription> 鼠标选中项目,右键菜单中只有最基本的操作,在Run As中也只有Run Configruation这一个子菜单。 现在我们在这个文件中的natures部分增加: <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> 这时,鼠标右键Run As中,会出现Java Application, Java Applet两个新的子菜单。 这个新增加的Nature是项目的某种标记tag,增加org.eclipse.jdt.core.javanature这个nature会告诉Eclipse这是个Java项目。 同理我们可以增加代表maven项目的标记, org.eclipse.m2e.core.maven2Nature,那么这个项目就不但是个java projet,同时也是一个maven project。 <natures> <nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature> </natures> 这个简单的测试,告诉我们Eclipse Project的特定类型是由project nature来标识的。 Eclipse的功能室通过插件方式来实现的,比如 Java Development Tool (JDT) 插件提供了java相关功能,JDT会声明我识别具有“org.eclipse.jdt.core.javanature”的项目为Java项目,所有java相关的功能就通过这个nature在插件和项目之间建立起联系。我们可以在跟具体了解下Eclipse对应的实现,主要和以下三个接口相关: IProject, IProjectDescription, IProjectNature 首先我们看看IProjet的部分接口: public interface IProject extends IContainer, IAdaptable { ... /** * Returns whether the project nature specified by the given * nature extension id has been added to this project. * * @param natureId the nature extension identifier * @return <code>true</code> if the project has the given nature * @exception CoreException if this method fails. Reasons include: * <ul> * <li> This project does not exist.</li> * <li> This project is not open.</li> * </ul> */ public boolean hasNature(String natureId) throws CoreException; public boolean isNatureEnabled(String natureId) throws CoreException; public IProjectNature getNature(String natureId) throws CoreException; public IProjectDescription getDescription() throws CoreException; public void setDescription(IProjectDescription description, IProgressMonitor monitor) throws CoreException; ... } IProjectDescription: public interface IProjectDescription { ... public void setNatureIds(String[] natures); public String[] getNatureIds(); public boolean hasNature(String natureId); ... } IProjectNature: public interface IProjectNature { /** * Configures this nature for its project. This is called by the workspace * when natures are added to the project using <code>IProject.setDescription</code> * and should not be called directly by clients. The nature extension * id is added to the list of natures before this method is called, * and need not be added here. * * Exceptions thrown by this method will be propagated back to the caller * of <code>IProject.setDescription</code>, but the nature will remain in * the project description. * * @exception CoreException if this method fails. */ public void configure() throws CoreException; /** * De-configures this nature for its project. This is called by the workspace * when natures are removed from the project using * <code>IProject.setDescription</code> and should not be called directly by * clients. The nature extension id is removed from the list of natures before * this method is called, and need not be removed here. * * Exceptions thrown by this method will be propagated back to the caller * of <code>IProject.setDescription</code>, but the nature will still be * removed from the project description. * * * @exception CoreException if this method fails. */ public void deconfigure() throws CoreException; 从以上代码可知, 当一个Project Nature被赋予一个项目之后, 那么这个Nature的Configure方法就会被调用,这这个方法里,可以把和这个Nature相关的业务逻辑增加到项目中去,比如Java Nature被增加后,Configure方法会把Java的builder注入到项目里,当运行项目build时,java 的builder就会被执行。 简单总结一下: Project Nature允许插件(业务方)把项目project标记tag为一种特定类型(业务), 这个Nature由业务方定义声明,通过这个Nature标记,可以把业务方定义的特定功能增加到project中去。 每个项目可以有多个nature,来自不同的业务方,可以说nature是业务方的一个标记。 通过这个标记,可以对项目的行为根据业务方的要求来定制。 我们可以到这是一种优美的对多业务的支持,业务能力通过一个标记来声明, 模型只需要简单的增加标记,就可以得到对应的业务能力,业务和模型之间通过标记解耦开来。 无论增加多少业务,都不会影响模型的实现,各业务之间也是隔离的。 如果有类似的方式对订单实现多业务扩展, 那么如果新增生活服务订单,只需要生活服务定义一个新的nature标记, 生活服务相关的行为能力只和这个nature关联。 当一个订单被生成识别成生活服务订单后,订单中会加入生活服务nature标记, 这个订单就具有所有和生活相关服务的能力了。 在稍微了解Eclipse对多业务的支持后,我们来了解下目前交易对订单多业务支持的实现, 目前对多业务是由TMF2.0重构来实现,我们用个实例来说明TMF的实现: 在Orderline.java(订单)中, @Override public boolean canUseMallPoint() { CanUseMallPoint provider = Providers.getProvider(CanUseMallPoint.class); //返回值一定包含一个值,所以此处不用判断provider的返回值是否为null return provider.canUseMallPoint(this); } 这个方法用来判断该订单是否可以使用天猫积分。 在这个实现里, 通过TMF的Provider和SPI的方式,根据业务方来决定是否可以使用天猫积分: 在对应的provider实现中: public class CanUseMallPointProvider extends SpiProvider2 implements CanUseMallPoint { @Override public Boolean canUseMallPoint(OrderLineSDO orderLineSDO) { return null; } @Nonnull @Override protected SpiNode<UnitedRequest, UnitedResponse> spiTree() { return mutex(NamespaceConstants.GuestNS, NamespaceConstants.DefaultFictitiousNs, NamespaceConstants.OfficialShopNs, NamespaceConstants.BSellerNs, NamespaceConstants.WuDiQuanNs, NamespaceConstants.TradeCenterNs); } } 这个类里标明有两个业务方可能可以使用天猫积分: GuestNS,DefaultFictitiousNs,OfficialShopNs,BSellerNs,WuDiQuanNs, TradeCenterNs。 每个业务方都必须提供一个CanUseMallPoint这个接口的实现类,在这个类中实现具体的业务逻辑,比如对于无敌卡WuDiQuanNs的SPI实现: @BizSpi public class WuDiQuanCanUseMallPointSpi implements CanUseMallPoint { @Override public Boolean canUseMallPoint(OrderLineSDO orderLineSDO) { return true; } } 这个接口实现根据业务需要,共有六个实现。 在这个简单的功能点实现过程中,可以看到实现这个功能点对不同业务方的支持,我们更改了Orderline这个核心类,增加了一个新的接口CanUseMallPoint, 和新的Provider类CanUseMallPointProvider, 以及6个业务方的SPI实现类(WuDiQuanCanUseMallPointSpi等)。 以后当一个新业务要开发时,这样的功能点,我们都要判断是否需要针对该业务提供支持,如果需要,那要修改CanUseMallPointProvider类和增加一个SPI实现。 这只是交易复杂逻辑中微小的一个功能点,可以看到对代码的侵入很大,如果功能点是一个维度 X,业务方是另一个维度 Y,那可能的实现类就有XY乘积。我们可以看到代码的膨胀以及以后代码维护的代价。 如果按照Eclipse的Nature设计思想,TMallPoint(天猫积分)可以定义一个Nature,比如com.taobao.buy.tmallpointNature, 如果该订单实例有这个Nature,就可以使用天猫积分。 那么如何决定这个订单是否应该有com.taobao.buy.tmallpointNature呢,这可以通过订单识别来决定,不同的业务订单,按照Nature的实际实现,对应的业务方可能会在订单上加入对应的业务方Nature,比如无敌卡(WuDiQuan),会在订单中增加com.taobao.buy.biz.wudiquanNature. com.taobao.buy.tmallpointNature可以指定requires-nature: <requires-nature> id="com.taobao.buy.biz.wudiquanNature"> </requires-nature> 那么只要项目具有无敌卡nature,就会自动有tmallpointNature,也就意味着可以有使用天猫积分的功能。 以上只是一个简单的例子,Eclipse有根复杂的引用,同样交易平台也有更复杂的业务逻辑。 但是对比我们发现,Eclipse的方式,只需要配置一些元数据和依赖关系,基本不需要增加新的代码。功能点,订单,业务的关系通过Nature这个标志解耦后,可以方便的配置。 功能点->功能点nature->订单<-业务nature<-业务 交易平台对多业务支持以及业务隔离的思考 目前TMF对多业务的支持以及业务隔离,会导致大量类的增加,比如一个新业务需求实现,需要对所有涉及到的功能点进行review并提供相应的SPI实现,同时新旧业务在同一个Provider里,新业务开发者很难区分对旧业务功能是否有干扰。 目前TMF对新业务的支持还是太重,对开发人员对TMF框架以及业务理解的要求很高,在平台化过程中,我们需要改进目前的缺点,借鉴Eclipse的方式,将业务,功能点通过Nature解耦,利用元数据信息动态管理,可以有效的减少业务代码,从而也达到快速支持业务发展的目标。 IProject, IProjectFacet 另外在Eclipse WTP项目中(Webtooling Project)引入的Facet概念,可以说是对Nature的跟进一步增强。 具体可以了解: http://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.jst.j2ee.doc.user%2Ftopics%2Fcfacets.html 这是对交易平台化的初步探讨,希望在我们做平台化过程中能吸收一些平台软件的成功经验。
创业这个领域是最有创新精神的,通过反思如何用技术解决当前痛点,这对于开发人员来说是一个很重要的素质,我们需要通过技术来解决目前的瓶颈,而不是墨守成规,用重复性的劳动来完成目标。
业 务平台化用来以平台化支持多业务构建,从某种程度说,平台化就是领域平台化,也和SaaS化有很大的目标一致性,能够快速支持多业务的定制开发,同是保证 业务之间互相隔离。比如交易平台化,你需要在一套交易系统中支持多种交易模式,多种业务实现。 阿里的中台化战略就是建立在业务系统平台化基础上的协同。
BaaS通过云端代码,消息,Hook等机制实现服务器端扩展,满足业务个性化定制需要。
BaaS特别适合社交内容类应用的开发,这类应用有一类特点,业务模型较为简单,对后端服务依赖同质化高。
BaaS将给开发者赋能,通过将后端能力服务化提供给开发者,来帮助开发者快速构建应用。BaaS同时是一种开发架构的进化,能够缩短开发链路,全面提高开发效率。
BaaS让开发者更注重于前端,将目光注视在业务实现上,而后端是成熟强大的服务,确保无后顾之忧,可以更快的将创意转换为产品,将资金、人力、时间等成本降到最低。
BaaS将形成开发生态,后端能力可以很简单被共享,你可以根据需要采购第三方的后端服务。所有的服务都有一致的标准,降低开发者的学习成本