from:http://www.infoq.com/cn/articles/micro-soa-2

上一篇文章中,我说到SOA是一个特别大的话题,不但没有绝对统一的原则,而且很多原则本身的内容也具备相当模糊性和宽泛性。虽然我们可以说SOA ≈ 模块化开发 + 分布式计算,但由于其原则的模糊性,我们仍然很难说什么应用是绝对符合SOA的,只能识别出哪些是不符合SOA的。

本篇将对8种可操作的服务设计原则进行细化的分析,作为SOA实践的参考。

服务设计原则1:优化远程调用

这里的远程调用特指RPC(Remote Procedure Call)。当然更面向对象的说法应该是远程方法调用或者远程服务调用等等。

由于SO接口通常要被远程访问,而网络传输,对象序列化/反序列化等开销都远远超过本地Object访问几个数量级,所以要加快系统的响应速度、减少带宽占用和提高吞吐量,选择高性能的远程调用方式经常是很重要的。

但是远程调用方式往往又要受限于具体的业务和部署环境,比如内网、外网、同构平台、异构平台等等。有时还要考虑它对诸如分布式事务,消息级别签名/加密,可靠异步传输等方面的支持程度(这些方面通常被称为SLA:service level agreement),甚至还包括开发者的熟悉和接受程度等等。

因此,远程调用方式往往需要根据具体情况做出选择和权衡。

以Java远程Service为例分析不同场景下,传输方式的某些可能较好选择:

  • 内网 + 同框架Java客户端 + 大并发:多路复用的TCP长连接 + kryo (二进制序列化) (kryo也可以用Protostuff,FST等代替)
  • 内网 + 不同框架Java客户端:TCP + Kryo
  • 内网 + Java客户端 + 2PC分布式事务:RMI/IIOP (TCP + 二进制)
  • 内网 + Java客户端 + 可靠异步调用:JMS + Kryo (TCP + 二进制)
  • 内网 + 不同语言客户端:thrift(TCP + 二进制序列化)
  • 外网 + 不同语言客户端 + 企业级特性:HTTP + WSDL + SOAP (文本)
  • 外网 + 兼顾浏览器、手机等客户端:HTTP + JSON (文本)
  • 外网 + 不同语言客户端 + 高性能:HTTP + ProtocolBuffer (二进制)

简单来说,从性能上讲,tcp协议 + 二进制序列化更适合内网应用。从兼容性、简单性上来说,http协议 + 文本序列化更适合外网应用。当然这并不是绝对的。另外,tcp协议在这里并不是限定远程调用协议一定只能是位于OSI网络模型的第四层的原始tcp,它可以包含tcp之上的任何非http协议。

所以,回答上面提到的问题,WebServices (经典的WSDL+SOAP+HTTP)虽然是最符合前述SOA设计原则的技术,但并不等同于SOA,我认为它只是满足了SOA的底线,而未必是某个具体场景下的最佳选择。这正如一个十项全能选手在每个单项上是很难和单项冠军去竞争的。更理想的SOA Service最好能在可以支持WebServices的同时,支持多种远程调用方式,适应不同场景,这也是Spring Remoting,SCA,Dubbo,Finagle等分布式服务框架的设计原则。

远程调用技术解释:HTTP + JSON适合SOA吗?

JSON简单易读,通用性极佳,甚至能很好支持浏览器客户端,同时也常被手机APP使用,大有取代XML之势。

但JSON本身缺乏像XML那样被广泛接受的标准schema,而一般的HTTP + JSON的远程调用方式也缺乏像Thrift,CORBA,WebServices等等那样标准IDL(接口定义语言),导致服务端和客户端之间不能形成强的服务契约,也就不能做比如自动代码生成。所以HTTP + JSON在降低了学习门槛的同时,可能显著的增加复杂应用的开发工作量和出错可能性。

例如,新浪微博提供了基于HTTP + JSON的Open API,但由于业务操作比较复杂,又在JSON上封装实现了各种语言的客户端类库,来减少用户的工作量。

为了解决这方面的问题,业界有很多不同方案来为HTTP + JSON补充添加IDL,如RSDL、JSON-WSP、WADL、WSDL 2.0等等,但事实上它们的接受度都不太理想。

另外值得一提的是,JSON格式和XML一样有冗余,即使做GZIP压缩之类的优化,传输效率通常也不如很多二进制格式,同时压缩、解压还会引入额外的性能开销。

远程调用技术解释:Apache Thrift多语言服务框架

Thrift是最初来自facebook的一套跨语言的service开发框架,支持C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, JavaScript, Node.js, Smalltalk, Delphi等几乎所有主流编程语言,具有极好的通用性。

Thrift被facebook,twitter等巨头以及开源社区都广泛使用,是非常成熟的技术。

Thrift的服务契约通过类似如下形式的IDL定义:

struct User {     1: i32 id,     2: string name,     3: string password }  service UserService {     void store(1: User user),     UserProfile retrieve(1: i32 id) } 

非常类似于C语言,易读易写,比WSDL简单明了得多。比用java之类的编程语言也更方便,有时候可以把所有相关的接口和数据结构定义放到同一个文件,发布出去的时候不用再打一个压缩包之类,甚至可以直接粘贴到文档中

Thrift还提供工具,可以基于IDL自动生成各种语言对应的服务端和客户端代码:

[lishen@dangdang thrift]thrift --gen java user.thrift [lishen@dangdang thrift]$ thrift --gen cpp user.thrift [lishen@dangdang thrift]$ thrift --gen php user.thrift [lishen@dangdang thrift]$ thrift --gen csharp user.thrift 

我认为thrift是比WebServices更简单高效的技术,是在SOA中对WebServices最具有替代性的技术之一。

远程调用技术解释:多路复用的TCP长连接

这是一种追求极致高性能高伸缩的方式,这里只做简要介绍。

比较典型的是twitter的Mux RPC协议以及google的SPDY协议,在其中多个请求同时共用同一个长连接,即一个连接交替传输不同请求的字节块。它既避免了反复建立连接开销,也避免了连接的等待闲置从而减少了系统连接总数,同时还避免了TCP顺序传输中的线头阻塞(head-of-line blocking)问题。

另外,国内比较著名的开源dubbo框架的默认RPC协议,以及业界许多小型开源RPC框架也都是类似的思路。

采用多路复用机制后,一般就要求服务器端和客户端都支持额外的类似于会话层(即OSI网络模型第六层)的语义,导致它们必须要依赖于同一套RPC框架。

其他很多RPC机制都是使用TCP短连接。即使有些RPC使用了长连接,但一个连接同一时间只能发送一个请求,然后连接就处于闲置状态,来等待接收该请求的响应,待响应完毕,该连接才能被释放或者复用。

HTTP 1.1也支持一种基于pipeline模式的长连接,其中多个HTTP请求也可共用一个连接,但它要求响应(response)也必须按照请求(request)的顺序传输返回,即FIFO先进先出。而在完全多路复用的连接中,哪个的响应先ready就可以先传输哪个,不用排队。

当然,短连接、长连接和多路复用长连接之间不存在绝对的好坏,需要取决于具体业务和技术场景,在此不详细展开了。

远程调用技术解释:Java高效序列化

最近几年,各种新的Java高效序列化方式层出不穷,不断刷新序列化性能的上限,例如Kryo,FST等开源框架。它们提供了非常高效的Java对象的序列化和反序列化实现,相比JDK标准的序列化方式(即基于Serializable接口的标准序列化,暂不考虑用诸如Externalizable接口的定制序列化),在典型场景中,其序列化时间开销可能缩短20倍以上,生成二进制字节码的大小可能缩减4倍以上。

另外,这些高效Java序列化方式的开销也显著少于跨语言的序列化方式如thrift的二进制序列化,或者JSON等等

远程调用技术解释:RMI/IIOP和分布式事务

RMI/IIOP是Java EE中标准的远程调用方式,IIOP是CORBA的协议,只有IIOP上的RMI才支持两阶段提交的分布式事务,同时提供和CORBA的互操作。

当然,严格的两阶段提交事务并不高效,还可能严重影响系统伸缩性甚至可用性等等,一般只应用在非常关键的业务中。

