架构设计:远程调用服务架构设计及zookeeper技术详解(上篇)

1、序言

     Hadoop是一个技术生态圈,zookeeperhadoop生态圈里一个很是重要的技术,当我研究学习hadoop的相关技术时候,有两块知识曾经让我十分的困惑,一个是hbase,一个就是zookeeperhbase的困惑源自于它在颠覆了我对数据库建模的理解,而zookeeper的困惑倒是我没法理解它究竟是干吗的。javascript

    前不久我结合我了解的一种远程调用服务的设计来帮助我理解zookeeper在实际的生产中运用,该文章的地址是:html

    http://www.cnblogs.com/sharpxiajun/p/3297852.html前端

   其实这篇文章写完后,我本身感受并非太好,由于写本文的时候,对远程调用服务的设计以及zookeeper的理解都不是很到位,可是这篇文章仍是受到了你们很大的关注,被博客园做为了推荐文章,还有不少网友但愿我写一篇更加详尽的文章,还有童鞋留言说这个设计方案和淘宝开源的dubbohsf相似。我相信你们的关注就是意味着这个主题是当下技术的热点,因此今天我要写一篇主题和上篇文章同样的博文,这是上篇文章的升级版,无论是远程调用服务仍是zookeeper我都会给出更加详尽的讲解。不过这里仍是要说明下,这篇文章里远程服务的设计我仍是没有参照dubbo,由于最近实在太忙,没有时间研究淘宝的dubbo,可是我但愿学习过dubbo的童鞋能够帮我对比下个人方案和dubbo的区别,区别就会产生新的问题,也会有新的知识须要研究。java

   本文是该主题的上篇,主要是讲解远程调用服务的相关知识,下篇则是根据远程调用服务架构设计中zookeeper的相关应用方法详细讲解关于zookeeper的知识。正则表达式

 

2、远程调用服务的架构设计总述

    首先咱们要再深刻理解下为何应用软件服务里须要一个远程调用服务,远程调用服务解决了软件设计中的什么问题,它的架构设计又有什么理论根据了?spring

    我曾写了一篇关于分布式网站架构设计的文章,文章地址是:数据库

    http://www.cnblogs.com/sharpxiajun/archive/2013/05/11/3072798.html缓存

    在文章开头我就把这个新的网站架构方案和传统的企业软件的B/S架构做了对比,我将一个网站里提供业务服务的组件抽象为独立的服务系统,接收用户信息的逻辑部分抽象为前端系统,服务系统和前端系统使用netty这样的通信组件进行通信,而到了讲解远程调用服务的框架设计时候我将netty通信组件进一步抽象为一个通信独立系统及远程调用服务,这就是为何要设计远程调用服务的缘起了,远程调用服务又会带来了网站架构的升级,若是传统的企业B/S架构为1.0版,我将前端和业务服务端分离为独立系统则是2.0版,那么引入了远程调用服务网站就是3.0版了,3.0版的架构带来的好处就是能够将N多的前端系统和N多的业务服务端系统融为一个总体,网站的规模会愈来愈大,提供的服务也会愈来愈多,这既避免重复造轮子的问题还使得网站规模愈来愈大。服务器

   3.0版本的网站架构带来了新的网站架构总图,以下所示:网络

 

     有了远程调用服务,咱们能够作到业务级别的集群,例如:一个制造企业,通常都会有采购业务,生产业务、销售业务以及财务业务,按照传统的思路咱们都会给每一个业务独立开发一个系统,若是引用了远程调用服务,咱们能够将这些业务都作成独立的服务,这些服务组成业务集群,而这些服务都是用统一的远程调用服务做为操做的入口,换句话说无论什么样的服务对于调用者来讲都是统一的,这样前端的调用者能够作到应用的统一,所谓的应用的统一淘宝网站是最典型的表明,咱们在一个同一的网站里能够操做各类不一样的应用,而不会发生由于应用的不一样咱们就得从新访问新的地址或者从新登陆到另一个系统里作其余业务的操做。而服务端这边,彻底能够摆脱传统的客户端和服务端耦合的开发,加强了整个服务端的专业性和稳定性,这样更易于服务端的扩展性和可维护性。若是服务端之间也须要相互调用也能够经过远程调用服务实现,因为远程调用服务的统一性,这样就避免了服务调用之间报文和调用方式的不统一,规范了整个开发的流程。若是远程调用服务还有负载均衡功能,整个服务集群就变成了一个私有的云,因此说远程调用服务是云计算的重要组成部分,这个说法一点都不为过。

    远程调用服务的理论依据是什么,这个问题的表述可能有点问题,其实我要讲的是远程调用服务的技术原型就是SOAService-Oriented Architecture),在云计算出现前,SOA曾一度是IT的技术热点,虽然以后不少人说中国的SOA作的一点很差,就和早年的DHTML同样,诟病远多于赞扬,写本文时候我在京东里搜索了下SOA,从书籍的出版日期和书籍评价数就能够看出SOA已经有点无人问津的凄凉了。下面我要简单介绍下SOASOA的定义:

     SOA是一个软件架构,它包含四个关键概念:应用程序前端、服务、服务库和服务总线。一个服务包含一个合约、一个或多个接口以及一个实现。

   应用程序前端能够理解为我上面所讲述的调用者和前端系统,服务库能够理解为服务集群,这里还有个服务是什么呢?服务就是调用者和服务提供者完成某一个特定业务的合约,换句话说就是封装的业务规则,打个比方,咱们在淘宝去购物,下订单,付款,查物流,肯定付款这些操做在服务端都有独立的服务提供,可是从购物这个概念去理解,这些独立的服务才能构成这个完整的购物行为,若是其中有地方出了问题,会有相应不一样的操做,那么这个就绝对不是调用者简单调用服务接口的问题,须要更高层次的业务封装,将上面这些操做封装为一个统一的服务,这个就是所谓的服务。最后一个要素服务总线,这就是咱们本文所谈的重要主题:远程调用服务了。

   这里谈谈SOA的目的是想起到抛砖引玉的做用,让那些想深刻研究远程调用服务的人能够从SOA的角度理解远程调用服务,而那些仍是不明白远程调用是何物的童鞋能够经过SOA的概念来理解远程调用服务。

 

