大型网站构建:二、服务化框架

SOA概述

当我们的网站系统越来越复杂,多个应用并存,业务之间互相依赖,越来越臃肿的时候,我们就要想办法把这些应用拆小了,一些共用的功能可以独立成服务供大家使用。这时候就需要采用服务化的架构,将底层的功能拆分成一个个服务提供给上层应用来调用。

采用服务化后的网站架构如下,在原来的应用和底层的数据库、缓存系统、文件系统等之间增加了服务层。

SOA(Service Oriented Architecture)面向服务架构,将底层功能模块化,一方面方便管理,灵活性很强,能够快速开发,另一方面将功能模块化后隔离,方便将功能解耦,可重用。

服务框架能帮我们对应用进行拆分,完成服务化。上面已经说过常用的SOA框架有dubbo、thrift等,大部分SOA框架的实现原理都大同小异,各自的区别主要在于运营管理,各企业定制化的功能模块。

一个服务框架需要解决的根本问题是客户端通过网络远程调用服务端的接口服务。看似简单,但是真正到细节会有很多的东西需要考虑。服务框架包括了客户端部分和服务端部分,先来看看服务框架为了完成一个请求从客户端到服务端,然后服务端返回给客户端都做了哪些工作。

客户端的工作:

  1. 寻址:获取可用的服务器地址列表
  2. 路由:确定要调用的目标机器
  3. 建立连接
  4. 请求序列化
  5. 发送请求
  6. 接收返回结果
  7. 结果反序列化并返回业务程序

服务端的工作:

  1. 监听等待请求
  2. 接收请求数据
  3. 获取请求中的服务信息(服务名称、方法名称、参数)
  4. 获取服务实例进行调用
  5. 服务调用返回的结果序列化后返回给请求发送端

下面再来细说下客户端和服务端具体是怎么样设计和实现的。

框架客户端的设计实现

1首先需要确定客户端是如何引入和使用的。

一种方案是客户端作为应用的一个依赖包与应用一起打包,框架作为应用的一个库,和应用一起启动。这种方式框架没办法接管classloader,不能做一些隔离以及包的实现替换工作。这种方案无法解决框架依赖的外部jar包与应用自身依赖的jar包之间的冲突问题。

另一种方案是框架作为容器的一部分,或者框架本身就是容器,比较灵活,可以将框架需要的东西加载运行好后再去加载运行应用,不过实现难度也提高了。这种方案能够解决框架依赖的jar包和应用自身依赖的jar包之间存在同一个jar包有不同版本的冲突问题,框架如果作为容器的话可以管控classloader,将框架依赖的类和应用依赖的类都控制在User-Defined Class Loader级别,使用不同的classloader加载控制,这样就实现了隔离,比如OSGi就采用了这种方法。

2寻址和路由

客户端调用服务器端的服务接口就是远程过程调用(RPC,Remote Procedure Call),首先需要选择通信方式。我们要与远程服务端建立连接,首先要知道IP和端口,但是我们是分布式部署,如何知道服务端的IP地址呢?上面的章节中介绍过名称服务的直连方式,服务框架采用的就是这种方式。

客户端框架在启动的时候,与服务注册中心(软负载中心)建立连接(服务注册中心后面会介绍,这里就先当做一个黑盒系统看待),拉取服务端的IP列表,这些IP列表可以缓存到本地用于后面的使用,因为一般不太会变更。框架客户端与服务注册中心需一直保持连接,如果服务端的IP有任何变更都会推送到框架客户端,然后将本地的IP列表进行更新。端口我们可以约定好一个固定的端口。

这里我们就完成了寻址的工作,后面就需要进行路由,将最终需要的那个服务端地址选出来进行请求。路由要考虑的就是集群的负载均衡,常见的有随机、轮询、权重等,也可以参考之前负载均衡的文章,里面介绍了一些常用的负载均衡算法,都可以拿来借鉴。《负载均衡的方案》 除了上面说的常见的路由算法,还可以更细粒度的进行路由,比如根据rpc接口来路由,有的接口耗时快,有的耗时慢,根据不同的接口路由到不同的机器。还可以根据参数、机房、城市等等进行路由,这里只是提供一种思路,不详细介绍,可以根据自己需要的场景进行定制化框架,这也是大公司都自己定制框架的原因。

