欢迎访问中国领先的企业技术服务平台贤集网

当前位置:首页行业资讯问答 — 正文

什么是中间件,它起到了什么作用?请教了~

请教大神,什么是中间件,它起到了什么作用?能不能详细给解释一下


什么是中间件,它起到了什么作用?请教了~

注:文章内的所有配图皆为网络转载图片,侵权即删!

最新回答

中间件(middleware)是基础软件的一大类,属于可复用软件的范畴。顾名思义,中间件处于操作系统软件与用户的应用软件的中间。中间件在操作系统、网络和数据库之上,应用软件的下层,总的作用是为处于自己上层的应用软件提供运行与开发的环境,帮助用户灵活、高效地开发和集成复杂的应用软件。


消息队列属于大型网站系统中间件的重要组件,主要解决了应用耦合、异步消息、流量削峰等问题,对于网站实现高性能、可伸缩的架构有很大的帮助。



消息中间件介绍、典型使用场景、以及使用原则


大型分布式架构里一定会涉及到消息中间件,常用的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ。


一、kafka


1、不完全符合jms规范,注重吞吐量,类似udp 和 tcp


2、一般做大数据吞吐的管道 我们现在的用途就是负责在各个idc之间通信


3、量大对数据不是百分之百保证的,会有数据丢失,不是百分百送达(amq和rmq等有重发机制,而kafka没有);在吞吐量有提升 ,在这方面就得有牺牲, 所以kafka适合大数据量流转, 比如日志数据 比如用作统计的数据。


二、activeMQ


ActiveMQ居于两者之间,类似于ZemoMQ,它可以部署于代理模式和P2P模式。类似于RabbitMQ,它易于实现高级场景,而且只需付出低消耗。它被誉为消息中间件的“瑞士军刀”。


三:RocketMQ(阿里官方指定消息中间件)


RocketMQ 是一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。同时,广泛应用于多个领域,包括异步通信解耦、企业解决方案、金融支付、电信、电子商务、快递物流、广告营销、社交、即时通信、移动应用、手游、视频、物联网、车联网等。


消息中间件使用的典型场景优四个


1.典型的异步处理


2.应用解耦


3.流量削锋


4.消息通讯四个场景


比如:今日头条的私信就是一个典型的消息通讯场景,因为消息通讯的数据不需要即使立即同步回来,不算是核心数据,可以延时通过异步的消息发送,这样可以降低系统的负荷。


所以,我们在架构设计的时候,有一个原则就是:消息原则上都是异步消息发送,除非涉及到交易的情况才考虑数据即使同步,否则能异步的都采用异步消息设计。


再比如:流量削锋的典型场景就有阿里的双11秒杀、团购抢购活动等。


应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。


a、可以控制活动的人数


b、可以缓解短时间内高流量压垮应用


用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面。


秒杀业务根据消息队列中的请求信息,再做后续处理。


总结:


1.消息中间件的四个典型场景:典型的异步处理、应用解耦、流量削锋、消息通讯四个场景。


2.能异步就不要同步:能异步的消息原则都尽量采用异步的方式。


3.如果消息性能要求高,用rocketMQ与kafka可以更优,rocketMQ与kafka 比较就看技术选型了,各有利弊,看业务需要。


4.实现语言来看,RabbitMQ(阿里官方指定消息中间件)最高,原因是它的实现语言是天生具备高并发高可用的erlang语言。综合来看,RabbitMQ是首选。


5.典型的秒杀活动、抢购、消息通讯、邮件发送、电话短信等都是典型的采用消息中间件的业务场景。


来源:mikechen的互联网架构


中间件(middleware)是一种特殊的软件。它可以从一个软件系统中提取数据,然后转化为合适的格式,让另一个系统能够直接使用。因此,中间件是不同软件之间的接口。它常被比作“翻译官”,或是连接软件的“胶水”。


图1:中间件(黑色部分)有利于软件之间的互联


中间体的作用


中间件的一大重要作用是促进软件(模块)之间的互连互通。例如,在供应链管理的过程中,同一企业内的不同软件系统(MES、TMS、WMS等)经常存在格式不兼容的现象,因此需要中间件来起整合作用,如图1中的黑色部分所示。在应用了中间件之后,不同系统之间可以实现自动通讯,让信息快速流动– 这是实现精益的必要条件。


当各个软件系统打通之后,数据可以集成到ERP中央数据库中,以便于实现工厂管理或供应链管理的整体优化。类似的功能对于跨公司合作也是非常重要的:供应链上的各个环节必须充分地共享数据,实现信息透明化,才能最大限度地消除”牛鞭效应”,提高合作效率。


图2:中间件(黑色部分)有利于实现软件产品的模块化开发


中间件的另一大作用是简化软件产品的开发。在企业中,可以把软件开发活动的输出结果分为底层(算法)、中间层(模块)和顶层(产品)。软件企业经常遇到的困境是:产品开发人员(与市场、客户接触较多)的编程知识有限,而一线程序员又缺乏市场意识或产品意识。


应用中间件的理想状态是:能够将底层算法封装成一定的模块(例如物流管理模块、财务结算模块等)。业务人员不需要懂得底层技术细节,只需要象“搭积木”一样将中间件进行排列组合,就能开发出产品。这样减轻了业务部门的负担,让他们能够以更快的速度对市场需求作出响应,从而增强企业的竞争力。


史上最全数据库中间件详解


主要从数据库拆分过程及挑战、主流数据库中间件设计方案、读写分离核心要点、分库分表核心要点展开说明。


1. 数据库拆分过程及挑战


互联网当下的数据库拆分过程基本遵循的顺序是:垂直拆分、读写分离、分库分表(水平拆分)。每个拆分过程都能解决业务上的一些问题,但同时也面临了一些挑战。


1.1 垂直拆分


对于一个刚上线的互联网项目来说,由于前期活跃用户数量并不多,并发量也相对较小,所以此时企业一般都会选择将所有数据存放在一个数据库 中进行访问操作。举例来说,对于一个电商系统,其用户模块和产品模块的表刚开始都是位于一个库中。



其中:user、useraccount表属于用户模块,productcategory、product表属于产品模块。


刚开始,可能公司的技术团队规模比较小,所有的数据都位于一个库中。随着公司业务的发展,技术团队人员也得到了扩张,划分为不同的技术小组,不同的小组负责不同的业务模块。例如A小组负责用户模块,B小组负责产品模块。此时数据库也迎来了第一次拆分:垂直拆分。


这里的垂直拆分,指的是将一个包含了很多表的数据库,根据表的功能的不同,拆分为多个小的数据库,每个库包含部分表。下图演示将上面提到的db_eshop库,拆分为db_user库和db_product库。



通常来说,垂直拆分,都是根据业务来对一个库中的表进行拆分的。关于垂直拆分,还有另一种说法,将一个包含了很多字段的大表拆分为多个小表,每个表包含部分字段,这种情况在实际开发中基本很少遇到。


垂直拆分的另一个典型应用场景是服务化(SOA)改造。在服务化的背景下,除了业务上需要进行拆分,底层的存储也需要进行隔离。 垂直拆分会使得单个用户请求的响应时间变长,原因在于,在单体应用的场景下,所有的业务都可以在一个节点内部完成,而垂直拆分之后,通常会需要进行RPC调用。然后虽然单个请求的响应时间增加了,但是整个服务的吞吐量确会大大的增加。


1.2 读写分离


随着业务的不断发展,用户数量和并发量不断上升。这时如果仅靠单个数据库实例来支撑所有访问压力,几乎是在 自寻死路 。以产品库为例,可能库中包含了几万种商品,并且每天新增几十种,而产品库每天的访问了可能有几亿甚至几十亿次。数据库读的压力太大,单台mysql实例扛不住,此时大部分 Mysql DBA 就会将数据库设置成 读写分离状态 。也就是一个 Master 节点(主库)对应多个 Salve 节点(从库)。可以将slave节点的数据理解为master节点数据的全量备份。



master节点接收用户的写请求,并写入到本地二进制文件(binary log)中。slave通过一个I/O线程与Master建立连接,发送binlog dump指令。Master会将binlog数据推送给slave,slave将接收到的binlog保存到本地的中继日志(relay log)中,最后,slave通过另一个线程SQL thread应用本地的relay log,将数据同步到slave库中。


关于mysql主从复制,内部包含很多细节。例如binlog 格式分为statement、row和mixed,binlog同步方式又可以划分为:异步、半同步和同步。复制可以基于binlogFile+position,也可以基于GTID。通常,这些都是DBA负责维护的,业务RD无感知。


在DBA将mysql配置成主从复制集群的背景下,开发同学所需要做的工作是:当更新数据时,应用将数据写入master主库,主库将数据同步给多个slave从库。当查询数据时,应用选择某个slave节点读取数据。



1.2.1 读写分离的优点


这样通过配置多个slave节点,可以有效的避免过大的访问量对单个库造成的压力。


1.2.1 读写分离的挑战


对于DBA而言,多了很多集群运维工作


例如集群搭建、主从切换、从库扩容、缩容等。例如master配置了多个slave节点,如果其中某个slave节点挂了,那么之后的读请求,我们应用将其转发到正常工作的slave节点上。另外,如果新增了slave节点,应用也应该感知到,可以将读请求转发到新的slave节点上。


