首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

多版本 Web 静态资源的一种管理方案(2)

多版本 Web 静态资源的一种管理方案(2)

正确返回Http 304 状态信息
对于每一次Web静态资源的请求,Web 服务应用的SampleServlet                通过文件最后一次被改动的时间戳和Http请求中相关信息来确认此资源文件是否被修改过。如果没有被修改过的话,返回HttpServletResponse.SC_NOT_MODIFIED,也就是http                code 304状态告诉浏览器请使用本地缓存信息。如果此资源文件已经更新了,SampleServlet                会从硬盘上读取该资源文件,并更新缓存信息。清单 3 示例代码如下:
清单 3. 正确返回Http 304                    状态信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
if (checkcache(request, resource.getLastModified())){
  // 如果在浏览器缓存中,返回 HTTP status code 304
  response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
  return;
}
private boolean checkcache(HttpServletRequest req, long FileModified){
  if (isdebug) //开发环境需要重新加载静态资源文件
    return false;
  long ModifiedSince = -1L;
  try{
    ModifiedSince = req.getDateHeader("If-Modified-Since");
  }
  catch (IllegalArgumentException iae){
    ModifiedSince = -1L;
  }
  long systemTime = System.currentTimeMillis();
  if (ModifiedSince != -1L){
    if (ModifiedSince / 1000L == FileModified / 1000L){
      return true; // 返回304
    }
    if ((systemTime >= ModifiedSince) && (ModifiedSince > FileModified)){
      return true; // 返回304
    }
    if (systemTime < ModifiedSince){
      LOG.log(Level.FINEST, "The IfModifiedSince date is later than the server's current
      time so it is invalid, ignore.");
    }
  }
  return false;
}




用NFS                服务器存储多版本的静态资源文件这个Web 应用服务器知道部署在本地的静态资源文件在哪里,通常是一个war 包或ear                包。如果用户的请求URL中的版本时间戳和本地部署的版本时间戳一致,那么SampleSevlet                很容易读取本地文件内容,缓存文件内容,并返回给客户端。
在SampleServlet 的doGet 方法中,我们发现资源文件的版本时间戳不是本地Web服务器部署的版本时间戳,就会向NFS                文件服务器发出请求。这需要Web 服务器事先能够访问NFS                文件服务器,否则时间开销会很大,用户请求的响应会很慢。通常在部署Web服务应用的脚本里面或在应用服务启动的过程中挂载到NFS                服务器。清单4给出了示例代码。
清单 4.                请求NFS资源文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
File file = new File(nfsfilepath);
if (file.isFile())
{
  long lastModified = file.lastModified();//获取文件的被修改的时间戳
  resource.setLastModified(lastModified);
  LOG.log(Level.INFO, "The web resource from NFS server is " + nfsfilepath + " and the last
  modified time is " + lastModified);
  if (isdebug || !cache.containsKey(resourcePath))//查看缓存信息
  {
    try
    {
      byte[] bytes = NFSFileUtil.nfs_getFileBytes(file, NFSFileUtil.NFS_RETRY_SECONDS);
      resource.setData(bytes);//从NFS 服务器上读取到资源文件
      LOG.log(Level.INFO, "Obtained web resource from NFS server for - " + nfsfilepath);
}
catch (IOException e)
    {
      LOG.log(Level.SEVERE, "Failed to obtain web resource from NFS server for - " +
      nfsfilepath, e);
      return null;
    }
  }
}
else
{
  LOG.log(Level.SEVERE, "Failed to obtain web resource's lastModified and data from NFS  
  server for - " + nfsfilepath);
  return null;
}




zooKeeper                管理静态资源文件读到这里,您一定会考虑这些问题:什么时机把新版本的静态资源文件拷贝到NFS 服务器上?为什么不把NFS 服务器上的资源文件下载到Web 服务器上呢?                什么时机去下载?如果NFS                服务器上的版本越来越多该怎么办?如何管理?这里面涉及到时间复杂度和空间复杂度的算法问题。一个最根本的指导原则,用户请求的响应要及时,妥善处理好程序的性能问题。一个最根本的价值观就是客户为要。
在云平台Web 服务器集群的复杂环境中,我们通过zooKeeper                服务器来管理NFS服务器上哪些静态资源版本在线,哪些版本已经下线,进而及时清理硬盘空间。清单5 给出了python脚本的示例代码:
清单 5.                    zooKeeper管理资源文件代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def updateZookeeperNode():  
  print "The online version is %s" % ONLINE_VERSION #打印当前准备上线的版本号
  if (ONLINE_VERSION in [None,'']):
    logging.error("Severe error - the version to be onlined is empty.")
    return
  offline = None
  try:
    baseTN = registryParser.getBaseTopologyName() #获取云平台的名称
    side = registryParser.getTopologyName() #获取云平台环境即将上线的面,值是A 或 B
    versionPath = '/%s/data/SampleApp/version/%s' %(baseTN, side) #版本路径信息
sideValue = zooKeeperClient.getData(versionPath)#获得该环境上已经上线的版本号
if (sideValue == "null" or sideValue in [None,'']): #节点上还没有上线的版本号,第一次上线      
  print "Create node %s on zookeeper and its value is %s" % (versionPath, ONLINE_VERSION)
      logging.info("Create node %s on zookeeper and its value is %s" % (versionPath, ONLINE_VERSION))
      zooKeeperClient.createNode(versionPath, ONLINE_VERSION) #节点上存储上线的版本号
      return
    else:
      logging.info("Old %s SampleApp version is %s" % (side, sideValue))#输出旧的版本号
      if(sideValue != ONLINE_VERSION):
        zooKeeperClient.setData(versionPath, ONLINE_VERSION) #新版本上线
        logging.info("New %s SampleApp version is %s" % (side, ONLINE_VERSION))
        children = zooKeeperClient.getChildNodes('/%s/data/SampleApp/version' %(baseTN))
        offlineFlag = True
        for node in children: # 循环查看即将下线的版本在其他云平台环境中有没有被用到
          #Filter itself and empty side name
          if(node == side or node == ''):
            continue
          else:
            data = zooKeeperClient.getData('/%s/data/SampleApp/version/%s' %(baseTN, node))
            if(data == sideValue):
              offlineFlag = False #其他环境正在使用该版本号,不删除其在NFS上的资源文件
              break
        if(offlineFlag):
          offline = sideValue # 没有其他环境用到,旧版本准备下线
        else:
          logging.info("The obsolete version %s is still using by anther side." % sideValue)
      else:
        print "Flip failover case - no new version is online."
        logging.info("Flip failover case - no new version is online.")
    if (offline is not None):
      logging.info("The offline version is %s" % offline)
      global OFFILE_VERSION
      OFFILE_VERSION = offline
  except Exception,e:
    logging.error(e)
    raise




在部署Web应用程序的时候,调用清单5的示例脚本把资源文件信息拷贝到NFS服务器上。在集群环境中,不需要在部署每一个服务节点的过程中都这么做。示例中,我们通过zookeeper选举出一个节点来完成静态资源到NFS                服务器的拷贝任务。
返回列表