知识库

记录点点滴滴

Web前后端解分离综述

一、前言

前后端分离的核心思想:前端Html页面通过Ajax调用后端的RestFul API并使用Json数据进行交互。前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务(多种客户端,例如:浏览器,车载终端,安卓,IOS等等)打下坚实的基础。

二、应用服务器与web服务器

在互联网架构中,web服务器一般指像nginx与apache这类服务器,只能解析静态资源(html/css/js等),将静态资源通过HTTP发送回客户端。web服务器一般指像tomcat、resin、jetty这类服务器,可以解析动态资源(如jsp等),以解析jsp为例,服务器首先需安装有java环境,根据页面内的语句解析完成后形成静态资源返回给客户端。因此在不做系统调优的情况下,tomcat一般支持一百多并发,而nginx则可以轻松支持上万并发。因此从定位上说,tomcat这类服务器是一款动态语言解析的服务器软件,而nginx是一款功能强大的静态资源负载软件。

可能会产生疑问,既然nginx是专用于解析静态资源的,那么nginx+php解析 php又是怎么实现的呢?

在处理php的过程中,nginx是一个转发器,php解析http请求依靠的是fastcgi实现,fastcgi是web服务器(nginx、apache)和处理程序之间的一种通信协议,而真正处理php的是代理后端fpm,fpm负责解析fastcgi,执行脚本,其本身是一个进程管理器,一个master进程负责进程worker的创建与回收,最终把结果返回给nginx,由nginx再返回给浏览器。因此从本质上说,nginx的作用依旧是静态资源的调度与返回。

《Web前后端解分离综述》

三、从MVC到前后端分离

MVC是一种经典的设计模式,全名为Model-View-Controller,即模型-视图-控制器。

其中模型用于封装数据的载体,在java中包含一些列的setter/getter方法。视图用于展现,决定了界面长什么样子,在java中可以通过jsp来充当视图,也可以用纯html来展现。而模型与视图之间通过控制器相连,当用户发送一个http请求后,该请求会首先进入到控制器中,然后控制器去获取数据将其封装为模型,最后将模型传递到视图中进行展示

《Web前后端解分离综述》

视图与模型之间信息的传递,即输入的是ajax请求,输出的是json数据。对于这一过程,常用REST服务实现,若将浏览器视为前端、服务器视为后端,则可以把上述的过程改进为以下模式

《Web前后端解分离综述》

四、REST架构

REST本质上是使用url来访问资源的方式。访问一个url包含了两部分:请求方式和请求路径。请求方式常见的有增(POST)删(DELETE)查(GET)改(PUT),还有HEAD和OPTIONS两种。需要注意的是REST是面向资源的,且REST是一个无状态的架构模式,在任何时候都可以由客户端发送请求到服务端,最终返回自己想要的数据。因此REST也可以被人们视为是一种轻量级的SOA实现技术,对于一个REST服务来说,可以通过不同的请求方式以及不同的请求路径实现对不同业务的不同操作。

五、耦合的开发方式

几曾何时,我们的JavaWeb项目都是使用了若干后台框架,springmvc/struts + spring + spring jdbc/hibernate/mybatis 等等。大多数项目在java后端都是分了三层,控制层(controller/action),业务层(service/manage),持久层(dao)。控制层负责接收参数,调用相关业务层,封装数据,以及路由&渲染到jsp页面。然后jsp页面上使用各种标签(jstl/el/struts标签等)或者手写java表达式(<%=%>)将后台的数据展现出来,玩的是MVC那套思路。我们先看这种情况:需求定完了,代码写完了,测试测完了,然后呢?要发布了吧?你需要用maven或者eclipse等工具把你的代码打成一个war包,然后把这个war包发布到你的生产环境下的web容器(tomcat/jboss/weblogic/websphere/jetty/resin)里,对吧?发布完了之后,你要启动你的web容器,开始提供服务,这时候你通过配置域名,dns等等相关,你的网站就可以访问了(假设你是个网站)。那我们来看,你的前后端代码是不是全都在那个war包里?包括你的js,css,图片,各种第三方的库,对吧?好,下面在浏览器中输入你的网站域名(www.xxx.com),之后发生了什么?(这个问题也是很多公司的面试题)浏览器在通过域名通过dns服务器找到你的服务器外网ip,将http请求发送到你的服务器,在tcp3次握手之后(http下面是tcp/ip),通过tcp协议开始传输数据,你的服务器得到请求后,开始提供服务,接收参数,之后返回你的应答给浏览器,浏览器再通过content-type来解析你返回的内容,呈现给用户。

