Java Web系统架构概览
大型网站系统架构的演进都是随着业务增长不断演进,所有的出发点都是为了满足业务需求。最初访问量下,功能简单时,单体软件可以解决所有问题;后来访问量逐渐增大,功能愈加丰富,此时单体软件的架构逐渐成为开发和运维的瓶颈。所以微服务拆分,集群化部署,消息中间件,内存数据库,数据库中间件等解决方案逐渐走进视野。
下图为简略版的Web系统架构,本文围绕此图展开,简要介绍其中涉及组件的功能和应用场景。
回到最初,Web项目的源头几乎全来源于用户的请求(此处忽略运维相关的定时监控等任务),当海量的请求并发量超过Tomcat服务器的峰值时,Tomcat的集群化应运而生,与之同时反向代理组件Ngnix走进视野。Ngnix本身作为入口,其稳定性是关键因素,所以利用keepalived实现高可用是正常思维。
综上,可以看到集群化的目的在于解决单服务能力不足的问题,高可用的机器备份方案是为了实现系统的稳定运行。但是,在集群化时又引入了负载均衡,高可用时怎样实现异常故障自动切换等技术问题,这里均不深入讨论,仅引入一个概念基础。另,每一种组件有很多相关的产品,这里仅针对图中列出的组件进行介绍。
拆分后的微服务部署在不同的机器上,服务间如何通信实现业务调用?先解释一个概念,调用有同步和异步两种,同步是在调用时发起方会阻塞线程等待调用结果返回后再往下执行,异步调用是调用方按约定将消息发送出去,不关注调用的执行结果,两者适用的场景不同。对应到上图中同步调用的方案为Dubbox,消息中间件ActiveMQ为异步调用。
-
Dubbox
Dubbox 是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,Dubbox 就是个服务框架,只有在分布式的时候,才有Dubbox 这样的分布式服务框架的需求,起本质上是个远程服务调用的分布式框架。
节点角色说明:
· Provider: 暴露服务的服务提供方。
· Consumer: 调用远程服务的服务消费方。
· Registry: 服务注册与发现的注册中心。
· Monitor: 统计服务的调用次调和调用时间的监控中心。
· Container: 服务运行容器。
调用关系说明:
1. 服务容器负责启动,加载,运行服务提供者。
2. 服务提供者在启动时,向注册中心注册自己提供的服务。
3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Dubbox推荐使用 zookeeper 注册中心。注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。所以在服务集成时需要同步部署Zookeeper。
-
ActiveMQ
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。对于消息中间件,常见的角色为Producer(生产者)、Consumer(消费者)。
ActiveMQ 是Apache的开源项目,是能力强劲的消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现。
什么是JMS?
JMS(Java Messaging Service)是Java平台上有关面向消息中间件的技术规范,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发。
JMS本身只定义了一系列的接口规范,是一种与厂商无关的 API,用来访问消息收发系统。它类似于 JDBC(java Database Connectivity):这里,JDBC 是可以用来访问许多不同关系数据库的 API,而 JMS 则提供同样与厂商无关的访问方法,以访问消息收发服务。许多厂商目前都支持 JMS,包括 IBM 的 MQSeries、BEA的 Weblogic JMS service和 Progress 的 SonicMQ,这只是几个例子。 JMS 使您能够通过消息收发服务(有时称为消息中介程序或路由器)从一个 JMS 客户机向另一个 JML 客户机发送消息。消息是 JMS 中的一种类型对象,由两部分组成:报头和消息主体。报头由路由信息以及有关该消息的元数据组成。消息主体则携带着应用程序的数据或有效负载。
JMS规定的消息类型有两种:点对点(一对一),发布/订阅模式(一对多),我们在使用消息中间件时只需遵从JMS提供的操作接口进行开发。
ActiveMQ集群化有两种方式Master/Slave(ActiveMQ5.10开始支持使用Zookeeper搭建集群),Broker Cluster。
-
Master/Slave实现了主从复制,实现高可用;
-
Broker Cluster实现了负载均衡。
可以将两种方式结合起来同时实现负载均衡和高可用。
为了进一步缓解服务集群的压力,可以将高频访问量大或者资源消耗严重的服务单独拆分出来,如图中的搜索和文件服务。
-
SolrCloud
SolrCloud是Solr提供的分布式搜索方案,当你需要大规模,容错,分布式索引和检索能力时使用SolrCloud。当一个系统的索引数据量少的时候是不需要使用SolrCloud的,当索引量很大,搜索请求并发很高,这时需要使用SolrCloud 来满足这些需求。
SolrCloud 是基于Solr和Zookeeper的分布式搜索方案,它的主要思想是使用 Zookeeper作为集群的配置信息中心。
它有几个特色功能:
1)集中式的配置信息
2)自动容错
3)近实时搜索
4)查询时自动负载均衡
问题来了,何为Solr?
Apache Solr是一个流行的开源搜索服务器,它通过使用类似REST的HTTP API,构建搜索应用程序。它建立在Lucene(全文搜索引擎)之上。 Solr是企业级的,快速的和高度可扩展的。 使用Solr构建的应用程序非常复杂,可提供高性能。
Solr可以和Hadoop一起使用。由于Hadoop处理大量数据,Solr帮助我们从大的源中找到所需的信息。不仅限于搜索,Solr也可以用于存储目的。像其他NoSQL数据库一样,它是一种非关系数据存储和处理技术。
使用Solr时需要整合中文分析器,例如IK Analyzer。
IK Analyzer 是一个开源的,基亍 java 语言开发的轻量级的中文分词工具包。IK已发展为面向 Java 的公用分词组件,独立亍 Lucene 项目,同时提供了对 Lucene 的默认优化实现。在2012版本中,IK 实现了简单的分词歧义排除算法,标志着 IK 分词器从单纯的词典分词向模拟语义分词衍化。
Solr如何使用?
我们可以利用Spring将Solr服务集成到项目中。Spring Data Sol就是为了方便Solr的开发所研制的一个框架,其底层是对SolrJ(官方API)的封装。搜索服务的功能开发参考Spring Data Solr相关接口。
-
FastDFS
FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。
Tracker server 作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到 Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。
Storage server 作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上,Storage server 没有实现自己的文件系统而是利用操作系统 的文件系统来管理文件。可以将Storage称为存储服务器。
服务端两个角色:
Tracker:管理集群,tracker 也可以实现集群。每个 tracker 节点地位平等。收集 Storage 集群的状态。
Storage:实际保存文件 Storage 分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。
由上图可知,FastDFS集群搭建完成后,我们需要开发client端向服务器发起请求实现文件上传下载功能,相关开发接口在client的jar包中提供如:fastdfs_client_v1.20.jar。
最后,说说数据库在大型软件系统中的改变。
-
Redis Cluster
为何要搭建Redis集群。Redis是在内存中以Key-Value格式保存数据的,而电脑内存一般都不大,这也就意味着Redis不适合存储大数据,Redis更适合处理高并发。为了扩展设备的存储能力,搭建服务集群是正常思路。
Redis 3.0之后版本支持Redis-Cluster集群,它是Redis官方提出的解决方案,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。其架构图如下:
客户端与 Redis 节点直连,不需要中间代理层。客户端不需要连接集群所有节点连接集群中任何一个可用节点即可。所有的 Redis 节点彼此互联(PING-PONG 机制),内部使用二进制协议优化传输速度和带宽。
Redis-Cluster分布存储机制-槽
(1)Redis-Cluster 把所有的物理节点映射到[0-16383] slot上,cluster 负责维护node<->slot<->value。
(2)Redis-Cluster中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,Redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个key 都会对应一个编号在 0-16383 之间的哈希槽,Redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
Redis-Cluster容错机制-投票
(1)选举过程是集群中所有master参与,如果半数以上master节点与故障节点通信超过(cluster-node-timeout),认为该节点故障,自动触发故障转移操作。故障节点对应的从节点自动升级为主节点。
(2)什么时候整个集群不可用(cluster_state:fail)?
如果集群任意master挂掉,且当前master没有slave。集群进入fail状态,也可以理解成集群的slot映射[0-16383]不完成时进入fail状态.。
为何要使用Redis?
高频高并发的访问数据库会给数据库造成很大的访问压力,甚至是瘫痪。为了解决此问题,我们可以使用Redis将常用数据进行缓存,分流访问流量。
如何使用Redis?
类似Solr,Spring提供了Spring Data Redis框架,在Srping应用中通过简单的配置访问Redis服务。Spring Data Redis对Reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了Redis各种操作、异常处理及序列化,支持发布订阅,并对Spring 3.1 cache进行了实现。
Spring Data Redis针对JRedis提供了如下功能:
1.连接池自动管理,提供了一个高度封装的“RedisTemplate”类。
2.针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口。
ValueOperations:简单K-V操作
SetOperations:set类型数据操作
ZSetOperations:zset类型数据操作
HashOperations:针对map类型的数据操作
ListOperations:针对list类型的数据操作
-
MyCat
MyCat是基于cobar(阿里开源产品)演变而来,对 cobar 的代码进行了彻底的重构,使用 NIO 重构了网络模块,并且优化了 Buffer 内核,增强了聚合,Join 等基本特性,同时兼容绝大多数数据库成为通用的数据库中间件。
简单的说,MyCat就是:一个新颖的数据库中间件产品支持MySQL集群,或者mariadb cluster,提供高可用性数据分片集群。你可以像使用MySQL一样使用MyCat。对于开发人员来说根本感觉不到MyCat的存在。
MyCat分片
分片是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。
数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。
(1)一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切分可以称之为数据的垂直(纵向)切分。
(2)另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。
MyCat分片策略:
数据库的读写分离:
数据库读写分离对于大型系统或者访问量很高的互联网应用来说,是必不可少的一个重要功能。对于MySQL来说,标准的读写分离是主从模式,一个写节点Master后面跟着多个读节点,读节点的数量取决于系统的压力,通常是1-3个读节点的配置。
MyCat读写分离和自动切换机制,需要MySQL的主从复制机制配合。