对于开发人员而言


基本读写分离功能:对sql类型进行判断,如果是select等读请求,就走从库,如果是insert、update、delete等写请求,就走主库。


主从数据同步延迟问题:因为数据是从master节点通过网络同步给多个slave节点,因此必然存在延迟。因此有可能出现我们在master节点中已经插入了数据,但是从slave节点却读取不到的问题。对于一些强一致性的业务场景,要求插入后必须能读取到,因此对于这种情况,我们需要提供一种方式,让读请求也可以走主库,而主库上的数据必然是最新的。


事务问题:如果一个事务中同时包含了读请求(如select)和写请求(如insert),如果读请求走从库,写请求走主库,由于跨了多个库,那么本地事务已经无法控制,属于分布式事务的范畴。而分布式事务非常复杂且效率较低。因此对于读写分离,目前主流的做法是,事务中的所有sql统一都走主库,由于只涉及到一个库,本地事务就可以搞定。


感知集群信息变更:如果访问的数据库集群信息变更了,例如主从切换了,写流量就要到新的主库上;又例如增加了从库数量,流量需要可以打到新的从库上;又或者某个从库延迟或者失败率比较高,应该将这个从库进行隔离,读流量尽量打到正常的从库上


1.3 分库分表


经过垂直分区后的 Master/Salve 模式完全可以承受住难以想象的高并发访问操作,但是否可以永远 高枕无忧 了?答案是否定的,一旦业务表中的数据量大了,从维护和性能角度来看,无论是任何的 CRUD 操作,对于数据库而言都是一件极其耗费资源的事情。即便设置了索引, 仍然无法掩盖因为数据量过大从而导致的数据库性能下降的事实 ,因此这个时候 Mysql DBA 或许就该对数据库进行 水平分区 (sharding,即分库分表 )。经过水平分区设置后的业务表,必然能够将原本一张表维护的海量数据分配给 N 个子表进行存储和维护。


水平分表从具体实现上又可以分为3种:只分表、只分库、分库分表,下图展示了这三种情况:



只分表:


将db库中的user表拆分为2个分表,user_0和user_1,这两个表还位于同一个库中。适用场景:如果库中的多个表中只有某张表或者少数表数据量过大,那么只需要针对这些表进行拆分,其他表保持不变。


只分库:


将db库拆分为db_0和db_1两个库,同时在db_0和db_1库中各自新建一个user表,db_0.user表和db_1.user表中各自只存原来的db.user表中的部分数据。


分库分表:


将db库拆分为db_0和db_1两个库,db_0中包含user_0、user_1两个分表,db_1中包含user_2、user_3两个分表。下图演示了在分库分表的情况下,数据是如何拆分的:假设db库的user表中原来有4000W条数据,现在将db库拆分为2个分库db_0和db_1,user表拆分为user_0、user_1、user_2、user_3四个分表,每个分表存储1000W条数据。



1.3.1 分库分表的好处


如果说读写分离实现了数据库读能力的水平扩展,那么分库分表就是实现了写能力的水平扩展。


存储能力的水平扩展


在读写分离的情况下,每个集群中的master和slave基本上数据是完全一致的,从存储能力来说,在存在海量数据的情况下,可能由于磁盘空间的限制,无法存储所有的数据。而在分库分表的情况下,我们可以搭建多个mysql主从复制集群,每个集群只存储部分分片的数据,实现存储能力的水平扩展。


写能力的水平扩展


在读写分离的情况下,由于每个集群只有一个master,所有的写操作压力都集中在这一个节点上,在写入并发非常高的情况下,这里会成为整个系统的瓶颈。


而在分库分表的情况下,每个分片所属的集群都有一个master节点,都可以执行写入操作,实现写能力的水平扩展。此外减小建立索引开销,降低写操作的锁操作耗时等,都会带来很多显然的好处。


1.3.2 分库分表的挑战


分库分表的挑战主要体现在4个方面:基本的数据库增删改功能,分布式id,分布式事务,动态扩容,下面逐一进行讲述。


挑战1:基本的数据库增删改功能


对于开发人员而言,虽然分库分表的,但是其还是希望能和单库单表那样的去操作数据库。例如我们要批量插入四条用户记录,并且希望根据用户的id字段,确定这条记录插入哪个库的哪张表。例如1号记录插入user1表,2号记录插入user2表,3号记录插入user3表,4号记录插入user0表,以此类推。sql如下所示:


这样的sql明显是无法执行的,因为我们已经对库和表进行了拆分,这种sql语法只能操作mysql的单个库和单个表。所以必须将sql改成4条如下所示,然后分别到每个库上去执行。


具体流程可以用下图进行描述:



解释如下:


sql解析:首先对sql进行解析,得到需要插入的四条记录的id字段的值分别为1,2,3,4


sql路由:sql路由包括库路由和表路由。库路由用于确定这条记录应该插入哪个库,表路由用于确定这条记录应该插入哪个表。


sql改写:因为一条记录只能插入到一个库中,而上述批量插入的语法将会在 每个库中都插入四条记录,明显是不合适的,因此需要对sql进行改写,每个库只插入一条记录。


sql执行:一条sql经过改写后变成了多条sql,为了提升效率应该并发的到不同的库上去执行,而不是按照顺序逐一执行


结果集合并:每个sql执行之后,都会有一个执行结果,我们需要对分库分表的结果集进行合并,从而得到一个完整的结果。


挑战2:分布式id


在分库分表后,我们不能再使用mysql的自增主键。因为在插入记录的时候,不同的库生成的记录的自增id可能会出现冲突。因此需要有一个全局的id生成器。目前分布式id有很多中方案,其中一个比较轻量级的方案是twitter的snowflake算法。


挑战3:分布式事务


分布式事务是分库分表绕不过去的一个坎,因为涉及到了同时更新多个分片数据。例如上面的批量插入记录到四个不同的库,如何保证要么同时成功,要么同时失败。关于分布式事务,mysql支持XA事务,但是效率较低。柔性事务是目前比较主流的方案,柔性事务包括:最大努力通知型、可靠消息最终一致性方案以及TCC两阶段提交。但是无论XA事务还是柔性事务,实现起来都是非常复杂的。


挑战4:动态扩容


动态扩容指的是增加分库分表的数量。例如原来的user表拆分到2个库的四张表上。现在我们希望将分库的数量变为4个,分表的数量变为8个。这种情况下一般要伴随着数据迁移。例如在4张表的情况下,id为7的记录,7%4=3,因此这条记录位于user3这张表上。但是现在分表的数量变为了8个,而7%8=0,而user0这张表上根本就没有id=7的这条记录,因此如果不进行数据迁移的话,就会出现记录找不到的情况。本教程后面将会介绍一种在动态扩容时不需要进行数据迁移的方案。


1.4 小结


在上面我们已经看到了,读写分离和分库分表带来的好处,但是也面临了极大的挑战。如果由业务开发人员来完成这些工作,难度比较大。因此就有一些公司专门来做一些数据库中间件,对业务开发人员屏蔽底层的繁琐细节,开发人员使用了这些中间件后,不论是读写分离还是分库分表,都可以像操作单库单表那样去操作。


下面,我们将介绍 主流的数据库中间件设计方案和实现。


2 主流数据库中间件设计方案


数据库中间件的主要作用是向应用程序开发人员屏蔽读写分离和分库分表面临的挑战,并隐藏底层实现细节,使得开发人员可以像操作单库单表那样去操作数据。在介绍分库分表的主流设计方案前,我们首先回顾一下在单个库的情况下,应用的架构,可以用下图进行描述:



可以看到在操作单库单表的情况下,我们是直接在应用中通过数据连接池(connection pool)与数据库建立连接,进行读写操作。而对于读写分离和分库分表,应用都要操作多个数据库实例,在这种情况下,我们就需要使用到数据库中间件。


2.1 设计方案


典型的数据库中间件设计方案有2种:proxy、smart-client。下图演示了这两种方案的架构:



可以看到不论是proxy还是smart-client,底层都操作了多个数据库实例。不论是分库分表,还是读写分离,都是在数据库中间件层面对业务开发同学进行屏蔽。


2.1.1 proxy模式


我们独立部署一个代理服务,这个代理服务背后管理多个数据库实例。而在应用中,我们通过一个普通的数据源(c3p0、druid、dbcp等)与代理服务器建立连接,所有的sql操作语句都是发送给这个代理,由这个代理去操作底层数据库,得到结果并返回给应用。在这种方案下,分库分表和读写分离的逻辑对开发人员是完全透明的。


优点:


1 多语言支持。也就是说,不论你用的php、java或是其他语言,都可以支持。以mysql数据库为例,如果proxy本身实现了mysql的通信协议,那么你可以就将其看成一个mysql 服务器。mysql官方团队为不同语言提供了不同的客户端却动,如java语言的mysql-connector-java,python语言的mysql-connector-python等等。因此不同语言的开发者都可以使用mysql官方提供的对应的驱动来与这个代理服务器建通信。


2 对业务开发同学透明。由于可以把proxy当成mysql服务器,理论上业务同学不需要进行太多代码改造,既可以完成接入。


缺点:


1 实现复杂。因为proxy需要实现被代理的数据库server端的通信协议,实现难度较大。通常我们看到一些proxy模式的数据库中间件,实际上只能代理某一种数据库,如mysql。几乎没有数据库中间件,可以同时代理多种数据库(sqlserver、PostgreSQL、Oracle)。


2 proxy本身需要保证高可用。由于应用本来是直接访问数据库,现在改成了访问proxy,意味着proxy必须保证高可用。否则,数据库没有宕机,proxy挂了,导致数据库无法正常访问,就尴尬了。


3 租户隔离。可能有多个应用访问proxy代理的底层数据库,必然会对proxy自身的内存、网络、cpu等产生资源竞争,proxy需要需要具备隔离的能力。


2.1.2 smart-client模式


业务代码需要进行一些改造,引入支持读写分离或者分库分表的功能的sdk,这个就是我们的smart-client。通常smart-client是在连接池或者driver的基础上进行了一层封装,smart-client内部与不同的库建立连接。应用程序产生的sql交给smart-client进行处理,其内部对sql进行必要的操作,例如在读写分离情况下,选择走从库还是主库;在分库分表的情况下,进行sql解析、sql改写等操作,然后路由到不同的分库,将得到的结果进行合并,返回给应用。


优点:


1 实现简单。proxy需要实现数据库的服务端协议,但是smart-client不需要实现客户端通信协议。原因在于,大多数据数据库厂商已经针对不同的语言提供了相应的数据库驱动driver,例如mysql针对java语言提供了mysql-connector-java驱动,针对python提供了mysql-connector-python驱动,客户端的通信协议已经在driver层面做过了。因此smart-client模式的中间件,通常只需要在此基础上进行封装即可。


2 天然去中心化。smart-client的方式,由于本身以sdk的方式,被应用直接引入,随着应用部署到不同的节点上,且直连数据库,中间不需要有代理层。因此相较于proxy而言,除了网络资源之外,基本上不存在任何其他资源的竞争,也不需要考虑高可用的问题。只要应用的节点没有全部宕机,就可以访问数据库。(这里的高可用是相比proxy而言,数据库本身的高可用还是需要保证的)


缺点:


1 通常仅支持某一种语言。例如tddl、zebra、sharding-jdbc都是使用java语言开发,因此对于使用其他语言的用户,就无法使用这些中间件。如果其他语言要使用,那么就要开发多语言客户端。


2 版本升级困难。因为应用使用数据源代理就是引入一个jar包的依赖,在有多个应用都对某个版本的jar包产生依赖时,一旦这个版本有bug,所有的应用都需要升级。而数据库代理升级则相对容易,因为服务是单独部署的,只要升级这个代理服务器,所有连接到这个代理的应用自然也就相当于都升级了。


2.2 业界产品


无论是proxy,还是smart-client,二者的作用都是类似的。以下列出了这两种方案目前已有的实现以及各自的优缺点:



proxy实现


目前的已有的实现方案有:


阿里巴巴开源的cobar


阿里云上的drds


mycat团队在cobar基础上开发的mycat


mysql官方提供的mysql-proxy


奇虎360在mysql-proxy基础开发的atlas(只支持分表,不支持分库)


当当网开源的sharing-sphere


目前除了mycat、sharing-sphere,其他几个开源项目基本已经没有维护,sharing-sphere前一段时间已经进去了Apache 软件基金会孵化器。


smart-client实现


目前的实现方案有:


阿里巴巴开源的tddl,已很久没维护


大众点评开源的zebra,大众点评的zebra开源版本代码已经很久没有更新,不过最近美团上市,重新开源大量内部新的功能特性,并计划长期维持。


当当网开源的sharding-jdbc,目前算是做的比较好的,文档资料比较全。和sharding-sphere一起进入了Apache孵化器。


蚂蚁金服的zal


等等


3 读写分离核心要点


3.1 基本路由功能


基本路由路功能主要是解决,在读写分离的情况下,如何实现一些基本的路由功能,这个过程通常可以通过下图进行描述:



3.1.1 sql类型判断


主要是判断出来sql是读还是写sql,将读sql到从库上去执行,写sql去主库上执行


write语句:insert、update、delete、create、alter、truncate…


query语句:select、show、desc、explain…


3.1.2 强制走主库


有的时候,对于一些强一致性的场景,需要写入后,必须能读取到数据。由于主从同步存在延迟,可能会出现主库写入,而从库查不到的情况。这次时候,我们需要使用强制走主库的功能。具体实现上有2种方案:hint 或API


hint,就是开发人员在sql上做一些特殊的标记,数据库中间件识别到这个标记,就知道这个sql需要走主库,如:


/*master*/select * from table_xx


这里的/*master*/就是一个hint,表示需要走主库。不同的数据库中间件强制走主库的hint可能不同,例如zebra的hint为/*zebra:w+*/,hint到底是什么样是无所谓的,其作用仅仅就是一个标记而已。之所以将hint写在/*…*/中,是因为这是标准的sql注释语法。即使数据库中间件未能识别这个hint,也不会导致sql语法错误。


api:主要是通过代码的方式来添加sql走主库的标识,hint通常只能加在某个sql上。如果我们希望多个sql同时都走主库,也不希望加hint,则可以通过api的方式,其内部主要利用语言的thread local线程上下文特性,如:


ForceMasterHelper.forceMaster()    //…执行多条sqlForceMasterHelper.clear()


在api标识范围内执行的sql,都会走主库。具体API到底应该是什么样,如何使用,也是由相应的数据库中间件来决定的。


特别的,对于一些特殊的sql,例如 select last_insert_id;或者select @@identity等,这类sql总是需要走主库。这些sql是要获得最后一个插入记录的id,插入操作只可能发生在主库上。


3.2 从库路由策略


通常在一个集群中,只会有一个master,但是有多个slave。当判断是一个读请求时,如何判断选择哪个slave呢?


一些简单的选择策略包括:


随机选择(random)


按照权重进行选择(weight)


或者轮训(round-robin)



特别的,对于一些跨IDC(数据中心)部署的数据库集群,通常需要有就近路由的策略,如下图:



图中,在IDC2部署了一个master,在IDC1和IDC2各部署了一个slave,应用app部署在IDC1。显然当app接收到一个查询请求时,应该优先查询与其位于同一个数据中心的slave1,而不是跨数据中心去查询slave2,这就是就近路由的概念。


当然一个数据中心内,可能会部署多个slave,也需要进行选择,因此就近路由通常和一些基本的路由策略结合使用。另外,对于就近路由,通常也会有一个层级,例如同机房、同中心、同区域、跨区域等。


3.3 HA、Scalable相关


数据库中间件除了需要具备上述提到的读写分离功能来访问底层的数据库集群。也需要一套支持高可用、动态扩展的体系:


从HA的角度来说,例如主库宕机了,那么应该从从库选择一个作为新的主库。开源的MHA可以帮助我们完成这个事;然而,MHA只能在主库宕机的情况下,完成主从切换,对于仅仅是一个从库宕机的情况下,MHA通常是无能为力的。因此,通常都会在MHA进行改造,使其支持更多的HA能力要求。


从Scalable角度来说,例如读qps实在太高,需要加一些从库,来分担读流量。


事实上,无论是HA,还是Scalable,对于数据库中间件(不论是proxy或者smart-client)来说,只是配置信息发生了变更。


因此,通常我们会将所有的配置变更信息写到一个配置中心,然后配置心中监听这个配置的变更,例如主从切换,只需要把最新的主从信息设置到配置中心;增加从库,把新从库ip、port等信息放到配置中心。数据库中间件通过对这些配置信息变更进行监听,当配置发生变更时,实时的应用最新的配置信息即可。


因此,一个简化的数据库中间件的高可用架构通常如下所示:



监控服务对集群进行监控,当发生变更时,将变更的信息push到配置中心中,数据库中间件(proxy或smart-client)接收到配置变更,应用最新的配置。而整个过程,对于业务代码基本是无感知的。


对于配置中心的选择,有很多,例如百度的disconf、阿里的diamond、点评开源的lion、携程开源的apollo等,也可以使用etcd、consul。通常如果没有历史包袱的话,建议使用携程开源的apollo。


特别需要注意的一点是,通常监控服务监控到集群信息变更,推送到配置中心,再到数据库中间件,必然存在一些延迟。对于一些场景,例如主从切换,没有办法做到彻底的业务无感知。当然,对于多个从库中,某个从库宕机的情况下,是可以做到业务无感知的。例如,某个从库失败,数据库中间件,自动从其他正常的从库进行重试。


另外,上图中的HA方案强依赖于配置中心,如果某个数据库集群上建立了很多库,这个集群发生变更时,将会存在大量的配置信息需要推送。又或者,如果数据库集群是多机房部署的,在某个机房整体宕机的情况下(例如光纤被挖断了,或者机房宕机演练),也会存在大量的配置信息需要推送。如果配置中心,推送有延迟,业务会有非常明显的感知。


因此,通常我们会在客户端进行一些轻量级的HA保障。例如,根据数据库返回异常的sqlstate和vendor code,判断异常的严重级别,确定数据库实例能否正常提供服务,如果不能正常提供服务,则自动将其进行隔离,并启动异步线程进行检测数据库实例是否恢复。


最后,很多数据库中间件,也会提供一些限流和降级的功能,计算sql的唯一标识(有些称之为sql指纹),对于一些烂sql,导致数据库压力变大的情况,可以实时的进行拦截,直接抛出异常,不让这些sql打到后端数据库上去。


