现在的Web应用,在主流上已经发展到前后端分离的架构。原因当然是因为这一架构的好处是非常明显的,或者说,这也是技术发展的一个正常的逻辑。但笔者觉得,需要更加深入的理解和思考为何它会成为主流的技术方案和发展方向,对于我们更好的理解Web应用开放和开发技术的体系演进,应该是有很多好处的。
Web应用开发的发展阶段
在开始之前,笔者认为,回顾和理清Web开发的技术发展过程,有助于我们更好的理解前后端分离的应用开发模式的底层逻辑。从技术上而言,笔者觉得到现在为止Web应用开发的技术发展,大致可以分为三个阶段。
阶段一: 静态网页
静态网页是Web技术发展的初始阶段。这一阶段主要以简单的网页和内容服务为主。Web应用的核心是Web服务器,它的作用主要是将文件系统的内容通过HTML协议提供给浏览器。典型的Web服务器如IIS和Apache,它们的编程语言就是HTML。在互联网的洪荒时代,浏览器和HTML都是非常先进稀有的技术。后来虽然增加了简单的Script脚本语言如JavaScript、VBScript等,但只能够进行非常简单处理的计算和提供简单的交互功能,如对话框、确认框等等,基本不直接参与比较复杂的数据和信息处理和计算。
静态网页的实现技术,基本上就是一个文件读取并转换为HTTP协议数据的过程。服务器接收到客户端请求之后,从请求地址中分离出需要访问的文件路径,并按照一些配置信息,转换为本地主机文件的地址。然后访问和读取这个文件,并将内容转换为HTTP协议数据发送给客户端。当然,在这个过程中,也会基于HTTP的规范,处理如各种文件权限和无法访问的错误信息。但总体而言,这个过程比较简单单纯,除了基于URL和配置信息和路径映射寻址之外,服务器也不会对文件和数据内容本身做什么额外的处理。
由于这个基于文件的处理方式,也奠定了Web应用基于"页面"的工作模式。一个文件就对应一个页面,页面,就是文件系统在互联网上的程序和映射形式。这一概念,其实一直延续到现在,即使它已经失去了原有的作用和意义。
阶段二: 动态网页
第二个阶段是动态网页阶段。就是Web服务器,根据请求的参数和应用的状态,按照HTML的语法规范,动态的生成一个HTML文件(只是内容),然后响应给客户端。客户端按照普通的HTML文件的处理方式来显示这个页面。
动态页面的方式,极大的扩展了Web应用对实际应用业务的支持能力。因为除了内容浏览和程序之外,如果要在互联网上真正的开展各种商务服务业务,必须有很好的业务流程的支持能力和用户交互能力。其形态和内容,必然是动态的。也就是从这一阶段开始,Web应用的模式,正式的从Web文件和页面服务,逐步过渡到基于Web技术的业务过程服务,Web技术,才真正的拥有了巨大的商业价值,并有了巨大的发展和普及。
从技术角度而言,动态网页就是使用数据动态的构造HTML文本的过程。但HTML本身是具有一定的图形界面属性的。这个模式其实就是需要让开发者承担界面设计的工作,这显然是不太合理的。这个问题的本质就是将内容和呈现混为一体。而这两者在逻辑上是可以,也应该是分离的。
所以,在真正的开放过程中,基本上是基于模板文件的方式来实现的。基本的思路就是还是先按照HTML文件的形式来设计界面,然后在这个文件中,嵌入相关的计算和处理代码,来在合适的位置,填充动态生成的内容。简单而言就是将代码写在HTML文件中,然后使用过滤器和应用服务器,告知这些页面文件中的代码,需要特定的应用服务程序来进行处理。
这一阶段的主流实现技术如ASP、JSP和PHP等,基本上都是这样实现和处理的。和原有的HTML不同,它们的页面文件后缀名称被改成了asp、jsp或者php,框架和内容基本上还是HTML混合代码,就是告知应用服务器来执行其中代码的部分。
下面我们举一个简单的jsp文件来说明一下:
regist.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Guru Logged In</title>
</head>
<body>
<table style="with: 50%">
<tr><td>
<% String username = request.getParameter("username"); %>
<a>Welcome <% out.println(username); %> User!!!! You have logged in.</a></td></tr>
<tr></tr><tr><td></td><td></td><td><a href="register_3.jsp"><b>Logout</b></a></td></tr>
</table>
</body>
</html>
可以看到,这个文件虽然是一个jsp文件,但里面的主体内容,基本上就是一个HTML文件。但使用了一些特别的标签" <% %>"来表明这些是一些Java代码,需要Java程序来进行执行。
从原理和实现的方式上来看,这种技术方案还是有很多的问题和弊端的。笔者认为主要包括:
- 效率
大多数Web应用,其实它们的流程和页面结构都不会太复杂,而且为了保证业务的一致性,很多页面的形式,都是一样的,差异的只是数据而言。但现在的动态页面实现方式,无论相同业务类型的两个页面的差异有多小,比如就是一个实时统计数据的差异,整个页面也要重新生成和传输一遍。这显然是非常低效和不够明智的。
- 性能
性能的问题也和效率相关。由于这个低效的模式,服务器需要额外的处理很多本质上不需要重复处理的信息,必然会影响处理性能和资源占用。还是以前面的统计数据页面为例,本来服务器已经完成了统计计算,但却还需要再次生成页面,额外的进行很多字符串的操作。
这个问题在一个阶段甚至比较严重。当时的计算机系统性能比较弱,人们甚至想出了一种"预生成页面"的解决方案。就是对于很多内容程序类型的应用,并不是在请求时动态生成页面,而是由程序在合适的时候,动态的生成一份HTML文件,并存放在文件系统当中。然后当访问的时候,直接使用这些静态的HTML文件,可以大大提高页面内容服务的效率。
- 开发流程
现代化的Web应用程序非常强调用户体验,所以漂亮和专业的页面图形设计就非常重要。就是更偏向于艺术和设计的领域,应当由专业的美术和视觉效果人员来完成。而现在的情况是在程序代码文件中,业务和数据代码,和界面和布局代码混在一起,两者的工作容易产生耦合和干扰,大大的影响了开发的分工流程和执行效率。
鉴于以上的内容和弊端,行业提出了以AJAX技术为核心的Web应用实现的改进技术。笔者称其为"动态内容"阶段。
阶段三: 动态内容
这个改进方案的基本思路就是,既然有两个页面中,差异的部分主要只是数据的差别,能不能只传输这些数据,而不是整个页面呢?
这个想法虽然很合理,听起来也不复杂,但在以页面处理为基础的Web应用中,却有很多问题,因为这和传统的HTTP协议的应用模式有关。HTTP的原有基础工作模式,就是简单有效的请求-响应模型,页面加载完成,这个操作的生命周期就结束了。要新的数据,就需要新的请求-响应。另外,基于模板的实现方式,页面的内容也是完整的,没办法区分开来。所以要实现部分内容可以重新动态的加载和更新,需要对整个应用的模式和架构,做比较大的调整。
首先是在客户端,需要浏览器在加载初始页面之后,还有能力按照需求,在不刷新页面的情况下,在页面内部的上下文环境中,再次的发起HTTP请求;而且,能够使用请求的结果数据,来部分的更新页面的结构和内容。这都需要给浏览器引入比较强的网络和文本信息处理能力。表现在具体的技术实现上,就是AJAX和JS脚本语言的支持,我们后面会深入探讨。
其次,是需要对服务器进行改造。就是服务器不仅能够提供HTML页面服务,也能够提供某种形式的结构化数据和信息服务。所幸在这方面,HTTP协议已经提供了足够的灵活性和可扩展性,它能够通过响应头信息,来声明响应内容的格式,并在理论上可以输出任何格式和形式的响应内容,可以由应用服务程序来灵活处理。
以上基本就是Web技术到目前为止的主要三个发展的阶段。作为重点,我们下面可以通过分析讨论前后端Web应用工作的基本模式和流程,来了解其相关核心的实现技术,以及这个模式的优势和特点。
基本过程
一个典型的前后端分离的Web应用程序的工作模式大致如下。
- 应用程序入口
和传统的Web应用相同。业务服务会提供给用户一个URL地址,让他们可以使用浏览器来进行访问。
- 客户端应用初始化
客户端浏览器访问应用程序的入口,将会加载一个初始的页面,这一般也是一个标准的HTML页面,在这里和传统Web应用也没有什么区别。但实际上,这时候加载的页面,只是一个应用程序的框架,里面一般是没有实际的业务信息和数据的。这个时候,在页面的定义和内容中,除了HTML内容之外,通常还有关联的资源文件,包括相关的js程序文件,CSS样式文件和UI图片文件等等。
在页面加载特别是相关JS程序文件加载完成之后,就会启动JS文件中的程序(也可能是内嵌在主HTML文件中),来执行后续的操作,通常就是加载动态的业务信息和数据了。这一部分操作,通常通过访问和程序入口不同的数据接口来实现。
- 数据接口
实际的动态业务信息和数据,通常是由另外的业务应用服务器来提提供的。因为一般而言,前面客户端初始化使用的文件都是静态的内容,由传统的静态Web服务器就可以很好的支撑。而后续的业务动态数据,则需要由相关的业务应用程序和数据库系统提供服务,所以一般是部署在另一个应用系统当中的。它们提供的URL地址通常被称为数据接口或者API,包括提供的内容和形式就是一些用于支撑业务应用的结构化数据。
- 数据渲染
客户端获取业务数据之后,就可以使用这些数据,重新来构造HTML文件中需要动态显示的内容,这个操作通常被称为"渲染",就是将这些数据显示和呈现出来。由于真正的UI渲染,其实是由浏览器根据HTML代码来完成的,所以这个渲染其实只是客户端JS程序,根据更新的数据重新生成一部分HTML代码,并将这个代码嵌入或者替换到主HTML文件内容当中的过程。
- 用户交互
由于在客户端浏览器中,提供了程序执行和动态访问网络数据接口的能力,就为支持更复杂和多变的用户交互提供了可能。用户可以在不离开或者刷新页面的情况下,多次按照需求访问和操作数据接口,来达成业务需求。
经过上面的简单分析,我们已经能够理解这一过程的主要阶段,以及为什么被称为"前后端"模式。前端就是Web应用在浏览器环境中的实例,它在浏览器访问Web程序入口时加载并创建。前端的主要目的是提供一个用户交互的界面,比如数据和信息的呈现,用户进行的信息输入和操作响应等等。后端就是一系列数据和信息接口,它可以使前端按照业务状态和需要,进行访问和操作,目的是使业务状态持久化,或者在不同的前端中进行互操作。
这种前后端分离的应用程序,相比以前的应用架构模式,有很多好处和优势。我们在下面的章节详细的探讨这些内容。
好处和优势
- 数据处理和传输的效率
小说《三体》中,地球人需要和三体人建立联系,在严苛的条件限制之下,最终选择的极端技术方案就是"只送大脑"。在对传统Web技术改造的过程中,没有那么极端,更容易想到的就是"只传数据"。早期Web应用,动辄就刷新页面的处理方式,现在看起来就比较低效可笑。要更新显示一个信息,就改那个信息好了,为什么要刷新整个页面呢?特别是对于结构类似的页面,这重新传输的90%以上的信息,可能都是重复的,完全就没有必要。
前后端分离架构很好的解决了这个问题,实现了只传输变化数据的基本诉求。除此之外,在应用服务器方面,原有需要处理整个页面的工作,也简化成为只需要处理业务数据的工作,更简单、更专注、更高效。
- 开发组织
前后端分离,将应用程序从架构上,分为前端和后端两个相对逻辑隔离而又有一定关联的部分。从而能够在系统架构的过程中,更加容易的进行模块化和进行专业上的分工。
这其实是一个现代工业专业化方法论的问题。在复杂系统当中,合理的系统分解,是成功顺利的进行系统建构的重要基础。前端和后端的开发需求,其实是两个差异比较大的专业领域。前端重视UI设计、用户交互、使用流程和应用体验;后端重视数据和信息的高效处理和应用安全。所以,将这两个领域和功能分开组织,是一个非常合理的安排。现实情况下,很多开发团队就是这样开发和组织的。另外,前后端分离让在两端分别的进行系统拆分和模块化成为可能。这可以使两个团队合理分工合作,减少直接的干涉和干扰,提高在系统方面的整体开发效率。
- 性能优化
前后端分离的架构,也更容易的进行应用性能的优化和扩展。在没有分离之前,Web应用的优化基本上就是一个恶魔,因为在优化和调整的过程中,数据和界面程序很容易就纠缠在一起,相互影响,使优化措施根本就无法实施。而前后端分离之后,显然就可以相对分离的进行处理,甚至还利于实现分步骤分阶段的优化和改进。
前端的优化项目和方向,主要是用户界面和应用体验方面的。对于一般的前端应用而言,特别是在现有的技术条件下,性能并不是一个太大的问题。后端的优化项目,主要是在信息处理、数据库操作、缓存等方面。前后端分离的架构,使这些操作可以在后端专注而平滑的进行,而不会影响到前端和用户的使用。
- 安全
和一般对于Web安全问题的认知不同,前后端分离的应用架构,其实更有助于提升系统的安全性。因为在后端接口,处理的都是单纯的业务数据,相关的信息安全规则和措施,可以有针对性的实施,实现重点保护。而原有的基于页面模式的Web应用,处理这些内容要付出更大的代价。
理论上来说,前端的安全性和业务系统是无关的,因为任何具体的业务数据和信息,就算是代码泄露或者注入,都不实际影响业务系统的安全性。这一点会提升攻击者发起网络攻击的代价和无效性。
- 扩展和升级
对于大型复杂系统,前后端分离的架构,相对更容易扩展和升级,并且能够提高更好的扩展和升级过程中的应用体验。
首先就是应用程序的扩展,可以简化为后端系统或者接口的扩展。在这个场景下,系统扩展最简单的方式,就应该是横向扩展了。就是可以通过简单的扩展同类后端接口和服务的数量,就可以快速扩展整个系统的处理能力。它的基本方式就是在后端接口前面引入负载均衡设备,它会将客户端请求比较有效的平均分配到后面具体来进行处理的后端服务接口之上。这些操作,对于用户或者前端应用而言,可以是完全透明的(图为负载均衡网络架构)。
从逻辑上而言,动态网页类型的Web应用也可以使用这种方式来进行扩展。但由于需要传输的重复和无效数据比较多,显然没有前后端分离架构高效,因为它只需要处理业务数据和信息,给网关和负载均衡设备造成的压力会小很多。
在升级方面,由于引入了服务群集的机制,可以做到更好的系统升级的体验。对于比较小的,缺陷修复或者性能优化的改进,后端系统可以逐一的离线、升级、测试和上线,不会影响到前端的应用,过程非常平滑顺畅。
- 支持多种形态的前端程序
这其实是一个新的,附带的收益。现在的Web应用的形态越来越丰富,前端使用的客户端不仅有传统的桌面浏览器,也可能有越来越多的手机浏览器,手机应用程序APP,甚至其他应用系统也有可能。前后端分离的架构,让同一套数据和接口,可以支撑多种多样的前端应用方式,并保证业务和数据的一致性。这个模式对于多端应用非常重要,也可以大大的简化复杂系统开发工作的协调和组织。
- 微服务和多数据来源支持
现在的Web应用越来越复杂,在一个界面上展示的信息和数据,可能已经不是简单的来自于单个业务模块甚至业务系统了。如果使用传统的处理方式,就需要在服务端来先获取这些内容,然后在页面生成的时候将它们集成起来,这样显然会带来更好的灵活性。处理的好的话,也能带来更好的应用体验(如不需要额外操作,就可以透明的获得和显示来自多个系统的信息和数据)和处理效率(减少客户端请求的数量和操作)。
- 更好的应用体验
前后端分离的Web应用,一般情况下可以提供更好的应用体验。完全静态化的入口页面可以加载的更快,因为可以充分利用缓存和CDN技术。如果发送数据加载错误或者处理错误,在前端的展示和处理也可以更加灵活和优雅,如果不是这样,我们可能只能使用传统的浏览器生成的那个简陋的默认错误页面,我们每个人都应该见到过那个白板404或者500错误页,这个体验绝对不能算很好。
- 复用公共模块和能力
有些大型的Web应用已经体系化,发展成为一系列相对独立的业务系统,但它们之间却需要共享一些业务数据和能力,甚至这些能力会完全开放给第三方来使用。这时,只有前后端分离的应用架构模式才能为这些场景提供有力的支撑。
典型的公共模块和能力,包括用户认证、对象和数据存储、消息发送、在线支付等等。它们的很多系统,都是以API,也就是后端服务的形式提供给应用在前端进行集成和调用。
缺陷和问题
当然,从辩证的角度而言,没有任何事物只有好处,而没有缺点或者代价。前后端分离的技术方案也是如此。这里也例举几条,我们也需要辩证的看待和理解。
- 复杂性
原有的静态文件或者动态页面开发模式非常简单,页面即是数据,是同时生成的。由程序开发人员一人处理页面和数据,简单粗暴,不存在分工和沟通的问题。
前后端分离,将这个工作分为在前端和后端分别处理。后端负责数据操作和接口处理;前端负责数据请求和页面布局;两者就需要不时的沟通协调,来最终实现业务整体的一致性。
对于一些非常简单的Web应用而言,比如就是简单的文件或者内容服务,前后端分离显然是没必要的复杂的。所以并不是所有的Web应用都适合使用前后端分离模式来架构。在选择架构方案之前,开发者应该认真评估业务的需求和特点,选择合适的实施方案。
- 状态一致性挑战
过去的页面开发,所有生成的页面,都是一个确定的状态,因为它是"一次性"的,要更改业务状态,就需要刷新页面,粗暴但是简单。引入前端概念之后,这个问题变得复杂起来。部分数据和状态的更新,是否能够和前端其他模块中的数据和状态的逻辑不产生冲突,是程序规划和实现中必须要考虑的问题。
对于更复杂的,比如多个微服务架构的系统而言,这个一致性的问题就更严重了,必须要考虑到多个应用模块间的一致和逻辑相容。
- 安全
这里的安全并不是说前后端系统的系统安全问题,而是说为了达成这个安全性,而引入的系统复杂性和代价。例如,对于分布式后端接口服务,前端需要使用访问令牌来控制访问的安全性。而这些令牌在多个后端接口服务中是如何共享和协调验证的,需要额外的处理机制。如密钥分发等等,这些都会造成整体系统的复杂性,性能和可靠性的问题。
- 基于内容的SOE支持不友好
SEO,意为搜索引擎优化(Search Engine Optimization)是指一种优化技术方式,它通过增加和设置一些网站和网页内容,来改善网站在搜索引擎中的排名和可见性。对于很多强调开放性和可达性的网站或者应用而言,这个特性非常重要。前后端分离的技术方案,对于需要对内容进行搜索优化的应用可能不太友好,因为很多内容是无法在页面上直接访问的,或者访问的页面和内容的内容的对应也不是那么稳定。出现这个问题的本质,其实是现在一般搜索引擎的工作模式还是基于页面的方式,和前后端系统的一般数据接口的应用模式不太匹配。
为了解决或者改善这个问题,在前后端分离系统架构下,有人提出了"后端渲染"的概念。有点像动态页面时代生成缓存静态页面的技术。
前面我们已经了解了前后端分离技术的优势和问题。下面,我们来聊聊前后端分离的具体实现,笔者想从其基本核心技术开始。
前后端分离核心技术
笔者认为,在主流的技术实现中,前后端模式的Web应用,主要涉及以下核心技术。
AJAX
前后端分离架构最主要的核心技术,无疑就是AJAX了。AJAX,全称为Aysnc Javascript And XML,直译为异步的Javascript和XML。这个表述还是比较直观和清晰的。异步的意思就是可以和页面异步,就是说操作可以在页面加载后进行,而不是被限制在页面加载过程中执行。执行的方式就是使用Javascript语言调用浏览器提供的异步请求API,来获取一般是XML编码和形式的信息内容。来达成某种业务操作的灵活性。
奇怪的是,在浏览器实现中,并没有一个什么AJAX类或者函数,它的实现主要是通过定义一个名为XMLHttpRequest (XHR)的类来达成的。下面的示例代码,可以帮助我们来了解一下相关的操作和细节:
js
function reqListener() {
console.log(this.responseText);
}
const req = new XMLHttpRequest();
req.addEventListener("load", reqListener);
req.open("GET", "http://www.example.org/example.txt");
req.send();
这是使用XHR的一般模式。在浏览器中,可以使用js代码来进行相关的实现和操作。上面的代码涉及到的过程和要素包括:
- 创建一个XHR实例
- 为此实例增加一个事件侦听方法,可以用于处理请求响应的内容,这个事件名称为load
- 使用XHR实例打开一个URL地址,这里可选请求方法,也支持POST
- 调用send方法,来执行请求
- 如果有响应信息,则会触发在前面定义的回调方法,来处理响应数据,如更新DOM等等
这段示例代码其实是最基本的模式。XHR提供的HTTP请求功能,其实是比较完善的,比如支持各种HTTP方法、设置请求头,错误处理等等,但这并不是本文需要讨论的内容,这里不再深入讨论,读者只需了解XHR可以实现一个比较完善的HTTP客户端即可。
XHR提供的功能虽然全面,但使用起来稍显繁琐。所以,其实市面上很多第三方HTTP库,其实基本上都是XHR的封装和扩展,只不过更容易使用,附加功能更丰富而已。
在比较新的浏览器版本中,还提供了fetch对象方法,也可以达成类似的效果,主要目的也是提供易用性和快速开发。如下面的示例:
js
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json(); // 将响应解析为 JSON 格式
})
.then(data => {
console.log(data); // 处理返回的数据
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
});
可以看到,fetch的使用,更加直接便利,代码整体性更强,调用和处理过程清晰流畅,并且支持async/await模式。
从技术上而言,两者没有本质区别,而且都是浏览器内置支持的特性,开发者可以根据实际需要选用(比如笔者一般使用fetch,因为它在nodejs环境中也是可用的)。
XML
AJAX中的X,当然就是这个XML语言了,这表明它处理的内容,主要就是使用XML这种编码形式。
随着互联网和Web技术的广泛使用,浏览器和HMTL技术也随之得到了广泛的应用,看起来也是大获成功。但HTML主要是针对网页的处理和呈现来设计的,并不具有通用性。因此在这个基础上,互联网行业进一步将HTML这种形式进行了扩展,推出了XML。这里的X就是Extendable扩展的意思,意为可以用于表达任何结构和形式的信息和内容。所以,虽然HTML是XML的子集,但XM的基本思想和逻辑,其实是来源于前者的。
在相当长的一段时间内,借助技术的惯性,XML的应用也是广泛而频繁的。它被广泛的被应用在数据传输、数据结构表达、配置信息存储等各种各样的应用场景中。但随着Web应用开发技术向前后端架构过渡,XML的一些弊端越来越显露出来,并逐步在这一领域被更新的技术-JSON替代,我们会在后面的内容中再次深入探讨这方面的内容。
但不管如何,起码在AJAX技术的初始阶段,XML就是事实上的内容承载和交换格式的标准,所以这项技术还是以它为名。
DOM
在Web技术中,浏览器可以将使用HTML编码的文本内容,转换为图形界面并最终呈现给用户。这里的HTML就是简单的文本内容,但遵循一系列语法规范和格式。简单而言,它使用"标记语言,Mark Language",就是一系列"标记"(尖括号加指令)来控制内容的布局和显示。
HTML文本的内容就这样可以分为两个逻辑部分,标记和实体内容。这个结构的设计目的,主要是为了在保证人类可读(方便直接编辑)的前提下,方便表示成一个程序数据结构,以利于计算机程序(如浏览器)来进行处理。这个结构和模型,就是文档对象模型(DOM,Document Object Model,下图)。
可以看到,DOM基本上就是一个树形结构。这样,浏览器可以方便的从根节点出发,方便的遍历整个树形结构,或者查询特定的节点进行处理。此外,DOM结构,还可以方便程序对于节点的内容进行处理,这给动态的修改网页内容和呈现方式提供了方便和可能。
实际上,在最新的Web前端技术当中,相关的网页应用,基本上都是通过程序来操作DOM来完成的。特别是对于大型的Web应用,操作DOM过程中的效率和性能,也是Web应用性能的一个重要的方面。
JSON
前面已经简单提到,虽然AJAX原本设计的处理内容形式是XML,但经过多年的发展,现代化的前后端架构Web应用广泛使用的数据内容交换格式其实是JSON。
发生这种转变的原因,笔者考虑,主要有以下几点:
- 原生Javascript支持
很多开发者以为JSON的优势主要是其简单和高效,但笔者认为那并不是最重要的。JSON是JS语言原生的对象表达形式,这一点更重要。在前端浏览器环境中,Javascript是事实的脚本语言技术标准,使用JS语言来处理JSON文本,其序列化和反序列化转换基本是无缝的,整个为JS优化的执行环境,都要考虑JSON内容的性能和效率。所以它几乎成为了前端数据和对象格式的唯一和必然选择。因为如果要使用XML作为标准交换格式,就需要在前后端和传输通道中,频繁的进行和JSON直接的格式转换,完全没有必要。
- 简洁和效率
其次才是相对于XML而言的表达简洁性和处理效率的问题。直观的来看,就知道相对于JSON而言,XML的无效信息太多。比如重复的key,尖括号包围等等。即使不考虑结构处理算法,即使在物理层面就会占用更多的内存和存储空间,给信息的存储和传输带来了更多的负担。
当然JSON也不是完全没有缺点。由于JSON原始的设计,是为程序处理而设计的。每个JSON对象结构,都是纯粹的对象信息,而且原生的JSON编码是密集编码,无法加入注释信息。这个问题即使不和XML相比较,也是在开发者中经常诟病的。其实也不是没有解决方案,比如在JSON对象中加入特别属性或者标识,可能考虑到这个问题在开发过程中并不是特别突出和迫切,还有向前兼容的问题,所以好像行业还没有达成一个共识并明确相关的处理方案。
另外,作为开发者应该理解。JSON的简洁是和XML相对的,它其实并不是最高效的技术方案。它只是要平衡可读性、可维护性和处理效率的一个中间方案。如果只考虑信息的传输和处理,可选的方案还包括ProtoBuf、CBOR甚至纯二进制编码等等。它们可以提供如更高效的编码效率、信息压缩、更高的处理性能等特性。但这些方案的应用场景相对较窄,目前来看,JSON还是通用性比较好的技术方案。
Javascript
显然,要使前端应该能够"动态"化,而不只是死板的呈现固定的内容,就必须引入某种程序运行的机制。现代浏览器就是通过引入对Javascript语言的支持,来满足这个需求的。
其实在HTML页面中,增加脚本语言和程序这个做法,很早就有了。但早期的电脑运行浏览器的性能太差,所以浏览器的设计定位主要就是解析和呈现HTML文本,并做一些最简单的呈现操作,比如现实对话、确认框,操作表单等等,都是辅助性的。
随着计算机处理的性能越来越高,人们发现其实可以使用浏览器做更多的事情。在前端的Javascript语言支持本身的定位也提升到一个工业化产品的定位,完整的考虑到大型复杂程序流程、更多的资源调度和使用能力、更好的性能和稳定性。这个时代的代表产品就是Google为Chrome浏览器设计的V8执行引擎。这其实是一次浏览器技术的革命,标志着浏览器这个产品,从网页呈现程序,过渡到Web应用系统平台的类型和范式的升级。
了解这一过程的人应该知道,JS的崛起并不是我们现在看起来那样理所当然。在早期其实也有类似的竞争技术存在的,HTML本身是开放的,各家都在上面展开了竞争,提出了各种各样的解决方案和技术,比如VB Script、JavaApplet、Flash/Active Script、SilverLight等等。当然最后的结果我们现在已经知道,JavaScript成为了最后存活下来的那个唯一的赢家。关于这个过程,其实有很多或精彩或狗血的故事,这不是本文的主体。这里只是强调JavaScript对于浏览器技术的重要性,以及不管你是否喜欢愿意,它确实是现在前端唯一可选的程序执行环境技术方案,和事实上的开发技术标准。
结合前面的一些内容,我们可以看到,Javascript在前后端架构的Web应用中,在各个阶段都要起到重要的作用,包括在前端应用程序加载后,在页面中响应用户操作或者自动发起HTTP请求,获取响应返回的数据,使用数据操作DOM来更新HTML内容并重新程序HTML页面等等。其实是这一切能够发生的基础和核心。
实践
那么,在一个典型业务类的Web应用中,是如何运用前后端分离的系统架构的呢。笔者这里尝试结合自己的一些运用的经验,来分成几个模块简单说明一下。
- 后端
笔者使用nodejs作为主要的后端技术系统。先按照业务的需求,并遵循REST的基本规范,规划相关的API体系。
前后端的数据传输,都统一到JSON格式。包括GET请求获取的内容,特别是POST请求提交的内容,不再使用传统的表单和编码格式,统一将数据编码成为JSON格式,并作为纯数据体提交。这样可以无需再次转换,简化后端的处理。
在业务授权管理方面,基本上是以API路径,作为授权和模块划分的单元。所以需要认真合理的规划HTTP路径,结合用户角色的业务权限,进行相关的验证和管理。具体实现,还涉及到后面的会话管理操作。
- 会话管理
笔者的Web应用使用Token来管理后端请求的会话。用户在正常完成认证过程后,会获得一个Token,用于标识当前这个用户和其角色。在后续的应用中,客户端会在请求业务接口的时候,在请求头中附带这个Token;服务端收到请求后,会先从请求中分离并验证这个Token,获得用户和角色标识,并结合请求地址、参数等确认其操作权限;最后来提供业务服务。
在多个后端中,使用相同的方式来验证Token,这样后端接口可以快速的横向扩展,包括相同的业务模块和不同的业务模块,都可以使用类似的方式来进行扩展,从而提高整个系统的处理能力。
- 负载均衡和路由
受条件限制,笔者的Web应用并不是一个非常复杂的系统,也没有使用多个公网的IP地址,所有的外部访问都通过一个域名入口进入。要在这一个端口提供提供所有服务,就需要配置负载均衡并合理规划前后端系统的访问路径。
笔者选用HaProxy作为负载均衡系统。笔者觉得,和Nginx相比,它的配置和管理相对比较灵活和容易,比如它可以做四层负载均衡,为以后的系统管理和应用提供了灵活性(实际上笔者使用haproxy的转发功能,实现了管理用的VPN网络)。
通过合理的规划和部署,用户在使用Web应用的时候,看起来是访问的同一个域名和IP地址,其实在内部,前端页面和后端API请求是根据路径不同,分别路由到前端静态文件服务和后端API接口之上的。当然,这些对于应用而言是完全透明的。
- 前端
笔者的Web应用前端使用VUE作为开发系统。前端应用开发完成后,会使用Webpack编译,并作为一套静态的文件包,部署在标准的Nginx服务上。然后通过Haproxy发布公网地址和端口之上。
一些随想
关于这个主题,笔者其实已经思考和构思了很长时间。因为相关的材料和内容比较多,如何很好的组织和表达,让其逻辑清晰、关联性和整体性比较好,是个很大的问题。至此,总算是有了一个相对完整的结构,也基本上表达了笔者想要体现和表达的内容,特别是跳脱出前后端分离相关的技术,想要探讨的更深层次的内容,比如为什么和它的本质是什么。本文的主要内容,已经基本体现了这一点。除此之外,关于这个主题,笔者下面还有一些不成体系的随想,以飨读者参考。
- 底层逻辑和本质
前后端分离的技术本质,就是将呈现和数据分离,就是将表现和内容分离,而不是浏览器、Javascript、HTTP这些具体的技术选择和路线。这一思想会体现在很多软件系统当中,对其规划、设计、架构和开发工作造成影响。
- 前后端的负载平衡
前后端架构的演进,也是计算机技术和性能演进的结果。从整体上来看,一个完整的Web应用体系包括了服务器、网络和客户端系统。现代计算机系统和网络的发展趋势,基于经济性的考虑,其实相对而言,服务器和终端的处理能力的差异,是越来越小的。以主流的工业标准服务器和桌面系统为例,它们的CPU、内存和磁盘的整体处理能力的差异,不会超过两个量级。但名义上每台服务器要为上千个客户端提供有效的服务,这样其实造成了巨大的落差,就是相对而言客户端系统的性能完全没有充分利用。 所以,前后端分离架构的另一个潜在逻辑,就是将整体负载,更多的移向客户端系统,让它尽可能的承担更多的处理工作,来给服务器减负。
- 动态页面模式
在非常偶尔和特殊的情况下,在前后端分离架构的Web应用中,还需要动态的生成完整的HTML页面,这时应该如何处理呢? 答案是其实可以非常方便的退回到动态页面的应用模式。就是在Web应用内部,引入模板技术,数据还是正常的处理,输出的时候,在内部套用到模板上以页面的方式输出就可以了。这样,一个Web应用可以以前后端和动态页面两者模式工作(可以用REST路由来区隔),并且保持业务处理的一致性。当然代价就是多一步服务端的处理,增加一些负载和资源占用。
- 一些失败的前端技术
在历史上,是有很多失败的前端技术的。 前有JavaApplet、VBScript、ActiveX,后有Flash、Slivelight等。失败的原因,其实很简单,主要的就只有一个:开放性。在互联网和Web应用这个天然开放的技术体系当中,你还要像通过一些私有的技术和协议,而不是在公开协议下做得更好,来建立市场优势,看来是有结构性的问题的。因为在这个市场上的玩家,都不是泛泛之辈,你不可能取得长久和独特的领先地位。
- Web技术的通用性
笔者觉得这个有点不可思议。因为现在的互联网网生态是非常丰富的,场景和需求也是千差万别。但它们的技术底层,竟然基本都是相同的。我们以两个常见的比较大的互联网应用类别为例。普通的完全开放的互联网服务,例如淘宝网,和另一种半封闭的,垂直领域的业务类应用,比如高考报名这种,它们其实可以使用相同的技术堆栈,比如都用前后端分离的架构,MVC框架、数据库系统等等。就好像同一台发动机,可以装在货车上,也可以装到轿车上,不是不能硬这么做,好像觉得不是特别合适吧。关于这个问题,笔者还没有想的特别清楚。这也许就是软件的一种特性,优秀的架构和实现,可以应用在任何使用的场景和场合中。