那么我们来看,我们先假设你的首页中有100张图片,此时,用户的看似一次http请求,其实并不是一次,用户在第一次访问的时候,浏览器中不会有缓存,你的100张图片,浏览器要连着请求100次http请求(有人会跟我说http长连短连的问题,不在这里讨论),你的服务器接收这些请求,都需要耗费内存去创建socket来玩tcp传输(消耗你服务器上的计算资源)。重点来了,这样的话,你的服务器的压力会非常大,因为页面中的所有请求都是只请求到你这台服务器上,如果1个人还好,如果10000个人并发访问呢(先不聊服务器集群,这里就说是单实例服务器),那你的服务器能扛住多少个tcp连接?你的带宽有多大?你的服务器的内存有多大?你的硬盘是高性能的吗?你能抗住多少IO?你给web服务器分的内存有多大?会不会宕机?

这就是为什么,越是大中型的web应用,他们越是要解耦。

理论上你可以把你的数据库+应用服务+消息队列+缓存+用户上传的文件+日志+等等都扔在一台服务器上,你也不用玩什么服务治理,也不用做什么性能监控,什么报警机制等等,就乱成一锅粥好了。但是这样就好像是你把鸡蛋都放在一个篮子里,隐患非常大。如果因为一个子应用的内存不稳定导致整个服务器内存溢出而hung住,那你的整个网站就挂掉了。如果出意外挂掉,而恰好这时你们的业务又处于井喷式发展高峰期,那么恭喜你,业务成功被技术卡住,很可能会流失大量用户,后果不堪设想。

注意:技术一定是要走在业务前面的,否则你将错过最佳的发展期。

此外,你的应用全部都耦合在一起,相当于一个巨石,当服务端负载能力不足时,一般会使用负载均衡的方式,将服务器做成集群,这样其实你是在水平扩展一块块巨石,性能加速度会越来越低,要知道,本身负载就低的功能or模块是没有必要水平扩展的,在本文中的例子就是你的性能瓶颈不在前端,那干嘛要水平扩展前端呢???还有发版部署上线的时候,我明明只改了后端的代码,为什么要前端也跟着发布呢???(引用:《架构探险-轻量级微服务架构》,黄勇)。正常的互联网架构,是都要拆开的,你的web服务器集群,你的应用服务器集群+文件服务器集群+数据库服务器集群+消息队列集群+缓存集群等等。

JSP的痛点

以前的javaWeb项目大多数使用jsp作为页面层展示数据给用户,因为流量不高,因此也没有那么苛刻的性能要求,但现在是大数据时代,对于互联网项目的性能要求是越来越高,因此原始的前后端耦合在一起的架构模式已经逐渐不能满足我们,因此我们需要需找一种解耦的方式,来大幅度提升我们的负载能力。

1、动态资源和静态资源全部耦合在一起,服务器压力大,因为服务器会收到各种http请求,例如css的http请求,js的,图片的等等。

一旦服务器出现状况,前后台一起玩完,用户体验极差。

2、UI出好设计图后,前端工程师只负责将设计图切成html,需要由java工程师来将html套成jsp页面,出错率较高(因为页面中经常会出现大量的js代码),
修改问题时需要双方协同开发,效率低下。

3、jsp必须要在支持java的web服务器里运行(例如tomcat,jetty,resin等),无法使用nginx等(nginx据说单实例http并发高达5w,这个优势要用上),
性能提不上来。

4、第一次请求jsp,必须要在web服务器中编译成servlet,第一次运行会较慢。

5.每次请求jsp都是访问servlet再用输出流输出的html页面,效率没有直接使用html高(是每次哟,亲~)。

6、jsp内有较多标签和表达式,前端工程师在修改页面时会捉襟见肘,遇到很多痛点。

7、如果jsp中的内容很多,页面响应会很慢,因为是同步加载。

8、需要前端工程师使用java的ide(例如eclipse),以及需要配置各种后端的开发环境,你们有考虑过前端工程师的感受吗。

基于上述的一些痛点,我们应该把整个项目的开发权重往前移,实现前后端真正的解耦!

