当前课程知识点:软件工程 > 第11章 软件系统设计 > 11.7 Web系统架构设计 > 讲授视频
我们知道近年来Web发展非常迅猛
而它的发展带来了大量机会
经过多年的检验
客户端、服务器这种
C/S结构一直是非常适用的
这个结构里面将前后端分离开来
后端,或者说服务端
现在应用最普遍的
就是HTTP服务器
为了更好地进行开发工作
后端服务器发展出了
MVC和其他架构
前端,或者说客户端
经过发展
有种浏览器一统天下的感觉
浏览器、微信之类
带浏览器的客户端
可以作为通用客户端
他们做的就是借助HTML
CSS、JS等前端手段
完成客户端的呈现和交互
因为前端的迅猛发展
也出现了前端MVC之类的
前端架构来提高前端开发的质量
此外还有现在非常流行的各种App
我们可以把它看做是专用客户端
所以我们看到
Web系统的架构
无非就分为前端和后端
那细化来说
应该怎么设计Web系统的架构呢
我们先回顾一下HTTP服务器的原理
从HTTP协议的规定来说
客户端向服务器发起请求
服务器上的服务器程序收到请求后
对请求进行解析
然后处理
完成后向客户端返回响应
当然
这个过程中的数据流传输
必须依照HTTP协议的规定
比如
我们用Python
写一个非常简单的HTTP服务器
它监听在本机的8000端口
所有的请求传递给
SimpleHttpRequestHandler去处理
数据流的解码
编码在BaseHTTPServer里面都做掉了
这个服务器程序
相当于就是把你的当前工作目录
映射到localhost的8000端口
让它可以通过HTTP请求
访问到程序的工作目录
当客户端
比如浏览器
向我们的这个服务器程序发起请求后
首先它会经过
BaseHTTPServer进行简单的解码
然后交给
SimpleHTTPRequestHandler进行处理
完成后写入数据流
就成为了这个HTTP请求的响应
所以我们看到
BaseHTTPServer里面做的就是
监听在固定的端口然后接收HTTP请求
接着对HTTP请求进行简单解码和解析
最后去调用RequestHandler
而在SimpleHttpRequestHandler里面
做的其实就是业务逻辑的实现
这个例子里面的业务逻辑
就是要读取请求的相应文件
或者目录的内容
完成业务逻辑处理后
就可以写入HTTP响应了
我们发现
其实这边做的业务逻辑是很简单的
如果业务逻辑复杂的话
该怎么办
这时候就很有必要引入服务器架构
有了服务器架构之后
开发人员就可以更好地
关注在业务逻辑的处理上
可想而知就可以更好地
处理更复杂的业务逻辑
HTTP后端服务器的架构有很多种
我们举个例子
比如MVC设计的后端
这样的架构中
HTTP请求一般会先经过router
或者说路由
它决定要把请求交给
哪个具体的Controller去处理
具体的Controller收到请求后
根据业务逻辑的需要
它可能需要与数据库进行交互
比如存取相关的数据
这些操作都交给Model层去做
这样在Model层的辅助下
Controller完成了业务逻辑的处理
它会把自己的处理结果交给View层
View层收到处理结果后
就据此向客户端以响应
为了追求便利
后端服务器的View层会去渲染模板
比如HTML模板
甚至CSS模板和JS模板
最后的HTTP响应
当然就是由View层完成的了
其实这边的View层是很有问题的
因为在这里前后端发生了耦合
这就会给开发和维护带来更多困难
前面说的View层前后端耦合的本质是
前端的代码交给后端去通过模板来渲染
要解决这个问题
我们可以让后端成为RESTful的API
因为前端的发展
如果还把前端模板放在后端服务器上
带来的困难程度远比以前高得多
所以
我们把前端模板从后端服务器剥离出来
这样后端就可以专注于业务逻辑的处理
RESTful的API说白了
就是充分利用具体的四种HTTP请求
包括GET、PUT、POST和DELETE
客户端向服务器端发起请求
比如GET /activity/list
以前后端服务器程序
会给客户端返回对应的网页
但在RESTful的后端服务器
返回的不是具体网页
而是特定格式的响应
比如JSON格式
XML格式之类的都可以
为了更好地
让前端反应后端的出错情况等
可以让返回的响应中增加特定的字段
比如code表示错误代码
message代表错误信息
我们前面访问的GET /activity/list
返回code=0代表没出错
实际应该返回的数据在data字段里
返回的是一个数组
每个元素都是一个object
里面有对应活动的信息
这样
实际上后端向前端返回的响应
就精简了非常多
以前需要返回整个网页
现在只需要返回前端需要的信息了
更大的好处是
可以完全把前端分离出来
也就是说
前端的开发可以先不依赖后端进行
然后用很小的代价
就可以完成前后端的衔接
有了这样RESTful API的后端
后端通信就可以在Web页面上
充分利用Ajax的特性
不仅带来更好的用户体验
还让前后端更好地分离
而且
这样的后端对App等
专用客户端也是通用的
在专用客户端发起HTTP请求
照样可以拿到需要的后端数据
真是一举多得
接下来的问题就在于
我们的Web页面服务要如何进行部署
对于Web页面而言
我们必须让前后端
能通过同一个域名的
同一个端口访问到
否则会出现跨域的问题
跨域的问题主要是说
我们不能用Ajax在一个域名的页面上
获取另一个域名
或者另一个端口提供的数据
因为这样会非常不安全
所以我们必须把前端页面
和后端的RESTful API
部署在相同域名的相同端口上
解决办法就是通过Nginx之类的
Web服务器托管我们的前端和后端
举个例子
我们把nginx绑定在服务器的80端口上
客户端请求80端口
会被nginx收到
根据请求的不同
nginx直接把前端文件内容
返回给客户端
或者通过端口转发
或者管道文件
把请求转发给我们的RESTful后端
后端响应后
nginx再把响应返回给客户端就可以了
值得一提的是
借助nginx
我们可以有多个RESTful后端
同时用同一个域名的
同一个端口提供服务
只要在nginx配置里
设置好相应的路由就可以了
我们已经通过RESTful API解决了
前后端难以分离开发的问题
同时还通过这个解决方案
带来了更好的用户体验
和更灵活的部署方式
但有时候我们会遇到这样的问题
因为HTTP请求是同步的
所以当它在服务器上响应之后
为了避免系统的复杂性
我们就不应该再做别的事了
而且浏览器
一般对HTTP请求有超时的限制
比如
假设我们开发了一个
自动根据用户上传的图片
进行人脸识别
并进行复杂分析的服务
每张图片经过优化以后
还需要处理1分钟
但一个HTTP请求
在浏览器限制30秒内必须响应
这种情况下
我们就不得不把图片的处理
变成一个异步的服务
所谓异步的服务
一般是以异步任务的方式呈现的
比如我们的服务器程序收到了一个请求
可以告诉任务的管理程序
说需要启动一个任务
管理程序说好呀
然后返回一个任务id
服务器程序就可以马上把任务id
作为响应返回给客户端了
而我们的管理程序
一般会在返回任务id之后
再去启动任务
这样
客户端程序就可以通过任务id
询问任务的进展情况
比如任务正在运行
或者任务成功了
或者任务失败了
如果任务成功了,我们也可以得到任务的响应
如果任务失败了,我们也可以得到任务的错误信息
这样就很好地
用异步的方式解决了问题
更棒的是我们可以设定一些定时器
让一些任务
可以在设定的时间自动启动
比如自动发邮件之类的
要实现异步任务
借助于现成的成熟框架
可以很容易做到
这里简单介绍
一个异步任务队列框架celery
借助于它
我们既可以实现定时任务
也可以实现异步任务
当然
定时任务
我们也可以通过操作系统的
cronjob之类的来实现
nodejs是
可以借助它本身异步的特点
来做异步任务的
但是非常非常不推荐这样做
因为自己实现的往往难以管理
而且很容易出错
借助于celery之类的
异步任务队列
不仅可以给我们
带来异步服务的好处
还为系统的规模化
提升带来了额外的增益
因为celery本身
就是一个分布式的任务队列
这样可以把异步任务
分配到服务器集群去处理
借助集群的力量
以很快的速度消化掉大量异步任务
我们知道计算机体系结构里面
划分了七个存储层级
其中网络访问是在最低层级
数据库往往是在硬盘层
要实现系统的更好性能
我们可以利用缓存的思想
把经常访问
但不经常变化的数据
放到上一层存储层级
也就是放到内存去
这样借助内存缓存
我们就可以提高Web访问的速度
为了实现内存缓存
我们可以借助内存型数据库来实现
内存型数据库
往往比传统数据库简单不少
比如redis、memcached
都是典型的Key-Value键值对存储
它们的一个用途就是作为缓存使用
比如假设我们发现
一个获取activity信息的
RESTful API的访问
平均耗时要500毫秒左右
但它在大多数情况下
返回的都是相同的结果
我们就可以把它的处理结果存入redis
再次访问的时候
直接从redis里取出结果
平均耗时就可以降低到100毫秒以内
这里需要特别注意缓存的过期机制
比如修改了activity后
记得删掉前面说的那个缓存
或者在
一些对实时性要求不那么高的场景下
我们可以直接简单粗暴
给缓存设置固定的过期时间
比如一小时或者一天
这样在系统访问频繁的时候
通过缓存可以有很好的性能表现
系统访问不频繁的时候
也可以让系统的负载适当均衡
回顾一下
我们可以通过合理的资源配置
可以让有限的机器发挥更大的价值
遵照RESTful的后端服务
能让我们前后端开发更容易配合
异步任务机制让我们能提供复杂的服务
而且可以弹性伸缩
而缓存机制
让我们的服务响应速度大大提高
而nginx
支持将请求转发至不同的端口
管道文件,甚至不同的机器
这也大大提高
我们整个软件系统的性能和扩展性
同学们可以想一想
在一些具体的应用场景下
比如我们如果要开发一个
用抢票的方式发放活动票的系统
该如何设计架构
才能在保证正确性的前提下
尽可能榨干服务器全部性能
-1.1 软件无处不在
--讲课视频
-1.2 软件的本质特性
--讲授视频
-1.3 软件工程的产生与发展
--讲授视频
-1.4 软件工程的基本概念
--讲授视频
-1.5 软件质量实现
--讲授视频
-1.6 业界人士谈软件工程
-测验题--作业
-讨论题
--讨论题
-作业题
--第一张 作业题
-2.1 编程过程与规范
--讲课视频
-2.2 良好的编程实践
--讲课视频
-2.3 Python集成开发环境
--讲课视频
-2.4 代码静态检查
--讲课视频
-2.5 代码性能分析
--讲课视频
-2.6 结对编程实践
--讲课视频
-2.7 刘贺谈软件工程
--讲课视频
--讨论
-测验题--作业
-作业题
--第二章 作业题
-3.1 单元测试概述
--讲课视频
-3.2 黑盒测试方法
--黑盒测试方法
-3.3 白盒测试方法
--基本概念
--代码覆盖标准
--基本路径测试
-3.4 单元测试工具
--单元测试工具
--html
-测验题--作业
-作业题
--第三章 作业题
--作业题附件
-4.1 软件过程
--讲课视频
-4.2 软件过程模型
--讲课视频
-4.3 敏捷开发过程
--讲课视频
-4.4 微软公司开发过程
--邹欣经理自我介绍
--微软开发过程之一
--微软开发过程之二
-测验题--作业
-5.1 团队组织与管理
--讲课视频
-5.2 项目沟通管理
--讲课视频
-5.3 软件项目计划
--讲课视频
-5.4 软件项目估算
--讲课视频
-测验题--作业
-讨论题
--讨论
-6.1 敏捷开发之Scrum
-- 敏捷开发之Scrum
--html
-6.2 用户故事与估算
--讲课视频
-6.3 团队协作工具Tower
-6.4 配置管理
--讲课视频
-6.5 配置管理工具Git
--讲课视频
-测验题--作业
-作业题--作业
-7.1 需求工程师
--讲课视频
-7.2 需求定义
--讲课视频
-7.3 需求的类型
--讲课视频
--讲课视频(2)
-7.4 需求工程过程
--讲课视频
-7.5 需求的主要来源
--讲课视频
-7.6 需求获取技术
--讲课视频
--讲课视频二
--讲课视频三
-7.7 撰写需求文档
--讲课视频
-测验题--作业
-讨论题
--讨论
-8.1 用例建模概念
--讲课视频
-8.2 用例建模过程
--讲课视频
-8.3 用例建模精讲
--讲课视频
-8.4 建模工具介绍
--讲课视频
-8.5 微信抢票应用案例
--讲课视频
-测验题--作业
-讨论题
--讨论
-9.1 面向对象分析
--讲课视频
-9.2 CRC卡片分拣法
--讲课视频-1
--讲课视频-2
-9.3 面向对象设计
--讲课视频-1
--讲课视频-2
-9.4 类图建模
--讲课视频-1
--讲课视频-2
-第9章 面向对象分析与设计--测验题
-讨论题
--讨论
-10.1 顺序图概念
--讲课视频
-10.2 顺序图建模
--讲课视频
-10.3 顺序图风格
--讲义视频
-10.4 状态建模
--讲课视频
-10.5 状态图
--讲课视频
-10.6 状态图精讲
--讲义视频
-测验题--作业
-讨论题
--讨论
-11.1 软件体系结构概念
--讲授视频
-11.2 软件设计原则
--讲授视频
-11.3 软件体系结构风格(一)
--讲授视频
-11.4 软件体系结构风格(二)
--讲授视频
-11.5 软件体系结构风格(三)
--讲授视频
-11.6 软件设计过程
--讲授视频
-11.7 Web系统架构设计
--讲授视频
-11.8 数据库选择策略
--讲授视频
-测验题--作业
-作业题
--html
--html
--html
-作业题--作业
-12.1 交互设计概述
--讲授视频
-12.2 交互设计目标
--讲授视频
-12.3 GUI设计原则
--讲课视频
-12.4 KLM效率模型
--Video
-12.5 Fitts定律
--讲授视频
-12.6 交互设计过程
--讲授视频
-测验题--作业
-13.1 软件测试概念
--讲课视频
-13.2 软件测试类型
--讲课视频
-13.3 软件功能测试
--讲课视频
-13.4 软件性能测试
--讲课视频
-测验题--作业
-14.1 软件部署与交付
--讲课视频
-14.2 软件演化与维护
--讲课视频
-测验题--作业
-第一部分:基础知识
-第二部分:编程与测试(选做)