4 分库分表核心要点


从业务开发的角度来说,其不关心底层是否是分库分表了,其还是希望想操作单个数据库实例那样编写sql,那么数据库中间件就需要对其屏蔽所有底层的复杂逻辑。


下图演示了一个数据库表(user表)在分库分表情况下,数据库中间件内部是如何执行一个批量插入sql的:



数据库中间件主要对应用屏蔽了以下过程:


sql解析:首先对sql进行解析,得到抽象语法树,从语法树中得到一些关键sql信息


sql路由:sql路由包括库路由和表路由。库路由用于确定这条记录应该操作哪个分库,表路由用于确定这条记录应该操作哪个分表。


sql改写:将sql改写成正确的执行方式。例如,对于一个批量插入sql,同时插入4条记录。但实际上用户希望4个记录分表存储到一个分表中,那么就要对sql进行改写成4条sql,每个sql都只能插入1条记录。


sql执行:一条sql经过改写后可能变成了多条sql,为了提升效率应该并发的去执行,而不是按照顺序逐一执行


结果集合并:每个sql执行之后,都会有一个执行结果,我们需要对分库分表的结果集进行合并,从而得到一个完整的结果。


4.1 SQL解析


用户执行只是一条sql,并传入相关参数。数据库中间件内部需要通过sql解析器,对sql进行解析。可以将sql解析,类比为xml解析,xml解析的最终结果是得到一个document对象,而sql解析最终得到一个抽象语法树(AST)。通过这个语法树,我们可以很简单的获取到sql的一些执行,例如当前执行的sql类型,查询了那些字段,数据库表名,where条件,sql的参数等一系列信息。


通常来说,对于sql解析,内部需要经过词法(lex)解析和语法(Syntax)解析两个阶段,最终得到一个语法树。



SQL解析器的内部实现原理对业务同学是屏蔽的,业务同学也感知不到。一些数据库中间件采用了第三方开源的sql解析器,也有一些自研sql解析器。例如mycat、zebra采用的都是druid解析器,shard-jdbc一开始也用的是druid解析器,后面自研了解析器。目前较为流行的sql解析器包括:


FoundationDB SQL Parser


Jsqlparser


Druid SQL Parser


其中,其中Fdbparser和jsqlparser都是基于javacc实现的。


mycat团队曾经做过一个性能测试,druid解析器的解析性能通常能达到基于javacc生成的sql解析器10~20倍。本人也进行过类似的测试,得出的结论基本一致。


如何对比不同的sql解析器的好坏呢?主要是考虑以下两点:


解析性能:druid最好。


druid采用的是预测分析法,它只需要从字符的第一个到最后一个遍历一遍,就同时完成了词法解析和语法解析,语法树也已经构造完成。


数据库方言:druid支持的最多。


SQL-92、SQL-99等都是标准SQL,mysql/oracle/pg/sqlserver/odps等都是方言,sql-parser需要针对不同的方言进行特别处理。Druid的sql parser是目前支持各种数据语法最完备的SQL Parser。


注:这里说的仅仅是基于Java实现的SQL解析器,druid是比较好的。大部分同学可能知道druid是一个为监控而生的连接池,事实上,druid另一大特性,就是它的SQL解析器。很多开源的数据库中间件,例如zebra、sharding-jdbc等,都使用了druid解析器。(sharding-jdbc后来自研了解析器)。虽然SQL解析是druid的一大亮点,不过github上也因为SQL解析的bug,收到了不少issue。


4.2 SQL路由


路由规则是分库分表的基础,其规定了数据应该按照怎样的规则路由到不同的分库分表中。对于一个数据库中间件来说,通常是支持用户自定义任何路由规则的。路由规则本质上是一个脚本表达式,数据库中间件通过内置的脚本引擎对表达式进行计算,确定最终要操作哪些分库、分表。常见的路由规则包括哈希取模,按照日期等。


下图展示了user表进行分库分表后(2个分库,每个分库2个分表),并如何根据id进行路由的规则:



路由分则分为:


库规则:用于确定到哪一个分库


表规则:用于确定到哪一个分表


在上例中,我们使用id来作为计算分表、分表,因此把id字段就称之为路由字段,或者分区字段。


需要注意的是,不管执行的是INSERT、UPDATE、DELETE、SELECT语句,SQL中都应该包含这个路由字段。否则,对于插入语句来说,就不知道插入到哪个分库或者分表;对于UPDATE、DELETE、SELECT语句而言,则更为严重,因为不知道操作哪个分库分表,意味着必须要对所有分表都进行操作。SELECT聚合所有分表的内容,极容易内存溢出,UPDATE、DELETE更新、删除所有的记录,非常容易误更新、删除数据。因此,一些数据库中间件,对于SQL可能有一些限制,例如UPDATE、DELETE必须要带上分区字段,或者指定过滤条件。


4.3 SQL改写


前面已经介绍过,如一个批量插入语句,如果记录要插入到不同的分库分表中,那么就需要对SQL进行改写。 例如,将以下SQL


insert into user(id,name) values (1,”tianshouzhi”),(2,”huhuamin”), (3,”wanghanao”),(4,”luyang”)


改写为:


insert into user_1(id,name) values (1,”tianshouzhi”)insert into user_2(id,name) values (2,”huhuamin”)insert into user_3(id,name) values (3,”wanghanao”)insert into user_0(id,name) values  (4,”luyang”)


这里只是一个简单的案例,通常对于INSERT、UPDATE、DELETE等,改写相对简单。比较复杂的是SELECT语句的改写,对于一些复杂的SELECT语句,改写过程中会进行一些优化,例如将子查询改成JOIN,过滤条件下推等。因为SQL改写很复杂,所以很多数据库中间件并不支持复杂的SQL(通常有一个支持的SQL),只能支持一些简单的OLTP场景。


当然也有一些数据库中间件,不满足于只支持OLTP,在迈向OLAP的方向上进行了更多的努力。例如阿里的TDDL、蚂蚁的Zdal、大众点评的zebra,都引入了apache calcite,尝试对复杂的查询SQL(例如嵌套子查询,join等)进行支持,通过过滤条件下推,流式读取,并结合RBO(基于规则的优化)、CBO(基于代价的优化)来对一些简单的OLAP场景进行支持。


4.4 SQL执行


当经过SQL改写阶段后,会产生多个SQL,需要到不同的分片上去执行,通常我们会使用一个线程池,将每个SQL包装成一个任务,提交到线程池里面并发的去执行,以提升效率。



这些执行的SQL中,如果有一个失败,则整体失败,返回异常给业务代码。


4.5 结果集合并


结果集合并,是数据库中间件的一大难点,需要case by case的分析,主要是考虑实现的复杂度,以及执行的效率问题,对于一些复杂的SQL,可能并不支持。例如:


对于查询条件:大部分中间件都支持=、IN作为查询条件,且可以作为分区字段。但是对于NIT IN、BETWEEN…AND、LIKE,NOT LIKE等,只能作为普通的查询条件,因为根据这些条件,无法记录到底是在哪个分库或者分表,只能全表扫描。


聚合函数:大部分中间件都支持MAX、MIN、COUNT、SUM,但是对于AVG可能只是部分支持。另外,如果是函数嵌套、分组(GROUP BY)聚合,可能也有一些数据库中间件不支持。


子查询:分为FROM部分的子查询和WHERE部分的子查询。大部分中对于子查询的支持都是非常有限,例如语法上兼容,但是无法识别子查询中的分区字段,或者要求子查询的表名必须与外部查询表名相同,又或者只能支持一级嵌套子查询。


JOIN:对于JOIN的支持通常很复杂,如果做不到过滤条件下推和流式读取,在中间件层面,基本无法对JOIN进行支持,因为不可能把两个表的所有分表,全部拿到内存中来进行JOIN,内存早就崩了。当然也有一些取巧的办法,一个是Binding Table,另外一个是小表广播(见后文)。


分页排序:通常中间件都是支持ORDER BY和LIMIT的。但是在分库分表的情况下,分页的效率较低。例如对于limit 100,10 ORDER BY id。表示按照id排序,从第100个位置开始取10条记录。那么,大部分数据库中间件实际上是要从每个分表都查询110(100+10)条记录,拿到内存中进行重新排序,然后取出10条。假设有10个分表,那么实际上要查询1100条记录,而最终只过滤出了10记录。因此,在分页的情况下,通常建议使用"where id > ? limit 10”的方式来进行查询,应用记住每次查询的最大的记录id。之后查询时,每个分表只需要从这个id之后,取10条记录即可,而不是取offset + rows条记录。


关于JOIN的特属说明:


Binding Table:


适用于两个表之间存在关联关系,路由规则相同。例如,有user表和user_account表,由于user_account与user表强关联,我们可以将这两个表的路由规则设置为完全一样,那么对于某个特定用户的信息,其所在的user分表和user_account分表必然唯一同一个分库下,后缀名相同的分表中。在join时,某一个分库内的join,就可以拿到这个用户以及账号的完整信息,而不需要进行跨库join,这样就不需要把用户的数据库拿到内存中来进行join。



小表广播:


小表广播通常是某一个表的数据量比较少, 例如部门表department。另外一个表数据量比较大,例如user。此时user需要进行分库分表,但是department不需要进行分库分表。为了达到JOIN的目的,我们可以将 department表在每个分库内都实时同步一份完整的数据。这样,在JOIN的时候,数据库中间件只需要将分库JOIN的结果进行简单合并即可。


下图演示了小表广播的流程,用户在更新department表时,总是更新分库db0的department表,同步组件将变更信息同步到其他分库中。



注:图中的同步组件指的是一般是伪装成数据库的从库,解析源库binlog,插入目标库。有一些开源的组件,如canal、puma可以实现这个功能,当然这些组件的应用场景非常广泛,不仅限于此。笔者曾写过一个系列的canal源码解析文章,目前完成了大部分。


4.6 二级索引


通常情况下,分库分表的时候,分区字段只有一个。例如对于用户表user,按照user_id字段进行分区,那么之后查询某个用户的信息,只能根据user_id作为分区字段。使用其他字段,则需要扫描所有分表,效率很低。但是又有根据其他字段查询某个用户信息的需求,例如根据手机号phone_id。


此时,我们可以将按照user_id插入的数据,进行一份全量拷贝。通过同步组件,重新按照phone_id插入到另一个分库分表集群中,这个集群就成为二级索引,或者叫辅维度同步。此后,对于根据user_id的操作,就在原来的分库分表集群中进行操作;根据phone_id的操作,就到二级索引集群中去进行操作。


需要注意的是,对于更新操作,只能操作原集群,二级索引集群只能执行查询操作。原集群的增量数据变更信息,实时的通过同步组件,同步到二级索引集群中。



注:这是一个很常见的面试题。阿里的一些面试官,比较喜欢问。一些面试者,可能自己想到了这个方案,因为考虑到这样比较浪费资源,就自行排除了。事实上,这点资源相对于满足业务需求来说,都不是事。


4.7 分布式id生成器


在分库分表的情况下,数据库的自增主键已经无法使用。所以要使用一个分布式的id生成器。分布式事务id生成器要满足以下条件:唯一、趋势递增(减少落库时的索引开销)、高性能、高可用。


目前主流的分布式id生成方案都有第三方组件依赖,如:


基于zk


基于mysql


基于缓存


twitter的snowflake算法是一个完全去中心化的分布式id算法,但是限制workid最多能有1024,也就是说,应用规模不能超过1024。虽然可以进行细微的调整,但是总是有数量的限制。


另外,美团之前在github开源了一个leaf组件,是用于生成分布式id的,感兴趣的读者可以研究一下。


这里提出一种支持动态扩容的去中心化分布式id生成方案,此方案的优势,除了保证唯一、趋势递增,没有第三方依赖,支持存储的动态扩容之外,还具有以下优势:


支持按照时间范围查询,或者 时间范围+ip查询,可以直接走主键索引;


每秒的最大序列id就是某个ip的qps等


12位日期+10位IP+6位序列ID+4位数据库扩展位


其中:


12位日期:格式为yyMMddHHmmss,意味着本方案的id生成策略可以使用到2099年,把时间部分前置,从而保证趋势递增。


10位ip:利用ip to decimal算法将12位的ip转为10进制数字。通过ip地址,来保证全局唯一。如果ip地址被回收重复利用了,也不用担心id的唯一性,因为日期部分还在变化。


6位序列id:意味着每秒最多支持生成100百万个id(0~999999)。不足6位前置补0,如000123。


4位数据库扩展位:为了实现不迁移数据的情况下,实现动态扩容,其中2位表示DB,2位表示TB,最多可扩容到10000张表。假设每张表存储1000万数据,则总共可以支持存储1000亿条数据。


关于数据库扩展位实现动态扩容图解:



首先明确一点,路由策略始终根据数据库最后四位,确定某一条记录要到哪个分库的哪个分表中。例如xxxx0001,意味着这条记录肯定是在00分库的01分表上。


接着,就要在id的生成策略上做文章。


假设初始状态为两个分库db_00,db_01,每个分库里面有10张分表,tb_00~tb_09。此时,业务要保证生成id的时候,始终保证db的两位在00~01之间,tb的两位始终在00~09之间。路由策略根据这些id,可以找到正确的分库分表。


现在需要扩容到10个分库,每个分表10个分表。那么DBA首先将新增的分库:db_02~db_09创建好,每个分库里面再创建10个分表:tb_01~tb_09。业务同学在此基础上,将id生成策略改成:db的两位在00~09之间,tb的两位规则维持不变(只是分库数变了,每个分库的分表数没变)。而由于路由从策略是根据最后四位确定到哪个分库,哪个分表,当这些新的分库分表扩展位id出现时,自然可以插入到新的分库分表中。也就实现了动态扩容,而无需迁移数据。


当然,新的分库分表中,一开始数据是没有数据的,所以数据是不均匀的,可以调整id扩展位中db和tb生成某个值的概率,使得落到新的分库分表中的概率相对大一点点(不宜太大),等到数据均匀后,再重新调整成完全随机。


此方案的核心思想是,预分配未来的可能使用到的最大资源数量。通常,100个分库,每个分库100张分表,能满足绝大部分应用的数据存储。如果100个分库都在不同的mysql实例上,假设每个mysql实例都是4T的磁盘,那么可以存储400T的数据,基本上可以满足绝大部分业务的需求。


当然,这个方案不完美。如果超过这个值,这种方案可能就不可行了。然而,通常一个技术方案,可以保证在5~10年之间不需要在架构上做变动,应该就算的上一个好方案了。如果你追求的是完美的方案,可能类似于TIDB这种可以实现自动扩容的数据库产品更适合,不过目前来说,TIDB等类似产品还是无法取代传统的关系型数据库的。说不定等到5~10年后,这些产品更成熟了,你再迁移过去也不迟。


4.7 分布式事务


在分库分表的情况下,由于操作多个分库,此时就涉及到分布式事务。例如执行一个批量插入SQL,如果记录要插入到不同的分库中,就无法保证一致性。因此,通常情况下,数据库中间件,只会保证单个分库的事务,也就是说,业务方在创建一个事务的时候,必须要保证事务中的所有操作,必须最终都在一个分库中执行。


事实上,在微服务的架构下,事务的问题更加复杂,如下图



Service A在执行某个操作时,需要操作数据库,同时调用Service B和Service C,Service B底层操作的数据库是分库分表的,Service C也要操作数据库。


这种场景下,保证事务的一致性就非常麻烦。一些常用的一致性算法如:paxios协议、raft协议也无法解决这个问题,因为这些协议都是资源层面的一致性。在微服务架构下,已经将事务的一致性上升到了业务的层面。


如果仅仅考虑分库分表,一些同学可能会想到XA,但是性能很差,对数据库的版本也有要求,例如必须使用mysql 5.7,官方还建议将事务隔离级别设置为串行化,这是无法容忍的。


由于分布式事务的应用场景,并不是仅仅分库分表,因此通常都是会有一个专门的团队来做分布式事务,并不一定是数据库中间件团队来做。例如,sharding-jdbc就使用了华为开源的一套微服务架构解决方案service comb中的saga组件,来实现分布式事务最终一致性。阿里也有类似的组件,在内部叫TXC,在阿里云上叫GTS,最近开源到了GitHub上叫fescar(Fast & Easy Commit And Rollback)。蚂蚁金服也有类似的组件,叫DTX,支持FMT模式和TCC模式。其中FMT模式就类似于TXC。


总体来说,实际上TCC更能满足业务的需求,虽然接入更加复杂。关于fescar,最近比较火,这是java写的,具体可以参考:https://github.com/alibaba/fescar。


来源:供应链4.0、微信公众号 - 数据和云(OraNews)


什么是中间件?中间件是干什么用的


1. 由来


因为工作的原因,我从金蝶集团调入金蝶中间件公司工作以来,经常遇到一个问题就是中间件公司是个什么公司,中间件是什么?,金蝶不是做ERP的吗?怎么也做中间件?。这是我以前在金蝶集团时无法想象的问题。因为金蝶,金蝶ERP的品牌以及大众对ERP的了解,是无需我解析什么是ERP,什么是财务软件一类的问题的。


毕竟,中间件在实际的应用过程中,是对应用软件起到支撑作用,最终用户并不直接使用中间件,中间件不是大众消费类软件产品。因此,除非是一个行业专业人士,一般不大可能与中间件打交道,不太了解什么是中间件。


因此,在系统软件之中,操作系统、数据库、中间件的三驾马车,中间件是最神秘的。因为,好歹大家通过Windows基本上会了解操作系统是个什么东西,尽管不会很全面,很专业,毕竟是有感觉的。数据库,虽然没有直接见过,但基本上明白数据是要一个仓库来储存的,因此,也大致知道数据库管理系统是干什么的。


长期以来,中间件是一个专业化非常强的细分产业。因为中间件的技术门槛比较高,玩家也不多,无论是国外还是国内都是如此。因此,行业内对什么是中间件并不特别在意。而公司名称直接叫中间件的就更少了,金蝶中间件应该是国内外直接在公司名称中冠以中间件字眼最早,也是很少的公司之一。另一方面,因为中间件软件还处于发展阶段,还没有完全成熟,因此对中间件的定义也就没有深究,或者权威的说法。