远程调用技术解释:Google ProtocolBuffer跨语言序列化

ProtocolBuffer是google开发的跨语言的高效二进制序列化方式,其序列化性能和thrift比较类似。事实上thrift最初就是ProtocolBuffer的仿制品。但它和thrift最大的不同是他没有自带的RPC实现(因为google没有将RPC部分开源,但有大量第三方实现)。

由于不和RPC方式耦合,反而使得ProtocolBuffer能被方便的集成进大量已有的系统和框架中。在国内它也被百度、淘宝等广泛的应用在Open API中,和HTTP搭配作为一种高效的跨平台跨组织的集成方式。

服务设计原则2:消除冗余数据

同样由于service的远程调用开销很高,所以在它的输入参数和返回结果中,还要尽量避免携带当前业务用例不需要的冗余的字段,来减少序列化和传输的开销。同时,去掉冗余字段也可以简化接口,避免给外部用户带来不必要的业务困惑。

比如article service中有个返回article list的方法

List<Article> getArticles(...)

如果业务需求仅仅是要列出文章的标题,那么在返回的article中就要避免携带它的contents等等字段。

这里经典解决方案就是引入OO中常用的Data Transfer Object (DTO)模式,专门针对特定service的用例来定制要传输的数据字段。这里就是添加一个AriticleSummary的额外数据传输对象:

List<ArticleSummary> getArticleSummaries(...)

额外的DTO确实是个麻烦,而一般OO程序通常则可直接返回自己的包含冗余的业务模型。

服务设计原则3:粗粒度契约

同样由于远程调用开销高,同时service的外部使用者对特定业务流程的了解也比不上组织内部的人,所以service的契约(接口)通常需要是粗粒度的,其中的一个操作就可能对应到一个完整的业务用例或者业务流程,这样既能减少远程调用次数,同时又降低学习成本和耦合度。

而OO接口通常可以是非常细粒度的,提供最好的灵活性和重用性。

例如,article service支持批量删除文章,OO接口中可以提供

deleteArticle(long id)

供用户自己做循环调用(暂不考虑后端SQL之类优化),但SO接口中,则最好提供

deleteArticles(Set<Long> ids)

供客户端调用,将可能的N次远程调用减少为一次。

例如,下订单的用例,要有一系列操作

addItem -> addTax -> calculateTotalPrice -> placeOrder 

OO中我们完全可以让用户自己来灵活选择,分别调用这些细粒度的可复用的方法。但在SO中,我们需要将他们封装到一个粗粒度的方法供用户做一次性远程调用,同时也隐藏了内部业务的很多复杂性。另外,客户端也从依赖4个方法变成了依赖1个方法,从而大大降低了程序耦合度。

顺便值得一提的是,如果上面订单用例中每个操作本身也是远程的service(通常在内网之中),这种粗粒度封装就变成了经典的service composition(服务组合)甚至service orchestration(服务编排)了。这种情况下粗粒度service同样可能提高了性能,因为对外网客户来说,多次跨网的远程调用变成了一次跨网调用 + 多次内网调用。

对这种粗粒度service封装和组合,经典解决方案就是引入OO中常用的Facade模式,将原来的对象屏蔽到专门的“外观”接口之后。同时,这里也很可能要求我们引入新的service参数/返回值的数据结构来组合原来多个操作的对象模型,这就同样用到前述的DTO模式。

一个简单Facade示例(FooService和BarService是两个假想的本地OO service,façade将它们的结果值组合返回):

class FooBarFacadeImpl implements FooBarFacade {     private FooService fooService;     private BarService barService;      public FooBarDto getFooBar() {         FooBarDto fb = new FooBarDto();         fb.setFoo(fooService.getFoo());         fb.setBar(barService.getBar());         return fb;     } }   

当然,有的时候也可以不用facade和DTO,而在是FooService和BarService之外添加另一个本地service和domain model,这要和具体业务场景有关。

服务设计原则4:通用契约

由于service不假设用户的范围,所以一般要支持不同语言和平台的客户端。但各种语言和平台在功能丰富性上有很大差异,这就决定了服务契约必须取常见语言、平台以及序列化方式的最大公约数,才能保证service广泛兼容性。由此,服务契约中不能有某些语言才具备的高级特性,参数和返回值也必须是被广泛支持的较简单的数据类型(比如不能有对象循环引用)。

如果原有的OO接口不能满足以上要求,则在此我们同样需要上述的Facade和DTO,将OO接口转换为通用的SO契约。

例如原有对象模型

class Foo {    private Pattern regex; }

Pattern是Java特有的预编译好的,可序列化的正则表达式(可提高性能),但在没有特定框架支持下,可能不好直接被其他语言识别,所以可添加DTO:

class FooDto {    private String regex; }

服务设计原则5:隔离变化

虽然OO和SO都追求低耦合,但SO由于使用者范围极广,就要求了更高程度的低耦合性。

比如前述的article service,OO中可以直接返回article对象,而这个article对象在OO程序内部可能做为核心的建模的domain model,甚至作为O/R mapping等等。而在SO如果还直接返回这个article,即使没有前面所说的冗余字段,复杂类型等问题,也可能让外部用户与内部系统的核心对象模型,甚至O/R mapping机制,数据表结构等等产生了一定关联度,这样一来,内部的重构经常都会可能影响到外部的用户。

所以,这里再次对Facade和DTO产生了需求,用它们作为中介者和缓冲带,隔离内外系统,把内部系统变化对外部的冲击减少到最小程度。

服务设计原则6:契约先行

Service是往往涉及不同组织之间的合作,而按照正常逻辑,两个组织之间合作的首要任务,就是先签订明确的契约,详细规定双方合作的内容,合作的形式等等,这样才能对双方形成强有力的约束和保障,同时大家的工作也能够并行不悖,不用相互等待。因此SOA中,最佳的实践方式也是契约先行,即先做契约的设计,可以有商务,管理和技术等不同方面的人员共同参与,并定义出相应的WSDL或者IDL,然后在开发的时候再通过工具自动生成目标语言的对应代码。

对于WSDL来说,做契约先行的门槛略高,如果没有好的XML工具很难手工编制。但对于Thrift IDL或者ProtocolBuffer等来说,由于它们和普通编程语言类似,所以契约设计相对是比较容易的。另外,对于简单的HTTP + JSON来说(假设不补充使用其他描述语言),由于JSON没有标准的schema,所以是没法设计具有强约束力的契约的,只能用另外的文档做描述或者用JSON做输入输出的举例。

但是,契约先行,然后再生成服务提供端的代码,毕竟给service开发工作带来了较大的不便,特别是修改契约的时候导致代码需要重写。因此,这里同样可能需要引入Facade和DTO,即用契约产生的都是Facade和DTO代码,它们负责将请求适配和转发到其他内部程序,而内部程序则可以保持自己的主导性和稳定性。

另外,契约先行可能会给前面提到的多远程调用支持带来一些麻烦。

当然契约先行也许并不是能被广泛接受的实践方式,就像敏捷开发中“测试先行”(也就是测试驱动开发)通常都是最佳实践,但真正施行的团队却非常之少,这方面还需要不断摸索和总结。但我们至少可以认为Echo中Java2WSDL并不被认为是SOA的最佳实践。

服务设计原则7:稳定和兼容的契约

由于用户范围的广泛性,所以SO的服务契约和Java标准API类似,在公开发布之后就要保证相当的稳定性,不能随便被重构,即使升级也要考虑尽可能的向下兼容性。同时,如果用契约先行的方式,以后频繁更改契约也导致开发人员要不断重做契约到目标语言映射,非常麻烦。

这就是说SO对契约的质量要求可能大大高于一般的OO接口,理想的情况下,甚至可能需要专人(包括商务人员)来设计和评估SO契约(不管是否用契约先行的方式),而把内部的程序实现交给不同的人,而两者用Facade和DTO做桥梁。

服务设计原则8:契约包装

前述原则基本都是针对service提供端来讲的,而对service消费端而言,通过契约生成对应的客户端代码,经常就可以直接使用了。当然,如果契约本身就是Java接口之类(比如在Dubbo,Spring Remoting等框架中),可以略过代码生成的步骤。