3、远程调用服务技术详解

     远程调用服务技术详解,详解,呜呜~~,这两个字颇有压力,我怕有童鞋看了这个标题会觉得我会将整套技术实现方案写到里面,这个难度过高了,写几万字估计都说不清楚,再说真的写的那么细致,估计不少人都看不懂了(嘿嘿,我本身也没有技术实现过哦,这些都是构思,构思哦),因此详解就是详解原理。

     下面我将上篇文章的架构图放进来,你们再仔细看看这张图:

 

     传统的服务调用都是服务提供者和服务调用者的直接调用,从架构图里咱们看到这里多了一个远程调用管理组件,远程调用管理组件是一个独立的服务系统,为了保证该系统的稳定性,它也必定是一个分布式的系统,可是这个分布式系统和Web的分布式系统是彻底不一样的分布式系统,传统Web应用集群是基于HTTP协议的无状态的特色设计的,由于每一个HTTP请求都是一个独立的事务,不一样请求之间是没有任何关系的,因此咱们能够将Web应用部署到不一样服务器上,请求无论到了那台服务器,都能正常的给用户提供相应的服务,可是Web应用的session机制是有状态的,因此传统Web集群都是要有session同步的操做,大型网站每每会把session功能抽象为独立的缓存系统,可是这里的远程调用管理组件的集群原理或者说分布式原理是有别于Web应用集群分布式原理的,远程调用管理组件能够当作一个注册中心,它会记录下服务提供者和服务调用者的相关信息,并将这些信息推送给服务提供者或者服务调用者,为了保证系统的执行效率,这些注册信息都是记录在内存里,咱们试想下,若是这些注册信息丢失,整个系统将会不可用,所以远程调用管理组件的集群是一种保证数据可靠性和服务提供健壮性的集群,而不是创建在HTTP无状态特性基础上的集群。咱们这里假想下远程调用服务的集群运行场景,咱们假若有5台服务器做为远程调用服务运行的服务器,那么每台服务器都必须有注册信息的冗余备份,当服务运行时候其中一台服务器发生了故障,这台故障的服务器上的数据不会丢失,此外集群应该还要有一个检查故障的机制,当发现有台服务器不可用的时候,能及时剔除该服务器,而zookeeper就是解决这种问题的技术框架。此外除了保证系统的稳定性和可用性外,集群的数据存储方式也是很重要的,前面我讲到集群的数据存储要有一个冗余机制,除了冗余机制还要有一个很适合快速访问和读写的数据模型,而zookeeper正好包含这种数据模型,因此我设计的远程调用服务是一个很适合zookeeper应用的场景,至于zookeeper的详细知识我会在下篇里详细讲到。

     远程调用管理组件还有一个心跳机制,心跳机制的做用是检测服务提供者的健康性及服务提供者是否可用,服务提供者启动时候会将本身的注册信息发送给远程调用管理组件,这个注册信息里包含服务端的ip地址和端口号,远程调用管理组件会启动一个线程,根据定时对这个ip地址和端口号去ping这个ip和端口号对应的应用是否可用,若是不可用远程调用管理组件会反复尝试几回,这个次数和多久检测心跳都是能够配置的,若是反复几回仍是不通,那么就认定该服务不可用了。有网友在QQ上问我,为何不检测服务调用者的心跳,这个彻底不必哦,调用者是主动方,提供者是被动方,这就比如你访问网站,若是你生病了不去访问了,系统没有必要检查你是否已经生病了。

     远程调用框架须要使用序列化和反序列化技术,这点也让不少童鞋不太理解,不理解的缘由仍是对序列化和反序列化技术的不理解,序列化技术主要是应用与数据持久化(数据存到硬盘)或者网络通信,无论是数据存储到硬盘仍是进行网络通信,这些数据都会转化为二进制,序列化就是将正在运行的对象转化为能够存储和传输的二进制数据,而反序列化是能够将这些二进制数据反向还原成原来的对象信息,还原的对象仍是能够被程序操做的,而咱们设计的远程调用框架传递就是不一样系统之间能够相互使用的程序代码,因此咱们须要使用序列化和反序列化技术。这里就有一个问题,例如咱们传输一个对象,这个对象对应的类是N多个类的继承子类,并且这个对象里可能还会引用其余的对象例如StringArrayList等等,那么为了让反序列化的对象可用,序列化的时候就会将这些信息也包含在二进制数据里,而且这些信息一块儿进行网络传输,这就致使数据传输量特别大,而jdk自带的序列化机制会致使这些附带信息更大,因此有必要使用比jdk更好的序列化机制,让数据量变小,而且序列化和反序列化的效率更高,上篇文章里我推荐了一种序列化框架hession,固然用户想使用什么序列化机制这个我也让用户能够本身配置,这也是外部配置文件的一个选项。

    前面文章里我还讲到了压缩技术而且推荐了google公司使用snappy,这个压缩技术也是为了让网络传输的数据量变小,提高网络的传输效率。

    对于服务提供者和服务调用者我会提供一个jar包,这些工程都要引入这个jar包,同时还须要一个配置文件来定义一些须要用户定义的参数,例如咱们使用一个名字叫ycdy_config.properties配置文件,里面的key值介绍以下:

Config_center_url=ip:port;这个就是配置远程管理中心的ip地址和端口号;

Server_type=provider/consumer;配置是服务调用者仍是提供者,不配置默认是提供者;

Provider_post=9999,这是服务提供者的端口号,调用者能够不配置,其实调用配置了也没啥用,若是提供者不配置,会有默认值的;

Provider_session_timeout=9000;服务提供者的超时时间,若是实际调用超过了这个时间,那么说明服务调用超时,适用于服务调用者,提供者无效

Tick_time=3000;心跳时间,这是远程调用中心检测服务端心跳的间隔时间,适用于服务提供者;

Again_time=3;当服务提供者不通的时候,心跳反复检测的次数,超过了次数就标记该服务不可用;Provider_session_timeout、Tick_time和Again_time三者之间是有必定关系,这个关系要实现这具体把控了;

Ip_include_pattern=172\\.17\\.138.*|192\\.168\\.0\\..*,这个适用于服务提供者,由于一台服务器可能存在多个ip地址,当远程调用服务组件接收到提供者的ip,用这个配置项来辨认那个ip可用,这里采用正则表达式的方式;

Ip_exclude_pattern=用于服务提供者,须要忽略的ip;

Consumer_policy=random/rotate;适用于调用者,调用者向提供者请求的负载均衡策略,我熟悉的只有两种一种是使用随机数,一种使轮询,因此这里目前就这两种选项;