但现在情况有点变化,其中一个原因在于2008年底,国家启动了核高基重大科技专项,在基础软件领域明确提出重点支持操作系统、数据库、中间件、文字处理等基础软件产业的自主创新,几乎一夜之间大大小小的软件公司都宣称是做中间件的了,只要不是做最终应用软件的,他们的产品都叫中间件了,一时间,中间件变得蓬勃发展起来了。


作为中间件行业内的专业化和领先企业来说,大家都重视起中间件来了,这是好事,说明社会上重视了。对行业的发展和繁荣固然重要,但这也隐含了重大的风险。中间件名字被滥用,无论是对用户,对这个产业,对政府和投资人来说,都会有负面的影响。鱼目混珠,泥沙俱下的局面,对中间件产业的正常发展未必就是好事情了,也可能对真正的中间件自主创新带来许多困扰,模糊了中间件的本质,可能会弱化中间件核心技术的创新和发展。


因此,在这种情况下,无论是对行业内,还是行业外,突然什么是中间件的问题变成了一个大问题了。


本文试图就中间件的来龙去脉,外延内涵和前世今生,来一个全面的阐释。一家之言,权作业界参考,希望带动大家做一些深入的思考。


2. 中间件的起源


2.1 中间件发展的历史


事情从1946年说起,世界上第一台电子计算机埃尼阿克诞生,人类进入信息时代。1955年,约翰巴克斯发明了最早的程序语言Fortran,现代意义上的软件就诞生了。


1964年,IBM发布OS/360操作系统,软件与硬件分离,同时,软件成为一个独立的产业正式登上产业界的舞台。中间件就是软件产业不断发展过程中自然产生的。


90年代,文顿·瑟夫这位互联网之父的发明成为改变IT业的重大革命性创新。互联网促使分布式系统和网络应用的诞生,中间件就是伴随网络技术的产生、发展而兴起的,可以说没有网络就没有现代意义上的中间件。因为,网络环境需要解决异构分布网络环境下软件系统的通信、互操作、协同、事务、安全等共性问题,提高异构分布网络环境下软件系统的互操作性、可移植性、适应性、可靠性等问题。


1968年IBM发布CICS交易事务控制系统,使得应用软件与系统服务分离,这是中间件技术萌芽的标志,因为CICS还不是分布式环境的产物,因此我们往往还不将CICS作为正式的中间件系统。


一般来说,我们将1990年诞生于ATT公司的BELL实验室的Tuxedo系统(后来被NOVELL从ATT公司随着UNIX系统一起买走,后来又卖给了BEA公司,现在归于ORACLE公司旗下了)作为中间件的诞生标志。Tuxedo解决了分布式交易事务控制问题,中间件开始成为网络应用的基础设施,中间件正式成型,这是最早的交易中间件。


1994年IBM发布消息队列服务MQ系列产品,解决分布式系统异步、可靠、传输的通讯服务问题,消息中间件诞生。


1995年,JAVA之父James Gosling发明JAVA语言,JAVA提供了跨平台的通用的网络应用服务,成为今天中间件的核心技术。JAVA是第一个天生的网络应用平台,特别是J2EE发布以来,JAVA从一个编程语言,演变为网络应用架构,成为应用服务平台的事实标准。应用服务器中间件,成为中间件技术的集大成者,也成为事实上的中间件的核心。


2001年,微软发布.NET,中间件演变为.NET和JAVA两大技术阵营。但由于.NET还不是一个完全开放的技术体系,只有一个玩家,因此,虽然.NET也是一种中间件,但由于IBM/ORACLE/SUN/SAP等巨头都无一例外成了JAVA阵营的支持者,因此,我们习惯上提到中间件时,往往不包括.NET中间件体系。


以上谈的是历史,但透过历史事实背后,为什么会出现中间件,这其中的本质因素是什么?


2.2 中间件发展的驱动力


中间件出现的驱动力主要来自软件研发过程碰到的种种问题。从软件出现最早是用于科学计算,然后是计算机辅助设计、辅助制造等等工业应用。在企业管理领域大规模应用后,业务需求不断的变化、系统不断增加、流程更复杂、系统越来越不堪重负,出现了需求交付方面的重大挑战,以至于人们用软件危机来描述软件工业所面临的困境。


总结起来,软件工业面临的主要问题是四个方面:质量问题、效率问题、互操作问题、灵活应变问题。这些问题今天依然困扰着这个行业。


造成这个局面的原因是异构性和标准规范的滞后。


屏蔽异构性


异构性表现在计算机的软硬件之间的异构性,包括硬件(CPU和指令集、硬件结构、驱动程序等),操作系统(不同操作系统的API和开发环境)、数据库(不同的存储和访问格式)等等。长期以来,高级语言依赖于特定的编译器和操作系统API来编程,而他们是不兼容的,因此软件必须依赖于开发和运行的环境。


造成异构的原因源自市场竞争、技术升级以及保护投资等因素。希望屏蔽异构平台的差异性问题是促成中间件发展的驱动力之一。


实现互操作


因为异构性,产生的结果是软件依赖于计算环境,使得各种不同软件之间在不同平台之间不能移植,或者移植非常困难。而且,因为网络协议和通信机制的不同,这些系统之间还不能有效地相互集成。


造成互操作性不好的原因,主要是标准的滞后。解决软件之间的互操作性问题也是促成中间件发展的驱动力之一。


共性凝练和复用


软件应用领域越来越多,相同领域的应用系统之间许多基础功能和结构是有相似性的,每次开发系统都从零开始绝对不是一种好的方法,也是对质量和效率的很大的伤害。


尽可能多地凝练共性并复用以提高软件开发效率和质量,通过中间件通过提供简单、一致、集成的开发和运行环境,简化分布式系统的设计、编程和管理,这也是中间件发展的重要驱动力。


在长期的探索过程中,解决软件的四个问题的办法总结起来两个方面:工程方法、平台与技术。


工程方法就是用工业工程、系统工程的理论、方法和体系来解决软件研发过程中的管理问题,包括团队管理、项目管理、质量控制等等,这就是软件工程。除了软件工程方法之外,我们发明了更多的架构规划、设计和实施的方法,不断累积领域的知识与经验等等。


更好的技术手段,包括更好的程序设计语言、更好的平台和软件开发技术,如面向对象、组件开发、面向服务等等。而这方面,在技术上逐渐发展的成果大部分都凝聚在今天的中间件平台之中。


而这些更好的技术手段,从本质上是通过复用、松耦合、互操作(标准)等机制来提高软件质量、加快软件研发效率、使研发出来的产品能够相互集成并灵活适应变化。


这些因素逐渐促成了中间件软件的形成和发展。


3. 中间件的概念


讲了这么多,究竟什么是中间件,也就是中间件的定义是什么?


针对这个问题,应该说还没有一个标准的定义,或者说还没有完全取得学术界和产业界的共识。


顾名思义,中间件就是处于中间的软件。但这种不是从功能,或者特性来定义的概念,而是用位置来定义的名字,就容易被不同的人从不同角度赋予其不同的含义。


IDC曾经给中间件下的定义是中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源,中间件位于客户机服务器的操作系统之上,管理计算资源和网络通信。


我国学术界一般认可的定义是中间件是指网络环境下处于操作系统、数据库等系统软件和应用软件之间的一种起连接作用的分布式软件,主要解决异构网络环境下分布式应用软件的互连与互操作问题,提供标准接口、协议,屏蔽实现细节,提高应用系统易移植性(北京大学梅宏)。


中科院软件所研究员仲萃豪形象地把中间件定义为平台+通信。这个定义限定了只有用于分布式系统中的此类软件才能被称为中间件,同时此定义还可以把中间件与支撑软件和实用软件区分开来。


中间件处于操作系统软件与用户的应用软件的中间。中间件在操作系统、网络和数据库之上,应用软件的下层,总的作用是为处于自己上层的应用软件提供运行与开发的环境,帮助用户灵活、高效地开发和集成复杂的应用软件。形象地说就是上下之间的中间。


此外,中间件主要为网络分布式计算环境提供通信服务、交换服务、语义互操作服务等系统之间的协同集成服务,解决系统之间的互连互通问题。形象地说就是所谓左右之间的中间。


要深入理解什么是中间件,形式化的定义固然重要,我们还得从概念本身去深入理解其核心特征才是最重要的。要理解一个概念,从内涵和外延两个方面去描述是哲学上非常重要的一套方法体系。


3.1 中间件的特征(内涵)


总结分析,中间件有几个非常重要的特征是必须具备的:


平台化


所谓平台就是能够独立运行并自主存在,为其所支撑的上层系统和应用提供运行所依赖的环境。显然,不是所有的系统或者应用都可以称之为平台的。中间件是一个平台,因此中间件是必须独立存在,是运行时刻的系统软件,它为上层的网络应用系统提供一个运行环境,并通过标准的接口和API来隔离其支撑的系统,实现其独立性,也就是平台性。


因此,目前许多的开发语言、组件库和各种报表设计之类的软件,很难满足平台性,将这类软件叫中间件,是很不合适的。例如,JAVA是一种语言,这种语言的开发工具和开发框架,如Eclipse、JBuilder、Struts,Hibernate等等就不能称为中间件,充其量叫中间件开发工具,而不能叫中间件本身,就如同各种建筑工程设备和机械,如吊臂、搅拌机等不能叫建筑,而只能成为建筑工具一样。而J2EE应用服务器提供JAVA应用的运行环境,就是经典的中间件。


