系统的演进,组件的使用 二
系统的演进,组件的使用
web服务器
想要让系统能够相互通信起来,首先我们需要搭建一个服务器
后端系统的基石
在Linux系统上,想要构建一个服务器,我们可以自己利用socket API和IO复用模型(select,poll,epoll)自己编写。不过,这有些问题,首先,自己编写的服务器不能保证没有bug,其次,服务器要满足高性能的要求不是件简单的事,不仅如此,在此基础上服务器还得满足易用性,高扩展性的要求。
博主曾经自己仿照java的netty框架自己编写了一个服务器,主要思路是利用主从reactor模型,主reactor接受连接请求,并将连接的IO文件放到从reactor上去监控,期间为了高性能做了很多优化,如构造线程池,内存池,利用epoll机制等等,在还未支持业务功能异步和流量监控的情况下,好不容易让程序跑起来。测试的时候发现建立了几十个连接之后就无法连接了,最后排查问题的时候发现是客户端断开连接的时候服务器忘记释放维护的连接了…修改之后才好不容易突破C10K…对于那些正在冲击C10M问题的高性能服务器框架,我深深地感受到了什么是差距…
总之,说这么多呢,主要是想说明,构建系统没有必要自己去编写服务器框架,用现成的就可以了,直接站在巨人的肩膀上,还能学习这些优秀框架的思想。
建立在web服务器上的子系统
C系比较热门的框架是Apache和nginx,Apache是利用传统的多进程模型,优势是稳定,之前企业里用的比较多,但现在相比nginx已经在走下坡路了。nginx是一个不断占领市场的高性能服务器框架,企业中用的越来越多,是学习的较好选择
java系的话已经有了spring这一保姆级的框架了,学这个就行了
使用web服务器框架的好处
一般,web服务器框架都会提供一个接口,在这个接口上我们能完成业务功能的编写,至于服务器的性能优化,在一般情况下不需要我们去考虑,只有在一些要求很高的高并发场景下,我们才需要在框架源码层级寻找解决方案,这大大简化了开发人员的任务,使他们的精力可以专注于业务功能的编写。
数据库
常见的数据库类型
数据库是除了web服务器之外另一个必不可少的组件了。
关系型数据库中,我们最常用的是MySQL和Oracle,MySQL由于开源的原因,更适合人们学习,市面上相关的资料很多。博主熟悉的也是MySQL
数据库的核心知识
数据库的作用主要是为了存储数据,在我看来,它主要关注的有3个问题
1.数据库中数据的物理存储方式,要如何存储才能在短时间内检索出我们所要的数据
2.数据库中如何各种情况下保证数据的一致性。
举几个例子
每个顾客都有他们的订单,订单的属性中有顾客的ID作为外键,我们修改相关数据库时必须要2个一起修改才能成功
数据库在修改数据时数据库突然挂了,这个时候应该如何处理才能保证数据是对的
数据库数据的备份和恢复等
3.数据库如何保证在高并发场景下读到的数据都是我们想要的数据
缓存
为什么需要缓存
由于数据库中的数据一般是存储在持久化介质磁盘之中,而这些介质有一个特点,那就是存储的数据非常多但是读取、写入的效率非常低。
为了提高数据读取的效率,我们可以借鉴计算机体系结构中存储介质分级的思想。
因为内存读取效率比磁盘要高得多得多,所以我们可以利用内存来存放一部分磁盘中的数据从而提高数据的读取效率。
因为内存资源较为宝贵,我们一台机器可能不够,我们此时可以使用分布式缓存。分布式缓存主要使用一致性哈希算法,决定将数据分配到哪个机器中,之后读取数据同样根据一致性哈希算法从对应的机器中读取。
因为缓存只存在一部分数据,所以出现缓存未命中的情况是在所难免的,这个时候我们一般有两种策略。
第一种,我们不更新缓存,发现缓存未命中时,我们就更改访问目标,直接从数据库中读取数据
第二,我们让缓存自动更新,发现缓存未命中后,我们等待缓存向数据库访问数据,缓存得到数据之后将得到的数据替换,然后再向请求方返回该数据
第一种策略一般用于热点数据固定不变的情况
第二种策略用得比较多,在写入数据时,这种策略的缓存会有两种方式。1.写入后缓存将数据写入后才返回响应。2.写入数据后缓存立刻返回响应,之后再异步更新数据。但这样会有遗失数据的风险
缓存的适用场景
缓存一般适用于读多写少,且读取的数据符合二八定律的场景,如果数据的访问完全随机,缓存频频失效,那这样效率反而会降低。
消息队列
为什么需要消息队列
引入消息队列主要是以下三个原因
1.试想下面一个场景
双十一购物期间,在短时间内请求数会非常多,但是服务器在一个时刻能够服务的请求是有效的,那么无法服务的请求该怎么办呢?不管吗?这样显然不行。
这时一个比较好的解决办法就是将请求消息存放到消息队列中,服务器从消息队列中读取请求,这其实就相当于洪水到来时我们利用水坝去泄洪。
消息队列用来平缓流量
2.第二个场景
之前我们的电商系统生成一个订单之后,下游的很多系统(如支付系统,库存系统)收到这个订单之后都要开始工作,此时如果我们需要再增加一个下游系统的话,我们得给订单系统再增加一个接口,这实际上违背了依赖倒置原则(上层模块不应该依赖下层模块,两者都应该依赖抽象),如果我们在其中增加一个消息队列的话,每个订单都发到消息队列中,让下游系统去订阅消息队列,这样就实现了服务的解耦。实际上这种思想和设计模式中的观察者模式有点相似。
消息队列实现服务解耦
3.第三个场景
有时候,我们的业务处理流程是比较复杂的。
拿淘宝举例,淘宝下单的流程包括风控,锁存,生成订单,短信通知等多个步骤,但实际上我们只需要完成风控和锁存就可以返回响应了,将后续的流程丢入消息队列中,让服务器之后再去处理。这样就能增加服务的请求数了
消息队列实现异步
现在的后端系统图
现在用户发送请求时,会先将请求发送到消息队列,电商网站系统订阅消息队列中的消息,在有新消息到来时读取消息并做出响应,在系统内部可能也利用到了消息队列作服务解耦和异步的作用。在处理业务时,我们可能需要用到缓存中的数据,若缓存中存在我们需要的数据,这时我们可以直接读取缓存中的数据,若缓存中的数据不存在,则缓存向数据库发送请求,数据库返回数据之后更新缓存并响应系统层的请求。
业务过程中要写入数据时可以采用两种策略,第一是同步,数据库更新之后再返回,第二种是异步,直接返回稍后更新数据库。第二种策略效率更高但是有安全隐患。软件设计就是这样,没有银弹,按下了葫芦起了瓢。