如何设计并发安全的多用户ajax web应用程序

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (68)

我有一个网页显示来自服务器的大量数据。通信是通过Ajax完成的。

每当用户交互和更改此数据(例如,用户A重命名某物)时,它会告诉服务器执行该操作,而服务器则返回新更改的数据。

如果用户B同时访问页面并创建一个新的数据对象,它将再次通过Ajax告诉服务器,服务器将为用户返回新的对象。

在A的页面上,我们有一个重命名对象的数据。在B的页面上,我们有一个新对象的数据。在服务器上,数据既有重命名的对象,也有新的对象。

当多个用户同时使用该页面时,我有哪些选择使页面与服务器保持同步?

可以避免这样的选项,比如锁定整个页面或在每次更改时将整个状态转储给用户。

如果有帮助,那么在这个特定的例子中,网页调用一个在数据库上运行存储过程的静态WebMethod。存储过程将返回它已更改的任何数据,并且不再返回。然后,静态WebMethod将存储过程的返回转发给客户端。

赏金编辑:

如何设计一个多用户Web应用程序,它使用Ajax与服务器通信,但避免并发性问题?

即同时访问数据库中的功能和数据,而不存在任何数据或状态损坏的风险

提问于
用户回答回答于

概述:

  • 介绍
  • 服务器体系结构
  • 客户架构
  • 更新案例
  • 提交案件
  • 冲突案件
  • 性能与可伸缩性

我不会在这里讨论任何特定的产品。其他人提到的是一个很好的工具集,可以看一看(可能在列表中添加node.js)。

从体系结构的角度来看,似乎遇到了与版本控制软件相同的问题。一个用户检查对一个对象的更改,另一个用户希望以另一种方式=>冲突来更改同一个对象。必须集成用户对对象的更改,同时能够及时高效地交付更新,检测和解决上述冲突。

如果我站在你的立场上,我会发展出这样的东西:

1.服务器端:

  • 确定一个合理的级别来定义我所说的“原子工件”。这将取决于您的Web服务器、数据库和缓存硬件、用户的#、对象的#等等。做决定可不容易。
  • 每个原子工件都有:
- an application-wide unique-id
- an incrementing version-id
- a locking mechanism for write-access (mutex maybe)
- a small history or "changelog" inside a ringbuffer (shared memory works well for those). A single key-value pair might be OK too though less extendable. see [http://en.wikipedia.org/wiki/Circular\_buffer](http://en.wikipedia.org/wiki/Circular_buffer)

  • 能够传递的服务器或伪服务器组件相关有效地对连接的用户进行切换。观察者模式是你的朋友。

2.客户端:

  • 能够长时间运行到上述服务器的HTTP连接或使用轻量级轮询的javascript客户端。
  • 一个javascript工件更新器组件,当连接的javascript客户端通知被监视的工件-历史记录中的变化时刷新站点内容。(同样,观察者模式可能是一个很好的选择)
  • 一个javascript工件-提交器组件,它可能请求更改原子工件,试图获取互斥锁。它将通过比较已知的客户端工件-版本-id和当前的服务器端工件-版本-id来检测工件的状态是否在几秒钟前被另一个用户更改(javascript客户端的延迟和提交过程因素)。
  • 一个javascript冲突的解决者,允许一个人--改变--是正确的决定。

那它会怎么滚动。

案例1:用于更新的顺序图的种类:

  • 浏览器呈现页
  • JavaScript“查看”工件,每个工件至少有一个值字段、唯一的-和一个版本-id。
  • JavaScript客户端开始启动,请求“监视”从其发现的版本开始的发现工件历史(旧的更改并不有趣)
  • 服务器进程记录请求并不断检查和/或发送历史记录。
  • 历史记录项可能包含简单通知“工件x已更改,客户端请请求数据”允许客户端独立轮询或完整数据集“工件x已更改为值foo”。
  • JavaScript工件更新器在已知更新后尽快获取新值。它执行新的Ajax请求或由javascript客户端提供数据。
  • 页面DOM-内容被更新,用户可选地被通知.历史观察继续。

案例2:现在开始承诺:

  • 工件-提交者知道用户输入中所需的新值,并向服务器发送更改请求。
  • 服务器端互斥
  • 服务器收到“嘿,我知道工件x的状态从123版本,让我把它设置为值foo请。”
  • 如果工件x的服务器端版本等于(不能小于123),则接受新值,生成一个新版本id为124。
  • 新的状态信息“更新到版本124”和可选的新值foo放置在工件x的环缓冲区(Changelog/History)的开头。
  • 服务器端互斥被释放
  • 请求工件提交者很高兴收到与新id一起的提交确认。
  • 同时,服务器端服务器组件一直在轮询/将环形缓冲区推送到连接的客户端。所有观察工件x缓冲区的客户端都将在它们通常的延迟时间内获得新的状态信息和值(参见案例1)。

案例3:冲突:

  • 工件提交者从用户输入中知道所需的新值,并向服务器发送更改请求。
  • 同时,另一个用户成功地更新了相同的工件(参见案例2),但是由于不同的延迟,我们的其他用户还不知道这一点。
  • 因此,获得一个服务器端互斥体(或等待“更快”的用户提交更改)。
  • 服务器收到“嘿,我知道版本123的工件x的状态,让我把它设置为值foo。”
  • 在服务器端,工件x的版本现在已经是124了。请求客户端无法知道他将覆盖的值。
  • 显然,服务器必须拒绝更改请求(不包括在上帝干预的覆盖优先级中),释放互斥对象,并且很好地将新版本id和新值直接发送给客户端。
  • 面对拒绝的提交请求和请求更改的用户尚不知道的值,javascript工件提交器引用冲突解决程序,它向用户显示并解释问题。
  • 智能冲突解决程序JS向用户提供了一些选项,允许用户再次尝试更改该值。
  • 一旦用户选择了他认为正确的值,这个过程就从案例2开始(或者如果其他人更快的话,再次重新开始)

关于性能和可伸缩性的几个字

HTTP轮询与HTTP“推送”

  • 轮询创建请求,每秒1次,每秒5次,不管认为这是一个可接受的延迟。如果没有很好地配置(Apache?)和(php?)作为“轻量级”启动器,这可能会对基础设施造成很大的伤害。希望优化服务器端上的轮询请求,使其运行的时间远远少于轮询间隔的长度。将运行时分割成两半可能意味着将整个系统的负载降低50%,
  • 通过HTTP进行推送(假设WebWork太远,无法支持他们)将要求为每个用户提供一个Apache/Lightthttpd进程。为每个进程保留的驻留内存和的系统总内存将是将遇到的一个非常确定的缩放限制。减少连接的内存占用将是必要的,并限制连续CPU和I/O在每一个连接中完成的工作量。

后端缩放

  • 忘记数据库和文件系统,将需要某种基于共享内存的频繁轮询后端(如果客户端不直接轮询,那么每个正在运行的服务器进程都会)
  • 如果选择了memcache,可以进行更好的扩展,但它仍然很昂贵。
  • 提交的互斥必须全局工作,即使希望有多个前端服务器来实现负载平衡。

前端缩放

  • 无论是轮询还是接收“推送”,尝试一步获得所有被监视工件的信息。

“创造性”调整

  • 如果客户端正在轮询,而且许多用户倾向于查看相同的工件,您可以尝试将这些工件的历史记录作为静态文件发布,允许Apache缓存它,但是当工件发生变化时,可以在服务器端刷新它。这会将PHP/memcache从游戏中删除,有些则用于请求。Lightthttpd在处理静态文件方面非常有效。
  • 使用像coteno.com这样的内容传递网络将工件历史推到那里。推送延迟将更大,但可伸缩性是一个梦想
  • 编写用户使用java或flash(?)连接到的真正服务器(不使用HTTP)。必须在一个服务器线程中处理为多个用户服务的问题。通过开放的套接字循环,完成(或委派)所需的工作。可以通过分叉进程或启动更多的服务器进行扩展。不过,穆特克斯必须保持全球独一无二。
  • 根据加载场景的不同,按工件标识范围对前端和后端服务器进行分组。这将允许更好地使用持久内存(没有数据库拥有所有数据),并且可以扩展互斥。但是,javascript必须同时维护到多个服务器的连接。

我希望这能成为你自己想法的开端。我相信还有更多的可能性。我不只是欢迎对这篇文章的任何批评或增强,wiki是启用的。

用户回答回答于

操作变换似乎很适合您对并发和一致的多用户编辑的需求。是一个Google文档中的技术(也用于GoogleWave):

有一个基于JS的库,用于使用操作转换-ShareJS,由GoogleWave团队的一名成员编写。

如果你愿意的话,就有一个完整的MVC web框架--DerbyJS建立在ShareJS上,这一切都是为了你。

它使用BrowserChannel在服务器和客户端之间进行通信(我认为WebSocket支持应该正在开发中--它以前是通过Socket.IO提供的,但由于开发人员与Socket.io的问题而被删除了),不过目前Begnerdocs有点稀疏。

扫码关注云+社区

领取腾讯云代金券