前戏
前后端分离已经成为互联网项目开发的业界标准,使用nginx+tomcat(也可以在中间加一个nodejs)就可以有效解耦。
另外,前后端分离将为未来的大规模分布式架构、弹性计算架构、微服务架构、多终端服务(多客户端,如浏览器、车载终端、Android、IOS等)奠定坚实的基础。
这一步是系统架构从猿猴进化到人类的必经之路。
核心思想是前端HTML页面通过Ajax调用后端restuful API接口,使用JSON数据进行交互。
(词汇表:
在互联网架构中,
Web服务器:一般是指nginx、apache这样的服务器,一般只能解析静态资源。
应用服务器:一般指tomcat、jetty、resin这样的服务器,可以解析动态资源,也可以解析静态资源,但是解析静态资源的能力不如web服务器。一般只有web服务器才能从外网访问,应用服务器只能从内网访问。
专业化(开发人员分离)
过去,Java Web项目大多是由既玩前端(ajax/jquery/js/html/css等)又玩后端(java/mysql/oracle等)的Java程序员来完成的。
随着时代的发展,很多大中小公司都逐渐开始明确区分前后端,前端工程师只管前端的事情,后端工程师只管后端的事情。
俗话说,各行各业都有自己的专长。如果一个人什么都知道,那他就不是专家。
大中型公司需要专才,小公司需要通才,但从个人职业发展来看,我建议把两者分开。
对于后端 Java 工程师:
重点关注Java基础、设计模式、jvm原理、spring+springmvc原理及源码、linux、mysql事务隔离与锁机制、mongodb、http/tcp、多线程、分布式架构(dubbo、dubbox、spring cloud)、弹性计算架构、微服务架构(springboot+zookeeper+docker+jenkins)、java性能优化、以及相关项目管理等。
后端追求:三高(高并发、高可用、高性能)、安全、存储、业务等。
对于前端工程师来说:
专注于html5、css3、jquery、angularjs、bootstrap、reactjs、vuejs、webpack、less/sass、gulp、nodejs、Google V8引擎、javascript多线程、模块化、面向方面编程、设计模式、浏览器兼容性、性能优化等。
前端追求:页面性能、速度、兼容性、用户体验等。
每个职业都有自己的专长,这样你的核心竞争力就会越来越高,俗话说,你付出什么,生活就会回报你什么。
而且两端的发展也越来越先进,如果你以为自己什么都能做,那你终究是什么都做不好。
通过将团队划分为前端和后端团队,双方工程师可以更加专注于各自的领域,独立进行管理,从而打造一支追求卓越的全栈团队。
原始时代(各种耦合)
曾几何时,我们的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、图片、各种第三方库,对吧?
OK,在浏览器中输入你的网站域名。接下来会发生什么?(这个问题也是很多公司的面试题。)
刚才说的是干的,基础不好的请自行搜索。
浏览器利用域名通过 DNS 服务器找到你服务器的对外 IP 地址,向你的服务器发送 HTTP 请求,经过 TCP 三次握手(HTTP 即 TCP/IP)后,开始通过 TCP 协议传输数据。你的服务器收到请求后,开始提供服务,接收参数,然后将你的响应返回给浏览器。浏览器再利用 content-type 解析你返回的内容,并呈现给用户。
假设你的首页上有100张图片,这时候用户看似一个http请求其实不是一个,用户第一次访问的时候,浏览器里还没有缓存,为了你的100张图片,浏览器要连续发出100个http请求(有人会跟我讲http长连接和短连接的问题,这里就不讨论了),你的服务器在收到这些请求的时候,需要消耗内存来创建socket进行TCP传输(消耗你服务器的计算资源)。
重点来了,这样的话你的服务器压力会非常大,因为页面上的所有请求都只请求到你的服务器上。如果是1个人访问的话还好。但是如果1万人并发访问呢(我们先不说服务器集群,这里说的是单实例服务器)。你的服务器能处理多少个TCP连接?你的带宽有多大?你的服务器有多少内存?你的硬盘是高性能的吗?你能承受多少IO?你给网站服务器分配了多少内存?会不会crash?
这就是为什么越是大型、中型的Web应用程序,越需要解耦。
理论上你可以把你的数据库+应用服务+消息队列+缓存+用户上传的文件+日志+等等放在一台服务器上,不需要做任何服务管理,性能监控,报警机制等等,会比较乱。
但这就像把鸡蛋都放在一个篮子里,风险很大,如果某个子应用的内存不稳定导致内存溢出,导致整个服务器挂掉,你的整个网站就都挂了。
如果意外失败了,而当时你的业务恰好处于爆发式增长的巅峰期,那么恭喜你,你的商业成功被技术阻断,你可能会失去大量的用户,后果不堪设想。
注意:技术一定要领先于业务,不然就错过最佳发展期了亲~
另外你所有的应用都耦合在一起了,相当于一块巨石,当服务器负载能力不足的时候,一般会用负载均衡把服务器集群起来,这样你其实就是在水平扩展一块块巨石,性能加速会越来越低。
你应该知道,没有必要对负载较低的函数或模块进行水平扩展,在本文的例子中,你的性能瓶颈并不在前端,那为什么需要对前端进行水平扩展呢???
还有我发布版本的时候只改了后端的代码,为什么前端也要发布呢???
(引自:《架构奇遇——轻量级微服务架构》,黄勇)
正常的互联网架构需要分解一下,包括你的Web服务器集群,应用服务器集群+文件服务器集群+数据库服务器集群+消息队列集群+缓存集群等等。
JSP的痛点
以前的Java Web项目大多采用JSP作为页面层,将数据展示给用户,因为访问量不大,所以对性能的要求没有那么严格,但现在是大数据时代,对互联网项目的性能要求越来越高。
所以原来前后端耦合在一起的架构模式已经渐渐不能满足我们的需求了,所以我们需要找到一种解耦的方法来大幅提升我们的负载能力。
1.动态资源和静态资源都耦合在一起了,这样对服务器的压力就很大,因为服务器会收到各种各样的http请求,比如css、js、图片等的http请求;
一旦服务器出现问题,前后端都毁了,用户体验极差。
2、UI设计完成后,前端工程师只负责将设计切分成HTML,而Java工程师需要将HTML转换成JSP页面,而JSP页面的错误率很高(因为页面中往往有大量的JS代码)。
修改问题需要双方协作开发,效率低下。
3.jsp必须运行在支持java的web服务器中(如tomcat、jetty、resin等),不能使用nginx(据说nginx单个实例http并发量可达5万,这个优势要好好利用),
性能无法提高。
4.第一次请求jsp时必须在web服务器中将其编译成servlet,第一次运行会比较慢。
5.每次请求jsp都是访问servlet然后使用输出流输出html页面,效率不如直接用html高(每次都是这样啊亲~)。
6.jsp中的标签和表达式较多,前端工程师在修改页面的时候会很吃力,遇到很多痛点。
7.如果jsp中的内容很多,由于是同步加载,页面响应会比较慢。
8、前端工程师需要使用 Java IDE(例如 Eclipse),配置各种后端开发环境,你有考虑过前端工程师的感受吗?
基于以上痛点,我们应该将整个项目的开发重心前移,实现前后端真正的解耦!
开发模式
旧的方法是:
1. 产品体验/领导力/客户要求
2.UI制作设计图
3. 前端工程师制作 HTML 页面
4、后端工程师将HTML页面转为JSP页面(前后端强依赖,后端必须等前端HTML完成之后才能进行JSP的转换,如果HTML发生变化那就更加痛苦了,开发效率低下)
5. 整合问题
6. 前端返工
7. 后端返工
8. 二次集成
9. 整合成功
10. 交付
新方法是:
1. 产品体验/领导力/客户要求
2.UI制作设计图
3.前后端约定接口、数据、参数
4、前后端并行开发(无强依赖,前后端可以并行开发,若需求发生变化,只要接口&参数不变,无需修改两边代码,开发效率高)
5. 前后端集成
6.前端页面调整
7. 整合成功
8. 交付
请求方法
旧的方法是:
1. 客户请求
2.服务器端servlet或者控制器接收请求(后端控制路由和渲染页面,整个项目开发的大部分重心都在后端)
3.调用service和dao代码完成业务逻辑
4.返回jsp
5.jsp显示一些动态代码
新方法是:
1.浏览器发送请求
2.直达HTML页面(前端控制路由和渲染页面,整个项目开发重心前移)
3.html页面负责调用服务端接口生成数据(通过ajax等方式,后台返回json格式的数据,json数据格式因为简单高效而取代xml)
4.填写HTML,显示动态效果,解析并操作页面DOM。
(有兴趣的可以去阿里巴巴等大型网站,然后刷新页面时按F12监控HTTP是如何工作的,大部分都是单独请求后台数据的。
使用json传输数据,而不是一个大而全的http请求返回整个页面包括动态+静态)
总结一下新方法的请求步骤:
大量并发浏览器请求 -> Web服务器集群(nginx) -> 应用服务器集群(tomcat) -> 文件/数据库/缓存/消息队列服务器集群
同时可以把模块玩起来,按照业务拆分成小的集群,为后续的架构升级做准备。
前后分离的优点
1、可以实现真正的前后端解耦,前端服务器采用nginx。
前端/WEB服务器存放CSS、JS、图片等一系列静态资源(甚至可以把CSS、JS、图片等资源放在特定的文件服务器,比如阿里云的OSS,使用CDN加速)。前端服务器负责控制页面引用、跳转、路由等,前端页面异步调用后端接口,后端/应用服务器采用Tomcat(可以把Tomcat看作数据提供者),加快整体响应速度。
(这里需要用到nodejs、react、router、react、redux、webpack等一些前端工程框架)
2. 当发现 Bug 时,可以快速确定问题,而不会有人推卸责任。
页面逻辑,跳转错误,浏览器兼容问题,脚本错误,页面样式等问题都是前端工程师的职责。
接口数据错误、数据无法提交成功、响应超时等问题均由后端工程师解决。
双方互不干扰,前端和后端就是一个充满爱的家庭。
3、在高并发的情况下,我可以同时水平扩展前后端服务器,比如淘宝的一个首页,需要2000+台前端服务器作为一个集群,才能承受日均几十亿以上的PV。
(我去参加阿里的技术峰会,听他们说他们的Web容器都是自己写的,单实例能处理10万并发HTTP请求,2000个实例就能处理2亿并发HTTP请求,而且还可以根据预测的峰值流量无限扩容,就一个首页,很恐怖啊……)
4.减少后端服务器的并发/负载压力
除了接口之外的http请求都转给前端nginx,接口请求调用tomcat,请参考nginx反向代理tomcat。
除了首次页面请求外,浏览器还会多次调用本地缓存。
5、即使后端服务暂时超时或者崩溃,前端页面也能正常访问,但是数据无法刷新。
6. 也许你还需要一个微信相关的轻应用,这样你的界面就可以共享了。如果你也有应用相关的服务,
然后通过一些代码重构,可以复用大量的接口,提高效率。(多端应用)
7.无论页面上显示多少东西,都无需担心,因为它是异步加载的。
8、nginx支持页面热部署,无需重启服务器,前端升级更加无缝。
9、增加代码的可维护性和可读性(前后端耦合的代码阅读起来比较困难)。
10、提高开发效率,前后端可以并行开发,不再像以前那样强依赖。
11、在nginx中部署证书,使用https访问外网,并且只开放443和80端口,其他端口全部关闭(防止黑客端口扫描)。