应用支撑


中间件的最终目的是解决上层应用系统的问题,而且也是软件技术发展到今天对应用软件提供最完善彻底的解决方案。


高级程序设计语言的发明,使得软件开发变成一个独立的科学和技术体系,而操作系统平台的出现,使得应用软件通过标准的API接口,实现了软件与硬件的分离。


现代面向服务的中间件在软件的模型、结构、互操作以及开发方法等四个方面提供了更强的应用支撑能力:


模型:构件模型弹性粒度化,即通过抽象层度更高的构件模型,实现具备更高结构独立性、内容自包含性和业务完整性的可复用构件,即服务。并且在细粒度服务基础上,提供了更粗粒度的服务封装方式,即业务层面的封装,形成业务组件,就可以实现从组件模型到业务模型的全生命周期企业建模的能力。


结构:结构松散化,即,将完整分离服务描述和服务功能实现以及服务的使用者和提供者,从而避免分布式应用系统构建和集成时常见的技术、组织、时间等不良约束。


互操作:交互过程标准化,即,将与互操作相关的内容进行标准化定义,如服务封装、描述、发布、发现、调用等契约,通信协议以及数据交换格式等等。最终实现访问互操作、连接互操作和语义互操作。


开发集成方法:应用系统的构建方式由代码编写转为主要通过服务间的快捷组合及编排,完成更为复杂的业务逻辑的按需提供和改善,从而大大简化和加速应用系统的搭建及重构过程。


而要最终解决软件的质量问题、效率问题、互操作问题、灵活应变问题这四大问题,需要在软件技术的内在结构(Structure)、架构(Architecture)层面进行思考。


解决这些问题,技术的本质是复用、松耦合、互操作(标准)等软件技术的内在机制。这也是中间件技术和产品的本质特征。


软件复用


软件复用,即软件的重用,也叫再用,是指同一事物不作修改或稍加改动就多次重复使用。从软件复用技术的发展来看,就是不断提升抽象级别,扩大复用范围。最早的复用技术是子程序,人们发明子程序,就可以在不同系统之间进行复用了。但是,子程序是最原始的复用,因为这种复用范围是一个可执行程序内复用,静态开发期复用,如果子程序修改,意味着所有调用这个子程序的程序必须重新编译、测试和发布。


复用对象复用范围


复用对象复用范围子程序一个可执行程序内复用,静态开发期 复用组件(DLL,Com等)系统内复用,动态运行期复用企业对象组件(Com+,.NET,EJB等)企业网络内复用,不同系统之间复用服务 (如WebService,SCA/SDO)不同企业之间,全球复用,动态可配置


为了解决这个问题,发明了组件(或者叫控件),如MS操作系统下的DLL组件。组件将复用提升了一个层次,因为组件可以在一个系统内复用(同一种操作系统),而且是动态、运行期复用。这样组件可以单独发展,组件与组件调用者之间的耦合度降低。


为解决分布式网络计算之间的组件复用,人们发明了企业对象组件,如(COM+, .NET, EJB等),或者叫分布式组件。通过远程对象代理,来实现企业网络内复用,不同系统之间复用。


传统中间件的核心是组件对象的管理。但分布式组件也是严重依赖其受控环境,由于构件实现和运行支撑技术之间存在着较大的异构性,不同技术设计和实现的构件之间无法直接组装式复用。


而现代中间件的发展重要趋势就是以服务为核心,如WebService, SCA/SDO等。通过服务,或者服务组件来实现更高层次的复用、解耦和互操作,即SOA架构中间件。


因为服务是通过标准封装,服务组件之间的组装、编排和重组,来实现服务的复用。而且这种复用,可以在不同企业之间,全球复用,达到复用的最高级别,并且是动态可配置的复用。


耦合关系


基于SOA架构的中间件,在松耦合解耦过程也发展到了最后的境界。传统软件将软件之中核心三部分——网络连接、数据转换、业务逻辑全部耦合在一个整体之中,形成铁板一块的软件,牵一发而动全身,软件就难以适应变化。分布式对象技术将“连接逻辑”进行分离,消息中间件将“连接逻辑”进行异步处理,增加了更大的灵活性。消息代理和一些分布式对象中间件将数据转换也进行了分离。而SOA架构,通过服务的封装,实现了业务逻辑与网络连接、数据转换等进行完全的解耦。


软件技术的不断解耦的过程


互操作性


传统软件互操作技术也存在问题。互联网前所未有的开放性意味着各节点可采用不同的中间件技术,对技术细节进行了私有化的约束,构件模型和架构没有统一标准,从而导致中间件平台自身在构件描述、发布、发现、调用、互操作协议及数据传输等方面呈现出巨大的异构性。各种不良技术约束的结果是软件系统跨互联网进行交互变得困难重重,最终导致了跨企业/部门的业务集成和重组难以灵活快速的进行。


在软件的互操作方面,传统中间件只是实现了访问互操作,即通过标准化的API实现了同类系统之间的调用互操作,而连接互操作还是依赖于特定的访问协议,如JAVA使用RMI,CORBA使用IIOP等。而SOA通过标准的、支持Internet、与操作系统无关的SOAP协议实现了连接互操作。而且,服务的封装是采用XML协议,具有自解析和自定义的特性,这样,基于SOA的中间件还可以实现语义互操作。


基于服务的中间件


总之,服务化体现的是中间件在完整业务复用、灵活业务组织方面的发展趋势,其核心目标是提升IT基础设施的业务敏捷性。因此,中间件将成为SOA的主要实现平台。


3.2 中间件的分类(外延)


中间件所包括的范围十分广泛,针对不同的应用需求涌现出多种各具特色的中间件产品。从功能性外延来看,中间件包括交易中间件、消息中间件、集成中间件等各种功能性的中间件技术和产品。


现在,中间件已经成为网络应用系统开发、集成、部署、运行和管理必不可少的工具。由于中间件技术涉及网络应用的各个层面,涵盖从基础通讯、数据访问到应用集成等众多的环节,因此,中间件技术呈现出多样化的发展特点。


根据中间件在软件支撑和架构的定位来看,基本上可以分为三大类产品:应用服务类中间件、应用集成类中间件、业务架构类中间件。


应用服务类中间件


为应用系统提供一个综合的计算环境和支撑平台,包括对象请求代理(ORB)中间件、事务监控交易中间件、JAVA应用服务器中间件等。


随着对象技术与分布式计算技术的发展,两者相互结合形成了分布对象计算,并发展为当今软件技术的主流方向。1990年底,对象管理组织OMG首次推出对象管理结构OMA(Object Management Architecture),对象请求代理(Object Request Broker)是这个模型的核心组件。它的作用在于提供一个通信框架,透明地在异构的分布计算环境中传递对象请求。CORBA规范包括了ORB的所有标准接口,是对象请求代理的典型代表。


随着分布计算技术的发展,分布应用系统对大规模的事务处理提出了需求,比如商业活动中大量的关键事务处理。事务处理监控界于Client和Server之间,进行事务管理与协调、负载平衡、失败恢复等,以提高系统的整体性能。它可以被看作是事务处理应用程序的操作系统。这类被称为交易中间件,适用于联机交易处理系统,主要功能是管理分布于不同计算机上的数据的一致性,保障系统处理能力的效率与均衡负载。交易中间件所遵循的主要标准是X/open DTP模型,典型的产品是Tuxedo。


JAVA从2.0企业版之后,不仅仅是一种编程语言,而且演变为一个完整的计算环境和企业架构。为JAVA应用提供组件容器,用来构造Internet应用和其它分布式构件应用,是企业实施电子商务的基础设施,这种应用服务器中间件发展到为企业应用提供数据访问、部署、远程对象调用、消息通信、安全服务、监控服务、集群服务等强化应用支撑的服务。使得JAVA应用服务器成为了事实上的应用服务器工业标准。由于它的开放性,使得交易中间件和对象请求代理逐渐融合到应用服务器之中。典型的应用服务器产品包括IBM Websphere Application Server、Oracle Weblogic Application Server和金蝶Apusic Application Server等。


应用集成类中间件


应用集成类中间件是提供各种不同网络应用系统之间的消息通信、服务集成和数据集成的功能,包括常见的消息中间件、企业集成EAI、企业服务总线以及相配套的适配器等。


消息中间件指的是利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可在分布环境下扩展进程间的通信,并支持多通讯协议、语言、应用程序、硬件和软件平台,实现应用系统之间的可靠异步消息通信,能够保障数据在复杂的网络中高效、稳定、安全、可靠的传输,并确保传输的数据不错、不重、不漏、不丢。目前流行的消息中间件产品有IBM的MQSeries、 BEA的MessageQ、金蝶Apusic MQ等。


企业应用整合,仅指企业内部不同应用系统之间的互连,以期通过应用整合实现数据在多个系统之间的同步和共享。这种类似集线器的架构模式是在基于消息的基础上,引入了前置机-服务器的概念,使用一种集线器/插头(hub-and-spoke)的架构,将消息路由信息的管理和维护从前置机迁移到了服务器上,巧妙的把集成逻辑和业务逻辑分离开来,大大增加了系统弹性。由于前置机和服务器之间不再直接通信,每个前置机只通过消息和服务器之间通信,将复杂的网状结构变成了简单的星型结构。典型的企业应用集成EAI的产品包括Tibico和Informatica等公司产品。