但是,service的返回值(DTO)和service接口(Facade),可能被消费端的程序到处引用到。

这样消费端程序就较强的耦合在服务契约上了,如果服务契约不是消费端定义的,消费端就等于把自己程序的部分主导权完全让渡给了别人。

一旦契约做更改,或者消费端要选择完全不同的service提供方(有不同的契约),甚至改由本地程序自己来实现相关功能,修改工作量就可能非常大了。

另外,通过契约生成的客户端代码,经常和特定传输方式是相关的(比如webservices stub),这样给切换远程调用方式也会带来障碍。

因此,就像在通常应用中,我们要包装数据访问逻辑(OO中的DAO或者Repository模式),或者包装基础服务访问逻辑(OO中的Gateway模式)一样,在较理想的SOA设计中,我们也可以考虑包装远程service访问逻辑,由于没有恰当的名称,暂时称之为Delegate Service模式,它由消费端自己主导定义接口和参数类型,并将调用转发给真正的service客户端生成代码,从而对它的使用者完全屏蔽了服务契约,这些使用者甚至不知道这个服务到底是远程提供的的还是本地提供的。

此外,即使我们在消费端是采用某些手工调用机制(如直接构建和解析json等内容,直接收发JMS消息等等),我们同样可以用delegate service来包装相应的逻辑。

delegate service示例1:

// ArticlesService是消费端自定义的接口 class ArticleServiceDelegate implements ArticlesService {     // 假设是某种自动生成的service客户端stub类     private ArticleFacadeStub stub;      public void deleteArticles(List<Long> ids) {         stub.deleteArticles(ids);     } }   

delegate service示例2:

// ArticlesService是消费端自定义的接口 class ArticleServiceDelegate implements ArticlesService {      public void deleteArticles(List<Long> ids) {         // 用JMS和FastJson手工调用远程service         messageClient.sendMessage(queue, JSON.toJSONString(ids));     } }   

从面向对象到面向服务,再从面向服务到面向对象

总结上面的几个原则,虽然只是谈及有限的几个方面,但大致也可看出OO和SO在实际的设计开发中还是有不少显著的不同之处,而且我们没有打算用SO的原则来取代过去的OO设计,而是引入额外的层次、对象和OO设计模式,来补充传统的OO设计。

其实就是形成了这种调用流程:

  • service提供端:OO程序 <- SOA层(Facade和DTO)<- 远程消费端

  • service消费端:OO程序 -> Delegate Service -> SOA层(Facade和DTO 或者 其他动态调用机制)-> 远程提供端

Facade、DTO和Delegate Service负责做OO到SO和SO到OO的中间转换。

现在,可以回答Echo示例中的问题:通过“透明的”配置方式,将OO程序发布为远程Service,虽然可能较好的完成了从本地对象到远程对象的跨越,但通常并不能较好的完成OO到SO的真正跨越。

同时,透明配置方式也通常无法直接帮助遗留应用(如ERP等)转向SOA。

当然,在较为简单和使用范围确定很有限应用(比如传统和局部的RPC)中,透明式远程service发布会带来极大的便利。

另外,上面对SO的所有讨论都集中在RPC的方式,其实SO中也用message的方式做集成,它也是个大话题,暂时不在此详论了。

为什么不能放弃面向对象?

SO是有它的特定场景的,比如远程的,范围不定的客户端。所以它的那些设计原则并不能被借用来指导一般性的程序开发,比如很多OO程序和SO原则完全相反,经常都要提供细粒度接口和复杂参数类型以追求使用的使用灵活性和功能的强大性。

就具体架构而言,我认为SOA层应该是一个很薄的层次(thin layer),将OO应用或者其他遗留性应用加以包装和适配以帮助它们面向服务。其实在通常的web开发中,我们也是用一个薄的展现层(或者叫Web UI层之类)来包装OO应用,以帮助它们面向浏览器用户。因此,Façade、DTO等不会取代OO应用中核心的Domain Model、Service等等 (这里的service是OO中service,未必是SO的)。

综合起来,形成类似下面的体系结构:

理想和现实

需要特别指出的是,上面提到的诸多SO设计原则是在追求一种相对理想化的设计,以达到架构的优雅性,高效性,可重用性,可维护性,可扩展性等等。

而在现实中任何理论和原则都可能是需要作出适当妥协的,因为现实是千差万别的,其情况远比理论复杂,很难存在放之四海而皆准的真理。

而且很多方面似乎本来也没有必要追求完美和极致,比如如果有足够能力扩充硬件基础设施,就可以考虑传输一些冗余数据,选择最简单传输方式,并多来几次远程调用等等,以减轻设计开发的工作量。

那么理想化的原则就没有意义了吗?比如领域驱动设计(Domain-Driven Design)被广泛认为是最理想的OO设计方式,但极少有项目能完全采用它;测试驱动开发也被认为是最佳的敏捷开发方式,但同样极少有团队能彻底采用它。但是,恐怕没有多少人在了解它们之后会否认它们巨大的意义。

理想化的原则可以更好的帮助人们理解某类问题的本质,并做为好的出发点或者标杆,帮助那些可以灵活运用,恰当取舍的人取得更大的成绩,应付关键的挑战。这正如孔子说的“取乎其上,得乎其中;取乎其中,得乎其下;取乎其下,则无所得矣”。

另外,值得一提的是,SOA从它的理念本身来说,就带有一些的理想主义的倾向,比如向“全世界”开放,不限定客户端等等。如果真愿意按SOA的路径走,即使你是个土豪,偷个懒比浪费网络带宽重要,但说不定你的很多用户是土鳖公司,浪费几倍的带宽就大大的影响他们的利润率。

延伸讨论:SOA和敏捷软件开发矛盾吗?

SOA的服务契约要求相当的稳定性,一旦公开发布(或者双方合同商定)就不应该有经常的变更,它需要对很多方面有极高的预判。而敏捷软件开发则是拥抱变化,持续重构的。软件设计大师Martin Fowler把它们归结为计划式设计和演进式设计的不同。

计划理论(或者叫建构理论)和演进理论是近代哲学的两股思潮,影响深远,派生出了比如计划经济和市场经济,社会主义和自由主义等等各种理论。

但是,计划式设计和演进式设计并不绝对矛盾,就像计划经济和市场经济也不绝对矛盾,非此即彼,这方面需要在实践中不断摸索。前面我们讨论的设计原则和架构体系,就是将SOA层和OO应用相对隔离,分而治之,在SOA层需要更多计划式设计,而OO应用可以相对独立的演进,从而在一定程度缓解SOA和敏捷开发的矛盾。

延伸讨论:SOA和REST是一回事吗?

从最本质的意义上讲,REST(Representational State Transfer)实际是一种面向资源架构(ROA),和面向服务架构(SOA)是有根本区别的。

例如,REST是基于HTTP协议,对特定资源做增(HTTP POST)、删(HTTP DELETE)、改(HTTP PUT)、查(HTTP GET)等操作,类似于SQL中针对数据表的INSERT、DELETE、UPDATE、SELECT操作,故REST是以资源(资源可以类比为数据)为中心的。而SOA中的service通常不包含这种针对资源(数据)的细粒度操作,而是面向业务用例、业务流程的粗粒度操作,所以SOA是以业务逻辑为中心的。

但是在实际使用中,随着许多REST基本原则被不断突破,REST的概念被大大的泛化了,它往往成为很多基于HTTP的轻量级远程调用的代名词(例如前面提到过的HTTP + JSON)。比如,即使是著名的Twitter REST API也违反不少原始REST的基本原则。

在这个泛化的意义上讲,REST也可以说是有助于实现SOA的一种轻量级远程调用方式。

SOA架构的进化

