运维服务_网站建设员月薪多少_app公司怎么赚钱_企业标识设计图案展示 上篇我补充了下SSI的知识,SSI是一个十分常见的技术,记得多年前我看到很多门户网站页面的后缀是.shtml,那么这就说明很多门户网站都曾经使用过SSI技术,其实现在搜狐网站也还在用shtml,如下图所示:

 

  由此可见SSI在互联网的应用还是非常广泛的。其实互联网很多网页如果我们按照动静分离策略拆分,绝大部分都是可以当做静态资源处理,例如新闻网站,文学网站,这些网页生成后,大部分的资源都是不变的,说白了这些网页本质就是一个静态页面,我们开发他们时候也不需要服务端的参入,每一个网站都有自己固定的板式,假如每个新网页都要完完整整的开发,重复性的工作实在太多了,出错的概率也非常高,在本系列第二篇里我曾经详细介绍了velocity的布局模板技术,其实SSI也可以制作出一套固定的模板,开发时候我们只需要在定义好的模板里添加或者修改我们需要更改的内容就可以完成一个页面的制作,可见SSI技术为了我们开发网页提供了很大的便利。

  与SSI相对应的还有ESI,这个技术不是太常用,在网上能收集到的资料也不是太多,网上有限的资料也基本都是和淘宝相关的,不过仔细研究下淘宝对ESI的运用,对于理解网站静态化处理是非常有借鉴意义的,下面我将重点讲讲ESI的知识。首先看个场景啊。

  我们登录支付宝网站,到了个人首页我们发现在支付宝下面有一个条目,如下图所示:

        

  这是支付宝默认给我们显示的【生活好助手】,右边有个箭头,我们点开它,如下图所示:

 

  我们看到这里有添加的按钮,通过添加按钮,我们可以添加其他常用的组件图标。(注意:我这里只是以支付宝这个功能为例,支付宝是否按照我说的设计,这个我就不清楚了)

  如果我们按照自己个性化添加了自己的组件,不同的人添加的常用组件也会是不尽相同的,如果我们自己开发的网站也有这样的功能,那么我们该如何设计了?我们一般都会很直观的把这个新增的组件信息存储在数据库里,用户每次登录时候该信息也会随之从数据库里读取,但是这个场景对于像支付宝这种用户量极大,日均访问量极高的大型网站,这种个性化又非核心的功能都从数据库里读取,那么它对数据库造成的压力一定是十分巨大的,在存储的瓶颈里我们讲了那么多优化数据库的手段,其核心手段有一个就是减少对数据库价值不高的操作,而这种个性化配置跟支付宝的支付操作相比起来,价值度实在太低,因此我们最好的选择是避免数据库承担过多此类的操作。

  不过像上面这个场景里的功能,它所使用的数据又不是那种可有可无的,假如数据存储的不可靠造成数据丢失还是会造成不必要的麻烦,所以我们还是会把这些信息做持久化存储。此外像上面的【生活好助手】条目还是页面的一个重要组成部分,因此像SSI那种使用html注释指令,当指令无法正常解析,就直接返回到浏览器,因为是注释,所以页面也不会显示它,SSI的这种做法用在上面场景肯定是不太合适的。这样的场景在电商网站里是十分常见的,例如一个商品页面,页面里会有商品的图片,还有商品的详细介绍,这些内容其实都是会用持久化系统进行存储,同时它们本身也是网页的重要做成部分,如果碰到问题就忽略最终会造成页面显示错误。

  结合上面的场景我们来讨论下ESI技术了,ESI技术和SSI技术类似,其实也和jsp里的include指令类似,它也是在页面里使用一个指令标签web容器解析这个标签后将获取的数据替换掉这个标签。我们来看看ESI的使用方法,我们可以在velocity里自定义一个esi标签,velocity里的使用如下所示:

esiTool.setTemplate('test.vm').addQueryData('id', 100)

 

  velocity引擎解析vm模板,最终会把vm解析成html页面,这个时候该页面里使用esi标签的地方就被转化为:

<esi:include src="test.vm.esi?id=100" />

 

  当页面到了服务端web容器之前的静态web容器(该web容器要安装好解析esi的模块),静态web容器就会解析这个esi标签,静态web容器会以test.vm.esi?id=100 作为key,到缓存系统里查找信息,如果查到了信息,缓存服务器就直接返回,用返回内容替换掉esi标签,如果缓存里没有找到则会直接请求持久化系统,持久化系统返回信息后,缓存系统将信息缓存起来,同时也将信息返回至静态web容器,那么下次用户再访问同样内容就会直接从缓存里读取了。

  ESI技术和SSI很像,只不过ESI技术是和缓存技术配合起来的,同时ESI标签也不像SSI标签那样使用注释的形式,因此ESI标签是一定要被解析的,如果仅仅是缓存,ESI和SSI比较起来也没显得那么有优势和特别,但是对于电商这种场景而言ESI的现实意义非常大,电商网站也是一个由用户产生内容的网站,每一个商家的店铺虽然我们都知道它是属于淘宝或天猫的,但是单独一个商家的店铺都是个性化很强的,与其他店铺差异很大,为了买卖商品,商家会上传自己商品的图片,还会使用图片和文字描述自己的商品,单个商品页面我们做动静分离分析,很容易分辨出动态内容和静态内容,但是如果一个电商平台拥有超乎想象数量的商家,那么每个页面的图片,文字和商家页面的关系就会变得有点微妙了。由于电商网站的图片特别多,那么电商网站系统一般都会设计一套管理这些小图片的分布式存储系统,例如淘宝的TFS文件系统,它是专门针对图片使用的分布式文件系统,这些文件系统里存储的图片会和商家紧密关联,这就让图片本身拥有了一定的动态属性,但是对于每个商家页面而言,商家自己的图片资源都是可以静态化的,也就是说图片的读取是要通过商家信息进行计算的,计算出的结果对于商家而言又是静态的,可以被缓存的。但是这个静态资源的处理时候就变得复杂了,而这些是SSI无法完成的。

  首先我们直接从文件系统读取图片,效率是非常低效,因此我们还是会把它们缓存在内存里,但是由于不同图片和不同商户是相关联,那么对于缓存查找时候是需要一定的条件,不同商户对自己页面的设计方案也会有所不同,一般商户这些资源,存储系统肯定会按照设计模板的维度存储,不同商家由于商品和文化的不一样,那么使用的模板也不一样,因此资源返回静态web容器前还需要一个整合过程,这样场景下的静态资源获取其实是需要一定逻辑计算的,那么这个计算一般都会在开发时候由代码完成,所以从上面ESI使用的例子看到,开发人员会使用velocity的esi标签,这个标签开发人员可以设置参数,velocity引擎最终会将这个标签解析成静态web容器可以解析的esi标签,标签里有这样的代码test.vm.esi?id=100,文件后面会带上参数,那么这个参数其实是动态的,那么这个参数也就是缓存系统获取正确信息的规则了。这样我们就完成了静态资源获取的逻辑计算,计算完毕后这些资源会在一段时间里长期有效,因此它就演变为静态资源,可以被缓存了。ESI比SSI强大多了,同时ESI也可以完成SSI的功能,所以使用了ESI也就没必要用SSI了。

  像我们平时做web开发时候可能没有太留心一个问题,一般的web开发里使用的静态资源例如图片,css文件,js文件我们都会放置在一个resource包里,如果是企业开发,这个web应用上线时候也就直接打包在web工程里,一些互联网网站也只不过会将这些资源放置在单独的静态服务器上,我平时开发时常听到有人说,项目里图片太多了,应该合并下,css文件和js文件也太多了也要合并下,这个多到底多多少了,几十个文件,几百个文件,这个要和社交网站,电商网站这种用户可以产生图片的网站比起来那就是小巫见大巫了,因为用户能产生内容的网站静态资源会随着时间推移文件规模变得异乎寻常的大,所以此类网站的静态资源已经没法放置在项目下,它就要求我们需要有新的手段管理这些静态资源,并且有新的手段使用这些静态资源,那么像TFS文件存储系统出现了,缓存技术出现了,最后我们在应用里使用ESI技术把它们整合到我们网页里,通过这个分析我们就能明白ESI适用的业务场景了。

  网站静态化处理我们首先要按规则拆分动静资源,拆分出来的静态资源该如何处理就是网站静态化处理的关键所在,把静态资源处理从服务端的web应用里剥离出来,不让服务端的web应用参入过多的静态资源解析,这样就可以为服务端的web应用减少不必要的处理操作,从而达到提升服务端web应用的运行效率,接着我们就把拆分出来的静态资源处理操作往前推移到静态web服务器,前两篇文章和今天的文章我着重讲解了静态web服务器处理静态资源的手段,那么这里有个问题了,这些处理可以再往前推到浏览器来完成吗?答案当然是否定的,首先浏览器的缓存是非常不可靠的,如果用户把浏览器设置为不缓存任何数据的模式,那么浏览器就没法缓存数据了,而用户的行为那是根本没法控制的,其次浏览器缓存的数据量是有限的,如果我们要在浏览器进行缓存也是缓存最有价值缓存的数据,更重要的一点,为了做好网站静态化处理我们对网页的动静资源做了拆分,但是拆分出的静态资源也并不是完全不需要进行任何逻辑处理就能使用的,例如前面讲到的ESI适用的场景我们就发现,有些静态资源的获取还是要很多条件的参入,而这些条件是由动态数据产生的,那么这样的静态数据浏览器是没法做缓存的,这点也说明了拆分出来的静态化资源绝大部分还是要停留在服务端的,居然只能停留在服务端,那么最为高效的处理这些静态资源的地方就是CDN和静态web容器了。所以在本系列的第一篇里我讲到网站生产部署时候最好是在服务端web应用之前放置一个静态web容器,如果有了这样的静态web容器做反向代理,那么我们就可以让它来完成静态资源的相关操作,而且静态web容器还能辅助完成一些逻辑上的处理,从而弥补了CDN的不足之处。当然这么做的好处不仅仅只有这些,第二篇文章里我曾经讨论了反向代理的好处,可能大家印象还不是很深刻,我将会在后面文章里对反向代理做更加深入的分析。

  讲完了ESI的妙用后,我下面将讲讲本篇的主题缓存了。其实单独讲缓存真的没啥太多内容,不过在网站静态化处理里的缓存还是和存储里讲到的分布式缓存有所不同,分布式缓存的数据都是存储在内存里,而网站静态处理的缓存既有存储在内存里还有存储在硬盘上,当然存储在内存里读取速度会更快,但是网站的读取效率还和资源距离用户的远近有关系,例如浏览器的缓存其实是把静态内容缓存在硬盘上,但是因为不需要通过网络获取资源,因此它的读取效率就显得特别高了,除了这个因素外还有个因素,我们前面做动静拆分的目的就是想拆分出静态资源,让这些静态资源远离服务端的web应用,这样可以减少服务端不必要的压力,从而达到提升服务端web应用处理能力,而把静态资源放置在静态web容器处理,它的处理静态资源效率又会高于在服务端web应用的处理,从而也达到提升静态资源读取的效率。不过如果静态资源最终只能放置在服务端,那么这个时候我们把静态资源存入到缓存里即内存里效率肯定比在硬盘上高,所以CDN的服务节点一般都是采取将静态资源存储在内存里,就算是服务端web应用前的静态web容器,如果我们让静态资源缓存在内存里,效率肯定也是比在硬盘上高。

  好了,今天就写到这里,最后祝大家生活愉快,新年快乐。

关于大型网站技术演进的思考(十三)--网站静态化处理—CSI(5)

