5

假设我们有一个由 Tomcat 之类的 servlet 引擎/容器支持的 Web 应用程序。用户登录。该用户的数据库记录(由 User 类的实例表示)被加载,我们可以将其存储为带有键“currentUser”的会话属性,设置一次,然后使用它处理后续请求。此外,我们在 session 属性中放入了更多的对象。很基本的东西。

现在,如果我们需要部署一些新代码并重新启动 Tomcat……只要我们不更改任何实例存储在用户中的类,用户会话在重新启动后仍然完好无损(从磁盘/数据库恢复)会话。但这是一个大问题。我不希望用户在新代码发布时失去他们的会话。

为了解决这个问题,我想我可以在会话对象中仅存储假定永远不会更改的类的实例(例如将登录用户的 ID 存储为整数而不是 User 类的实例)。然后我永远不会遇到无法在重新启动时反序列化会话对象的问题。但这使事情变得稍微复杂一些,因为现在我必须使用 ID 从数据库等加载实际对象(并且使用缓存,性能损失并不是真正的问题)。

这是解决此问题的通常做法吗?

4

3 回答 3

3

简单的解决方案

正如您所建议的,仅在会话中加载 userId,并使用 ehcache 等缓存服务来检索用户对象。如果用户对象不存在,它将第一次加载,但随后将被缓存,后续请求将非常快。如果您担心用户类更改,请仅基于缓存内存,以便在服务器重新启动之间重置。

平台解决方案

看看兵马俑 (http://www.terracotta.org/)。如果您能够在他们的平台上部署会话,它们将允许您在服务器重新启动之间维护它们,并在保留旧字段的同时更新实际用户类。一旦你启动并运行它,它就会变得非常酷。

请记住,尽管 Terracotta 集成并不简单,也不适合新手。然而,好处是所有 Web 应用程序通常需要的可扩展性和高可用性。

于 2011-05-12T15:15:08.940 回答
2

就序列化而言,只要您不更改序列化格式,它就不会成为问题。否则你会得到可怕的InvalidClassException。你需要做的是:

  1. 在您正在序列化的每个对象上设置一个serialVersionUID:如果您在重新启动之间没有大幅更改类上的字段,这应该足够了。
  2. 覆盖writeObject/readObject方法以精确控制序列化格式:如果这样做,您甚至可以解决类中的剧烈变化。
  3. 使用 JSON/XML 或任何其他可以干净地处理翻译的存储格式。也许你最好的选择。
于 2011-05-12T04:04:09.913 回答
1

有趣的情况和很好的问题。

通常,从我在企业环境中看到的情况来看,会话通常在部署新版本的 Web 应用程序后被终止 - 因为无论如何它是一个新部署,应用程序服务器通常不知道新应用程序和新应用程序之间的区别版本。我认为如果服务器保留会话不是一个好主意,因为它可能只是一个完全不同的应用程序,用户可能没有任何访问权限等,所以这可能是一个安全问题。

但是,让我们假设会话将被持久化并且即使对于新部署也可以恢复。您描述的方式听起来差不多,您也可以做得更复杂一些,例如检查会话是否有需要根据版本转换(或再次从数据库加载)的旧对象。在 Java 中,你会努力让你的类是二进制兼容的,所以新版本的应用程序仍然可以反序列化它。这就是 writeObject/readObject 的用途。

另一个想法是将“会话”以您自己的数据格式存储在数据库中,并且仅将会话用于轻量级数据,例如用户身份验证和您自己的应用程序会话 ID。虽然我从未在 Java 应用程序中看到过这种情况,但它可以做您想做的事情并在部署时执行迁移步骤,以将数据迁移到新格式。连同自动登录/记住我的 cookie,您的用户不会看到任何差异。

于 2011-05-11T22:34:49.777 回答