前面讨论的SOA的所有问题,基本都集中在service本身的设计开发。但SOA要真正发挥最大作用,还需要不断演进成更大的架构(也就是从微观SOA过渡到宏观SOA),在此略作说明:

  • 第一个层次是service架构:开发各种独立的service并满足前面的一些设计原则,我们前面基本都集中在讨论这种架构。这些独立的service有点类似于小孩的积木。

  • 第二个层次是service composition(组合)架构:独立的service通过不同组合来构成新的业务或者新的service。在理想情况下,可以用一种类似小孩搭积木的方式,充分发挥想象力,将独立的积木(service)灵活的拼装组合成新的形态,还能够自由的替换其中的某个构件。这体现出SOA高度便捷的重用性,大大提高企业的业务敏捷度。

  • 第三个层次是service inventory(清单)架构:通过标准化企业服务清单(或者叫注册中心)统一的组织和规划service的复用和组合。当积木越来越多了,如果还满地乱放而没有良好的归类整理,显然就玩不转了。

  • 第四个层次是service-oriented enterprise架构……

总结

至此,我们只是简要的探讨了微观层面的SOA,特别是一些基本设计原则及其实践方式,以期能够略微展示SOA在实践中的本质,以有助于SOA更好的落地,进入日常操作层面。

最后,打个比方:SOA不分贵贱(不分语言、平台、组织),不远万里(通过远程调用)的提供服务(service),这要求的就是一种全心全意为人民服务的精神……

作者简介

沈理,当当网架构师和技术委员会成员,主要负责当当网的SOA实施(即服务化)以及分布式服务框架的开发。以前也有在BEA、Oracle、Redhat等外企的长期工作经历,从事过多个不同SOA相关框架和容器的开发。他的邮箱:shenli@dangdang.com


感谢马国耀对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

posted @ 2014-10-16 14:06 小马歌 阅读(216) | 评论 (0)编辑 收藏
 
from:http://www.infoq.com/cn/articles/micro-soa-1

大量互联网公司都在拥抱SOA和服务化,但业界对SOA的很多讨论都比较偏向高大上。本文试图从稍微不同的角度,以相对接地气的方式来讨论SOA,集中讨论SOA在微观实践层面中的缘起、本质和具体操作方式,另外也用相当篇幅介绍了当今互联网行业中各种流行的远程调用技术等等,比较适合从事实际工作的架构师和程序员来阅读。

为了方便阅读,本话题将分为两篇展现。本文是上篇,着眼于微观SOA的定义,并简单分析其核心原则。

亚马逊CEO杰夫•贝佐斯:鲜为人知的SOA大师

由于SOA有相当的难度和门槛,不妨先从一个小故事说起,从中可以管窥一点SOA的大意和作用。

按照亚马逊前著名员工Steve Yegge著名的“酒后吐槽”,2002年左右,CEO贝佐斯就在亚马逊强制推行了以下六个原则(摘自酷壳):

  1. 所有团队的程序模块都要以通过Service Interface 方式将其数据与功能开放出来。
  2. 团队间的程序模块的信息通信,都要通过这些接口。
  3. 除此之外没有其它的通信方式。其他形式一概不允许:不能使用直接链结程序、不能直接读取其他团队的数据库、不能使用共享内存模式、不能使用别人模块的后门、等等,等等,唯一允许的通信方式只能是能过调用 Service Interface。
  4. 任何技术都可以使用。比如:HTTP、Corba、Pubsub、自定义的网络协议、等等,都可以,贝佐斯不管这些。
  5. 所有的Service Interface,毫无例外,都必须从骨子里到表面上设计成能对外界开放的。也就是说,团队必须做好规划与设计,以便未来把接口开放给全世界的程序员,没有任何例外
  6. 不这样的做的人会被炒鱿鱼

据说,亚马逊网站展示一个产品明细的页面,可能要调用200-300个Service,以便生成高度个性化的内容。

Steve还提到:

Amazon已经把文化转变成了“一切以Service第一”为系统架构的公司,今天,这已经成为他们进行所有设计时的基础,包括那些绝不会被外界所知的仅在内部使用的功能。

那时,如果没有被解雇的的恐惧他们一定不会去做。我是说,他们今天仍然怕被解雇,因为这基本上是那儿每天的生活,为那恐怖的海盗头子贝佐斯工作。不过,他们这么做的确是因为他们已经相信Service这就是正确的方向。他们对于SOA的优点和缺点没有疑问,某些缺点还很大,也不疑问。但总的来说,这是正确的,因为,SOA驱动出来的设计会产生出平台(Platform)。

今天,我们都知道亚马逊从世界上最大图书卖场进化为了世界上最成功的云平台……

贝佐斯的六原则展示出高度的远见和超强的信念,即使放到十几年后的今天,依然觉得振聋发聩……想起一句老话:“不谋万世者,不足以谋一时;不谋全局者,不足以谋一隅。”

当然,像贝佐斯这种将神性与魔性集于一身的专横人物,既可能创造划时代的进步,也可能制造前所未有的灾难。

SOA漫谈:宏观与微观

SOA即面向服务架构,是一个特别大的话题。

为了方便讨论,我在此先草率的将SOA分为两个层面(大概模仿宏观和微观经济学,但这里的划分没有绝对界限):

  • 宏观SOA:面向高层次的部门级别、公司级别甚至行业级别;涉及商业、管理、技术等方面的综合的、全局的考虑;架构体系上包括服务治理(governance,如服务注册,服务监控),服务编排(orchestration,如BPM,ESB),服务协同(choreography,更多面向跨企业集成)等等。我认为SOA本身最主要是面向宏观层面的架构,其带来益处也最能在宏观高层次上体现出来,同时大部分SOA的业界讨论也集中在这方面。
  • 微观SOA:面向有限的、局部的团队和个人;涉及独立的、具体的服务在业务、架构、开发上的考虑。

很多业界专家都认为SOA概念过于抽象,不接地气,我认为主要是宏观SOA涉及面太广,经常需要做通盘考虑,而其中很多方面距离一般人又比较远。而在微观层面的SOA更容易达到涛哥过去提出的“三贴近”:贴近实际、贴近生活、贴近群众。

同时,宏观SOA要取得成功,通常的前提也是SOA在微观层面的落地与落实,正如宏观经济学一般要有坚实的微观基础(比如大名鼎鼎的凯恩斯主义曾广受诟病的一点就是缺乏微观基础)

因此,我们着眼于SOA落地的目的,着重来分析微观SOA,也算是对业界主流探讨的一个小小的补充。

SOA定义

按照英文维基百科定义:SOA是一种“软件”和“软件架构”的设计模式(或者叫设计原则)。它是基于相互独立的软件片段要将自身的功能通过“服务”提供给其他应用。

什么是“服务”?按照OASIS的定义:Service是一种按照既定“接口“来访问一个或多个软件功能的机制(另外这种访问要符合“服务描述”中策略和限制)

Service示例(代码通常以java示例)

public interface Echo {     String echo(String text); }  public class EchoImpl implements Echo {     public String echo(String text) {         return text;     } }

可能每个开发人员每天都在写类似的面向对象的Service,难道这就是在实施SOA吗?

SOA设计原则

既然SOA是设计原则(模式),那么它包含哪些内容呢?事实上,这方面并没有最标准的答案,多数是遵从著名SOA专家Thomas Erl的归纳:

标准化的服务契约 Standardized service contract 服务的松耦合 Service loose coupling 服务的抽象 Service abstraction 服务的可重用性 Service reusability 服务的自治性 Service autonomy 服务的无状态性 Service statelessness 服务的可发现性 Service discoverability 服务的可组合性 Service composability ....

这些原则总的来说要达到的目的是:提高软件的重用性,减少开发和维护的成本,最终增加一个公司业务的敏捷度。

但是,业界著名专家如Don Box,David Orchard等人对SOA又有各自不同的总结和侧重。

SOA不但没有绝对统一的原则,而且很多原则本身的内容也具备相当模糊性和宽泛性:例如,所谓松耦合原则需要松散到什么程度才算是符合标准的呢?这就好比一个人要帅到什么程度才算是帅哥呢?一栋楼要高到多少米才算是高楼呢?可能不同人心中都有自己的一杆秤……部分由于这些理论上的不确定因素,不同的人理解或者实施的SOA事实上也可能有比较大的差别。

浅析松耦合原则

