随着云服务的不断发展,客户对部署在云平台上的Web 服务应用程序有了更高的要求。用户新的需求需要尽快得到响应,也就是产品上线的周期越来越短。一般情况下,一个版本的应用服务正在线上满足用户的需求。下一个新版本的应用服务,经过了功能测试,性能测试,模拟产品环境测试,随时准备上产品线。值得注意的是,如果您正在应用微服务的架构方案,会和本文讲述的有所不同。
为了加快云平台上Web服务的迭代开发周期,充分利用云平台的软件和硬件资源,进而更快更好的满足更多用户的使用需求,云平台需要在一段时间内,可以有新旧两个不同版本的Web 服务应用程序同时满足用户的需求。用户感觉不到新产品的上线,运维时间也大大缩小。比如,已经打开了的、正在使用的应用服务可以继续连接到旧版本的服务器上,用户新打开的应用服务则连接到部署了最新版本的应用服务器上。但这样也就带来了一个问题,如何管理部署在云平台上同一个Web应用服务的多个版本的静态资源。
不同版本静态资源的管理问题我们把正在线上满足用户需求的软硬件资源、Web应用服务看成是活着的(active),标记为SideA。那么随时准备上线的这一套云平台软硬件资源是不活着的(inactive),标记为SideB。经过优化后,两套环境都可以是活着的(active-active)。现在云平台上部署Web应用服务都是集群化的,根据业务逻辑增长的不同需求会添加、减少部署Web应用服务的服务器节点数目。对Web应用服务的功能请求也会使用例如F5, Nginx 等负载均衡服务来加以管理。
云平台环境SideA和SideB 都活着的,同时向用户提供服务。那么负载均衡服务器会同时统一管理原属于SideA 和SideB 的Web应用服务的节点。这样用户对Web应用服务的功能请求会路由到SideA或SideB 环境中的任何一个部署Web应用服务的节点。负载均衡服务并不是对Web 应用服务的每一个请求都会保持持久化连接(session affinity)。如果部署版本相同的话,在任何一个服务器上请求到的Javascript,html,样式表CSS,图片等静态资源文件都是一样的,也就不存在静态资源多版本管理的问题。显而易见,如果静态资源请求中包含不同版本的时间戳,Web 应用服务器会返回404 资源找不到的Http 请求错误。请求不到资源,这样Web应用服务的功能就不能正常工作了。
多个版本静态资源的解决方案大道至简。我们把不同版本的Web静态资源放置到云平台环境SideA 和SideB 都能访问到的NFS 文件服务器上。在Web 应用程序中,调用Servlet 主动响应对静态资源的请求。程序发现是本地的静态资源,则读取本地的文件并缓存到内存中。不是本地的静态资源,则读取NFS 服务器上对应版本的静态资源文件。返回给客户端并缓存到内存中。接下来,我们分别进行详细介绍。
SampleServlet 响应对静态资源的请求通常开发人员不用写程序来处理客户端静态资源的请求,Web容器(Tomcat, Weblogic , WebSphere 等)本身会提供缺省的实现。为了解决这个多版本的问题,我们需要自己写SampleServlet来处理静态资源文件的请求。清单1给出了web.xml 中SampleServlet的配置示例信息。从URL mapping 中,我们可假定对Web 服务静态资源的请求类似于下面的URL:"http://domain/{contextRoot}/static/20170509-1101/js/SampleApp.js"。
清单 1. SampleServlet 在web.xml中的配置1
2
3
4
5
6
7
8
9
10
11
12
| <servlet>
<servlet-name>SampleServlet</servlet-name>
<servlet-class>com.ibm.developerworks.paper.SampleServlet</servlet-class>
<init-param>
<param-name>isDebug</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SampleServlet</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
|
在实现SampleServlet的过程中,我们需要解决以下几个问题。这些问题涉及到了如何更好的组织、管理这些静态资源文件,进而响应用户的请求?用户的请求是不是需要准确的知道资源文件的内容类型? 资源文件是不是需要在内存中缓存?资源文件更新了,SampleServlet如何及时响应?
正确返回请求资源的Mime 类型信息
HttpServletResponse 的实例要调用setContentType方法正确返回请求资源的Mime 类型信息。如果不是这样的话,显而易见浏览器不知道返回来的资源文件是什么东西。举例来说,浏览器请求到了Javascript 文件,如果SampleServlet 在响应头中不主动的设置contentType 属性为"application/javascript",它的响应类型默认是"text/plain"。那么出于安全方面的原因该javascript 文件就不能被浏览器加载运行。清单 2 给出了常见的静态资源文件的Mime 类型信息。示例中,我们用静态HashMap来存储, 在SampleServlet 类第一次加载的时候初始化。读者可以根据需要把资源及其Mime类型信息存储为xml 文件、json 文件或者properties 配置文件。
清单 2. 静态资源Mime类型示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // test/javascript is no long used by html 5
mimeTypes.put("js", "application/javascript");
mimeTypes.put("json", "application/json");
mimeTypes.put("css", "text/css");
mimeTypes.put("ico", "image/x-icon");
mimeTypes.put("gif", "image/gif");
mimeTypes.put("jpg", "image/jpeg");
mimeTypes.put("jpeg", "image/jpeg");
mimeTypes.put("jpe", "image/jpeg");
mimeTypes.put("png", "image/png");
mimeTypes.put("html", "text/html");
mimeTypes.put("htm", "text/html");
mimeTypes.put("xml", "text/xml");
......
response.setContentType(resource.getMimeType());
|
SampleServlet 的缓存机制
如果应用程序本身来管理静态资源文件,SampleSevlet就需要文件I/O 操作读取请求的文件内容到内存中,并把结果返回给客户端。为了减少不必要的文件读取,提高响应速度,SampleServlet 需要缓存机制缓存文件内容。一个最重要的原因是,SampleServlet 会从NFS 上读取其他版本的Web静态资源,文件I/O操作会很耗时。另外,在上面的清单1中,SampleServlet 初始化配置参数中有isDebug参数变量,在Debug的环境中,开发人员如果不需要这个缓存机制,就把其设置为true。 |