比如上图,是目前京东网站统一在使用的页面尾部,如果想要修改尾部的文案或者链接,那就需要去推动上百个系统和研发团队去排期修改并上线。为了解决这一问题,京东统一头尾管理系统就这样诞生了,基本上实现了五分钟修改京东全站公共头尾内容。
整个管理后台实现了前后端分离,后端负责提供HTTP接口,前端只负责页面渲染。管理后台按照模块划分,主要分为了三个模块,包括文件管理模块、应用管理模块和个人中心模块。
•文件管理模块
提供公共头尾文件的维护功能,可以将公共头尾的HTML内容在管理后台进行创建保存,并针对公共头尾文件进行了版本控制,用户可以在管理后台对头尾文件进行编辑、发布和回滚等操作。
•应用管理模块
提供业务系统的维护功能,用户可以在管理后台添加新应用,创建配置环境,添加业务系统依赖使用的公共头尾配置关系,查看应用信息以及业务应用接入的头尾客户端请求信息。
•个人中心模块
用来记录管理后台用户的各种操作日志,包括文件操作和应用操作,并提供操作日志查询功能。还针对公共头尾文件的发布操作进行上线审批处理。
前边介绍的头尾管理后台已经实现了头尾文件创建维护与版本控制,而业务系统如何依赖引用这些头尾文件,则是我们下一步需要面临的问题。首先,我们需要解决的问题就是如何将头尾管理后台中创建的头尾文件分发到各业务系统中。目前主要有两种方式,分别是头尾系统Push方式和业务系统Pull方式。
•头尾系统Push方式:
意味着需要将各业务系统中每一台服务器作为服务端,而头尾系统则作为客户端,头尾系统中的头尾文件有更新时,主动连接各业务系统服务端,连接成功后,将头尾文件的最新内容发送到业务系统。为了保证头尾系统客户端能够随时与各业务系统服务端建立连接,需要业务系统监听固定端口,并时刻提供服务,否则就会有头尾文件push失败的风险。而现实环境是京东的业务系统众多,部署环境也是多种多样,还有不同的开发语言。如果要开发头尾服务端,首先就要解决跨语言的问题,京东目前常用的开发语言有Java、Js、Php、Golang和Lua,我们就需要提供并维护五种语言的头尾服务端版本。而且由于业务系统监听的端口众多,头尾服务端启动时还会面临着端口被占用的风险,也同样会导致头尾服务端无法正常启动,从而无法更新头尾文件。但是该方式也有优点,就是只有在头文件有更新需要Push的时候,才建立连接去Push,头尾文件不仅能够实时更新并生效,还可以节省服务器资源。
•业务系统Pull方式:
该方式与头尾系统Push方式正好相反,将头尾系统作为服务端,可以解决因端口占用而导致无法启动的问题,但还是会面临跨语言客户端版本的问题。但是我们通过对业务系统进行调研分析,基本上所有的业务系统都会用到Nginx做为反向代理,这个Nginx就给了我们一个支持跨语言业务系统的可能。然后只需要开发一个Java版本的头尾客户端来给Java业务系统引入并Pull头尾文件。不过该方式也有缺点,就是头尾客户端不知道头尾文件何时会更新,头尾客户端只能定时轮询头尾系统来检查头尾文件是否有更新,如果文件有更新,则拉取新的头尾文件内容。这样就会造成头尾文件不能实时更新,并且定时轮询也会消耗一定的服务器和网络资源。
最后经过综合考虑,我们选择了业务系统Pull方式来进行头尾文件的分发。而为了解决业务系统跨语言的问题,我们提供了两个版本的头尾客户端,即Nginx头尾客户端和Java头尾客户端,基本满足了所有业务系统的头尾文件拉取功能。但是业务系统如何引用这些头尾文件,这里就涉及到一个SSI(服务端网页包含)技术。下面就介绍一下两种方式的头尾客户端如何解决头尾文件的拉取和SSI问题。
•Nginx头尾客户端
该方式主要是利用了Nginx的SSI模块来实现头尾文件的拉取和SSI问题,ngx_http_ssi_module模块是Nginx中的一个过滤器,在经过它的响应中处理SSI(服务端包含)指令。目前用到的就是inclued指令,配置示例:
<!--# include file="/fragment/footer.html" -->
location ~ ^/fragment/ {
proxy_cache header_cache;
proxy_cache_key $uri;
proxy_cache_valid 200 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_cache_lock_timeout 1s;
proxy_connect_timeout 1s;
proxy_ignore_headers Set-Cookie Cache-Control;
proxy_hide_header Cache-Control;
proxy_hide_header Set-Cookie;
# 参考头尾系统中配置,请注意区分测试环境和生产环境,返回的文件内容默认都是UTF-8编码内容,如果需要GBK编码内容需要在env后面拼接参数?charset=GBK
# 只需要替换{appId} {token} 和 {env}
rewrite ^/fragment/(.*) /open/fragment/$1/Nginx/$nginx_version/$server_addr/{appId}/{token}/{env} break;
proxy_set_header Accept-Encoding "";
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://xxx.jd.local;
}