前后端分离实践的架构设计分析

 

摘要

在当前互联网软件开发模式中,一部分公司在软件开发方面采用传统的开发模式,即:系统功能实现主要以后台为主,前端人员只负责写HTML或者JavaScript代码。在该方式下的程序前后端高度耦合,系统维护困难。文章通过对传统的软件开发方式进行分析,引入当前流行的前后端分离软件设计方式,进行深入阐述分析。

关键词:软件结构;前后端分离


1、概述

前后端分离已成为互联网项目开发的业界标准使用方式,通过nginx+tomcat的方式(也可以中间加一个nodejs)有效的进行解耦,并且前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务(多种客户端,例如:浏览器,车载终端,安卓,IOS等等)打下坚实的基础。

核心思想是前端html页面通过ajax调用后端的restuful api接口并使用json数据进行交互。

在互联网架构中,名词解释:

Web服务器:一般指像nginx,apache这类的服务器,他们一般只能解析静态资源。

应用服务器:一般指像tomcat,jetty,resin这类的服务器可以解析动态资源也可以解析静态资源,但解析静态资源的能力没有web服务器好。

一般都是只有web服务器才能被外网访问,应用服务器只能内网访问。

2、发展历程

    2.1未分离时代

早期主要使用MVC框架,Jsp+Servlet的结构图如下:

upfile

大致就是所有的请求都被发送给作为控制器的Servlet,它接受请求,并根据请求信息将它们分发给适当的JSP来响应。同时,Servlet还根据JSP的需求生成JavaBeans的实例并输出给JSP环境。JSP可以通过直接调用方法或使用UseBean的自定义标签得到JavaBeans中的数据。 需要说明的是,这个View还可以采用Velocity、Freemaker等模板引擎。使用了这些模板引擎,可以使得开发过程中的人员分工更加明确,还能提高开发效率。

    2.2半分离时代

几曾何时,我们的JavaWeb项目都是使用了若干后台框架,springmvc/struts+spring+jdbc/hibernate/mybatis等等。

大多数项目在java后端都是分了三层,控制层,业务层,持久层。控制层负责接收参数,调用相关业务层,封装数据,以及路由渲染到jsp页面。然后jsp页面上使用各种标签或者手写java表达式将后台的数据展现出来。

前后端半分离,前端负责开发页面,通过接口(Ajax)获取数据,采用Dom操作对页面进行数据绑定,最终是由前端把页面渲染出来。这也就是Ajax与单页应用结合的方式,其结构图如下:

upfile

     2.3分离时代

随着互联网技术的高速发展,目前Web项目所需要应付的场景和以往有着很大的不同,以往的时候我们将代码、数据库放在同一个服务器中,所有的压力都由一个服务器承担。但目前各个网站访问量都有了很大的上升。如果还是将所有的应用放在一个服务器中,很容易使得服务器崩溃,为解决这类问题,现在许多系统都是用集群或者分布式模式,将一个项目部署到多个服务器或者分为多个子项目分别部署。当用户发送请求时,走完一个请求的全部流程可能需要经过多个服务器,还存在着请求跨域的问题,如果项目前后端耦合度较高,那么必然使得系统复杂性上升。为解决这一情况,前后端分离已经变成了一种必然的趋势。前后端分离是指将前端和后端从之前的相互融合中分离开来,两者不再共用一个Server,前端作为一个独立Server存在。这种情况下使得一部分业务逻辑转移到前端,后台通过API将数据交给前端后对前端就已经是无感知状态,后端不再接触任何的HTML或者模板页面,前端获得数据后进行业务逻辑的处理。目前市面上流行的前端框架分别为:VueJS,React,Angular,使用这些框架都能很好地实现前后端分离,其中VueJS和Angular都使用了MVVM的开发模式实现业务逻辑,MVVM是对MVC的一个改进,将其中的View的状态和行为进行抽象化,将页面和业务分开。视图独立于模型而存在,单独对视图或模型修改不会影响对方,同时可以将一些视图逻辑放到一个ViewModel中,实现逻辑的重用。结构图如下:

upfile

        2.3.1、对于后端java工程师

把精力放在java基础,设计模式,jvm原理,spring+springmvc原理及源码,linux,mysql事务隔离与锁机制,mongodb,http/tcp,多线程,分布式架构,弹性计算架构,微服务架构,java性能优化,以及相关的项目管理等等。

后端追求的是:三高(高并发,高可用,高性能),安全,存储,业务等等。

        2.3.2、对于前端工程师

把精力放在html5,css3,jquery,angularjs,bootstrap,reactjs,vuejs,webpack,less/sass,gulp,nodejs,Google V8引擎,javascript多线程,模块化,面向切面编程,设计模式,浏览器兼容性,性能优化等等。

前端追求的是:页面表现,速度流畅,兼容性,用户体验等等。

通过将team分成前后端team,让两边的工程师更加专注各自的领域,独立治理,然后构建出一个全栈式的精益求精的team。

3、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、idea),以及需要配置各种后端的开发环境。

4、开发模式

4.1以前老的方式是

1、产品经历/领导/客户提出需求。

2、UI做出设计图。

3、前端工程师做html页面。

4、后端工程师将html页面套成jsp页面(前后端强依赖,后端必须要等前端的html做好才能套jsp。如果html发生变更,就更痛了,开发效率低)。

5、集成出现问题。

6、前端返工。

7、后端返工。

8、二次集成。

9、集成成功。