讲完了SSI,ESI,下面就要讲讲CSI了 ,CSI是浏览器端的动静整合方案,当我文章发表后有朋友就问我,CSI技术是不是就是通过ajax来加载数据啊,我当时的回答只是说你的理解有点片面,那么到底什么是CSI技术了?这个其实要和动静资源整合的角度来定义。

  CSI技术其实是在页面进行动静分离后,将页面加载分为两个步骤完成,第一步是加载静态资源,静态资源加载完毕后进行第二步骤加载动态资源。不过这个定义还是表述的不全面,不全面的地方就是我们要强调动静分离的目的,我们把页面里的动静资源拆分出来是为了将静态资源做有效的缓存,这个静态资源可能是在静态web容器上,也有可能是在CDN上,也有可能是在浏览器上,不管静态资源是如何缓存的,我们的目的都是为了让静态资源加载的速度更快,如果我们没有让静态资源加载变得高效,就算我们使用了CSI的形式来设计页面,其实也没有发挥CSI的优点,反倒还会一不小心引入CSI的缺点。那什么是CSI的缺点呢?具体如下:

  CSI的缺点一:CSI不利于页面的SEO即搜索引擎优化。搜索引擎的网络爬虫一般是根据url访问页面,获取页面的内容后去掉没用的信息例如:css样式,js脚本,然后分析剩下的文本内容,因此假如页面的一部分内容需要进行异步加载,那么这个加载控制肯定是由javascript代码来完成的,因此网络爬虫爬下来的页面里异步加载的操作是没法执行的(听说有些高级的爬虫可以执行异步的操作,抓取异步的内容,即便有这个技术,大部分主流的爬虫还是会忽略掉javascript代码的也会忽略异步加载的内容的),这就会导致爬虫爬的页面里有部分信息丢失了,所以说CSI对SEO不太友好。不过这个缺点我们仔细分析下,可能并不会是那么严重,前面我们谈论了很多静态分离的策略,如果我们动静分离策略做的好,那么动态资源基本都是不能被缓存的内容,经常发生变化的内容,这些变化的内容本来就不需要被网络爬虫爬到,就算真的被爬到,搜索引擎有个查询结果指向了这个页面,我们点开这个页面结果也是在页面找不到被搜索的关键字,这种情形我相信很多朋友在使用搜索引擎时候都会碰到过。不过我想如果开发人员没有正确使用CSI,那么这块他们可能也不会处理的特别好,因此这个缺点还是很容易被引入的。

  CSI的缺点二:我们那么费时费力想让自己的网站静态化,目的就是想让页面加载更快点,我们简简单单把页面加载分成了两个步骤进行,那么这么做就真的快吗?这可不一定啊,其实动静分离的做法和我上一个系列里讲到的数据库读写分离有类似之处,数据库读写分离我们是通过拆分原表的读写之间的关联关系,从而达到解决读的瓶颈问题,而网页的动静分离是因为静态资源很容易被优化,所以我们要拆分动静资源。所以当我们对资源进行了动静分离,但是又没有优化静态资源,这个一看就知道我们缺少一个加速页面加载速度的操作,那么真的能让页面加载快点,还真的很难说了,而且异步加载需要执行javascript代码才行,但是静态资源加载时候很容易造成javascript脚本被阻塞,如果阻塞的脚本正好是异步加载的部分,结果只会是比以前加载的更慢了。

  由此可见,我在前面讲到的SSI和ESI技术对于我们在浏览器端发挥CSI技术优点是非常有必要的,SSI和ESI做好了能让动静分离出的静态资源加载的更加高效,这也就让CSI操作的第一个步骤变得高效,第一个步骤处理好了我们只要在页面控制好脚本阻塞对异步加载的影响,那么我们就可以达到提升整个页面加载效率的目的了。此外我觉得CSI对SEO有重大影响是个伪命题,假如使用CSI造成了SEO效果不佳,那么肯定是我们CSI方案设计的不到位。

  有人认为CSI还会有个缺点,不过笔者我并不认为这是一个缺点,这其实是一个设计问题,好与坏是根据个人的操作习惯所决定的。这个别人认为的缺点是什么呢?它就是使用CSI技术时候,虽然页面很快的被加载出来了,但是动态内容那部分可能会显示一个正在加载的提示,那么这就导致页面用户友好性降低,其实这种同步和异步加载混搭操作实在太常见了,几乎所有大型门户网站,电商网站还有一大堆数不尽的网站都是采用同步和异步混搭的加载方式,假如这些网站不这么做,我相信这些网站例如首页加载一定会慢的让人吐血,因为它们很多网页里面内容实在太多,图片也都有点爆棚了,所以它们不得不使用同步和异步混搭的加载方式,甚至很多静态资源例如图片,flash这些东西也会采取异步加载方式。说到这里,估计有人还是觉得不服气,他就是不喜欢页面加载时候还要出现个正在加载提示,但是网页里又非常需要CSI带来的好处,那么我们该如何解决这个问题呢?这个问题很好解决,首先愿意使用CSI技术也就说明用户还是很愿意使用异步的加载技术的,不喜欢则是正在加载的提示,这说明用户想要在做同步加载操作时候不要掺杂异步操作,虽然现在ajax技术大行其道,但是ajax技术有个同步加载是没有办法解决的,那就是我们在浏览器地址栏里输入网站url请求页面 ,所以面对上面的需求我们只要保证这种同步操作只是一个纯粹的同步操作而不要掺杂异步加载即可,这个方案还是很好实施的,这里我就不再累述了。

  动静分离后我们会把静态资源进行缓存,前面文章里讲了一大堆都是在讲服务端的静态资源缓存,现在讲到了CSI已经到了浏览器端,那么我们就得谈谈浏览器的缓存操作。页面的缓存操作就是使用http的expires和cache-control,我们首先看看下面的写法:

<meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0">

 

  这是我现在做的java的web项目里,jsp和vm文件都会使用的meta配置,它的目的就是让页面不要被浏览器缓存,但是如果使用CSI技术,同时动静分离做的很好,那么在页面头部其实我们可以不再这么写了,我们可以让页面在合理的时间范围内被浏览器缓存,如果该页面做了缓存操作,那么以后我们再访问该页面,网页的加载效率就会变得更高了。

  这里还有个问题,在雅虎优化网站的建议里,为了充分利用网页并行加载的特点,我们往往会把图片,外部的js和css文件放置在单独的静态web容器或CDN上,那么这些文件往往也是可以被浏览器缓存,这个我们又如何设置才能让浏览器知道要缓存它们呢?这里我们以apache为例,为了让静态资源被浏览器缓存,apache需要使用mod_expires模块,然后在apache的配置文件里添加如下配置:

 

<FilesMatch "\.(gif|jpg|png|js|css">ExpiresDefault "access plus 10 years"</FilesMatch>

  那么浏览器访问此apache上的静态资源后,浏览器就会把图片和该服务器上的js和css文件缓存在浏览器里。

  我们看看被缓存的静态资源是如何被使用的,如下图所示:

 

  当http的响应码是304的时候,那么浏览器就会从缓存里读取资源了,这里有的朋友可能会感到奇怪为什么缓存的资源还要发送个http请求了?理解这个我们就要了解下缓存的机制,缓存的含义是临时保存某些东西,既然是临时保存,那么就应该有个保存的有效期,我们定义缓存的方式是通过http完成的,那么按道理检查缓存是否过期也应该是http来决定的,因此每次使用缓存时候我们要发个请求到服务端,服务端会检查下资源是否过期了,如果没有过期,服务端返回个304的响应码,304的返回响应是没有http报文体的,所以这个http请求的返回数据是非常小的,因此这个http效率还是很高的,如果服务端发现资源过期了那么服务端就会把新资源返回给浏览器了,其实这个检测资源是否过期的请求有个专有名词叫做条件Get请求。至于服务端是如何完成检查操作,本系列在讲web前端优化时候会详细阐述,这里就不深入了。看到这里估计有朋友又有疑问了,为什么缓存是否过期不能在浏览器端来做了?这主要是浏览器做这个检查非常不准,因为用户的电脑时钟不一定准确,或者用户电脑时钟和服务端不一致,如果再加上时区那么就更加麻烦了,所以缓存失效最好是在服务端进行,这样缓存的有效期的准确性才能得到保证。html5的出现,浏览器缓存的能力大大增强了,不过使用html5技术进行缓存我还没有深入研究过,所以这里也不讲述了,有兴趣的朋友可以自己研究下。

  好了,CSI主题内容讲完了,讲到CSI技术和浏览器我们就可以开始本系列另一个重要内容前后端分离了,这将是我下篇的主题,我在自己博客里多次讲到前后端分离,马上又要再次讲了,这次讲是我这么长时间做前后端分离研究的大总结了。

  最后,祝大家新年快乐,新的一年喜气洋洋,开开心心哦。

关于大型网站技术演进的思考(十四)--网站静态化处理—前后端分离—上(6)

前文讲到了CSI技术,这就说明网站静态化技术的讲述已经推进到了浏览器端了即真正到了web前端的范畴了,而时下web前端技术的前沿之一就是前后端分离技术了,那么在这里网站静态化技术和前后端分离技术产生了交集,所以今天我将讨论下前后端分离技术,前后端分离技术讨论完后,下一篇文章我将会以网站静态化技术的角度回过头来重新审视下前后端分离技术,希望通过这种审视来加深我们对两套技术的理解。

  前后端分离技术我个人认为是web前端被专业化以后的必由之路,而nodejs的出现是前后端分离技术的一个强兴的催化剂,原因是nodejs的出现削平了前端技术和服务端技术之间的鸿沟,使得前后端两套不同技术体系进行真正意义的解耦提供了无限的可能性。但是如果我们把nodejs技术的使用认为就是实现了前后端分离,这种理解又实在太肤浅了,下面我将讲讲我研究过的前后端分离技术方案,以及这些技术方案隐藏在背后思考,希望这些思考能给大家以一个新的思路来理解前后端分离技术。

  我们要深刻理解前后端分离技术有一个重要的前提,那就是要把前后端分离技术认为是传统的web应用里的MVC设计模式的进一步演进。那么我们首先来看看MVC的定义,下面的内容摘录于维基百科的解释,具体如下:

MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。MVC模式最早由Trygve Reenskaug在1978年提出[1] ,是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件设计模式。MVC模式的目的是实现一种动态的程式设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更加直观。软件系统通过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。专业人员可以通过自身的专长分组:(控制器 Controller)- 负责转发请求,对请求进行处理。(视图 View) - 界面设计人员进行图形界面设计。(模型 Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。

  各类用于Web应用开发的语言里都有属于自己的MVC框架,例如本人最熟悉的服务端语言java里就有大名鼎鼎的struts2,springMVC的MVC应用框架,我早期从事java的web开发时候认为这些MVC框架都是非常的博大精深,用途广泛,但是当我逐渐转向了web前端技术开发以后又觉得这些框架的很多功能显得那么的多余和累赘,因此我曾写过一篇文章专门讨论过这些问题,该文章的名字叫做《为什么做java的web开发我们会使用struts2,springMVC和spring这样的框架?》。

  其实这篇文章被写的源头就是在于我认为像struts2和springMVC这样的框架做了太多浏览器本身就可以完成的工作,例如:页面的渲染操作,因为服务端抢了浏览器端的部分工作,这其实也就等于限制了web前端技术的深入运用,像很多前端的优化技术以及很多提升用户体验的技术就很难派上用场,之所以产生这些问题,我认为传统的MVC框架本质其实是一个服务端的MVC框架,虽然MVC设计模式里的V即View视图层是想把界面开发工作专业化,让界面设计人员能专心于界面开发,但是传统的MVC框架下的View层的本质却是一个不折不扣的服务端技术。

  我们以java的web开发里jsp为例,JSP全名为Java Server Pages,中文名叫java服务器页面,其根本是一个简化的Servlet设计,它是java里动态网页的技术标准,这就说明jsp虽然看起来像html,其实它并不是真正的html,它需要被java的web容器进行解析转化为浏览器可以解析的html页面,然后通过网络传输到浏览器后,浏览器才能正确的展示这个jsp页面,其他web开发语言里都有类似的动态网页技术标准,但是不管什么语言的动态网页技术标准,我们使用它时候就是让web前端技术被服务端技术所绑架,这也就是为什么每个招聘web前端工程师的岗位都要问你是否会java,php语言的源头。但是随着互联网的大发展,对web前端的要求是越来越专业化,web前端本身所包含的技术难度已经不亚于任何一个服务端语言开发难度,因此我们需要web前端更高的专业化,而不希望web前端工程师被服务端技术束缚的更多而限制了自身能力的发展,这就导致前后端分离技术的出现。

  不过前后端分离技术的第一阶段倒不是从改变view层即视图层开始的,而是从连接客户端和服务端的C层即控制层开始的,控制层既要作用于客户端又要作用于服务端,如果一个功能页面是一个程序员从浏览器端一直写到模型层,控制层也就不是什么问题了,但是如果当我们想按MVC的设计思想,让界面开发人员专注于页面开发,服务端开发人员专注于服务端开发,那么这个时候控制层的归属问题就显的非常重要了。在传统的MVC框架里,因为M层和C层是使用同样的语言体系,因此我们很自然会把M层和C层的开发工作都交由服务端开发人员完成,这个决定无可厚非,但是传统的MVC框架里V层和C层其本质也是同一个技术体系下的(例如java的web开发里的jsp本质就是个servlet),因此V层和C层也是紧耦合的,因此界面开发人员开发页面时候如何没有C层支撑,那么这个页面其实是根本跑不起来的,如果前端开发人员这时候跑去写写C层即控制层的代码,这就打破了原有的横向分工,这个时候控制层的编码工作就会变得混乱而难以控制,看到这里有人一定会说既然控制层是属于服务端的,那么前端技术人员就等等服务端的开发进度,再不行就自己写个mock模拟下服务端的控制层,听到这种建议,我相信不管是前端的还是服务端的技术人员都会头脑发麻,第一反应就是这不是自找麻烦啊,还不如一个人全部搞定算了。由此第一阶段的前后端分离技术方案出现了,这个方案需要解决的问题就是如何能让web前端技术人员和web服务端技术人员协同起来工作,合理的分工,换句话说就是按web前端和web服务端角度如何能横向的分解web的开发工作。

       前后端分离的第一阶段需要解决问题的核心就是控制层的归属问题,从技术角度而言就是控制层到底是应该和视图层解耦比较合理还是跟模型层解耦比较合理的问题。那么我们这里先回顾下MVC设计模式里对控制层的定义,维基百科里的定义是:

(控制器 Controller)- 负责转发请求,对请求进行处理。

 

  不过这个解释我认为并不全面,以java的web开发里的控制层设计为例,我们发现控制层以沟通视图层和模型层的角度而言,控制层其实主要完成三项具体的工作,它们分别是:

  工作一:控制层起到一个路由的作用。客户端请求到达控制层后,控制层根据请求内容将请求路由到服务端某个模型层进行处理,模型层将请求处理完毕后,会把响应结果返回给控制层,控制层在根据响应信息路由到特定的页面。

  工作二:控制层起到一个报文信息格式转化的作用。这里以java的web开发为例,浏览器的数据都是以http报文形式发送给服务端,而控制层就是将http报文信息解析成java的对象,当然也可以是java的基本数据类型,然后控制层把解析好的信息传递给模型层进行处理。

  工作三:传统的MVC框架里,控制层其实深入参入到了页面渲染的操作。在java的web开发里的控制层不管如何被包装,其本质就是一个servlet,而jsp页面本质也是个serlvet,因此我们可以这么理解jsp,jsp就是以页面开发的方式写java,而servlet就是以java的方式写页面,所以我们可以在servlet里以文件流的方式输出页面,也可以让servlet跳转到jsp页面。

  由上面的论述里我们发现,其实传统MVC框架里控制层和模型层的联系方式相对很简单的,它们的联系主要是路由和报文格式的转化上,而控制层与视图层的联系除此之外还多了一个页面渲染,而页面渲染本身应该是属于浏览器的技术范畴,是浏览器技术不可分割的一部分,也是我上面内容里诟病传统MVC框架问题所在,如果控制层承担了页面渲染工作,那么控制层和视图层的耦合度就变得非常高,要想将其解耦是十分困难,一般只有我们打破了现有MVC框架的技术体系才能完成,相比之下,控制层与模型层的解耦就显得容易多了。那么控制层与模型层如何解耦呢?具体如下:

  首先我们来解决下报文格式转化的问题,这个技术方案很简单就是借鉴http统一报文格式的特点,我们为控制层和模型层定义一套统一的报文格式,例如我们定义控制层和模型层都以map的数据类型进行数据传递,这个map里有个专门的字段用来定义被路由到的模型接口信息,有个字段专门存储需要传递的数据,具体的设计方案可以根据实际的业务需要来设计。

  接下来就是路由的问题了,在解决报文格式转化问题的论述里我讲到要在统一报文格式里专门定义一个字段用来存储该数据到底路由到哪个模型进行处理,不过这个字段并不能完全解决路由问题,因此我们需要模型层对控制层提供一个统一的接口,任何控制层与模型层的沟通都通过这个统一接口来完成,只不过不同请求报文组装的内容不一样而已,而这个接口还有个重要职责就是解析报文里的路由信息,让请求能被正确的路由到对应的模型接口所处理。当然这个接口的返回值最好也是一个统一的报文格式,这样控制层解析模型层的返回数据也会便利的多了。

  由上所述,我们发现第一阶段的前后端分离工作控制层应该归属于web前端,这么做更加合理,也更加容易实现,其实之后进化版的前后端分离方案,控制层也都是属于web前端,只不过形式不同而已,这个我在下一篇文章里继续讨论。

  第一阶段前后端分离方案解决的核心就是让控制层和模型层解耦,这个方案进一步演化一下,我们可以把控制层和视图层独立成一个web应用,模型层也独立成一个web应用,两个web应用之间通过远程调用方式进行沟通,这个方案我在以前文章里写过,这篇文章的名字叫做《我设计的网站的分布式架构》。

  这个进化版的方案增加了系统开发的难度,因为我们需要增加网络通信的编程以及远程调用的实现,更麻烦的是我们还需要进行复杂的多线程编程,既然增加了开发的难度为什么我还要这么做呢?首先我们通过应用分层,可以动态的调节web前端和web服务端的负载压力,还可以在模型层之前提供一道安全屏障,不过被服务端绑架的web前端在提升整个web应用负载能力这块还是很有限的,其实这种做法的最大好处就是利于SOA框架的设计,也就是说这种架构我们可以为服务端的SOA化提供有力的保障,因为控制层和模型层的解耦,可以让模型层真正做到专注于业务,而不会再发生那种把业务逻辑写到控制层的问题了从而降低代码的健壮性。

  好了,今天就写到这里,最好祝大家新年快乐,晚安了。

关于大型网站技术演进的思考(十五)--网站静态化处理—前后端分离—中(7)

上篇里我讲到了一种前后端分离方案,这套方案放到服务端开发人员面前比放在web前端开发人员面前或许得到的掌声会更多,我想很多资深前端工程师看到这样的技术方案可能会有种说不出来的矛盾心情,当我的工作逐渐走向越来越专业化的前端开发后,我就时常被这套前后端分离方案所困惑,最近我终于明白了这个困惑的本源在哪里了,那就是这套前后端分离方案其实是服务端驱动的前后端分离方案,它的实现手段又是从服务端的MVC架构体系演化而来,因此该方案最大的问题就是它并没有从根本上改变web前端从属于服务端的被动局面。那么问题来了,有没有以web前端为驱动的前后端分离方案呢,该方案能让web前端的能力获得更大的释放了?答案是绝对有。本篇就要讲讲以web前端驱动的前后端分离方案

  首先要提的就是javascriptMVC,下面我摘抄的是维基百科里对javascriptMVC的解释,具体如下:

  首先是简介:

JavaScriptMVC 是一套开放源代码的多样化互联网应用程序框架,以 jQuery 与 OpenAJAX 为基础。JavaScriptMVC 利用 MVC 架构与工具扩展这些函式库,以便开发与测试。由于 JavaScriptMVC 不需要任何服务器端的配合,因此它可以和任何的网站服务接口与编程语言整合,如 ASP.NET、Java、Perl、PHP、Python 或 Ruby。

 

  接下来是历史:

JavaScriptMVC 的第一个版本是在2008年5月释出。稳定版的 JavaScriptMVC 2.0 在2009年6月释出,并以 jQuery 为基础。主要开发目标为维持程式码的简短和专注在它独特的功能上。3.0版本在2010年12月释出。而从 JavaScriptMVC 中所独立出来的 MVC 架构“CanJS”则在2012年4月释出。

  从维基百科里的解释我们会发现如下启示,它们分别如下:

  启示一:javascriptMVC是一个应用框架的名字,这和jQuery的命名是一样的,所以这里我要声明一下,本系列里的javascriptMVC不是指代这个框架,而是指代的是使用javascript语言实现出的一类的web前端的MVC框架,本系列后面的javascriptMVC和前端MVC的含义是一致的。

  启示二:从javascriptMVC历史里我们可以看到第一版的javascriptMVC产生于2008年,这个历史要远早于nodejs出现的时间,这说明了前端的MVC并不是因为nodejs的出现而产生的,应该是nodejs推动了前端的MVC框架的应用和普及。

  启示三:维基百科里有一段解释:

由于 JavaScriptMVC 不需要任何服务器端的配合,因此它可以和任何的网站服务接口与编程语言整合,如 ASP.NET、Java、Perl、PHP、Python 或 Ruby。

 

  这段话说明了前端MVC的一个很重要的特点就是前端MVC可以摆脱服务端语言的束缚做到真正的独立,同时前端MVC又可以和任何服务端语言进行整合,大家可以试想下如果我们开发的web应用前端达到了前端MVC的程度,那么公司在招聘web前端工程师的时候就不在会问你“你会java吗?”或者“你会php吗?”假如这个前端工程师所会的服务端语言能力和公司不匹配,面试官也不会再犹豫和摇头了。

  启示三同时还隐含了一个问题,为什么好的前端MVC框架可以做到和任何服务端语言配合呢?这个解决手段之一我在前文中的第一阶段前后端分离方案里就提到了,那就是解决报文格式的统一和交互接口的统一的技术手段,只有这样前端MVC和服务端的灵活对接就不会再是问题了。但是仅仅这个手段还是远远不够的,我们要达到这个需求还需要解决一个问题,这个问题就是要把服务端MVC霸占web前端的工作也要抢回来。那如何抢呢?

  上篇文章里我分析过服务端MVC的视图层的问题,服务端MVC的视图层技术例如java里的jsp技术,这个技术是将html和java代码整合的技术,java的web容器把jsp解析完毕后最终生成为html文件发送给浏览器,浏览器在解析这个html将最终效果展示给用户。那么我们要抢回服务端霸占的web前端的工作我们就得分析下这些动态页面技术到底做了哪些事情特别是侵占web前端的事情。

  这里首先我们要谈谈服务端在动态页面里的作用,其实服务端为动态页面作用很单一就是提供了网站需要展示的数据而已,服务端是不会创造一个新页面的。服务端提供的数据的类型也是很统一,要不就是服务端语言提供的基本数据类型例如:字符、数字、日期等等,要不就是复杂点的数据类型例如数组、列表、键值对等等,不过归属服务端的动态页面还需要服务端语言帮助做一件事情,那就是把服务端提供的数据整合到页面里,最终产生一个浏览器可以解析的html网页,这个操作无非就是使用服务端语言可以构造文件的能力构建一个符合要求的html文件而已。不过一个页面里需要动态变化的往往只是其中一部分,所以做服务端的动态页面开发时候我们可以直接写html代码,这些html代码就等于在构造页面展示的模板而已,而模板的空白处则是使用服务端数据填充,因此在java的web开发里视图层技术延生出了velocity,freemark这样的技术,我们将其称之为模板语言的由来。

  由此可见,服务端MVC框架里抢夺的web前端的工作就是抢占了构建html模板的工作,那么我们在设计web前端的MVC框架时候对于和服务端对接这块只需要让服务端保持提供数据的特性即可。从这些论述里我们发现了,其实前端MVC框架要解决的核心问题应该有这两个,它们分别是:

  核心问题一:让模板技术交由浏览器来做,让服务端只提供单纯的数据服务。

  核心问题二:模板技术交由浏览器来承担,那么页面的动态性体现也就是根据不同的服务端数据进行页面部分刷新来完成的。

  而这两个核心问题解决办法那就是使用ajax技术,ajax技术天生就符合解决这些问题的技术手段了。

  要让web前端承担模板技术,就得使用javascript的模板技术,时下javascript的模板技术可谓是百花齐放,百家争鸣,很多朋友曾为这些技术称奇,其实探求它的本源无非就是用javascript为基础实现了个jsp,velocity而已,如果有朋友还没接触过javascript模板技术,可以在百度里搜索下【javascript模板引擎】,本文这里就不展开谈论了。

  前端的MVC讨论到这里又出现了一个新的疑问,我上面讲到解决前端MVC两大核心问题的手段是ajax技术,ajax是异步请求,那么这是不是就是说让网站全部使用异步请求我们就可以实现前端MVC,并且解决网站所有的问题呢?

  这个问题的回答当然是不可能的。一个网站是永远没法摆脱与异步请求相对的同步请求,就算有个网站把异步做到了极致,但是它也无法摆脱用户第一次访问要在浏览器地址栏填写网站入口页面url地址的同步请求问题,网站把异步操作做到极致也无非就是把网站做成了一个纯粹的单页面形式而已。

  纯粹单页面的网站很多人一听到就觉得好牛逼啊,很前卫,很厉害,对前端有所了解的人还会想到单页面也就意味要运用更多的javascript编程和DOM编程,前端代码难度也会大大增强,好的单页面应用如果这个应用还包含复杂的业务逻辑,那么单页面前端开发里很可能还会使用到如今很火爆的javascript模块技术例如requirejs或者seajs技术,单页面听起来实在太完美了,但是我们冷静下来思考下,单页面真的完美吗?下面我要为单页面泼泼凉水了,具体如下:

  泼凉水一:单页面其实指的是网站只有一个入口,但是并不代表用户看到的网页就是一个样子的,单页面里也会有很多页面切换,但是不管页面里的模样如何变化,浏览器地址栏的地址都不会变化,能做到这点就得归功ajax的超强能力了,单页面不同模样的展示都是在javascript代码里实现的,那么问题来了,单页面对于搜索引擎的网络爬虫就非常不友好了,因为网络爬虫是根据url抓取页面,抓取完毕后会忽略javascript代码,那么单页面的设计方案就会导致SEO优化只能作用于首页,而网站其他页面将无非有效的被SEO技术进行优化。

  泼凉水二:一个网站做成单页面以后那么网站不同的展示都在一个url下面,但是如果有些用户只是对网站的某一部分功能很感兴趣,而这部分功能又不是被单页面的唯一同步请求所展示的首页里的内容,那么结果就是这些用户每次登陆网站时候都要手动操作一下才能进入自己想要的功能页面里,假如首页进入功能页面的操作步骤比较繁琐,那么这个必然会导致网站用户体验的下降。

  那么上面的问题该如何来破呢?

  这里我首先来讲讲第二个问题的解决方案,第二个泼凉水的问题的核心就是要记录单页面的状态问题,这个状态可以帮助首页能快速切换到具体的功能页面,要让客户端网页有状态最常用的手段就是cookie了,如果浏览器支持html5,那么保存状态的手段就更多,能力也更强了。但是这种手段是和客户端紧耦合的,那么如果碰到这种情况,该手段就会出现问题了,例如如果有个人发现单页面网站里一个很有趣的功能,这时候他正好和朋友QQ聊天,他告诉了他的朋友,他的朋友也该兴趣,让他把链接发过来,那么这个朋友就不得不在从首页在重复操作一遍,由此可见,cookie的手段并没有全面解决这个问题,那我们还有其他手段嘛?

  答案是还真有,那就是使用html的锚链接,锚链接的形式如下所示:

http://www.baidu.com/#sharpxiajun

 

  下面是我摘抄下百度百科对锚链接的解释:

锚链接实际上就是链接文本,又叫锚文本。可以理解为:带有文本的超链接,就叫锚链接。锚文本可以作为文本链接所在的页面的内容的评估。一般的来讲,网站页面中增加的锚链接都和页面本身的内容有一定的必然联系。网站建设的行业网站上会增加一些同行网站的链接或者一些做网站建设的知名设计网站的链接;另一方面,锚文本能作为对所指向页面的评估。锚文本能精确的描述所指向页面的内容,个人网站上增加Google的链接,锚文本为 “搜索引擎”。这样通过锚文本本身就能知道,Google是搜索引擎。

 

  那么在单页面里的功能切换时候我们改变一下url上的锚文字,反过来说使用锚文字做路由器,让其可以路由到对应的功能页面那么上面的问题不就可以解决了。关于锚链接我这里要补充一些知识,首先锚链接的形式是url#文字,锚的起始标记是#号,这个#号的内容其实是属于浏览器端的,也就是说#包括#号后面的内容是不会被发送到服务端的,那么我们想改变锚链接只能在客户端进行,但是传统的锚链接的变化是很难被javascript语言监控到的,直到html5的出现才从根本上解决了这个问题,html5提供了hashchange事件,该事件可以监控锚链接的变化,因为javascript语言可以监控锚链接的变化,那么使用锚链接路由功能页面就成为了可能,那么低版本的浏览器该怎么办了?这个主要是ie的问题了,其实ie8包括ie8都支持hashchange事件,再低就不行了,不过jQuery有个插件可以让低版本的ie支持hashchange事件,有兴趣的童鞋可以百度下啊。

  看来泼凉水二问题是有解的,那么泼凉水一怎么解决了?我的回答是基本无解,这个问题的关键在网络爬虫这边,如果我们被动解决这个问题,那只能是抛弃javascript了,这个玩笑就开大了,所以我们只好祈求各大搜索引擎能不能智能化再厉害点了。这里加个题外话,我最近几天突然意识到一个问题,那就是讲到web前端技术我一定要加强对SEO的思考,因为绝大多数网站都会把搜素引擎当做入口的生命线,这是一个很难回避的问题,不管我们网站做的如何优秀,假如用户很难找到它,那一切都将会是百搭,而在前端设计里要加入SEO的思考,这必然会导致整个架构的重大变化。这个问题我会在以网站静态化角度审视前后端分离方案时候重点讲下。

  前端MVC讨论到这里我们会发现我们的谈论里缺了一环那就是MVC的M层模型层,web前端要侵入到模型层了,这不就等于web前端要造反了,它不仅仅想改变从属服务端的悲惨命运,还要抢夺服务端的部分功能,让服务端成为浏览器对应的存储系统,这不是无异于虎口夺食,在时下服务端如此强势的大环境下,这种想法简直就是活得不耐烦了,哈哈,当然这是戏言了,做技术做工程还是要讲求个合理性和逻辑性的,技术和工程都是实在的东西很讲道理的,只要道理站得住脚怎么个做法都是其次,回到问题本身,我个人觉得在PC端讨论web前端做模型层其实往往利大于弊,就安全而言,模型层意味有大量业务逻辑推移到web前端,那么安全的保障难度会加大,就技术难度而言,web前端做模型层会让javascript编程巨复杂,所以要做这个抉择时候一定要结合业务做仔细的权衡,其实我现在接触的一些说包含模型层能力的前端框架在实际运用里模型层的功能还是使用太少,不过这个问题如果放到移动端,或者是PC和移动端融合可能就会有些不同,这个问题我将在本系列的终篇里再谈谈,这里也不累述了。

  说到这里需要总结下了,前端的MVC不应该等于单页面开发,前端MVC也不是把ajajx用到极致,根据实际业务场景,我们需要适当的把同步请求和异步请求结合起来。如果前端MVC里包含了更多同步请求,那么对于MVC里的C层即控制层就会有更高的要求。前后端分离主题还有个下篇,下篇里我还会提到一种前后端分离方案那就是nodejs的运用,而nodejs的运用就是和控制层有密切的关系,上篇里我提到nodejs是前后端分离方案的催化剂,其实我个人认为nodejs参入的前后端分离方案才是更加完美些的前后端分离方案,这个完美的评价原因之一就是从前端承担控制层作用角度思考的,所以前端控制层这个内容我将放在下篇讨论。

  好了,本篇写完了,从本篇我们可以看到前端MVC的历史很早,它的出现早于nodejs,这就说明前端MVC其实并不是什么新技术,只不过是现在才被大家重视起来,完善它的人也越来越多。从本篇我们还发现前端MVC其实并不完美,问题很多,最致命的就是对网络爬虫的不友好,所以我们需要考虑到SEO技术参入其中的前后端分离方案。

  最后,祝大家晚安了。

关于大型网站技术演进的思考(十六)--网站静态化处理—前后端分离—下(8)

我第一次听说nodejs技术大概是在2009年年末,不过我真正认真在网络上进一步了解nodejs还是在2010年年中,当时对nodejs的认识和我现在对nodejs的认识有着天壤的区别,开始想了解nodejs我只是为了感慨谷歌公司开发的V8引擎居然如此强大,它不仅仅可以作为chrome浏览器的javascript内核运行平台,居然还能为服务端使用javascript语言作为平台,通过对nodejs的了解让我认识到chrome浏览器是如此的优秀,但是如此相对的是我并不认为javascript作为服务端语言真的会有市场。

  为什么我当时会认为javascript作为服务端语言的前景堪忧呢?我当时有如下的思考,这些思考放到时下nodejs已经非常火爆的背景下,我相信对很多朋友任然有参考意义,下面是我当时的思考,具体如下:

  质疑nodejs思考一:2010年之前我还不是敢自称自己是一名专业web前端的工程师,因此对于javascript的认识和掌握程度也不能和现在相比,但是对于javascript的难学,难深入却是有着切肤之痛,因此我想javascript作为服务端语言就是让会其他服务端语言的工程师更加深入的学习常被服务端工程师诟病的javascript,这么做的结果无异于逼迫服务端工程师转向成web前端工程师嘛,这个想想就让人觉得不现实。

  质疑nodejs思考二:我对web应用开发的技术选型认识比较肤浅。技术的选型是个很宽泛的问题,回到我对nodejs的质疑思考主要是体现在web应用服务端语言选择上,在中国用作web服务端开发的语言非常多,但是主流的无非就是java、php、C#以及C语言系列,当然web服务端技术发展到现在Python、ruby也是有一定市场,作为一名具体干活的软件工程师对于项目选择何种技术是没啥发言权的,因此我常常觉得技术选型就是项目经理或者是技术经理以及架构师的问题,而大多时候我们去询问为什么用这个服务端语言得到的答案都是非技术性的回答,例如:公司主要是使用php啊,java比较流行人好找啊,C#开发快啊能很快的完成工作,很少有人会这么告诉你我们的项目是个什么样的项目,这个项目使用A语言比使用其他的B语言、X语言有何种好处和优势,其实中国很多软件企业做项目在技术选型这块都很粗,说的难听点其实就是很多能控制项目的人技术水平很难被恭维,当然大部分项目其实使用什么技术实现并不是太重要的问题,但是这个到了技术架构异常复杂的大型网站技术选型问题就显得尤为重要,这个认识主要是来自于我阅读《淘宝技术这十年》所感受到的,淘宝网站的技术选型随着业务的发展变化的如此之大,颠覆性如此之高,这个在我待过的很多项目组都是难以令人想象的。

  Web应用发展这么多年,那些占据了天时、地利和人和的现有技术基本都是处于一个垄断的地位,新的同类型语言想突破重围必然有着自己独有的技术优势,这就好比在中国做互联网如果有家新型互联网公司可以突破BAT的围追堵截,那么这家公司一定是有着自己得天独厚的优势,所以nodejs一定是获得一种得天独厚的优势,那么nodejs优势在哪里了?不过在讲述nodejs的优势之前我们先来讲讲上篇文章里遗留下来的问题。

  其实上篇里我讲到前端MVC,文章里只是着重讲到了V层即视图层和M层即模型层的问题,而唯独没有专门讲解C层即控制层的问题。在前后端分离文章第一篇里,我谈到如果把MVC框架里的C层以作为连接web前端和web服务端的角度来理解,C层主要承担了三个方面的工作,它们分别是:路由、报文格式转化和页面渲染的工作。前端MVC在处理报文格式转化和页面渲染这两个方面还是比较容易做到,但是在做路由这块存在一定问题,前端MVC框架对于获取服务端数据这块以及异步请求处理方面其实和传统MVC框架的处理的手段本质上是类似,只是实现载体有所不同而已,但是控制层还有一个路由功能,其中用于页面切换的路由存在一定的问题,不过这个切换也要限定一下范围,页面通过ajax技术让页面部分刷新,假如这种部分刷新让页面展示效果发生很大变化,对于用户而言也是页面发生了切换,但是这种切换是不会让地址栏的url产生任何改变,这就是问题的根本所在了,我在上篇里已经讨论过这些问题,通过这些问题我们发现如果页面转化时候地址栏的地址随之也发生改变是会给用户体验、网站的友好度以及SEO优化带来好处的,如是乎我提供了一种手段,那就是使用锚链接来帮助我们实现url的变化,因为锚链接只是作用于浏览器,因此这种手段是对前端MVC的C层实现页面路由功能的一种很好的支持,但是因为这种方式需要在javascript里完成,那么对于SEO优化就产生了问题,最后我提出了页面切换我们最好使用同步请求的方式。

  这个时候问题来了,如果要使用同步请求,那么这个同步操作自然是要让服务端来控制,这么做的结果就是让服务端再去回收部分控制层的功能,这样下来一个使用前端MVC架构的网站就有点不太纯粹了,具体点就不是一个单页面的网站,这里我们的讨论又回归到了单页面的问题了,前文讲前端MVC框架很多热心网友对我的论述发表了有价值的评论,但是我发现我的想法有些朋友可能没有真正理解(这也许是我的表述的问题吧),我前端MVC讲述的一个思路是以批判前端MVC的角度进行的,我早些时候和一些网友探讨过前端MVC的设计问题,有些朋友在没有做具体web前端MVC架构前总是想实现纯粹的前端MVC框架,延着这些朋友的思路我们就会把所有的C层和M层的东西都移到web前端,我常想如果真的这么实现了,结果自然就是单页面网站了,或者就是在前端引入了复杂的模型层设计,不过探讨毕竟是探讨真的实现时候很多朋友就会知道了难度所在了,所以说理想和现实是有差距的,这话又一次灵验了。

  这种理想和现实的差距,其实就告诉我们一定有个地方出问题了,那么问题在哪里了?下面我将我对这个问题的思考,总结如下:

  问题思考一:让前端承担大部分MVC的工作,那么前端本身的技术能力是否能达到所有的要求吗?这个回答似乎是肯定的,例如单页面的出现就代表了这种可能性,javascript也是拥有强大的面向对象的编程能力,因此再写复杂的业务模型层也是没问题,但是前端这么做了以后其实并不能满足所有人的需求,例如:SEO的要求,SEO很多技术都是以同步网站请求技术为根基,这个和前端MVC框架以ajax技术为根基产生了冲突,这就让前端技术产生了局限性。使用javascript面向对象技术来实现业务模型,这个也是有问题,javascript的面向对象的学习成本和精通难度超出了传统的面向对象的语言例如像java这样的语言,而且javascript要设计和写出更加容易维护的代码是非常不容易的,这么做不符合我在存储系列里讲到的要用最简单的方式实现的原则,这其实也是说明前端技术能力不足的问题。

  问题思考二:其实不管什么形式的前后端分离方案它最根本的思想就是让前后端进行解耦,让不同技术语言体系下的人能做到工作的隔离,最后协同起来各自发挥出自己的最大价值,但是如果我们只是按前端,后端的角度来做分离,是不是有点粒度过粗,考虑是不是过于片面了?特别是这个片面的问题,web应用的问题并不是一个纯技术问题,而是一个技术和业务结合的问题,因此任何应用于生产的技术方案都会受到业务的影响,例如上面当我们要考虑SEO的问题,考虑开发难度的问题,那么纯粹的前端MVC的框架就会显示出自己的局限性。前端技术无法改变浏览器地址栏的url,这个从很多角度思考是个合理的设计,但是到了前端MVC里对C层的设计而言则变成了一种技术手段的局限性了。因为这种局限性就让我们不得不回到问题的原点状态,例如页面的同步请求,而同步请求最合理的控制地点就是服务端了。

  问题思考三:本思考是一个延生性的思考,我从事这么多年的web开发,我其实一直困惑于web应用开发和MVC的关系,为什么我们做web应用开发时候都要那么强调和重视MVC设计思想呢?难道web应用开发的世界没有MVC就不能活了吗?回首下web应用发展的历史,在web应用开发的忙慌年代,的确是看不到MVC的影子,那个时代的确很自由,自由到许多web应用混乱不堪,质量和健壮性差的不能再差了,这个时候一个英雄出现了那就是MVC,MVC代表了一种次序,一种基本的法则,这就好比人类社会建立的根本原则一样,这些原则让人类和野兽有了区别,人类也因为这些原则而成为万物之灵长,相比之下MVC就是web开发世界里的游戏规则和行为准则,因此只有当我们从MVC角度思考web应用的建设,才会让web应用更加的优秀,这也就是在讲述前后端分离技术时候我都是以MVC思想作为准则进行思考的。思考回到具体的场景,MVC思想的运用就是让我们把web应用开发里可以归为一类的场景聚集在一个范畴之下,不同范畴使用一种双发都可以接受的统一准则进行沟通,这么一来我们就把需要解决的问题简单化了,各个独立的范畴因为减少许多不必要的干扰,因此能让它们发挥出更大的潜力,更重要的MVC还让web应用伸缩性,健壮性,可维护性大大增强。例如在很多传统web应用开发里在控制层这块前后端的矛盾就是属于MVC规则使用不完善所致。

  单页面的应用存在很多问题,因此需要同步请求的介入,这就导致了服务端再度回收了失去的控制层的功能,这么做也无可厚非,但是我很担心这个改进的引入会不会导致传统MVC框架里控制层的混乱问题,根据我的经验,这种混乱的程度已经降低了很多很多,基本我们可以忽视原来C层的问题了。

  不过很多有追求的web前端工程师对于这种不纯粹的前端MVC的异议还是很多的,大部分异议还是源自浏览器能力的局限性,当服务端很多方面被弱化后,也许可以解决我们以前在前端被服务端束缚的很多问题,但是同时又产生了新的问题,这些新问题我总结如下:

  新问题一:在传统的网站动静划分里,我们常把浏览器端的技术html、css和javascript归结于静态技术的范畴,如果网站使用Web前端MVC那么前端就会接过很多动态网站的功能,这个时候传统的静态技术就被人为的演变为动态技术。回顾网站的发展历程,基本是从静态到动态的转变,这个结论用在时下其实已经有点不太对了,随着网站越来越庞大越来越复杂,网站技术发展逐渐开始逆向进行了,网站从动态化向静态化转变的需求变得越来越强烈了,这也是时下前沿的前端技术正在解决的问题,例如本系列的主题网站静态化技术就是顺应这个发展趋势而来的,所以前端MVC框架在这点上有点逆历史潮流的问题了。

  新问题二:前端MVC让web前端的技术难度和架构难度成指数级上升,而javascript语言天生有着自己设计的缺陷,这个缺陷在写大规模复杂应用时候就显得尤为突出,例如:javascript没有模块化管理,javascript面向对象的实现难度,所以前端MVC的应用可能会变相的提升企业的技术成本和开发成本,当然很多新的技术手段能解决javascript固有的缺陷,对这些新技术有个更大的问题就是“你会吗“,不会的话首先要解决会的问题,这也是个成本问题。

  新问题三:当前端真的越来越独立于服务端后,这会导致服务端一些可以优化web前端的重要技术就很难实现了,例如网站静态化系列里讲到了缓存运用,CDN的运用就很难达到预期效果,或者根本没法使用,因为这些技术的根基都是认为网站动态性是由服务端发生的,而客户端霸占了动态性,那么这些技术的作用就被限制住了。

  由此我可以下个结论:如果前后端分离方案是以浏览器和服务器角度来划分并不是最好的前端分离方案,那么前后端分离方案还有没有新的解决思路了?这个真的有,那就是nodejs参入的前后端分离方案。

  其实前后端分离的驱动永远都是前端强于服务端,而前后端分离的重要目的也是要给web前端创造一个更加干净的开发环境,那么写的代码是否是在浏览器上跑还是在服务端上跑这个并不是太重要,所以引入nodejs,就是让服务端也能跑javascript代码并不会是让人无法接受的事情,回到前后端分离方案里以服务端驱动的前后端分离方案,我曾说过这个方案能获得服务端开发人员更多的掌声,我相信这个掌声不会是服务端为前端的喝彩,而是服务端终于从web前端解脱出来了,这样服务端运用更加高级的SOA技术就成为了可能,那么我们把web前端的控制层使用nodejs替代,这么一来我们既可以继承所有传统MVC框架的优点,同时也达到以前后端分离的根本的问题就是为web前端创造一个很干净的开发环境问题,那么我们在前端MVC框架使用时候遇到的问题都会很好的被解决。

  Nodejs的运用让动态网站的动态性再度停留在了服务端,那么我前面讲的那么多网站静态化技术就可以和前后端分离方案很好的融合了,因此本篇先不具体讨论nodejs做前后端分离的实现手段了,在下篇讲从网站静态化角度重新审视前后端分离方案时候一起讲解,这么做会更加符合本系列的主题。

  现在我们可以解答为什么nodejs技术可以突破传统服务端技术的包围,因为nodejs可以让前后端达到更高程度的分离,从而让前后端各自发挥自己的优势,很有意思的是,虽然nodejs技术属于服务端范畴,但是它却是前端工程师驱动来普及的,这绝对是web前端逆袭啊。

  好了,本文就讲到这里,最后祝大家工作和生活都愉快。

关于大型网站技术演进的思考(十七)--网站静态化处理—满足静态化的前后端分离(9)

前后端分离的主题虽然讲完了,但是前后端分离的内容并没有结束,本篇将继续前后端分离的问题,只不过这次前后端分离的讲述将会围绕着本系列的主题网站静态化进行。在讲本篇主题之前,我需要纠正一下前后端分离主题讲述中会让朋友们产生误导的地方,这种误导就是对时下流行的一些前后端分离方案(没有使用nodejs的前后端分离方案)的评价问题,其实本人任然觉得不管什么样的前后端分离方案只要成功被实施,并且产生了良好的效果,那么它就是一个成功的前后端分离方案,前面我以一种批判的角度讲述这些前后端分离方案,并不是想在否定它们,而是出于一种鸡蛋里挑骨头的较真态度想重新审视这些方案,希望这种审视能让我们的设计方案变得更加优秀,同时自己也在这个较劲的过程里得到自身技术能力的提升。其实那些被我批判的技术方案也许在某些特定场景下它就会变的更加优秀,我推崇的技术方案在某些场景下可能就变的苍白而无力,这种情况很有可能发生,不说别的,我之所以批评前端MVC,其私心就是因为它不符合网站静态化的处理,如果把前端MVC内容放置在网站静态化的主题下谈论,被批的命运那是必然的。

  网站静态化技术相对于前后端分离技术的关注度要低的多,如果业界的一些公司因为看了本人的文章能对网站静态化技术有一种新的认识,从而考虑在自己网站上使用网站静态化技术,同时也想实现前后端分离技术,那么新的问题出现了,这两种技术同时使用会发生矛盾吗?如果有矛盾,我们到底将如何解决这些矛盾?解决这些矛盾的时候我们是不是可以做好两者的兼顾,而不会发生其中一方妥协于另一方,最终导致其中一方没有充分的发挥自己的能力。要解答上面的一系列问题,我首先要探求的就是网站静态化技术和前后端分离方案里那些方面会产生矛盾。

  从我前面对网站静态化技术的阐述,我们知道网站静态化的技术最佳作用位置应该是服务端而非是浏览器端,之所以会这样是因为网站静态化技术的技术基础是动静分离和缓存,这两个方面如果落到浏览器端会碰到很多难以解决的问题,那么我们要分析下这些难以解决的问题,具体如下:

  浏览器之缓存问题:浏览器也有缓存,不过浏览器端的缓存那就不是指内存里的缓存,而是持久化的缓存,实际上浏览器端的缓存非常不可靠,会被很多非技术的因素所限制,例如我们手动删除缓存或者使用无痕模式上网,那么这些持久化的缓存就会失效,用户再度访问网站时候都将是第一次访问这个网站,这就使得很多优秀的缓存策略方案在浏览器端实施效果大打折扣。

  浏览器之动静分离问题:网站静态化技术里一个重要的手段就是如何设计动静分离策略,纯粹的静态内容这个没啥好说的,但是动态的内容在一定的条件(例如:时间,一些业务属性例如商户属性)下是可以转化为静态内容,这些内容如果能被有效缓存,对网站性能提升是不可估量的,而且这种动静转化的策略也可以减少业务服务器上处理不必要的请求,从而减轻业务服务器的压力,达到提升后台核心业务服务端的负载压力。但是如果我们使用前端MVC框架,一股脑子把很多服务端功能往前端迁移,那么这种动静处理手段就很难做,而且很多场景基本上是无法应用了。

  因此我认为前后端分离方案使用nodejs价值更高,因为使用nodejs我们就可以根据网站静态化技术将需要保留在服务端的功能可以继续保留在服务端,这样就能达到二者兼顾的目的。但是如果我们认为把nodejs引入后,nodejs的目的就是用来做网站整体MVC架构下的C层即控制层,这个思路到底合理不合理呢?这个问题还是很值得玩味的,因此我们需要分析下网站整体MVC架构下的C层即控制层的作用。

  在前面文章里我曾总结过C层即控制层在MVC框架里的作用,这个作用分别是:路由、报文格式转化以及页面渲染,但是这个作用的总结我是有个前提条件的,那就是以C层即控制层作为前后端沟通介质的前提下。如果前后端分离方案引入后把控制层归为前端的组成部分,那么控制层跟前端的结合问题都是人民内部的矛盾,都是比较好解决,但是控制层就仅仅是用来连接前后端一个作用吗?对于网站架构里的控制层,有一个不可避免的功能那就是作为后端服务端的安全入口的作用,也就是说控制层是做请求安全检查和安全监控的地方,而且很多安全校验还会和业务相关,例如检查报文是否被篡改啊,防钓鱼的功能,如果这些功能被前端来承担,首先不谈前端技术人员会不会做这些,但是至少一点问题是会发生的,前端工程师在关心页面开始同时还要写服务端的业务逻辑了,不管怎么说,这些功能迁移到前端总不是太合适。当网站演变为超大型网站后,大型网站往往是很多小中型网站项目的集合体,为了减少网站整体的异构性,我们常常把不同的模块网站的入口整合在一个大型控制层项目下面,这个大型控制层项目一般称为网关项目,它的作用和网络里的网关非常相似。除此之外,还有些网站的控制层非常特别,例如一些做第三方支付的网站,那么这样网站项目本身就是个大网关,而且这个网关很特别,它后台的服务就是其他银行的系统,它的路由工作就会变得异常复杂,例如:根据用户使用银行的不同,控制层要组装不同的报文信息,而这些功能都是属于控制层,这样的场景无疑大幅度提升了控制层再和模型层对接的技术难度,而增加的难度问题又和模型层耦合度很高,由此可见,web应用整体的MVC的控制层比我们想象中要复杂的多。

  回到用nodejs替代控制层这个主题,我们来看看实际的场景吧,假如我们的网站控制层相对比较简单,好了,这时候我们跟领导或老板说“现在很流行前后端分离,我们项目也使用下前后端分离技术”,领导或老板一听可能会为之一振,那么就会问你”那么该怎么做了”,你这时对他说“首先把控制层用nodejs重写下”,领导或老板听到这个回答他会同意你这么干嘛?一个不会给网站增加任何新功能,同时不能很直接有效的提升网站的性能,而且执行它还会有很大风险的方案,头儿们会同意吗?好了,假如你终于找到合理理由说服头儿们,那么如果我们的网站规模已经很大,控制层已经演变成了网关项目,控制层本身已经巨复杂了,你敢用nodejs重写一遍网关项目吗?所以说吧nodejs直接当做控制层,其实实践起来困难重重,而且nodejs完全承担控制层,它的性能,它能否很好的运用于集群开发这都是很难把控的问题。分析到这里,我们似乎又进入了死胡同了,那如何来破这个局呢?

  上面的问题只是反映出整个网站MVC里的控制层其实还有部分功能是和服务端的模型层紧耦合的,因此要解决这个问题就是把传统的控制层再细分一下,属于前端的部分划分给web前端作为web前端的控制层,属于服务端的部分任然留给服务端,这么拆分后,当我们引入了以nodejs为基础的前后端分离方案,服务端的控制层改造无非就是去掉页面路由,页面渲染,再修改下返回数据格式即可,因为不用修改服务端的业务代码,其代价是很低的,头儿们也很容易接受这样的方案,并支持我们大胆去尝试新技术。

  服务端网站静态化技术SSI和ESI,主要是根据动静分离策略把网页不会经常变化的模板进行缓存,然后在静态资源服务器位置整合动静资源,如果我们使用nodejs只是简单替换原来的控制层,那么这些策略其实还是有问题的,那么怎样做可以让nodejs兼容SSI和ESI了?这里我列举个实际的案例,nodejs有一个模板语言叫做jade,nodejs里还有个技术叫做handlebarsjs,其中handlebarsjs和struts的标签类似,它可以处理一些简单的业务逻辑,我们开发时候使用jade编写页面的模板,使用handlebarsjs让动态数据和模板进行整合,项目发布时候,使用像grunt这样的项目管理工具编译项目,jade文件变成html文件,而handlebarsjs则会转化为javascript代码,这样我们就可以把生成的html文件在服务端进行有效缓存,而handlebars生成的javascript文件负责整合动静数据,这样nodejs就可以达到兼容SSI和ESI的作用了。

  不过引入nodejs会让网站处理请求的过程里增加一个环节,这样可能会导致部分性能的损失,但是我上面的实例却能有另外的方式规避这个问题,因为nodejs的代码是用javascript语言编写的,那么这个代码是可以运行在浏览器上的,那么这就会产生了一个处理手法,那就是我们在生产部署时候其实不需要部署nodejs的,我们把静态模板就缓存在服务端或者推送到CDN上,然后handlebarsjs生成的js代码就让它传送到浏览器端,因为这个js代码生成后基本不会变化,浏览器可以缓存它,当然CDN或静态资源服务器也可以缓存它,其实它在浏览器运行时候变化无非就是获取一次服务端数据而已。这么一来,生产上的web前端又转变成了前端MVC的形式,还把动静整合的事情交由了浏览器来完成,这不仅是兼顾的网站静态化要求,还让动静整合推到了更加靠前的浏览器端,这不是达到了一个双赢的效果了嘛。

  好了,本篇就写到这里,最后祝大家晚安,生活愉快

关于大型网站技术演进的思考(十八)--网站静态化处理—反向代理(10)

反向代理也是一种可以帮助实现网站静态化的重要技术,今天我就来讲讲反向代理这个主题。那么首先我们要了解下什么是反向代理。和反向代理相对应的是正向代理,正向代理也就是我们常说的代理服务,正向代理是非常常见的,例如在某些公司里我们想使用互联网,那么我们就得在浏览器里设置一个代理服务器,通过代理服务器我们才能正常使用互联网,而这个代理服务器就是一个正向代理服务器。正向代理更加让人熟悉的使用场景估计还是在FQ技术里的使用,我们使用一个放置在国外的代理服务器来访问那些在国内无法正常访问的网站,这其实也是在使用一个正向代理服务。

  其实不管是正向代理还是反向代理,这两个概念的定义都是以浏览器侧为基准进行的,正向代理是代理浏览器来访问互联网,反向代理是指代理不再是代理浏览器侧了,而是反过来代理浏览器需要访问的应用服务器。那为什么我们要使用正向代理服务器了?答案当然不是为了FQ了,下面我来列举些实例来说明这个问题了。

  例如公司里使用代理服务器主要是为了安全的考虑,很多公司内部都有自己的局域网,一般我们称之为内网,内网里有公司的各种资源,如果公司员工的电脑随意连接到互联网,假如碰到那些别有用心的黑客,通过攻击员工的工作电脑截取了公司重要的文件资料,那样就会造成公司的重大损失,正向代理除了能防范外部的黑客攻击外还能监控和控制公司内部员工将公司重要文件通过互联网传递给不恰当的人,因此公司让员工使用代理上网基本都是出于安全的角度来考虑的。

  正向代理的合理使用还能帮助一些企业提升自己产品的核心竞争力,例如在移动端有一款非常流行的浏览器,它之所以非常受用户的欢迎,是因为使用该浏览器上网速度比其他浏览器明显的快多了,那么这款浏览器是如何做到这点的呢?奥秘就是这家公司为自己的浏览器对应建立一个十分强大的代理服务器集群,用户使用该浏览器访问网站时候用户首先访问的是该公司的代理服务器,而这些代理服务器使用缓存技术缓存了海量的网站信息,再加上使用一些web加速的技术例如CDN技术,这就让该浏览器访问网站的效率明显快于其他浏览器。

  反向代理和正向代理从技术角度上基本上是一致的,区别主要是代理的内容不一样了,反向代理代理的是应用服务器。反向代理技术也基本上是互联网公司的一个标配技术,但是反向代理能否正确使用,能否更进一步的发挥它的实用价值,我觉得并不是所有公司都能做好的,下面我来总结一下反向代理的使用目的吧,具体如下:

  使用目的一:反向代理可以隐藏真实的应用服务器。该目的属于安全的范畴,反向代理隐藏真实的应用服务器,那么就可以让别有用心的黑客很难掌握正确的应用服务器,从而增加黑客的攻击难度。

  使用目的二:反向代理可以实现负载均衡的功能,例如在java的web开发里有一种很简单的实现集群的手段,这个手段就是使用apache加上tomcat的组合,用户请求先到达前置的apache服务器,apache再使用负载均衡策略将请求分配给后台不同的tomcat服务器上。

  使用目的三:反向代理可以起到动态调节应用服务器并发数的目的,一般用作反向代理的服务器都是静态资源服务器,这样的服务器在并发处理能力上要远强于后台的web应用服务器,那么可以通过控制web应用服务器前置的反向代理服务器,这样就可以动态调节后台服务的负载的大小,这个做法的好处可能很多朋友都不太了解,这里我列举个例子,一个网站最需要稳定性的部分是哪个部分呢?很多朋友会说是数据库,的确数据库是最重要的,因为数据库做分布式很难,很容易形成单点故障,要是数据库挂了基本一切都没法玩了,那么除了数据库之外还有别的吗?当然有,那就是用于处理业务的应用服务器了,应用服务器如果做了集群,集群中其中一台服务器挂了其影响面会比数据库挂掉低多了,但是一个网站的做业务处理的应用服务器挂掉,对公司的损失还是很大的,而web应用服务器前面的用作反向代理的静态资源服务器挂掉问题就会小多了,至少不会产生公司业务无法正常完成的事情了,因此当网站负载过高,让过载的请求被反向代理拦截或者阻止,这对应用服务器的稳定性提升有莫大的好处。当然反向代理调节应用服务器的负载水平的用途不仅仅这些,有兴趣的朋友可以在网络上找找相关的介绍。

  使用目的四:反向代理可以缓存静态数据,一般用作反向代理的服务器都是使用像apache或者是ngnix这样的静态资源服务器,因此我们可以把web应用里的静态资源缓存在反向代理服务器上,从而达到提升请求处理的速度问题。反向代理的这个功能就和本系列的主题网站静态化处理切合了。

  分析完反向代理的使用目的后,我们现在将反向代理应用到项目里,这里应用的一个前置限定就是将反向代理应用到网站静态化的处理之上,首先是第一个应用方式,如下图所示:

 

      第一种反向代理应用方式就是让反向代理和应用服务器一一对应,也就是每台应用服务的部署服务器上都对应部署一台反向代理服务器,这么做有怎样的好处呢?首先我们来讲第一个好处,如果我们将网页做了动静分离,那么反向代理服务器就可以负责对请求中的静态资源访问进行处理,同时反向代理还可以承担动静资源整合的目的。这里要特别说明下,前文里我说道动静资源会因为我们使用的动静策略而发生转化,那么有些动态内容在一定条件被转化为静态资源后,我们可以将这些做了转化的静态资源在服务器上缓存起来,这个时候上图展示的架构模型就会发生变化,如下图所示: 

      我们看到反向代理服务器和应用服务器之间会形成一个cache层,反向代理访问cache层的效率会比直接访问应用服务器要高的多,这等于是给应用服务器做了一个加速操作,同时通过缓存我们可以减少应用服务器的运算压力,从而达到提升应用服务器性能的目的。以前有朋友问我这么做会不会增加应用服务器的压力,因为一台服务器上部署了两台可以处理web请求的服务器,那么它们之间一定会有发生冲突的时候,不过我想产生冲突肯定是我们没有很好的处理二者关系所致,所以我们要理清在同一台服务器上部署反向代理和应用服务器后,它们之间的关系到底是怎样的?

     其实反向代理和应用服务器从物理形态角度上它们是两个不同的东西,但是二者在逻辑上其实是一个整体,它们共同完成一个逻辑性的应用服务器的功能,只不过二者因为应用场景不同而形成了一种分工合作的关系,反向代理服务器主要完成对静态资源请求的处理,而应用服务器则是负责业务逻辑的处理,它们最终形成一个强大的合力使得整体的逻辑性应用服务器的性能得到显著的提升。

     除此之外,这个反向代理还可以发挥动态调节应用服务器的并发数的目的,但是上面的技术方案却没有发挥反向代理的负载均衡以及安全性这两个方面的作用。为了让反向代理四个使用目的得到充分的发挥,那么我们该如何来做了?

     方法很简单就是把反向代理的部署地点从应用服务器所在的物理服务器上迁移出来,放到一台独立的物理服务器上,但是这个做法会有性能上的损失,同时还会增加整个技术架构的复杂性。为什么性能会损失呢?因为原来的反向代理服务器和应用服务器部署在同一个物理服务器上,那么它们之间的通讯都是以内存共享的方式进行的,这样的通讯效率是非常高,现在换成了通过网络通讯进行沟通,而网络通讯是IO设备里效率最差,可靠性最差的,因此单独部署反向代理服务器或多或少都会造成一定性能的损失。

    为什么说单独部署反向代理会增加整个网站技术架构的复杂性了?我们把反向代理服务器单独部署,那么单独部署时候我们还会是使用一一对应的策略吗?先不谈这么做,从技术和业务角度的好处和坏处,但从成本这个考虑就是会让很多公司望而却步,因为这个做法就会导致用于部署应用服务器的成本翻倍的增加,而增加的服务器用于反向代理,这样的做法怎么体会都不是觉得物有所值,再说用于反向代理的静态资源服务器本身处理请求的并发能力是普通应用服务器的数百倍,一一对应本身也没有完全发挥反向代理服务器的潜力,因此最好的解决方法就是把反向代理服务器做成一个反向代理服务器集群,做成集群问题又来,集群里每台反向代理缓存的数据是不是要保持一个同步了?这就好比处理应用服务器的session同步问题,如果真的这么做会不会导致反向代理服务器上缓存大量使用率不高的数据从而导致缓存的利用率很差,同时同步操作本身也会影响到反向代理集群的性能,所以要设计一个好的反向代理集群是一件十分复杂的事情,其实合理的反向代理集群的做法就是在集群里在进行分组,每个分组应该是和后端的SOA服务相匹配,这个时候反向代理集群的效率才能得到最大的发挥,同时资源利用率也会更加的合理。其实使用反向代理集群方式,也会给生产部署造成麻烦,如果网站进行了静态化处理,那么反向代理需要承担对静态资源的处理操作,这个时候反向代理和对应的应用服务器结合起来才能形成一个完整的应用服务器,但是现在我们将一个完整的逻辑应用服务器分开部署了,那么当我们发布新应用的时候就得面临更加复杂的情况,这就增加了部署和运维的风险和难度。

     我如此批评单独部署反向代理的问题,但是我并不是说这种做法完全不可取,而是想告诉大家这种做法其实是一种高级的做法,但是也是一个复杂的做法,要做好这个集群是很麻烦的一件事情的,我觉得只有当我们的网站业务量和请求量很大的时候,同时原有方案出现了瓶颈时候可以认真考虑反向代理集群方案的实现,不过将反向代理形成集群会给网站的安全性带来莫大的好处,反向代理可以隐藏后台的应用服务器,这种隐藏就是客户端只需要访问代理服务器即可,应用服务器对外都是以反向代理来展示的,但是如果反向代理和应用服务器一一对应,那么恶意黑客找准了某台反向代理服务器后,对这个反向代理服务器进行反复的攻击,那么这个攻击也就等于攻击与之对应的应用服务器,这就导致反向代理隐藏真实应用服务器的作用就没有得到有效的发挥,而集群这块就可以很好的处理这个问题,不过我们如果觉得使用集群代价太高,我们也有变通的方法,那就是在所有逻辑应用服务器前面再放置一个反向代理服务器,这个反向代理服务器不再承担缓存的功能,而只是用来做负载均衡和安全处理,这样一一对应的策略安全性也可以得到保证,不过如果公司技术能力好可以考虑使用LVS这种软件化的负载均衡技术方案,假如公司还很有钱还可以考虑使用更加高级的硬件负载均衡设备例如F5设备。

     如果我们网站除了使用网站静态化技术还使用了前后端分离技术,当然这个前后端分离技术应该是使用nodejs的前后端分离技术,那么nodejs应该放置在生产部署的什么位置上了?上篇文章里我曾列举了一个nodejs的应用实践场景,在这个实践场景里我曾经提到如果在原有的网站生产架构下引入nodejs会增加一个请求处理环节,而nodejs使用主要是为了满足前后端分离而非增加网站性能,因此增加的环节可能会让请求处理的性能下降,因此我最后提出一种变通手法,就是nodejs项目发布时候编译源代码,然后将编译出的javascript和html文件干脆推移到浏览器端处理,这样就变相形成了前端MVC框架,这个做法总是有点不伦不类的意味,假如我们真的想把nodejs引入到应用生产的网络架构里,我们不希望无端的增加请求处理环节,那么最好是让nodejs服务器替换某个部分。按照这个思路思考,那么我觉得nodejs在生产的引入最好是和反向代理相关,最简单的方式就是让nodejs和反向代理一一对应,这样就可以很好的降低引入nodejs带来的问题,当然复杂点的就是反向代理集群对应的应用服务器应该是nodejs的应用服务器,而不是用来做业务处理的业务级别的应用服务器。

    不管怎么说,我认为在网站静态化方案里我们一定要考虑反向代理的运用,如果静态化技术方案里没有反向代理的身影,那么这个网站静态化处理可能很难达到我们预期的效果。

    好了,今天就写到这里,最后祝大家晚安。

关于大型网站技术演进的思考(十九)--网站静态化处理—web前端优化—上(11)

网站静态化处理这个系列马上就要结束了,今天我要讲讲本系列最后一个重要的主题web前端优化。在开始谈论本主题之前,我想问大家一个问题,网站静态化处理技术到底是应该归属于web服务端的技术范畴还是应该归属于web前端的技术范畴,要回答清楚这个问题我们要明确下网站应用的本质到底是什么?网站的本质其实就是BS,这里的BS我没有带上架构二字,而就是指Browser和Server即浏览器和服务器,而网站静态化技术的作用目标就是让客户端即浏览器的用户体验更好,但是如果我们想让网站在浏览器上运行的更快,在更快的基础上能设计更多更好的用户体验功能,那么我们需要做的工作其实就不仅仅是着眼于浏览器本身,而是要把和浏览器相关的一切作用因子结合在一起考虑,这就是网站静态化技术的本源所在,所以有些朋友认为网站静态化技术其实是一个服务端技术多于web前端的技术,因此认为网站静态化技术是不属于web前端的范畴,我认为这种理解是不正确的,我想产生这种误导的原因是很多人都是狭义的理解web前端技术,认为web前端就是以javascript、css以及html所代表的技术,超出这个范畴的技术就不应该属于web前端范畴,我个人觉得这种理解也无可厚非,但是这种理解可能会让那些有追求的前端工程师产生一个不好的后果,这个后果就是不灵活的划分自己需要掌握的技术范畴,最终影响自身技术能力的突破,不管是web前端还是web服务端都应该把做好优秀的网站为己任。BS本身就是一个整体,只有二者结合起来才能产生网站,缺少其中任何一方,那又何来的网站呢?BS中的S就犹如蝴蝶效益里蝴蝶的翅膀,虽然蝴蝶看起来只是在亚马逊雨林轻轻的挥动了一下,可是这个挥动却能让相距千万里的太平洋上刮起可怕的飓风,因此本人对web前端有个新的认识,我们不应该把前端只是局限于javascript、css和html这些技术之上,而是应该把自己当做浏览器应用开发专家,一切用作于浏览器的技术和手段都是web前端工程师需要掌握的知识,就像时下的nodejs出现,逼得前端工程师不得不去做服务端开发,不要觉得这是被迫的,而要把它当做web前端的逆袭,认为这是理所当然的事情。

  好了,我们现在回到web前端优化这个主题吧。Web前端优化技术的普及还是要归功于互联网两大巨头雅虎和谷歌的贡献,他们通过多年的积累和总结,将这些web前端优化的经验无偿的公布给全世界,从而推动了web前端的发展,这些技术都不是什么秘密,我在网上找到一篇讲解这些技巧的文章,文章就是《Web前端优化最佳实践及工具集锦》。

  web前端优化技术和网站静态化技术使用目的是一致的,就是让网站变得更快,用户体验更好,我个人认为网站静态化技术其实就是web前端优化的一部分,只不过网站静态化技术是通过服务端的大规模技术改造来实现web前端技术优化,而服务端的这种改造的目的就是让整个网站的后台技术架构更加切合web前端的要求,从而能更好的实现web前端优化。我这里之所以能如此评价网站静态化技术,其实说明网站静态化技术和web前端优化技术一定存在某种强烈的切合点,我个人认为这个切合点就是它们背后使用的理论基点是一致的。那么它们之间这个切合的理论基点到底是什么呢?

  优秀的网站应该是用户体验好的网站,当人们使用这个网站感觉爽,好评不断,那么这个网站就是一个用户体验优秀的网站,但是用户体验好的网站就是网站布局精美,图片很炫,人性化设计到位这么简单吗?这些要素都是网站使用者的感受,但是对于网站设计和开发人员而言,再好的网站一定要解决一个根本问题,那就是网站加载的速度要快,如果网站加载速度不快,你就算把网站设计的再漂亮,估计也会搞的无人问津,说到这里,是不是有较真的朋友不信我的结论呢?我把前面引用文章里的一张图再给大家瞧瞧,如下图所示:

 

  其实当我们开发网站如果只考虑如何把网站做的漂亮而忽视网站的性能,我们就会发现漂亮的网站和网站的性能其实是矛与盾的关系,例如精美的图片往往需要高质量的图片格式,而高质量的图片格式就意味图片会变得很大,那么在图片通过网络加载时候就需要花费更多的时间,所以我们在设计和开发优秀网站时候,漂亮和效率是需要我们认真权衡的,认真思考的,最终要找到一个最好的方式实现二者的平衡,同时更加充分的发挥双方的潜在价值。而直观的用户体验好这其实更多的是一个设计问题,而解决用户体验好的根基:速度问题,这就是一个技术问题了。

  要解决网站的速度和效率问题,那么我们就得思考网站的载体计算机到底哪些因素会影响网站的速度和效率。其实计算机的本质很简单,那就是计算和存储,计算主要是CPU来完成,而计算机用于存储的介质就多了,它们主要是内存、硬盘,如果是网站应用还有个很关键的存储介质需要考虑那就是网络了。那么计算机用于计算和存储的这些介质的效率是怎样的一个情况呢?这个问题我在以前一篇文章里有过阐述,这篇文章就是《关于如何提高Web服务端并发效率的异步编程技术》

  这篇文章的其他内容太多了,我把关键部分在本文摘抄一遍,内容如下:

对于一个网络请求的处理,是由两个不同类型的操作共同完成,这两个操作是CPU的计算操作和IO操作,如果我们以处理效率角度来评判这两个操作,CPU操作效率是光速的,而IO操作就不尽然了,计算机里的IO操作就是对存储数据介质的操作,计算机里有如下几个介质可以存储数据,它们分别是:CPU的一级缓存、二级缓存、内存、硬盘和网络,一级缓存存储和读取数据的能力接近光速,它比二级缓存快个5倍到6倍,但是不管是一级缓存还是二级缓存,它们存储数据量太少了,做不了什么大事情,下面就是内存了,以一级缓存的效率做参照,一级缓存比内存速度快100多倍,到了硬盘存储和读取数据效率就更慢了,一级缓存比硬盘要快1000多万倍,到了网络就慢的更不像话了,一级缓存比网络要快一亿多倍,可见一个请求处理的效率瓶颈都是由IO引起的。

 

  由此可见网站的速度和效率问题似乎都是由存储即IO造成的。不过我们不能因为感觉发现问题根源在于存储,而就忽视对CPU的思考,所以我先讲讲CPU和网站性能的关系吧。CPU是计算机用于做计算的设备,现在的电脑能看电影,能听歌,可以和朋友聊天,还能用于工作,这些令人称奇的功能其实到了CPU这里也就是通过加减乘除这类基本的数学运算完成的,说到这个真是难以让人想象,读书时候学数学总是觉得那么枯燥乏味,没想到如此强大的人类神器居然就是通过数学运算得来的,难怪有国外科学家说宇宙都是通过数学运算得来的,这还是有道理的。不过网站背后的数学运算却有着自己的特点,虽然CPU计算能力很强,但是在实际场景下很多业务的计算其实很消耗时间的,如果网站某些请求响应背后的运算是需要消耗太多的时间,那么这个时候CPU也就会成为网站性能的瓶颈所在,网站应用有个重要的特点,这个特点有个专有名词描述那就是网站的实时性,根据网站实时性的特点,那么就要求我们网站每个请求所包含的计算都要简单和快捷,简单快捷的计算也就让每个请求背后所包含的业务性运算要更加简单,这也就是为什么很多人会说互联网的网站和企业的web应用相比,互联网的业务逻辑比较简单的道理,但是随着网站的规模扩大,业务模式越来越丰富,这个时候网站在某些业务环节不可避免的变得复杂,假如这些复杂的业务又需要实时的反应给用户,那么CPU不能快速完成业务计算就是网站的效率问题的根源了,例如我在存储系列里说到的海量数据的计算操作,就是这样的场景之一,那么这个时候我们该如何来做了?

  碰到这个问题,我们首先要明确一个问题,计算出现了瓶颈,那么最直接的手段就是增加计算机的计算能力,比如使用运算更快的CPU,但是更快的CPU面对快速增长的业务而言,增加的效率是非常有限的,所以在CPU这块出现了多核技术,我们可以把一个计算任务拆分成诺干个子运算,这些子运算在不同CPU上计算,最终把结果汇总起来,但是这个手段和用更快的CPU手段一样,面对快速增长的业务很快就会达到性能瓶颈,最终我们发现我们的业务计算任务其实已经超出了单机计算机的能力,如是乎分布式技术出现了,我们这回不再是在CPU上做文章了,而是使用多台计算机联合计算,但是分布式计算系统是需要网络进行互联的,而网络是计算和存储里最大的短板,再加以现在互联网的所使用的计算资源规模达到了超乎想象的程度,我们发现想通过扩展计算机的计算能力来解决网站快速响应的问题基本是一件无法完成的任务,那么这个时候我们又该怎么办呢?

  这个时候我们就要转化思路了,因为当网站的计算瓶颈问题已经到了这个地步了,我们再去更加深入挖掘计算机的计算能力这对最终的结果影响已经意义不大了,因此我们只能从计算的相关方哪里寻找问题的解决方案。那什么是相关方呢?仔细分析计算相关方的确太多了,但是有一个最根本的相关方就是用户的实际业务需求了,用户可能认为自己的业务需求都是很明确的,例如电商里的用户想查询自己的交易数据,但是这个业务问题转移到网站的开发人员和业务人员,面对这么多用户的交易查询那就是一个超级复杂的计算问题,如是网站的业务和开发人员就会根据自己系统本身的特点和问题,进一步思考用户业务计算问题的本质,谈论业务计算本质这个问题如果展开细化是非常复杂的,因为现实的业务场景实在是太多太复杂了,但是放到网站实时计算这个角度,其实有一个很简单的解决思路,我们回顾下我们前面讨论的计算瓶颈问题,其实这个问题的本质不是计算能否成功完成的问题,而是计算是否能及时完成的问题,如果用户的请求计算的确是没法很快完成,那么我们就不要让用户觉得这个计算是能很快的完成,这个做法也有一个专有名词那就是异步计算,但是如果我们把难以快速完成的计算都这么来处理,虽然让用户感觉网站已经很坦诚的告诉自己能力有限啊,但是苛刻的用户可不一定会买这个账,因此当有同类型网站使用新的技术手段解决了快速实时计算问题后,假如我们的网站还是驻步不前,那么后果就会很严重了,那么这个时候我们又该如何突破了?

  那么我们就得进一步思考计算本身到底哪里出现了影响速度的问题,计算本身包含三个方面,首先是用于计算的计算资源,再就是做运算的工具即CPU,最后是计算的最终结果,如果业务计算慢的原因是因为数据量太大了,CPU很难快速完成,那么这个时候我们有一些手段可以解决这个问题,我们可以把海量数据做一个分类,例如存储系列里说的历史交易数据和当日交易数据的分类,当日数据因为数据量有限在一定条件下可以快速计算出来,面对历史数据,如果我们的计算结果最终是很简单的而且在一定时间范围里是不会变化的,那么我们可不可以这么考虑,让这些结果提前计算出来,然后将结果存储在效率更高的存储设备里例如内存,当用户请求操作这个业务计算时候我们只需要直接读取缓存里的计算结果就行了,这样就避免了计算,同时计算结果存储在效率高效的缓存里,用户获得响应的速度也会快多了,这个其实就是网站静态化技术里ESI技术背后的深意了。

  当然当我们要解决网站性能问题,不太可能单独从计算或者存储一个维度来思考,一般都是把双方放在一起思考,按照我前面提到计算和存储介质的效率问题,我们发现存储其实是最容易影响网站效率的痛点,实际情况也是如此,当网站发生计算瓶颈问题之前,更多的效率问题还是由存储所导致的,而且复杂计算过程也是需要存储参入才能正常完成,例如计算过程里的中间结果当超出CPU缓存大小后我们就不得不将中间结果放到内存里,当内存也不够的时候我们就得放到硬盘里,所以解决计算效率问题也受到存储性能很大的影响。假如我们还是按照木桶理论来理解这个问题,我们发现不管是单纯的存储问题还是计算和存储混合的问题,最终的短板都是其中效率最差的哪一方,而计算和存储里效率最差的一方就是网络了,不过有些马虎的朋友可能说现在宽带好快了,我在网上下载一部几个G的电影也就几十秒,甚至有时比我硬盘拷贝还快,像你说网络是最大的短板其实不准确的,这位朋友的想法的确有他的道理,但是不是每个人使用的网络都是你那么快呢,而且现在移动互联网已经普了及,移动互联网速度比普通宽带就差多了,而且你在移动设备上使用网络流量越大,成本也就越高,如果你认为我说的这些问题都不算啥,网络还和地域的距离有关,你宽带很快,你想访问大洋彼岸美国的网站(这个网站在中国没有任何缓存处理),访问速度肯定还是快不起来,而且互联网的连通路径本身也很复杂,例如你感觉自己访问的是一个上海本土的网站,但是这个网站说不定好多重要服务器是放置在北京,这么复杂的网络环境,这么多不可控的因素还会影响网络的传输效率,网络谈何能说自己性能比硬盘强呢?

  由此我们就可以发现谷歌和雅虎总结的web前端优化技巧以及我这里谈的网站静态化技术大部分都是围绕如何解决网络传输效率来进行了,因为它是整个木桶最大的短板,我们只有首先解决了这个短板,那么再去解决其他因素的效率问题,才能发挥其作用。这里的这个解释也可以解答前不久一个网友问我,为什么我讲网站优化很少讲解如何编写高效的代码,而都是从一些和代码无关的角度来阐述的了,其实你想通过代码优化提升网站性能,你首先要解决好对网站效率影响更大更关键的要素例如网络通讯问题,否则你代码优化的再好,对最终效果影响都是有限的。

  看来本文今天写不完了,关于存储和web前端优化的内容我将在下一篇文章进一步讨论。最后祝大家晚安,生活和工作愉快。

关于大型网站技术演进的思考(二十)--网站静态化处理—web前端优化—中(12)

Web前端很多优化原则都是从如何提升网络通讯效率的角度提出的,但是这些原则使用的时候还是有很多陷阱在里面,如果我们不能深入理解这些优化原则背后所隐藏的技术原理,很有可能掉进这些陷阱里,最终没有达到最佳的预期效果,今天我在这里分析下浏览器和服务端通讯的一些细节问题,希望通过分析这些细节问题,能给大家一个启迪,能更好的理解这些优化原则背后的隐秘,最终能更好的运用这些原则。

  网站的通讯技术是构建在http协议上,http协议底层通讯手段使用的是tcp/ip协议,但是tcp通讯协议在建立连接和断开连接这两个动作上是非常消耗通讯性能的,这主要是因为tcp/ip协议在连接建立时候的三次握手机制和断开连接时候的四次挥手机制所致,我们来看看下面的图形:

 

  图中中间被红色标记的方块就是tcp/ip协议在建立连接时候需要发送三次报文才能确认连接是否建立成功,中间四个蓝色的方框就是说明tcp/ip协议在断开连接时候要发四次报文才能确定连接最终被断开,而一个具体的http请求和响应也就发送两次报文,这也就说明如果浏览器每次和服务端的交互都要新建和关闭一个tcp/ip连接,那么浏览器和服务器之间就要往返9次报文通讯,而真正用来处理用户请求的报文确只有其中的两次,换句话说这样的一个请求大概会有80%左右的性能都不是用来处理业务需求,等于是损失了80%左右的性能,当然这个比率是9次报文交互的数据大小一致情况下得出的,如果用户业务请求和响应的数据量比较大,那么建立连接和断开连接的性能损失占比会降低,不过就算占比降低了那也是在请求处理本身的时间变的更慢的基础上的降低,要是浏览器和服务器之间的距离特别大,那么多出来的7次报文交换的效率问题就更加严重了,不管怎样,tcp/ip的三次握手机制和四次挥手机制只要发生都会对网络请求效率产生重大影响。

  为了解决这个报文交互次数过多的问题,http协议本身也发生了改变,那就是http开始采用了长连接,使用长连接后网站只需要开启一个长连接,在用户关闭浏览器关闭之前浏览器里的网页都会复用这个长连接。不过http协议的1.0版本默认是不启用长连接的,所以在使用http协议1.0版本时候我就得手动的打开长连接,这个方法就是在http头里设置Connection: Keep-Alive,而http1.1版本里长连接是默认打开的,所以不需要我们手动的设置,而且时下的浏览器几乎都支持http1.1协议,因此大多时候情况下我们是没有必要手动去打开长连接的。

  虽然http协议采用长连接后可以减少网站通讯时候三次握手和四次挥手的次数,但是长连接建立起来后需要浏览器和服务器长时间维护,这本身会消耗浏览器和服务器的性能,特别是服务器端长时间维护长连接本身还会损坏服务器处理并发的能力,所以早期浏览器会限制http1.1开启连接的数量,例如ie7这个古董浏览器,它准许http1.1最多开启2个长连接,而http1.0因为默认使用短连接它默认可以开启4个,下面有张图可以说明,如下所示:

 

  提升浏览器加载效率的手段除了提升每个连接的传输效率外,其实还有一种方式,这个方式就是使用多个连接进行并行加载,这个等于几个人联合起来一起完成一个任务,那么效率肯定就比一个人高,而页面加载时候很符合使用并发加载的场景,例如我们让页面里的图片并行加载肯定会比一个个加载图片的效率要高多了。回到浏览器支持的连接数的问题,由于早期浏览器在http1.0和http1.1连接数的差异,某些网站例如维基百科这样的网站,它的静态资源特别多,为了充分发挥并发的优势,它将存放这些静态资源的服务器采用http1.0协议,这样就能并行加载更多的静态资源,因为这个并行加载的总体效率提升相比tcp/ip握手和挥手的损失要高的多,不过现在这个手法已经起不到什么作用了,因为新版的浏览器已经把两种版本的http协议支持的连接数调整一致了,因为长连接可以复用链路,因此使用长连接的效率会比非长连接更好。

  上面连接数也是有一个限制的,这个限制就是必须是在同一个域名下,如果一个页面某些静态资源放在不同域名下面,那么这个做法就可以增加页面里的并发数量,例如我们把一些不是经常变化的静态资源例如图片、外部的css文件以及javascript文件单独放置在一个静态资源服务器上,静态资源服务器对外的url地址和页面本身的url地址不在同一个域名下,那么页面本身的并发加载连接数就会增加一倍,不过这也就意味着浏览器端要维护的长连接数会变得更多,雅虎工程师曾经总结过一个页面里合理的域名数量,那就是两个,这个结论的提出已经过去了好多年了,现在的浏览器和服务器的性能已经今非昔比了,这个跨域数量应该可以增加点,不过我个人认为一个页面的里包含的域名数量还是不要太多,其实如果我们web前端优化手段使用得当,两个不同域名就足够用了,多了价值不大,除非你网站情况是在特殊,例如你看看现在浏览器本身支持的连接数量已经很高了,大部分都是6,ie9甚至还达到了10,翻个倍就有12和20个连接数,我们在翻个倍就是24和40个,这个数字看起来就很恐怖了,一个计算机支持这么多并发,假如你在浏览器还打开个网站也是这么干的,那么浏览器的并发数多的实在太吓人了,我估计到时计算机本身就跑不动了,所以10多个连接数很够用了,你合理发挥下这些连接数网站的性能就能有很大提升,再说了一个网站并发连接数太多那本身就说明了你在减少http个数这个手段没有运用好。

  回到web前端优化的手段,我们如果把这些手段再仔细分析下就会发现很多手段使用都是在同步请求这个场景下进行了,当然这些手段在合适情况下也能作用于异步加载场景,但是异步加载场景发生并发加载之前需要一个单线程的异步加载,这个单线程的异步加载就和分布式系统里的单点故障有点像了,它很有可能是整个流程的软肋所在,所以合理使用同步请求还能让异步操作性能更加优秀做好准备。上面我讲到浏览器在同一个域名下最多可以开启多少个连接数,但是从事web前端开发的人都能感觉到,我们做页面开发时候其实是没法控制这个连接数的,那么问题来了,这么多连接到底是在什么条件下被开启的呢?这个问题非常有意思的,我们来看下面的瀑布图:

 

  从上面的瀑布图我们发现,并行下载的是图片,这个推而广之要是我们看见某些网站的网页做过并发优化处理的设计,我们就会发现并发的资源都是纯静态的资源,那么这个并发连接数跟我们页面的设计存在一个怎样的关系呢?首先我们总结一下页面里的静态资源,在页面里静态资源有html,如果html里面有内联的css代码和javascript代码,那么这些代码也会归属于html,除了html外还有外部的css文件、外部的javascript文件和页面里使用到的图片,那么这些要素怎样会促发页面的并行加载了,换个说法这些要素又是如何促使浏览器同时打开更多连接呢?

  首先我们要明确一个问题,浏览器之所以可以打开更多连接数,让这么多连接并行执行是有个前提的,这个前提就是这些资源是不是被并行加载的,例如像外部css文件,图片这样的资源,这些资源下载完毕后马上就可以使用,因为它们下载完毕后没有逻辑性问题要处理因此下载完毕后就可以直接拿来使用,因此它们并行加载不会影响到页面的展示问题,这个情况如果碰到javascript就有点麻烦了,外部javascript代码是包含逻辑在里面,而且有些逻辑很有可能会影响页面的展示,所以javascript下载完毕后,浏览器就得马上执行,所以我们就会看到这样的瀑布图,如下图所示:

 

  上面的空白区就是浏览器在执行javascript代码所要花费的时间。浏览器开启多少个连接是浏览器自发的行为,这个自发行为主要出于提升浏览器并发下载效率的角度出发的。由于现在浏览器的连接基本都是采取的是http1.1协议,也就是使用的长连接,那么连接建立后这个连接就会长期维护,如果这个长连接是单独的静态资源服务器上的长连接,这个问题倒没什么,如果这个长连接放在主域名下面,问题就来了,主域名在页面初始化加载时候会用来下载html,如果我们为提高并发下载效率,让这个主域名下还放置其他的静态资源,那么可能会导致浏览器和主域名的服务器下维护更多的长连接,而页面后续操作基本是使用ajax来操作的,而ajax往往只会复用其中一个长连接,那么其他多余的长连接等于要空转了,这个空转还需要消耗浏览器和服务器的系统资源,所以我们发现主域名下的请求资源类型一定要认真加以控制,能迁移到单独的静态资源服务器上的一定要进行迁移,尽量让主域名下处理的请求都是包含业务逻辑的请求,这样就可以有效提升系统资源的使用率。这个问题进一步思考下去,我们就会发现如果服务端的业务应用服务器之前放置一个反向代理,反向代理都是使用静态资源服务器,而静态资源服务器对并发的承载能力是远超业务应用服务器,如果主域名下我们不小心放置了太多静态资源,要是后台使用了反向代理,那么反向代理也可以减轻这种长连接所造成的计算资源损失。

  上面这些场景都是在浏览器同步请求下进行了,那么换到异步请求这个并行加载静态资源的手段还有效吗?回答这个问题前,我们首先要想想异步加载会导致新的静态资源被加载吗?这个当然可能,特别是在前端MVC的场景下,我们会把模板技术放到浏览器端完成,这个时候有些html模板一开始可能会包含在javascript代码里,作为一个变量存储下来,而这个模板里很有可能包含好多新的图片被使用,当ajax从服务端获取数据后,解析了这个模板,然后我们把构造好的模板加入到页面的DOM结构里,浏览器重新渲染页面时候看到很多新图片需要加载,就有可能会开启多个连接进行并行加载来提升资源加载效率,如果碰到通过ajax技术动态加载外部CSS文件,那么这个并行加载情况就会更加突出了,因为css文件里很有可能包含大量的图片资源,如果我们把不变的静态资源都放置在了单独的静态资源服务器,那么这个并行加载就不会在主域名下打开更多长连接,由此可见,将静态资源使用单独的域名的静态资源服务器处理的好处非常之多。

  现在http2.0协议还在起草之中,http2.0如果落地将会给web前端优化技术产生重大影响,http2.0打算在一个页面里只使用一个tcp/Ip连接,不过http2.0会在这个连接上进行链路复用,也就是让一个连接上也能做到并行操作,让连接的利用率更高,如果http2.0落地后,web前端里那些用于减少http连接数的手段都会失去市场了,因为协议本身就能处理好并发的问题了,到时像外部css文件,外部javascript文件,css sprite技术说不定就要成为历史了。

  看来本主题又写不完了,下篇接着写吧,今天是元宵节,这里我祝大家节日快乐。


">

我们一起做网店17广州

游戏开挂需要学什么编程、学web前端开发好学吗、永久使用、不限域名、互联网创业项目推荐2022

解决方案怎么写?

运维服务_网站建设员月薪多少_app公司怎么赚钱_企业标识设计图案展示

在其默认布局和配色方案中,Unicon 给人的感觉非常坚固,但不会过于沉闷。其简洁的设计和简洁的布局肯定会吸引那些想要说服客户他们能够胜任这项任务的人。但是,您不必满足于纯粹的功能设计 — 借助捆绑的 Visual Composer 插件,几乎可以实现任何布局。

怎样建设自己的商业网站洛可可设计公司上市了吗承德最新消息今天网站页面优化哪家好html网页制作基本代码河源企业网站建设价格吸引会员活动方案综合门户网站门户网站西南石油大学嘉兴seo结算佛山网站优化推广苏州小程序开发报价独立网站收费标准有道网页翻译广州建设工程交易中心图片百度爱采购推广方式话术中国互联网公司的排名做一个网站出来收多少钱店铺推广营销网站下载的文件打不开网络维护主要做什么长春市招聘网最新招聘信息网络服务商的规定建筑设计软件培训濮阳做网站的费用工商登记官网网络营销策划方案范文免费十堰网站推广设计网名的网站各大电商平台市场份额重庆建设摩托车

猜你喜欢

  • 友情链接:
  • 无极招聘 香港设计公司排名 乌鲁木齐招聘网 住房和城乡建设教育培训网 淘宝网页版网址 海外批发平台