SOA原则比较多,真正的理解往往需要逐步的积累和体会,所以在此不详细展开。这里仅以服务的松耦合为例,从不同维度来简单剖析一下这个原则,以说明SOA原则内涵的丰富性:

  • 实现的松耦合:这是最基本的松耦合,即服务消费端不需要依赖服务契约的某个特定实现,这样服务提供端的内部变更就不会影响到消费端,而且消费端未来还可以自由切换到该契约的其他提供方。

  • 时间的松耦合:典型就是异步消息队列系统,由于有中介者(broker),所以生产者和消费者不必在同一时间都保持可用性以及相同的吞吐量,而且生产者也不需要马上等到回复。

  • 位置的松耦合:典型就是服务注册中心和企业服务总线(ESB),消费端完全不需要直接知道提供端的具体位置,而都通过注册中心来查找或者服务总线来路由。

  • 版本的松耦合:消费端不需要依赖服务契约的某个特定版本来工作,这就要求服务的契约在升级时要尽可能的提供向下兼容性。

SOA与传统软件设计

我们可以认为:SOA ≈ 模块化开发 + 分布式计算

将两者传统上的最佳实践结合在一起,基本上可以推导出SOA的多数设计原则。SOA从软件设计(暂不考虑业务架构之类)上来讲,自身的新东西其实不算很多。

SOA原则的应用

基于SOA的原则,也许我们很难说什么应用是绝对符合SOA的,但是却能剔除明显不符合SOA的应用。

用上述标准化契约,松耦合和可重用这几个原则来尝试分析一下上面Echo示例:

  • Echo的服务契约是用Java接口定义,而不是一种与平台和语言无关的标准化协议,如WSDL,CORBA IDL。当然可以抬杠,Java也是行业标准,甚至全国牙防组一致认定的东西也是行业标准。

  • Java接口大大加重了与Service客户端的耦合度,即要求客户端必须也是Java,或者JVM上的动态语言(如Groovy、Jython)等等……

  • 同时,Echo是一个Java的本地接口,就要求调用者最好在同一个JVM进程之内……

  • Echo的业务逻辑虽然简单独立,但以上技术方面的局限就导致它无法以后在其他场合被轻易重用,比如分布式环境,异构平台等等。

因此,我们可以认为Echo并不太符合SOA的基本设计原则。

透明化的转向SOA?

修改一下上面的Echo,添加Java EE的@WebServices注解(annotation)

@WebServices public class EchoImpl implements Echo {     public String echo(String text) {         return text;     } }

现在将Echo发布为Java WebServices,并由底层框架自动生成WSDL来作为标准化的服务契约,这样就能与远程的各种语言和平台互操作了,较好的解决了上面提到的松耦合和可重用的问题。按照一般的理解,Echo似乎就成为比较理想的SOA service了。

但是……即使这个极端简化的例子,也会引出不少很关键的问题,它们决定SOA设计开发的某些难度:

  • 将一个普通的Java对象通过添加注解“透明的”变成WebServices就完成了从面向对象到面向服务的跨越?
  • 通过Java接口生成WSDL服务契约是好的方式吗?
  • WebServices是最合适远程访问技术吗?

面向对象和面向服务的对比

面向对象(OO)和面向服务(SO)在基础理念上有大量共通之处,比如都尽可能追求抽象、封装和低耦合。

但SO相对于OO,又有非常不同的典型应用场景,比如:

  • 多数OO接口(interface)都只被有限的人使用(比如团队和部门内),而SO接口(或者叫契约)一般来说都不应该对使用者的范围作出太多的限定和假设(可以是不同部门,不同企业,不同国家)。还记得贝佐斯原则吗?“团队必须做好规划与设计,以便未来把接口开放给全世界的程序员,没有任何例外”。

  • 多数OO接口都只在进程内被访问,而SO接口通常都是被远程调用。

简单讲,就是SO接口使用范围比一般OO接口可能广泛得多。我们用网站打个比方:一个大型网站的web界面就是它整个系统入口点和边界,可能要面对全世界的访问者(所以经常会做国际化之类的工作),而系统内部传统的OO接口和程序则被隐藏在web界面之后,只被内部较小范围使用。而理想的SO接口和web界面一样,也是变成系统入口和边界,可能要对全世界开发者开放,因此SO在设计开发之中与OO相比其实会有很多不同。

小结

在前述比较抽象的SOA大原则的基础上,我们可尝试推导一些较细化和可操作的原则,在具体实践中体现SO的独特之处。请关注本系列文章的下篇!

作者简介

沈理,当当网架构师和技术委员会成员,主要负责当当网的SOA实施(即服务化)以及分布式服务框架的开发。以前也有在BEA、Oracle、Redhat等外企的长期工作经历,从事过多个不同SOA相关框架和容器的开发。他的邮箱:shenli@dangdang.com


感谢马国耀对本文的审校。

posted @ 2014-10-16 14:05 小马歌 阅读(184) | 评论 (0)编辑 收藏
 

在《直接拿来用!最火的Android开源项目(一)》中,我们详细地介绍了GitHub上最受欢迎的TOP20 Android开源项目,引起了许多读者的热议,作为开发者,你最常用的是哪些开源项目?使用起来是否能让你得心应手?今天,我们将介绍另外20个Android开源项目,在这些项目中,你又用到了哪些呢?

21. drag-sort-listview

DragSortListView(DSLV)是Android ListView的一个扩展,支持拖拽排序和左右滑动删除功能。重写了TouchInterceptor(TI)类来提供更加优美的拖拽动画效果。

 

DSLV主要特性:

 

  • 完美的拖拽支持;
  • 在拖动时提供更平滑的滚动列表滚动;
  • 支持每个ListItem高度的多样性
  • 公开startDrag()和stopDrag()方法;
  • 有公开的接口可以自定义拖动的View。

 

DragSortListView适用于带有任何优先级的列表:收藏夹、播放列表及清单等,算得上是目前Android开源实现拖动排序操作最完美的方案。

22. c-geo-opensource

c:geo是Android设备上一个简单而又强大的非官方地理寻宝客户端。与其他类似应用不同的是,c:geo不需要Web浏览器,也不需要文件输出。你可以在毫无准备的情况下,毫无后顾之忧地带上你的智能手机去进行地理寻宝。当然,你也不需要付钱,因为它是免费的。

c-geo-opensource包含了c:geo所有开源代码。

详情请参考:c:geo

23. NineOldAndroids

自Android 3.0以上的版本,SDK新增了一个android.animation包,里面的类都是跟动画效果实现相关的,通过Honeycomb API,能够实现非常复杂的动画效果。但如果开发者想在3.0以下的版本中也能使用到这套API,那么Nine Old Androids就会是你最好的选择,该API和Honeycomb API完全一样,只是改变了你使用com.nineoldandroids.XXX的入口。

该项目包含两个工程,一个是Library,即为动画效果的实现库,另一个则是Sample,是对如何使用该API的演示。开发者可以直接登陆Google Play下载安装Nine Old Androids Sample,查看演示。

详情请参考:Nine Old Androids

24. ppsspp

PPSSPP是由GC/Wii模拟器Dolphin联合创始人之一Henrik Rydgård开发的一款免费的跨平台开源模拟器,支持Windows、Linux、Mac、Android、iOS、BlackBerry 10等主流计算机与移动操作系统,可直接工作在x86、x64、ARM等CPU平台上,以GNU GPLv2许可协议发布,主要使用C++编写以提高效率和可移植性。

只要支持OpenGL ES 2.0,PPSSPP就可以在相当低规格的硬件设备上运行,包括基于ARM的手机及平板电脑。

详情请参考:PPSSPP

25. androidquery

Android-Query(AQuery)是一个轻量级的开发包,用于实现Android上的异步任务和操作UI元素,可让Android应用开发更简单、更容易,也更有趣。

26. droid-fu

Droid-Fu是一个开源的通用Android应用库,其主要目的是为了让Android开发更容易,包含有许多工具类,还有非常易用的Android组件。

Droid-Fu提供支持的领域包括:

 

  • Android应用的生命周期帮助
  • 支持处理Intents和diagnostics类
  • 后台任务支持
  • HTTP消息处理
  • 对象、HTTP响应及远程图像高速缓存
  • 定制各种Adapter及View

 

Droid-Fu最大的优势在于它的应用生命周期帮助类,如果你正在开发一款Android应用,而它的主要任务是运行后台任务,比如从Web上抓取数据,那么,你一定会使用到Droid-Fu,不过,目前该项目在GitHub上已经停止更新维护。