10、交付。

4.2新的方式是

1、产品经历/领导/客户提出需求。

2、UI做出设计图。

3、前后端约定接口&数据&参数。

4、前后端并行开发(无强依赖,可前后端并行开发,如果需求变求变更,只要接口&参数不变,就不用两边都修改代码,开发效率高)。

5、前后端集成。

6、前端页面调整。

7、集成成功。

8、交付。

5、请求方式

5.1以前老的方式是

1、客户端请求。

2、服务端的servlet或controller接收请求(后端控制路由与渲染页面,整个项目开发的权重大部分在后端)。

3、调用service,dao代码完成业务逻辑。

4、返回jsp。

5、jsp展现一些动态的代码。

5.2新的方式是

1、浏览器发送请求。

2、直接到达html页面(前端控制路由与渲染页面,整个项目开发的权重前移)。

3、html页面负责调用服务端接口产生数据(通过ajax等等,后台返回json格式数据,json数据格式因为简洁高效而取代xml)。

4、填充html,展现动态效果,在页面上进行解析并操作DOM。

总结一下新的方式的请求步骤:

大量并发浏览器请求--->web服务器集群(nginx)--->应用服务器集群(tomcat)--->文件/数据库/缓存/消息队列服务器集群。

6、前后分离的优势

1、可以实现真正的前后端解耦,前端服务器使用nginx。前端/WEB服务器放的是css,js,图片等等一系列静态资源(甚至你还可以css,js,图片等资源放到特定的文件服务器,例如阿里云的oss,并使用cdn加速),前端服务器负责控制页面引用&跳转&路由,前端页面异步调用后端的接口,后端/应用服务器使用tomcat(把tomcat想象成一个数据提供者),加快整体响应速度。

2、发现bug,可以快速定位是谁的问题,不会出现互相踢皮球的现象。页面逻辑,跳转错误,浏览器兼容性问题,脚本错误,页面样式等问题,全部由前端工程师来负责。接口数据出错,数据没有提交成功,应答超时等问题,全部由后端工程师来解决。双方互不干扰。

3、在大并发情况下,我可以同时水平扩展前后端服务器,比如淘宝的一个首页就需要2000+台前端服务器做集群来抗住日均多少亿+的日均pv。

4、减少后端服务器的并发/负载压力。除了接口以外的其他所有http请求全部转移到前端nginx上,接口的请求调用tomcat,参考nginx反向代理tomcat。且除了第一次页面请求外,浏览器会大量调用本地缓存。

5、即使后端服务暂时超时或者宕机了,前端页面也会正常访问,只不过数据刷不出来而已。

6、也许你也需要有微信相关的轻应用,那样你的接口完全可以共用,如果也有app相关的服务,那么只要通过一些代码重构,也可以大量复用接口,提升效率。(多端应用)

7、页面显示的东西再多也不怕,因为是异步加载。

8、nginx支持页面热部署,不用重启服务器,前端升级更无缝。

9、增加代码的维护性&易读性(前后端耦在一起的代码读起来相当费劲)。

10、提升开发效率,因为可以前后端并行开发,而不是像以前的强依赖。

11、在nginx中部署证书,外网使用https访问,并且只开放443和80端口,其他端口一律关闭(防止黑客端口扫描),内网使用http,性能和安全都有保障。

12、前端大量的组件代码得以复用,组件化,提升开发效率,抽出来!

7、注意事项

1、在开需求会议的时候,前后端工程师必须全部参加,并且需要制定好接口文档,后端工程师要写好测试用例(2个维度),不要让前端工程师充当你的专职测试,推荐使用chrome的插件postman或soapui或jmeter,service层的测试用例拿junit写。

2、上述的接口并不是java里的interface,调用接口就是调用你controler里的方法。

3、加重了前端团队的工作量,减轻了后端团队的工作量,提高了性能和可扩展性。

4、我们需要一些前端的框架来解决类似于页面嵌套,分页,页面跳转控制等功能。(上面提到的那些前端框架)。

5、如果页面上有一些权限等等相关的校验,那么这些相关的数据也可以通过ajax从接口里拿。

6、对于既可以前端做也可以后端做的逻辑,建议放到前端,因为你的逻辑需要计算资源进行计算,如果放到后端去run逻辑,则会消耗带宽&内存&cpu等等计算资源,服务端的计算资源是有限的,而如果放到前端,使用的是客户端的计算资源,这样你的服务端负载就会下降(高并发场景)。类似于数据校验这种,前后端都需要做!

7、前端需要有机制应对后端请求超时以及后端服务宕机的情况,友好的展示给用户。

8、总结

本文介绍了软件开发中前后端模式的基本历程。软件开发中前后端进行分离后由后端提供数据然后前端来进行渲染使得项目分工明确,开发过程中前后端可以各自测试已经完成的功能,最后将前后端进行对接,有效地提升开发效率。前后端分离并非仅仅只是一种开发模式,而是一种架构模式(前后端分离架构)。千万不要以为把前端和后端分开就是前后端分离了,需要区分前后端项目。前端项目与后端项目是两个项目,放在两个不同的服务器,需要独立部署,两个不同的工程,两个不同的代码库,不同的开发人员。前后端工程师需要约定交互接口,实现并行开发,开发结束后需要进行独立部署,前端通过ajax来调用http请求调用后端的restful api。前端只需要关注页面的样式与动态数据的解析&渲染,而后端专注于具体业务逻辑。