随着SOA思想和技术的逐渐成熟,EAI发展到透过业务服务的概念来提供IT的各项基本应用功能,让这些服务可以自由地被排列组合、融会贯通,以便在未来能随时弹性配合新的需求而调整。Web Services是SOA的一种具体实现方式,SOA的世界是由服务提供者(Service Provider)、服务请求者(Service Requester) 以及服务代理者(Service Broker)所组成,目标是将所有具备价值的IT资源,不论是旧的或新的,通通都能够透过Web Services的包装,成为随取即用的IT资产,并可将各种服务快速汇整,开发出组合式应用,达到整合即开发的目的。SOA的架构只是实现和解决了服务模块间调用的互操作问题,为了更好的服务于企业应用,引入了企业服务总线的应用架构(Enterprise Service Bus,ESB)。这一构架是基于消息通信、智能路由、数据转换等技术实现的。ESB提供了一个基于标准的松散应用耦合模式,这就是企业服务总线中间件,是一种综合的企业集成中间件。典型的ESB产品包括IBM Websphere ESB、Oracle公司的Weblogic ESB以及金蝶Apusic ESB等。


业务架构类中间件


作为共性的凝练,中间件不仅要从底层的技术入手,将共性技术的特征抽象进中间层,还要更多地把目光投向到业务层面上来,根据业务的需要,驱动自身能力的不断演进,即,不断出现的新的业务需要驱动了应用模式和信息系统能力的不断演进,进而要求中间件不断地凝练更多的业务共性,提供针对性支撑机制。近年来,这一需求趋势愈发明显,越来越多的业务和应用模式被不断地抽象进入中间件的层次,如业务流程流、业务模型、业务规则、交互应用等等,其结果是中间件凝练的共性功能越来越多,中间件的业务化和领域化的趋势非常明显。


业务架构类中间件包括业务流程、业务管理和业务交互等几个业务领域的中间件。


业务流程是处理业务模型的非常重要方法。管理流程与各职能部门和业务单元有密切关系,须藉各部门间的紧密协调,以达到企业运营和管理功能的目标。在业务流程支持方面,从早期的WfMC定义的工作流,到基于服务的业务流程规范BPEL,由业务流程的支撑,逐渐形成了完整的业务流程架构模型,包括流程建模、流程引擎、流程执行、流程监控和流程分析等。有名的业务流程中间件包括基于工作流的IBM Lotus Workflow,基于BPEL的IBM Webshpere Process Server以及同时支持工作流和BPEL的金蝶Apusic BPM等。


业务管理就是对业务对象的建模和业务规则的定义、运行和监控的中间件平台。策略管理员和开发人员将业务逻辑捕获为业务规则。使用规则管理器可以将规则轻松地嵌入 Web、现有应用程序和后台办公应用程序。常见的业务管理中间件包括IBM Websphere ILOG 业务规则管理系统,金蝶BOS等。


业务交互的中间件平台提供组织的合作伙伴、员工和客户通过WEB和移动设备等交互工具,实现基于角色、上下文、操作、位置、偏好和团队协作需求的个性化的用户体验。这种门户服务器软件基于标准Portlet组合的应用程序访问框架,实现用户集成和交互集成,构建灵活、基于 SOA 的应用架构。典型的门户中间件有IBM Websphere Portal Server和金蝶Apusic Portal Server等。


4. 中间件的未来


中间件是互联网时代的IT基础设施,提供业务的灵活性,消除信息孤岛,提高IT的研发和运营效率。作为网络计算的核心基础设施,中间件正在呈现出服务化、自治化、业务化、一体化等诸多新的发展趋势,中间件进入2.0时代,将极大提升互联网统一计算平台的敏、睿、融、和能力。


中间件将变宽变厚


以互联网为核心的多网融合产生了丰富多样的新型网络应用模式,作为主流的应用运行支撑环境,中间件无处不在,越来越多的应用模式被抽象到中间件层,中间件将变宽变厚。


中间件将面向服务、易于集成


随着SOA技术逐渐成为主流,以及异构系统的集成问题日益严峻,中间件将向面向服务、易于集成的方向发展。


中间件将向一体化的方向发展


中间件产品的种类日趋多样(如交易中间件、消息中间件、应用服务器、集成中间件、业务中间件等),但其技术架构将向一体化的方向发展,主要包括:


统一内核,易于演化:各大厂商的中间件产品将构建在统一内核之上,使其易于平台演化。


统一编程模型,易于开发:不同中间件产品提供了不同的编程模型,这些编程模型将趋向统一,从而达到易于开发的目的。


统一管理模型,易于系统维护:不同中间件产品提供了不同的管理工具与管理手段,这些管理工具与手段将趋向统一,使其易于管理,降低运维成本。


中间件产品将支持云计算,易于交付


中间件产品将成为云计算的支撑平台,使应用易于交付。


后端平台深度融合


一个大胆的设想是:未来五年,浏览器将统一前端,而后端平台(中间件、操作系统、 数据库)走向深度融合。(对中间件的未来发展趋势的详细介绍参加另文《中间件技术的发展趋势分析》,在此不做详细论述。)


综上所述,我们可以认为中间件是一种独立的系统软件平台,为网络应用软件提供综合的服务和完整的计算环境,借助这种软件使得网络应用能够实现集成,达到业务的协同,实现业务的灵活性。


来源:博客水木 本文由金蝶中间件有限公司总经理 奉继承 博士 撰写


中间件是三大基础软件之一,是 IT 系统进行通信和传递消息的纽带。


中间件是什么?


中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相连接的系统,即使它们具有不同的接口,但通过中间件相互之间仍能交换信息。


执行中间件的一个关键途径是信息传递。通过中间件,应用程序可以工作于多平台或 OS 环境。


中间件是介于操作系统和应用软件之间,为应用软件提供服务功能的软件,有消息中间件,交易中间件,应用服务器等。由于介于两种软件之间,所以,称为中间件。



常见的中间件服务器有哪些


我们经常管中间件叫做中间件服务器,也会叫作应用服务器。正常情况下一次web的访问顺序是:web浏览器---web服务器(W3的那个,非广义)---web容器---应用服务器---数据库服务器。


一般本地开发经常使用的应用服务器就是tomcat,linux系统经常使用的是jetty或apache hpptd,大型的项目一般就用的JBOSS或webloigc


Tomcat:是Sun的JSWDK(JavaServer Web Development Kit)中Servlet的运行环境(servlet容器)。Tomcat是Apache Jakarta软件组织的一个子项目,Tomcat是一个JSP/Servlet容器,它是在SUN公司的JSWDK(Java Server Web Development Kit)基础上发展起来的一个JSP和Servlet规范的标准实现,使用Tomcat可以体验JSP和Servlet的最新规范。经过多年的发展,Tomcat不仅是JSP和Servlet规范的标准实现,而且具备了很多商业Java Servlet容器的特性,并被一些企业用于商业用途。


JBoss:是一个运行EJB的J2EE应用服务器。它是开放源代码的项目,遵循最新的J2EE规范。从JBoss项目开始至今,它已经从一个EJB容器发展成为一个基于的J2EE的一个web 操作系统(operating system for web),它体现了J2EE规范中最新的技术


WebLogic服务器:是企业级的应用服务器,支持EJB, 集群以及 ERP(企业资源计划)的连通性 ,开发公司:BEA。


WebSphere:此产品系列是IBM公司一套典型的电子商务应用开发工具及运行环境


来源:黑客技术爱好者


注:文章内的所有配图皆为网络转载图片,侵权即删!

相关新闻

App的阴暗面,性骚扰屡见不鲜

张小龙在当年微信上线时,谈了自己对“泡妞是互联网的最大动力”的理解:互联网用户的需求是多种多样的,满足用户需求的产品和服务也是多种多样的,这些产品和服务或许会对“泡妞”提供便利,但大多数情况下这并非它们存在和发展的原因。...

01月25日 16:51

被冒犯的消费主力军,女性和二手车广告的套路

​全棉时代的卸妆巾广告引发了网络间的愤怒。事实上,冒犯女性和其他弱势群体的广告正变得越来越多。商业对于流量的焦渴,让低俗创意大行其道,作为流行文化文本之一的广告,正在失去下限。...

01月25日 16:02

普通的人也能赚到钱,圈子是赚钱的秘密

​如果有人说,赚钱有套路而且很简单,你可能很容易就联想到传-销。但是如果有人说,为什么你赚不到钱?你肯定愿意往下听。今天这篇,就是"教"人赚钱的话术欣赏。...

01月18日 13:25

激发企业创新创业活力,器鼓励农业领域创新创业

​设立创业加速器、创新型孵化器,推进产学研深度融合……不少国家多措并举,支持初创小微企业精耕细作、健康发展,成为创新创业的重要源泉...

01月18日 13:21

创业失败教训10条,墨菲定律无处不在

我从银行离职创业一年多,现金投入200多万,如果再算本该在上海买房等各种机会成本,损失高达500万以上。...

01月18日 11:07

最热资讯