详情请参考:droid-fu

27. TextSecure

TextSecure是Whisper Systems团队开发的一个Android上的加密信息客户端,旨在增强用户和企业通信的安全性,其源代码于2011年被Twitter发布在GitHub开源数据库中。

该软件允许用户将在Android设备上所有发送和接收的短信内容进行加密,还可以将加密信息发送给另一个TextSecure用户。

28. XobotOS

XobotOS是Xamarin的一个研究项目,用于将Android 4.0从Java/Dalvik移植到C#,并对移植后的性能及内存占用情况进行检测。

29. ignition

在编写Android应用时,通过提供即用组件和包含许多样板文件的实用类,ignition可以让你的Android应用快速起步。ignition涵盖的区域包括:

 

  • Widget、Adapter、Dialog等UI组件;
  • 允许编写简单却强大的网络代码的HTTP Wrapper库;
  • 加载远程Web图像并进行缓存的类;
  • 简单但有效的缓存框架(将对所有对象树做出响应的HTTP缓存到内存或硬盘中);
  • Intents、diagnostics等几个能让API级别更容易向后兼容的帮助类;
  • 更友好、更强大的AsyncTask实现。

 

ignition包括三个子项目:

 

  • ignition-core——是一个可以直接编译到App中的Android库项目。
  • ignition-support——一个标准的Java库项目,被部署为一个普通的JAR,包含了大部分实用工具类。开发者可以独立使用该工程的核心模块。
  • ignition-location——一个可以直接编译到应用程序中的Android AspectJ库项目。能够让定位应用在不需要Activity位置更新处理的情况下获取到最新的位置信息。

 

详情请参考:ignition Sample applications

30. android_page_curl