3序列化与反序列化

这里我们已经找到要调用服务地址了,我们需要把调用远程服务的请求服务名称、方法、参数传给服务端。首先就要把请求对象等序列化成二进制数据,通过网络再传输给服务端,如果只使用Java语言,Java本身就提供序列化反序列化的功能。但是如果要支持跨语言,那么就需要选择其他的序列化反序列化方式了,比如服务端是Java的,但是客户端有的是C++,有的是Java,还有的是PHP。目前比较常用的是Google的protobuf,有些大公司还会定义自己的协议来进行对象序列化。序列化反序列化对CPU等性能影响比较大,而且每次请求都需要这些操作,所以对序列化的方式选择需要特别注意,从易用性、跨语言、性能、数据长度等多个方面综合评估。

这里有点需要注意的是一般序列化和反序列化是比较耗资源的一个过程,尤其是遇到特别复杂的大对象,所以一般不要直接在IO线程里面去序列化的工作,这样会占用网络IO的读写资源,降低了框架的网络性能。

这里说到协议,服务框架的协议分为两部分,一个是通信中的数据报文的协议,另一个是远程调用本身的协议。比如远程调用协议我们使用HTTP协议,数据报文的协议使用XML协议,将请求对象序列化成XML格式报文,通过HTTP请求后拿到的相应体也是XML报文,再解析成自己的结果对象。

4网络通信的处理

网络模型分为BIO、NIO、AIO模式,具体可以参考文章《IO模型》

服务框架这里使用的是NIO的方式,通信的处理分为4部分:IO线程、数据队列、通信对象队列、定时任务。IO线程负责socket的连接,数据的接收和发送。需要发送的数据是放在数据队列中,每个请求线程就不需要直接和网络连接打交道。通信对象队列保存请求线程使用的通信对象,请求线程把数据放入数据队列后生成一个通信对象,请求线程在这个通信对象队列上等待处理结果,远程调用结果返回,IO线程会通知通信对象然后缓存请求线程。定时任务则是检查通信对象队列中的通信对象超时,则让这些通知对象通知等待的请求线程已经超时。

上面说的是同步调用,另外还有几种异步调用方式:

  • oneway:只发送请求,不关心结果,不关心服务端是否收到请求数据。
  • callback:请求线程发送请求后继续执行后面的逻辑,请求收到响应时会进行一个调用,这里会提前设置好回调方法。
  • future:请求后生成Future对象放入队列后继续自己的逻辑,IO线程接收到响应后会将结果放入future,业务自己去监听Future对象。
  • 可靠异步:一般是通过消息中间件来完成。

框架服务端的设计实现

1暴露远程服务

服务端的工作是管理本地的服务,然后根据请求定位到服务进行执行调用。服务端获取请求中的最重要的信息是目标接口、目标方法、参数。所以服务注册中心也是需要这些信息的:

  • 接口定义
  • 接口方法定义
  • 参数定义

当系统启动加载的时候,这些信息封装成服务信息对象,放入本地服务管理中心,与实际服务对象做好映射,然后注册到远程的服务注册查询中心后才能被服务调用者发现。

2服务调用过程

上面说到的序列化和网络通信其实在服务端也是一样的,一般是客户端和服务端都是统一的。整个服务的调用过程是:网络通信->协议解析/反序列化->定位服务->调用服务->结果序列化返回。

这里的网络通信也是在IO线程,反序列化可以考虑单独开个序列化线程,调用服务一定是在工作线程中进行。工作线程一般会一定的线程池,我们可以固定好线程池的线程数量,把所有服务调用排队在线程池中一次调用,也可以使用多个工作线程池,把不同的服务方法放在不同的线程池中,这样就不会出现争抢线程,耗时多的服务占用资源的情况了。

3流控

流控是保证系统稳定的重要方式,每个服务接口都有自己的调用上限的,超过服务接口本身的处理能力,也就是服务过载后,系统可能直接就雪崩了。这时候就需要考虑过载保护,其实过载保护不仅业务上需要考虑,框架上也是需要考虑的,这时候就需要流控了,每个服务接口可以配置自己本身处理能力的上限,也可以对服务接口进行分级,确保优先级高的服务被优先调用。