Monitor_log=true/false;是否开启监控日志,适用于服务提供者,任何系统日志时最重要的,不然无法查生产问题,其实这个配置项应该能够充实点,可是我如今还没想好,因此先给个提示,具体到了生产看如何实现吧。

  

     你们看到了无论是做为服务提供者仍是服务调用者使用的配置文件是一致的,并且一个应用既能够配置成服务的调用者也能够配置成服务的提供者,很是的灵活。

     远程调用服务还须要一个重要的技术就是通信技术,这里的通信技术我推荐nettynetty是个很是好的选择,讲到通信是个复杂的课题,若是之后有空我再作详细介绍,通信层的东西是封装到服务提供者和服务调用者引入的jar包里,可是通信的ip地址和端口号则是须要远程调用管理组件推送过来的。

     那么在应用里远程调用服务到底如何使用了?哈哈,这时候spring就要上场了,咱们看看服务调用者和服务提供者的spring配置,以下所示:

 

<!-- 服务提供者配置 -->
<bean id="serverProvider" class="cn.com.sharpxiajun.RmifSpringProviderBean">
    <property name="interfaceName" value="cn.com.ITest"></property><!-- 远程调用的接口 -->
    <property name="target" ref="clsTest"></property><!-- clsTest实现ITest的实现类,clsTest这里是一个bean的id值 -->
</bean>

<!-- 服务调用者配置 -->
<bean id="clientConsumer" class="cn.com.sharpxiajun.RmifSpringConsumerBean">
    <property name="interfaceName" value="cn.com.ITest"></property><!-- value就是Provider定义的target的接口实现类 -->
    <property name="serialType" value="hessian"></property><!--序列化方式  -->
    <property name="compressEnabled" value="true"></property><!-- 压缩标记 -->
</bean>

 

     咱们发现这个新配置和之前不一样了,这个配置将更加适合生成的开发。

     咱们首先看看serverProvider的设计,这个bean对应的classcn.com.sharpxiajun.RmifSpringProviderBean,里面有个参数是一个interfaceName即提供者对外的接口,这里我会使用反射机制将接口注入到RmifSpringProviderBean,而target则是具体的实现对象了,这就是业务对象,注意interfaceName必定要是接口,由于调用者会根据接口进行转化,若是是类的话,那么通用性就不好了。

     clientConsumer的设计,这个bean所对应的classcn.com.sharpxiajun.RmifSpringConsumerBean,其中interfaceNamevalue值对应的就是远程定义的接口,和提供者的interfaceName保持一致,当提供者的数据传导调用者后,就会根据这个双方约定好的接口反序列化成能够操做的对象,serialType是选择序列化机制,不写的话就是调用jdk的序列化机制,这里附带提下啊,外部的序列化程序也是放到jar包里的哦,还有一个选项是compressEnabled做用是是否启用传输报文压缩。

    当调用者调用提供者服务时候,jar包里netty程序会根据推送的信息(主要是ip,端口)和spring配置的bean结合起来就能够完成一次服务的调用。

   好了,上篇写好了,本篇主要是讲解远程调用服务的架构设计,我自我感受这篇文章比上篇更接地气,但愿看了本文的童鞋,能对远程调用框架设计的原理更加清晰。

   其实netty的使用学问也很大,也是远程调用服务的核心之一,本文这块讲的比较少,之后有时间我尽可能补充上这块知识。

   下篇文章我将详细介绍远程调用框架里使用到的zookeeper技术。

   这是2013年的最后一篇博文了,祝你们新年快乐哦。

相关文章
相关标签/搜索