当前位置: 首页 > 科技观察

架构设计:一个远程调用服务的设计理念(zookeeper的一个应用实践)

时间:2023-03-19 14:38:38 科技观察

在深入学习zookeeper的过程中,介绍一个zookeeper相关的应用实例。我将这个实例命名为远程调用服务。通过本应用实例的描述,我们将对zookeeper的应用场景有更深入的了解。远程调用是系统间的一种通信机制,另一种理解就是进程间通信。对于分布式系统的开发,远程调用技术是其核心技术。远程调用技术可以将一组计算机系统组成一个网络系统,对外提供整体服务,再由这组计算机系统组成一个更大的、性能更高的计算机系统。在之前的博客中,我介绍了一个分布式网站架构设计,其中有一个使用netty技术编写的组件,作为前端系统和服务器端系统之间的通信媒介。大型互联网公司会有很多这样的网站系统。如果每个网站都按照我博客中描述的方式开发,那么系统通信的维护和管理,以及每个系统的网络资源分配和管理都会造成严重的问题。某个问题,对于这样的问题,我给大家举个例子,大家可能比较清楚。例如,一家互联网公司有几个对外提供服务的网站。有的网站访问量大,有的访问量比较小,但是公司的宽带资源是有限的,所以我们希望对这些资源进行动态的管理和分配。如果我们网站和网站的通信功能紧耦合,那么分配这些资源的工作就会复杂繁琐,而且很容易出问题。这样的问题还有很多,这里就不做详细分析了。做软件开发,有一个原则。如果某个功能是通用的,需要统一管理,就应该把这个功能抽离成一个独立的系统或组件,给这个系统或组件一些增强。系统的功能特性必将提高整个系统的健壮性、可用性和效率。而我在分布式网站中描述的通信技术是一种远程调用技术。远程调用技术是客户端与服务器之间的通信技术。可以看成是cs架构技术的一种。有很多优秀的框架实现了远程调用,比如java自带的RMI,spring自带的Httpinvoker,webservice技术等等。但是,现有的这些技术并不能满足互联网公司的远程调用需求。今天就来说说自己构想的一套远程调用技术。这是基于我们公司一些类似软件的实践。该框架主要针对java,其他语言目前暂不支持。首先,我想总结一下远程调用技术应该包括哪些技术,它们是:通信技术:远程调用是通过网络技术形成一个完整的系统,所以通信技术是重点。我这里选择的通信技术是netty技术,Netty提供了一个异步的、事件驱动的web应用框架和工具,用于快速开发高性能、高可靠的web服务器和客户端程序。Netty会让我们开发通信程序变得简单高效,效率也非常的好。同时,它还支持多种不同的网络协议。序列化和反序列化技术:Java的序列化技术是指将对象转换为字节数据,可以还原为java对象。这个恢复过程就是反序列化。这种机制可以自动处理不同操作系统之间的差异,比如window下序列化的对象,可以在linux上重新构建。Java的jdk自带序列化和反序列化机制。熟悉Hadoop的都知道,Hadoop设计了一套序列化和反序列化机制。为什么Hadoop的作者不选择使用Java自带的序列化机制呢?这是因为java序列化机制非常复杂,带来了低效率,java序列化机制的另一个重要缺点是它序列化的二进制数据会非常大,因为java序列化会包含太多关于对象的信息,过多的数据体积会影响网络传输的效率,所以hadoop设计了一套序列化和反序列化机制,hadoop不同节点之间的通信也是一种远程调用机制,所以我们发现一个好的序列化和反序列化技术对于远程调用来说是相当重要的。我们公司的远程调用框架序列化技术有两种,一种是java自带的序列化和反序列化机制,一种是hessian技术,这是一种比较高效的序列化和反序列化技术。压缩技术:对于网络节目来说,最稀缺的资源就是宽带资源。如果传输的数据太大,那么数据压缩就会很重要。这里推荐一个压缩技术snappy,它是一种高效的压缩解压包,是Google内部广泛使用的一种压缩技术。高并发技术:远程调用技术必须是多线程的,这样才能满足多个并发处理请求。Java在1.5版本提供了Executor框架,在线程开发中引入了任务的概念,使得多线程程序开发会更加合理和可控。执行器技术可以看一本经典的书《java并发编程实践》。要让线程更高效,池技术也是必不可少的。Apache的common-pool是一个很好的池技术。我们可以预先创建线程,放到common-pool中管理。非侵入式:这也可以称为松散耦合。对于javaweb开发,最好的解耦方式就是使用spring技术。当我们在系统中引入远程调用框架并配置好相关参数后,就可以使用了。远程调用的方法定义在spring的配置文件中,所以在程序中调用的时候,使用spring直接获取bean,那么远程调用的开发与在action中调用服务器的方法没有区别。以下是示例代码:负载均衡:分布式系统离不开负载均衡,好的负载均衡可以充分利用不同服务器的计算资源提供并发和计算能力系统的功率。对于网站(我们公司网站服务器不是太多),10台以下的服务器可以采用两种策略:一种是简单的round-robin比如有6台服务器,我们会把第一个请求发给第一个服务器,第二个请求发给第二个服务器,以此类推。6台服务器循环完成后,我们再从第一台服务器开始;第二种是随机方法,即使用随机函数。当然,对于更多的服务器,我不知道哪种轮询机制更好。希望有了解的童鞋能给我推荐一下我这里设计的远程调用框架。除了以上功能,我希望它还可以有心跳管理机制,超时管理机制,服务分类管理,也就是说可以根据服务的重要性或者系统的繁忙程度来调整网络资源。哈哈,说了这么久,可能童鞋们有点烦了。不就是用zookeeper的例子吗?怎么还没看到zookeeper的影子呢。别着急,动物园管理员要上场了。先从我之前博客写的分布式网站说起。服务器系统可以看作是服务提供者,前端系统可以看作是服务调用者。提供者可以比作商家,调用者可以比作客户。商户与客户可以直接进行交易。这种直接交易的方式非常原始,甚至有风险。现代社会商家与顾客的直接交易是非常有效率的。为了更安全、更高效,我设计的分布式框架最大的特点是提供了一个市场化的角色来管理服务提供者和服务调用者。我将这个功能模块称为远程调用管理组件。远程调用管理组件是这个框架的核心。它的主要功能是接收服务器提供商的注册通知。通知一般是接口,接口的实现类,服务器的IP地址。管理组件会将这些通知记录下来,并根据配置对这些服务程序进行分组标记,注册的信息管理组件将信息推送给服务调用者。远程调用管理组件还包括心跳机制。这种心跳机制是针对服务提供者的。通过心跳机制检测服务提供者的健康状态。管理组件不会检测服务调用者的健康状态,因为这是不必要的,因为框架使用或调用者直接请求提供者。从逻辑上讲,不需要关心调用者的状态。这和bs架构中的浏览器是一样的。我们不会关心浏览器用户是否存在。服务提供者、服务调用者和远程调用管理组件的关系如下图所示:远程调用框架的运行过程是:服务提供者启动时,会把自己的ip地址和注册的方法传给远程调用管理组件,管理组件接收到注册信息后会存储注册信息。存储技术是使用zookeeper。存储成功后,管理组件会将成功通知返回给服务提供者。同时,管理组件也会通过心跳检测服务提供者是否健康;服务调用者启动时会向管理组件请求服务提供者信息,管理组件收到请求后将相关信息推送给服务调用者。实际系统运行时,服务调用者直接与服务提供者进行通信。通讯方式是netty。如果调用者和提供者之间有相关变化,会先通知服务管理组件,由服务管理组件推送相关变化信息。到相应的系统。远程调用管理组件主要是通过zookeeper实现的。Zookeeper有一个层次化的命名空间。它的模型是一个树结构。树结构是一种强大的数据类型,几乎可以存储所有不同的数据类型。我们使用zookeeper保存这些信息,方便我们管理整个远程调用框架。同时zookeeper的可靠性还是很高的。我在之前的zookeeper文章中提到过这个,保证了整个远程调用框架的稳定性。在实际应用中,我们会将components编译成一个jar包,不同的项目直接引用这个jar包,从而实现管理组件服务器与服务提供者和调用者的连接。至于提供者和调用者之间的通信机制,是直接进行的,因为我们把通信程序集成在jar包里,只是把相应的管理机制抽取出来给外部服务器统一管理。这是我设计的远程调用框架。不幸的是,我还没有真正实现这个想法。今天拿出来反映zookeeper的实际应用,为我后面讲解zookeeper做铺垫。至于是否可行,看以后有没有机会开发类似的系统,估计会有很多意想不到的问题需要解决。(远程调用服务的设计参考了我的技术朋友马德信的设计,他曾是淘宝的技术架构师)