android_page_curl是一个在Android上使用OpenGL ES实现类似书本翻页效果的示例程序。(点击链接查看视频演示

31. asmack

说到aSmack,自然要先提提Smack。Smack API是一个完整的实现了XMPP协议的开源API库,而aSmack则是Smack在Android上的构建版本,于2013年2月初迁移到GitHub上,该资源库并不包含太多的代码,只是一个构建环境。开发者可以利用该API进行基于XMPP协议的即时消息应用程序开发。

详情请参考:asmack

32. AndroidBillingLibrary

In-app Billing是一项Google Play服务,能够让你在应用内进行数字内容销售,可销售的数字内容范围非常广,包括媒体文件、照片等下载内容,还包括游戏级别、药剂、增值服务和功能等虚拟内容。Android Billing Library可以实现In-app Billing的所有规范,并提供更高级的类来进行使用。

Google于2012年底正式发布了v3版Android In-app Billing,但截至目前,GitHub上的Android Billing Library还只能支持到v2版,据悉Google将于2013年初对它进行更新。

详情请参考:Google Play In-app Billing

33. Crouton

Crouton是Android上的一个可以让开发者对环境中的Toast进行替换的类,以一个应用程序窗口的方式显示,而其显示位置则由开发者自己决定。

开发者可以直接登陆Google Play下载安装Crouton Demo,查看应用演示。

34. cwac-endless

CommonsWare Android Components(CWAC)是一个开源的Android组件库,用来解决Android开发中各个方面的常见问题,每个 CWAC组件打包成一个独立的jar文件,其中就包含cwac-endless。

cwac-endless提供一个EndlessAdapter,这是一个自动分页的List,当用户浏览到List最后一行时自动请求新的数据。

详情请参考:Commons Ware

35. DiskLruCache

在Android应用开发中,为了提高UI的流畅性、响应速度,提供更高的用户体验,开发者常常会绞尽脑汁地思考如何实现高效加载图片,而DiskLruCache实现正是开发者常用的图片缓存技术之一。Disk LRU Cache,顾名思义,硬件缓存,就是一个在文件系统中使用有限空间进行高速缓存。每个缓存项都有一个字符串键和一个固定大小的值。

点击链接下载该库项目。

36. Android-SlideExpandableListView

如果你对Android提供的Android ExpandableListView并不满意,一心想要实现诸如Spotify应用那般的效果,那么SlideExpandableListView绝对是你最好的选择。该库允许你自定义每个列表项目中的ListView,一旦用户点击某个按钮,即可实现该列表项目区域滑动。

37. gauges-android

Gaug.es for Android是由gaug.es推出的一款在Android设备上对网站流量数据进行实时统计的应用。gauges-android包含了该应用的源代码,开发者可以直接登陆Google Play下载安装该应用。

38. acra

ACRA是一个能够让Android应用自动将崩溃报告以谷歌文档电子表的形式进行发送的库,旨在当应用发生崩溃或出现错误行为时,开发者可以获取到相关数据。

39. roboguice

RoboGuice是Android平台上基于Google Guice开发的一个库,可以大大简化Android应用开发的代码及一些繁琐重复的代码。给Android带来了简单、易用的依赖注入,如果你使用过Spring或Guice的话,你就会知道这种编程方式是多么的便捷。

40. otto

Otto是由Square发布的一个着重于Android支持的基于Guava的强大的事件总线,在对应用程序不同部分进行解耦之后,仍然允许它们进行有效的沟通。

详情请参考:Otto

 

转自:http://www.csdn.net/article/2013-05-06/2815145-Android-open-source-projects-two

分类: android
posted @ 2014-09-26 17:15 小马歌 阅读(229) | 评论 (0)编辑 收藏
 
     摘要: GitHub在中国的火爆程度无需多言,越来越多的开源项目迁移到GitHub平台上。更何况,基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。为此,CSDN特整理了在GitHub平台上最受欢迎的Android及iOS开源项目,以飨开发者。下面,就让我们一起来看看,在GitHub平台上,究竟有哪些Android开源项目最火...  阅读全文
posted @ 2014-09-26 17:14 小马歌 阅读(201) | 评论 (0)编辑 收藏
 
mysql高可用方案MHA介绍
 
概述
 
MHA是一位日本MySQL大牛用Perl写的一套MySQL故障切换方案,来保证数据库系统的高可用.在宕机的时间内(通常10—30秒内),完成故障切换,部署MHA,可避免主从一致性问题,节约购买新服务器的费用,不影响服务器性能,易安装,不改变现有部署。
 
   还支持在线切换,从当前运行master切换到一个新的master上面,只需要很短的时间(0.5-2秒内),此时仅仅阻塞写操作,并不影响读操作,便于主机硬件维护。
 
在有高可用,数据一致性要求的系统上,MHA 提供了有用的功能,几乎无间断的满足维护需要。
 
 优点
 
1        master自动监控和故障转移
 
  在当前已存在的主从复制环境中,MHA可以监控master主机故障,并且故障自动转移。
 
即使有一些slave没有接受新的relay log events,MHA也会从最新的slave自动识别差异的relay log events,并apply差异的event到其他slaves。因此所有的slave都是一致的。MHA秒级别故障转移(9-12秒监测到主机故障,任选7秒钟关闭电源主机避免脑裂,接下来apply差异relay logs,注册到新的master,通常需要时间10-30秒即total downtime)。另外,在配置文件里可以配置一个slave优先成为master。因为MHA修复了slave之间的一致性,dba就不用去处理一致性问题。
 
     当迁移新的master之后,并行恢复其他slave。即使有成千上万的slave,也不会影响恢复master时间,slave也很快完成。
 
      DeNA公司在150+主从环境中用MHA。当其中一个master崩溃,MHA4秒完成故障转移,这是主动/被动集群解决方案无法完成的。
 
2        互动(手动)master故障转移
 
 MHA可以用来只做故障转移,而不监测master,MHA只作为故障转移的交互。
 
3        非交互式故障转移
 
 非交互式的故障转移也提供(不监控master,自动故障转移)。这个特性很有用,特别是你已经安装了其他软件监控master。比如,用Pacemaker(Heartbeat)监测master故障和vip接管,用MHA故障转移和slave提升。
 
4        在线切换master到不同主机
 
 在很多情况下,有必要将master转移到其他主机上(如替换raid控制器,提升master机器硬件等等)。这并不是master崩溃,但是计划维护必须去做。计划维护导致downtime,必须尽可能快的恢复。快速的master切换和优雅的阻塞写操作是必需的,MHA提供了这种方式。优雅的master切换, 0.5-2秒内阻塞写操作。在很多情况下0.5-2秒的downtime是可以接受的,并且即使不在计划维护窗口。这意味着当需要更换更快机器,升级高版本时,dba可以很容易采取动作。
 
5        master crash不会导致主从数据不一致性
 
    当master crash后,MHA自动识别slave间relay logevents的不同,然后应用与不同的slave,最终所有slave都同步。结合通过半同步一起使用,几乎没有任何数据丢失。
 
其他高可用方案
 
6        MHA部署不影响当前环境设置
 
MHA最重要的一个设计理念就是尽可能使用简单。使用与5.0+以上主从环境,其他HA方案需要改变mysql部署设置,MHA不会让dba做这些部署配置,同步和半同步环境都可以用。启动/停止/升级/降级/安装/卸载 MHA都不用改变mysql主从(如启动/停止)。
 
当你需要升级MHA到新版本时,不需要停止mysql,仅仅更新HMA版本,然后重新启动MHAmanger即可。
 
   MHA 支持包含5.0/5/1/5.5(应该也支持5.6,翻译文档时MHA开发者没更新对于5.6版本)。有些HA方案要求特定的mysql版本(如mysqlcluster,mysql with global transaction id 等),而且你可能不想仅仅为了MasterHA而迁移应用。很多情况下,公司已经部署了许多传统的mysql应用,开发或dba不想花太多时间迁移到不同的存储引擎或新的特性(newer bleeding edge distributions 不知道这个是否该这么翻译)。
 
7        不增加服务器费用
 
MHA 包含MHA Manager和MHA node。MHA node运行在每台mysql服务器上,Manager可以单独部署一台机器,监控100+以上master,总服务器数量不会有太大增加。需要注意的是Manager也可以运行在slaves中的一台机器上。
 
8        性能无影响
 
当监控master,MHA只是几秒钟(默认3秒)发送ping包,不发送大的查询。主从复制性能不受影响
 
9        适用任何存储引擎
 
Mysql不仅仅适用于事务安全的innodb引擎,在主从中适用的引擎,MHA都可以适用。即使用遗留环境的mysiam引擎,不进行迁移,也可以用MHA。
 
 
 
 与其他HA方案比较
 
Doing everything manually
Mysql replication 是同步或半同步。当master崩溃时,很有可能一些slave还没有接受最新的relay log events,这意味着每一个slave都相互处在不同的状态。人为修复一致性问题显得不再平凡。没有一致性问题,主从也可能不会启动(如duplicate key error)。花费1个多小时重新启动主从复制显得不同寻常。
 
 
 
Single master and single slave
在单一主从情况下,一些slave 落后与其他slave的情况将不会发生。其中一个master崩溃,可以轻松的让应用转移到一个新的master上面,提供对外服务,故障迁移很简单。
 
 
 
Master, one candidate master, and multiple slaves双主多从
双主多从的架构也很常见。主master挂掉,备用master将接替主master提供服务。某些情况配置为多主架构。
 
 
 
M(RW)-----M2(R)                      M(RW), promoted from M2
 
       |                                          |
 
  +----+----+          --(master crash)-->   +-x--+--x-+
 
 S(R)     S2(R)                             S(?)      S(?)
 
                                          (Fromwhich position should S restart replication?)
 
但是这并不作为master故障转移方案。当前master挂掉,剩余slave不一定接受全部relay log events,修复数据一致性还是问题。
 
 
 
这种架构使用广泛,但是不是所有人都能深刻理解上述问题。当前master挂掉,slave变得不统一或者slave不能从新的master复制数据。
 
也许双master,其中一个master只读,每个master都至少有一个slave也许可能解决问题。
 
         M(RW)--M2(R)
          |      |
        S(R)   S2(R)
 
 
Pacemaker + DRBD
Pecemaker(Heartbeat)+DRBD+Mysql是一个通用方案。但是这个方案也有以下问题
 
1 费用问题,特别是跑大量主从环境。Pecemaker+DRBD是主动/被动的解决方案,因此需要一台被动服务器对外不提供任何应用服务。基本的需要四台mysql服务器,one active master,one passive master,two slaves。
 
2 宕机时间(downtime)。Pacemaker+DRBD是主备集群,主master挂掉,备用master启用。这可能花费长的时间,特别是没有用innodb plugin。即使用innodb plugin,花费几分钟开始在备用master上接受连接也不寻常。另外,因为备用master上数据/文件缓存是空的,恢复时间,热身(填充数据到data buffer pool)花费不可忽视的时间。实践中,需要一台或更多slave提供足够的读服务。在热身时间内,空缓存导致写性能降低
 
3 写问题下降或一致性问题。为了让主动/被动集群真正的工作,每次提交(commit)后,必须刷新事务日志(binary log和innodb log),也就是必须设置innodb-flush-log-at-trx-commit=1,sync-binlog=1。设置sync-binlog=1会降低写性能,因为fsync()函数被序列化(sync-binlog=1,group commit失效)。大部分案例中,不设置sync-binlog=1.如果没有设置sync-binlog=1,活动master crash,新的master(先前被动服务器)可能会丢失一些已经发送到slave的binary log events。假如 master 挂掉,slave A接受到mysqld-bin.000123,位置1500。binlog data刷新到硬盘的位置在1000,那么新的master数据也只能mysqld-bin.000123的1000处,然后在启动时创建一个新的binary log mysqld-bin.000124。如果发生这种情况,slave A不能继续复制,因为新的master 没有mysqld-bin.000123位置1500.
 
 4 复杂。对多数人来说,安装/初始化pacemake和DRBD不是容易的事情。相对于其他案例,初始化DRBD需要重新创建系统分区也不容易。要求dba在DRBD和linux内核层有足够的技能。如果dba执行了一个错误命令(如执行drbdadm–overwrite-data-of-peer primary 在被动节点),那么将会损坏活动的数据。重要的是另外一旦硬盘io层出现问题,多数dba处理这种问题不是容易的。
 
MySQL Cluster
Mysql cluster是真正的高可用解决方案,但是必须得用NDB存储引擎。如果你用innodb,将不能发挥mysql cluster集群优势。
 
Semi-Synchronous Replication
半同步复制大大降低了binlog event仅仅存在于崩溃master上的这种风险。这非常有用的能避免数据丢失。但是半同步不能解决所有一致性问题,只能保证一个(不是所有)slave接受到master端的commit的binlog events,其他slave也许还没有接受全部的binlog events。不能apply不同的binlog events 从新的slave到 其他slave上,也不能保证相互一致性
 
Global Transaction ID
GlobalTransaction ID所要达到的目的跟MHA相同,但它覆盖更多。MHA只是两级复制,但是global transaction id覆盖任何级别的复制环境,即使第两级复制失败,dba也能覆盖第三级。Check Google'sglobal transaction id project for details。
posted @ 2014-08-05 13:45 小马歌 阅读(275) | 评论 (0)编辑 收藏
 

第一种方法:

RENAME database olddbname TO newdbname

这个是5.1.7到5.1.23版本可以用的,但是官方不推荐,会有丢失数据的危险

第二种方法:

1.创建需要改成新名的数据库。 
2.mysqldum 导出要改名的数据库 
3.删除原来的旧库(确定是否真的需要) 
当然这种方法虽然安全,但是如果数据量大,会比较耗时,哎,当时连这种方法都没有想到,真有想死的冲动。

第三种方法:

我这里就用一个脚本,很简单,相信大家都看的懂

#!/bin/bash # 假设将sakila数据库名改为new_sakila # MyISAM直接更改数据库目录下的文件即可  mysql -uroot -p123456 -e 'create database if not exists new_sakila' list_table=$(mysql -uroot -p123456 -Nse "select table_name from information_schema.TABLES where TABLE_SCHEMA='sakila'")  for table in $list_table do     mysql -uroot -p123456 -e "rename table sakila.$table to new_sakila.$table" done

这里用到了rename table,改表名的命令,但是如果新表名后面加数据库名,就会将老数据库的表移动到新的数据库,所以,这种方法即安全,又快速。

posted @ 2014-07-31 10:02 小马歌 阅读(58230) | 评论 (1)编辑 收藏
 
from:http://passover.blog.51cto.com/2431658/732629

在线上环境中我们是采用了tomcat作为Web服务器,它的处理性能直接关系到用户体验,在平时的工作和学习中,归纳出以下七种调优经验。

1. 服务器资源

    服务器所能提供CPU、内存、硬盘的性能对处理能力有决定性影响。
    (1) 对于高并发情况下会有大量的运算,那么CPU的速度会直接影响到处理速度。
    (2) 内存在大量数据处理的情况下,将会有较大的内存容量需求,可以用-Xmx -Xms -XX:MaxPermSize等参数对内存不同功能块进行划分。我们之前就遇到过内存分配不足,导致虚拟机一直处于full GC,从而导致处理能力严重下降。
    (3) 硬盘主要问题就是读写性能,当大量文件进行读写时,磁盘极容易成为性能瓶颈。最好的办法还是利用下面提到的缓存。

2. 利用缓存和压缩

    对于静态页面最好是能够缓存起来,这样就不必每次从磁盘上读。这里我们采用了Nginx作为缓存服务器,将图片、css、js文件都进行了缓存,有效的减少了后端tomcat的访问。

    另外,为了能加快网络传输速度,开启gzip压缩也是必不可少的。但考虑到tomcat已经需要处理很多东西了,所以把这个压缩的工作就交给前端的Nginx来完成。可以参考之前写的《利用nginx加速web访问》。

    除了文本可以用gzip压缩,其实很多图片也可以用图像处理工具预先进行压缩,找到一个平衡点可以让画质损失很小而文件可以减小很多。曾经我就见过一个图片从300多kb压缩到几十kb,自己几乎看不出来区别。

3. 采用集群

    单个服务器性能总是有限的,最好的办法自然是实现横向扩展,那么组建tomcat集群是有效提升性能的手段。我们还是采用了Nginx来作为请求分流的服务器,后端多个tomcat共享session来协同工作。可以参考之前写的《利用nginx+tomcat+memcached组建web服务器负载均衡》。

4. 优化tomcat参数

    这里以tomcat7的参数配置为例,需要修改conf/server.xml文件,主要是优化连接配置,关闭客户端dns查询。

  1. <Connector port="8080"   
  2.            protocol="org.apache.coyote.http11.Http11NioProtocol"  
  3.            connectionTimeout="20000"  
  4.            redirectPort="8443"   
  5.            maxThreads="500"   
  6.            minSpareThreads="20"  
  7.            acceptCount="100" 
  8.            disableUploadTimeout="true" 
  9.            enableLookups="false"   
  10.            URIEncoding="UTF-8" /> 

5. 改用APR库

    tomcat默认采用的BIO模型,在几百并发下性能会有很严重的下降。tomcat自带还有NIO的模型,另外也可以调用APR的库来实现操作系统级别控制。

    NIO模型是内置的,调用很方便,只需要将上面配置文件中protocol修改成org.apache.coyote.http11.Http11NioProtocol,重启即可生效。上面配置我已经改过了,默认的是HTTP/1.1。

    APR则需要安装第三方库,在高并发下会让性能有明显提升。具体安装办法可以参考http://www.cnblogs.com/huangjingzhou/articles/2097241.html。安装完成后重启即可生效。如使用默认protocal就是apr,但最好把将protocol修改成org.apache.coyote.http11.Http11AprProtocol,会更加明确

    在官方找到一个表格详细说明了这三种方式的区别:

  1.                   Java Blocking Connector   Java Nio Blocking Connector   APR/native Connector 
  2.                              BIO                         NIO                       APR 
  3. Classname                AjpProtocol               AjpNioProtocol           AjpAprProtocol 
  4. Tomcat Version           3.x onwards                 7.x onwards              5.5.x onwards 
  5. Support Polling              NO                          YES                       YES 
  6. Polling Size                 N/A                   maxConnections             maxConnections 
  7. Read Request Headers      Blocking                  Sim Blocking                   Blocking 
  8. Read Request Body         Blocking                  Sim Blocking                   Blocking 
  9. Write Response            Blocking                  Sim Blocking                   Blocking 
  10. Wait for next Request     Blocking                  Non Blocking               Non Blocking 
  11. Max Connections        maxConnections              maxConnections             maxConnections 

6. 优化网络

    Joel也明确提出了优化网卡驱动可以有效提升性能,这个对于集群环境工作的时候尤为重要。由于我们采用了linux服务器,所以优化内核参数也是一个非常重要的工作。给一个参考的优化参数:

  1. 1. 修改/etc/sysctl.cnf文件,在最后追加如下内容: 
  2.  
  3. net.core.netdev_max_backlog = 32768 
  4. net.core.somaxconn = 32768 
  5. net.core.wmem_default = 8388608 
  6. net.core.rmem_default = 8388608 
  7. net.core.rmem_max = 16777216 
  8. net.core.wmem_max = 16777216 
  9. net.ipv4.ip_local_port_range = 1024 65000 
  10. net.ipv4.route.gc_timeout = 100 
  11. net.ipv4.tcp_fin_timeout = 30 
  12. net.ipv4.tcp_keepalive_time = 1200 
  13. net.ipv4.tcp_timestamps = 0 
  14. net.ipv4.tcp_synack_retries = 2 
  15. net.ipv4.tcp_syn_retries = 2 
  16. net.ipv4.tcp_tw_recycle = 1 
  17. net.ipv4.tcp_tw_reuse = 1 
  18. net.ipv4.tcp_mem = 94500000 915000000 927000000 
  19. net.ipv4.tcp_max_orphans = 3276800 
  20. net.ipv4.tcp_max_syn_backlog = 65536 
  21.  
  22. 2. 保存退出,执行sysctl -p生效 

7. 让测试说话

    优化系统最忌讳的就是只调优不测试,有时不适当的优化反而会让性能更低。以上所有的优化方法都要在本地进行性能测试过后再不断调整参数,这样最终才能达到最佳的优化效果。

 

补充Bio、Nio、Apr模式的测试结果:

    对于这几种模式,我用ab命令模拟1000并发测试10000词,测试结果比较意外,为了确认结果,我每种方式反复测试了10多次,并且在两个服务器上都测试了一遍。结果发现Bio和Nio性能差别非常微弱,难怪默认居然还是Bio。但是采用apr,连接建立的速度会有50%~100%的提升。直接调用操作系统层果然神速啊,这里强烈推荐apr方式!

 

参考资料:
http://16.199.geisvps.com/bbs/2836/24238.html

posted @ 2014-07-29 10:31 小马歌 阅读(1724) | 评论 (0)编辑 收藏
 
     摘要: 1、MySQL错误日志里出现:140331 10:08:18 [ERROR] Error reading master configuration140331 10:08:18 [ERROR] Failed to initialize the master info structure140331 10:08:18 [Note] Event Scheduler: Loaded 0 events&n...  阅读全文
posted @ 2014-07-25 14:56 小马歌 阅读(22011) | 评论 (0)编辑 收藏
 
     摘要: http://www.oschina.net/question/12_90733作为 MySQL 5.5 和 5.6 性能比较的一部分,我研究了下两个版本默认参数的差异,为了了解差异内容,我使用如下的 SQL 语句分别在 MySQL 5.5 和 5.6 版本进行查询,得出下表(点击图片查看大图):让我们来看看这些差异的配置中最重要的也是影响最大的部分:performance_schema...  阅读全文
posted @ 2014-07-23 15:41 小马歌 阅读(215) | 评论 (0)编辑 收藏
 
     摘要: ZooKeeper是一个分布式开源框架,提供了协调分布式应用的基本服务,它向外部应用暴露一组通用服务——分布式同步(Distributed Synchronization)、命名服务(Naming Service)、集群维护(Group Maintenance)等,简化分布式应用协调及其管理的难度,提供高性能的分布式服务。ZooKeeper本身可以以Standalone模式...  阅读全文
posted @ 2014-07-23 11:57 小马歌 阅读(303) | 评论 (0)编辑 收藏
仅列出标题
共95页: First 上一页 17 18 19 20 21 22 23 24 25 下一页 Last