六、前后端分离的优点

  1. 可以实现前后端的解耦,前端静态资源可以采用nginx,后端数据动态资源可以采用tomcat等应用服务器。对于静态文件而言,可以放到特定的文件服务器,例如阿里云的OSS,并使用cdn缓存加速,前端服务控制了页面的引用、跳转以及路由,而后端应用可采用分布式架构,利用tomcat提升整体响应的速度。
  2. 容易定位bug。前后端分离使得单元测试变得简单,对于大工程的开发有助于定位错误的根源。
  3. 在高并发的应用场景下,可以灵活根据需求扩展前后端服务器的数量。例如淘宝首页,需要抗住上亿级别的并发请求,需要单独设置上千台前端服务器,用于前端响应。
  4. 减少后端服务器的并发负载压力。在耦合开发模式下,http请求全部命中服务器,而前后端分离开发下,http请求命中前端服务器,由前端服务器选择是否调用后端接口,请求后端数据服务器。
  5. 即便后端服务器宕机,前端服务器也可以正常使用,只是没有数据填充而已。
  6. 接口可以重复使用,多个轻应用可以使用同一个后端接口。
  7. 不用担心页面内容过多,由于异步加载的特性,页面的内容会从多个数据源调取。
  8. nginx支持热部署,可以不用重启服务器就可以升级前端页面。
  9. 这个代码的维护性、易读性,提升开发效率。

七、总结

如果团队内部没有明确的职能划分,预期流量不大的情况。在开发上我更倾向于耦合开发。耦合开发具有快速高效性,将前后端实现整合在一个文件中或者一套文件中 ,减少了前端页面布局、页面资源调用、页面加载的问题,也解决了后端可能存在的跨域、封装响应数据包的麻烦。

反之,如果团队内部有稳定且明确的前后端分工,预期有较高的并发流量,在这种业务情境下,更倾向于前后端分离开发。在开发中,对于一些既可以在前端实现又可以在后端实现的逻辑,我更倾向于是放在前端实现,这样有利于减轻服务器的负载,同时前后端分离开发模式下,前端页面最好有足够的机制对应后端请求超时及宕机的情况。

前后端分离并非仅仅只是一种开发模式,而是一种架构模式(前后端分离架构)。

千万不要以为只有在撸代码的时候把前端和后端分开就是前后端分离了。需要区分前后端项目。前端项目与后端项目是两个项目,放在两个不同的服务器,需要独立部署,两个不同的工程,两个不同的代码库,不同的开发人员。

前后端工程师需要约定交互接口,实现并行开发,开发结束后需要进行独立部署,前端通过ajax来调用http请求调用后端的restful api。

前端只需要关注页面的样式与动态数据的解析&渲染,而后端专注于具体业务逻辑。

八、扩展阅读

1、其实对于js,css,图片这类的静态资源可以考虑放到类似于阿里云的oss这类文件服务器上(如果是普通的服务器&操作系统,存储在到达pb级的文件后,或者单个文件夹内的文件数量达到3-5万,io会有很严重的性能问题),再在oss上配cdn(全国子节点加速),这样你页面打开的速度像飞一样, 无论你在全国的哪个地方,并且你的nginx的负载会进一步降低。

2、如果你要玩轻量级微服务架构,要使用nodejs做网关,用nodejs的好处还有利于seo优化,因为nginx只是向浏览器返回页面静态资源,而国内的搜索引擎爬虫只会抓取静态数据,不会解析页面中的js,这使得应用得不到良好的搜索引擎支持。同时因为nginx不会进行页面的组装渲染,需要把静态页面返回到浏览器,然后完成渲染工作,这加重了浏览器的渲染负担。

浏览器发起的请求经过nginx进行分发,URL请求统一分发到nodejs,在nodejs中进行页面组装渲染;API请求则直接发送到后端服务器,完成响应。

3、如果遇到跨域问题,spring4的CORS可以完美解决,但一般使用nginx反向代理都不会有跨域问题,除非你把前端服务和后端服务分成两个域名。
JSONP的方式也被淘汰掉了。

4、如果想玩多端应用,注意要去掉tomcat原生的session机制,要使用token机制,使用缓存(因为是分布式系统),做单点,对于token机制的安全性问题,可以搜一下jwt。

5、前端项目中可以加入mock测试(构造虚拟测试对象来模拟后端,可以独立开发和测试),后端需要有详细的测试用例,保证服务的可用性与稳定性。

 

本文内容整理自互联网

点赞

发表评论

邮箱地址不会被公开。 必填项已用*标注