大学的时候,在所有不同系统的开发模式中,对我个人来说最困难的就是Web开发,现在想来可能是因为在Web开发中,默认就有多个运行时环境共同作用,才能完成Web应用的整个流程。这个过程中涉及的一些运行时视图,在当时的认知水平情况下,无法全面理解。
后来在实际的互联网项目中,通过LAMP、Django、nodejs和Spring MVC等一系列不同语言和框架的开发学习,从摸索到完全可以在大脑中构建清晰的运行时视图,这个时候就可以像《架构实践》中所描述的篮球教练一样,看到细节。在组建培训团队的过程中,我也发现,对于这个运行时的理解程度,几乎就决定的新人、中间水平一直到全栈工程师的表现能力,还有对架构的理解。在面试的时候也能够很好的考察开发人员的全局视野。
因此,我认为,理解从输入网址,到网页完整呈现出来的过程,对于Web应用开发,是至关重要的;具备了整个图景的思考和实战能力,就可以挑战真正意义上的全栈开发。本文将会试着简单描述这个过程,并且思考一下我对全栈开发的理解。
运行时组件
首先我会从运行时的视角开始,在整个过程中从多个粒度,解析所有发生的事情的运行时。
最大粒度的划分,由两个运行时,本地的浏览器和云端的服务器组成;两个实体逻辑上是运行在不同机器上的完全隔离的进程里,因此,也可以把http协议理解为一种进程间通信。
在浏览器这个运行时里,至少需要三个必要的组件,才能完成工作,分别是进行http远程通讯的http客户端、javascript脚本解释器还有html+css样式的渲染;简单的来看,就是这三个组件,加上浏览器的运行时控制器,组成了完整的Web浏览器。
在云端,也有必要的细分,可能物理上不一定完全分离,但是逻辑上必须由对应的单元组成。http服务器负责处理http协议的通讯,以文本或文件形式将url指向的资源返回给客户端;应用服务器执行特定语言的处理程序,进行计算;数据管理软件,负责提供和存储数据。根据不同架构和应用场景,还可以有很多细分的组件,通过各自的接口相互协作,完成数据计算和资源处理,对资源进行处理,并且返回给请求的客户端。
基本的运行时组件:
- javascript解释器,如Google V8
- html和css渲染
- http客户端
- http服务器,如Apache httpd、tomcat和nginx
- 应用容器,如Tomcat、PHP解释器和Python运行时
- 数据库,例如:MySQL、MongoDB、Redis等
- 其他各种中间件,Kafka、ElasticSearch、ZooKeeper等
上面这些运行时环境,参与并实现了从浏览器请求一直到完整网页信息和交互效果的呈现。
基于LAMP的简单描述
- 用户在浏览器中输入一个链接地址,
- 浏览器通过DNS服务,找到url指向的服务器。
- 浏览器通过TCP协议,向Apache Httpd服务器请求资源。
- Apache Httpd服务器根据配置,决定如何处理资源;并且处理请求及响应的header和body。
- 直接返回静态资源,如果是配置了其他应用服务器或者网关,那么apache将请求转发给网关或应用容器,例如Tomcat或者调用php解释器,在应用容器返回处理的结果之后,返回给客户端。
- php运行时或者Tomcat分析路由和请求头部,执行相应的服务端代码,调用服务器配置的资源,根据请求,进行计算。
- php程序调用MySQL数据库,通过mysql协议获取程序对应的数据。
云端服务器
服务器存储了根据系统设计需要提供的各种资源,包括核心的MVC处理代码,这里MVC和分层架构是一种入门级的最佳实践,可以应付大多数的Web场景需要。整个MVC和三层架构,都是通过代码实现的一种逻辑结构,运行在PHP解释器或者其他语言的对应运行时中;理论上说,每个web应用,通过MVC就可以实现业务逻辑,其他的所有资源,都可以通过MVC代码的调用实现。
除了核心的业务逻辑以外,根据模块化,设计的需要,或者现实的限制,单个Web应用可能并不拥有所需的所有资源,就需要通过调用各种本地或者远程的资源来获取自身没有的数据。一般来说,服务端的语言运行环境,通过驱动可以做物理机能做的任何事情。外部资源无论数据库还是消息队列,rpc都是基于互联网通讯的,通过tcp或者http,以自己设计的一套协议提供对外服务。在mvc执行的运行时,可以自己实现对应服务的协议来调用远程资源,但是更一般的情况,我们直接调用远程资源在对应语言下提供的API来完成资源获取,这些API和我们的调用程序会实现某种设计模式,使应用和API以更有逻辑完整性的方式整合起来。
而浏览器端的主角javascript,除了nodejs之外,在服务端看来,跟另外两个主角html、css一样,从html诞生之初,就与普通文本无异,是一种超文本。虽然通过语法和语义需要,服务端也可以实现处理文本的API,更优雅的生成最终的三大主角,例如freemarker对应html,或者grunt、gulp的uglify、less等构建过程放在服务器端处理。
浏览器客户端
用户在浏览器里看到的一切,从输入url获取对应的html文本开始,在html解析阶段,script、link标签所指向的资源,会发起新的http请求来获取,这个跟img加载图片是一样的。css样式的解析也是并行发生的,从link到style各个优先级的css将会应用到浏览器建立的dom结构中,用户自定义的css样式加上浏览器为每种标准html标签内置的style结合起来,就是用户看到的页面效果。script标签加载执行javascript代码也是同步开始的,尤其是现代前端,单页面web时代,javascript扮演了更加重要的作用,包括在前端更像一个完整的客户端应用,前端通过mvc、mvvm、amd等技术具备了完整端系统的能力。通过浏览器的ajax支持,websocket实现异步通讯,就能够实现纯粹前后端分离的应用。
这样我们就可以去试着理解各种web应用如何构建,并且自己亲自权衡取舍,设计构建一个合适的web应用。
对开发的意义
web全栈开发,对于我自己而言,我觉得来自于达芬奇更早的维特鲁威,在2000多年前的古罗马时代对建筑师所需要具备的能力和素质的描述,要构建永恒之道的系统,理所当然需要理解整个系统如何构建。
从系统架构的角度,软件系统,本身就是复杂的有机系统,任何构建的系统,应该是可以被一个人能够清晰完整的理解,在大脑中建立模型的。否则就需要将系统的复杂性分散到不同的子系统,对于一个逻辑完整边界清晰的系统应该能够理解。
从开发角度,在精益mvp模式开发中,团队总是处在能力不足的困扰之下,围绕系统的薄弱环节,应该有人既能看到整体,又有能力实现具体的细节。