4服务升级

我们在开发服务的时候,随着时间推移,肯定会有很多逻辑需要变化,如果接口定义不需要变更,服务端自己逻辑修改,客户端可以不感知。但是如果接口定义出现了变化,比如接口返回结果多了个字段,这时客户端就需要感知了。

一般的服务新增方法,结果类新增字段,就比较简单,一般这种序列化比较容易直接向下兼容,客户端只需要感知到新的方法或类定义就可以,比如Java直接更新jar包就可以。可是如果我们要修改参数,那怎么处理呢,可以通过版本号来解决,每个服务接口可以有自己的版本号,新老版本的服务允许出现并存的情况,新系统调用新版本,老系统调用老版本。当然这种方式其实也可以使用一个新方法来替代,本质上是一样的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
zheng项目不仅仅是一个开发架构,而是努力打造一套从前端模板 - 基础框架 - 分布式架构 - 开源项目 - 持续集成 - 自动化部署 - 系统监测 - 无缝升级 的全方位J2EE企业级开发解决方案。 zheng项目是一个基于Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构,它提供了整套公共微服务服务模块:集中权限管理(单点登录)、内容管理、支付中心、用户管理(支持第三方登录)、微信平台、存储系统、配置中心、日志分析、任务和通知等,支持服务治理、监控和追踪,努力为中小型企业打造全方位J2EE企业级开发解决方案。 模块介绍: zheng-common Spring+SpringMVC+Mybatis框架集成公共模块,包括公共配置、MybatisGenerator扩展插件、通用BaseService、工具类等。 zheng-admin 基于bootstrap实现的响应式Material Design风格的通用后台管理系统,zheng项目所有后台系统都是使用该模块界面作为前端展示。 zheng-ui 各个子系统前台thymeleaf模板,前端资源模块,使用nginx代理,实现动静分离。 zheng-upms 本系统是基于RBAC授权和基于用户授权的细粒度权限控制通用平台,并提供单点登录、会话管理和日志管理。接入的系统可自由定义组织、角色、权限、资源等。用户权限=所拥有角色权限合集+用户加权限-用户减权限,优先级:用户减权限>用户加权限>角色权限 zheng-oss 文件存储系统,提供四种方案:阿里云 OSS、腾讯云 COS、七牛云和本地分布式存储。 zheng-api 服务网关,对外暴露统一规范的接口和包装响应结果,包括各个子系统的交互接口、对外开放接口、开发加密接口、接口文档等服务,可在该模块支持验签、鉴权、路由、限流、监控、容错、日志等功能。 zheng-cms 内容管理系统:支持多标签、多类目、强大评论的内容管理,有基本单页展示,菜单管理,系统设置等功能。 zheng-pay 一站式支付解决方案,统一下单接口,支持支付宝、微信、网银等多种支付方式。不涉及业务的纯粹的支付平台。 统一下单(统一下单接口、统一扫码)、订单管理、数据分析、财务报表、商户管理、渠道管理、对账系统、系统监控。 zheng-ucenter 通用用户管理系统, 实现最常用的用户注册、登录、资料管理、个人中心、第三方登录等基本需求,支持扩展次开发。 zheng-wechat-mp 微信公众号管理平台,除实现官网后台自动回复、菜单管理、素材管理、用户管理、消息群发等基础功能外,还有维码推广、营销活动、微网站、会员卡、优惠券等。 zheng-wechat-app 微信小程序后台 zheng-message 基于Netty实现SocketIO的实时推送系统。支持命名空间、进制数据、SSL、ACK等功能。 环境搭建 开发工具: MySql:数据库 jetty:开发服务器 Tomcat:应用服务器 SVN|Git:版本管理 Nginx:反向代理服务器 Varnish:HTTP加速器 IntelliJ IDEA:开发IDE PowerDesigner:建模工具 Navicat for MySQL:数据库客户端 开发环境: Jdk7+ Mysql5.5+ Redis Zookeeper ActiveMQ Dubbo-admin Dubbo-monitor

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值