如何设计一个同时安全的多用户 Ajax Web 应用程序

2024-04-08

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

每次用户交互并更改此数据(假设用户 A 重命名某些内容)时,它都会告诉服务器执行该操作,然后服务器返回新更改的数据。

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

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

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

诸如锁定整个页面或在每次更改时将整个状态转储给用户之类的选项是可以避免的。

如果有帮助,在此特定示例中,网页会调用在数据库上运行存储过程的静态 Web 方法。存储过程将返回它已更改的所有数据,仅此而已。然后,静态 Web 方法将存储过程的返回转发给客户端。

赏金编辑:

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

IE。并发访问数据库上的功能和数据,没有任何数据或状态损坏的风险


概述:

  • Intro
  • 服务器架构
  • 客户端架构
  • 更新案例
  • 提交案例
  • 冲突案例
  • 性能和可扩展性

嗨雷诺斯,

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

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

如果我处于你的立场,我会开发出这样的东西:

1.服务器端:

  • 确定一个合理的级别,在该级别上定义我所说的“原子工件”(页面?页面上的对象?对象内的值?)。这将取决于您的网络服务器、数据库和缓存硬件、用户数量、对象数量等。这不是一个容易做出的决定。

  • 对于每个原子工件有:

    • 应用程序范围内的唯一 ID
    • 递增的版本 ID
    • 用于写访问的锁定机制(可能是互斥锁)
    • 环形缓冲区内的小历史记录或“更改日志”(共享内存非常适合这些)。单个键值对也可能没问题,尽管可扩展性较差。看http://en.wikipedia.org/wiki/Circular_buffer http://en.wikipedia.org/wiki/Circular_buffer
  • 能够交付的服务器或伪服务器组件relevant有效地向连接的用户发送变更日志。观察者模式是你的朋友。

2. 客户端:

  • 一个 JavaScript 客户端,能够与上述服务器建立长期运行的 HTTP 连接,或者使用轻量级轮询。

  • 一个 javascript 工件更新程序组件,当连接的 javascript 客户端通知监视工件历史记录中的更改时,该组件会刷新站点内容。 (观察者模式可能是一个不错的选择)

  • 一个 JavaScript 工件提交者组件,可能会请求更改原子工件,尝试获取互斥锁。它将通过比较已知的客户端工件版本 ID 和当前的服务器端工件版本 ID 来检测工件的状态是否在几秒钟前被另一个用户更改(JavaScript 客户端的延迟和提交过程因素)。

  • 一个 JavaScript 冲突解决程序,允许人类做出哪个更改是正确的决定。您可能不想只告诉用户“有人比您更快。我删除了您的更改。去哭吧。”。来自技术差异或更用户友好的解决方案的许多选择似乎都是可能的。

那要怎么滚呢...

情况 1:更新的序列图:

  • 浏览器渲染页面
  • javascript“看到”每个工件至少有一个值字段、唯一的和版本 ID
  • javascript 客户端启动,请求从找到的版本开始“观察”找到的工件历史记录(旧的更改并不有趣)
  • 服务器进程记录请求并持续检查和/或发送历史记录
  • 历史条目可能包含简单的通知“工件 x 已更改,客户端请请求数据”,允许客户端独立轮询或完整数据集“工件 x 已更改为值 foo”
  • javascript artifact-updater 会在得知新值已更新后立即获取新值。它执行新的 ajax 请求或由 javascript 客户端提供数据。
  • 页面 DOM 内容被更新,用户可以选择收到通知。历史观察仍在继续。

案例 2:现在提交:

  • 工件提交者从用户输入中知道所需的新值并向服务器发送更改请求
  • 获取服务器端互斥锁
  • 服务器收到“嘿,我知道版本 123 中的工件 x 的状态,让我将其设置为值 foo pls。”
  • 如果工件 x 的服务器端版本等于(不能小于)123,则接受新值,并生成新版本 id 124。
  • 新的状态信息“更新到版本 124”和可选的新值 foo 被放置在工件 x 的环形缓冲区的开头(变更日志/历史记录)
  • 服务器端互斥体被释放
  • 请求工件的提交者很高兴收到提交确认以及新的 ID。
  • 同时,服务器端服务器组件不断轮询/推送环形缓冲区到连接的客户端。所有观察工件 x 缓冲区的客户端都将在其通常的延迟内获取新的状态信息和值(参见案例 1。)

情况 3:对于冲突:

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

关于性能和可扩展性的一些话

HTTP 轮询与 HTTP“推送”

  • 轮询会创建请求,每秒 1 个、每秒 5 个,无论您认为可接受的延迟是什么。如果您没有将您的(Apache?)和(php?)配置得足够好以成为“轻量级”启动器,这对您的基础设施来说可能相当残酷。需要优化服务器端的轮询请求,使其运行时间远小于轮询间隔的长度。将运行时间分成两半很可能意味着将整个系统负载降低多达 50%,
  • 通过 HTTP 推送(假设 webworkers 距离太远而无法支持它们)将要求您为每个用户提供一个 apache/lighthttpd 进程每时每刻。为每个进程保留的驻留内存和系统总内存将是您将遇到的一个非常确定的扩展限制。减少连接的内存占用是必要的,并限制每个连接中连续执行的 CPU 和 I/O 工作量(您需要大量的睡眠/空闲时间)

后端缩放

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

前端缩放

  • 无论您是轮询还是接收“推送”,请尝试一步获取所有观看的工件的信息。

“创意”调整

  • 如果客户端正在轮询并且许多用户倾向于观看相同的工件,您可以尝试将这些工件的历史记录发布为静态文件,允许 apache 缓存它,但当工件发生更改时在服务器端刷新它。这使得 PHP/memcache 不再参与一些请求。 Lighthttpd 在提供静态文件方面非常有效。
  • 使用像 cotendo.com 这样的内容交付网络将工件历史记录推送到那里。推送延迟会更大,但可扩展性只是一个梦想
  • 编写一个真实的服务器(不使用 HTTP),用户使用 java 或 flash(?)连接到该服务器。您必须在一个服务器线程中为许多用户提供服务。通过打开的套接字循环,执行(或委派)所需的工作。可以通过分叉进程或启动更多服务器进行扩展。不过,互斥体必须保持全局唯一。
  • 根据负载场景,按工件 ID 范围对前端和后端服务器进行分组。这将允许更好地使用持久内存(没有数据库拥有所有数据),并且可以扩展互斥。不过,您的 JavaScript 必须同时维护与多个服务器的连接。

好吧,我希望这可以成为您自己想法的开始。我相信还有很多更多的可能性。 我非常欢迎对这篇文章提出任何批评或改进,维基已启用。

克里斯托夫·斯特拉森

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何设计一个同时安全的多用户 Ajax Web 应用程序 的相关文章

随机推荐

  • 将观察者添加到 BOOL 变量

    是否可以将观察者添加到简单变量 例如 BOOL 或 NSIntegers 并查看它们何时发生变化 Thanks 您观察键 当它们的值发生变化时会收到通知 数据类型可以是任何类型 对于定义为 Objective C 属性的任何内容 在 h 文
  • Camel如何以流模式处理json?

    为了在流模式下读取 json 并解组 Camel OOB 中有哪些可用选项 如果不是现成的 如何实施 我发现camel xstream 可能会有所帮助 流模式是默认的还是我们需要做其他事情来使其以流模式读取 还有 Jackson Strea
  • 如何在 Symfony 4 中使用事件处理异常?

    现在我正在尝试捕获这样的异常事件 try echo 1 0 catch Exception e subs new ExceptionSubscriber this gt dispatcher gt addSubscriber subs 我定
  • 是否应该始终使用 boost::ptr_vector 代替 std::vector ?

    这只是我遇到的一个概念性问题 在我当前的项目中 感觉我过度使用了 boostsmart ptr and ptr container图书馆 我正在创造boost ptr vectors在许多不同的对象中 并调用 Transfer 方法从一个对
  • AttributeError:“float”对象没有属性“lower”

    我面临这个属性错误 并且我陷入了如何处理浮点值 如果它们出现在推文中 的问题 流式推文必须小写并标记化 因此我使用了 split 函数 有人可以帮我解决这个问题 有任何解决方法或解决方案吗 这是error我正在 AttributeError
  • 如何清理redis中不活跃的玩家?

    我正在制作一个使用 redis 来存储游戏状态的游戏 它可以很好地跟踪位置和玩家 但我没有一个好的方法来清理不活跃的玩家 每当玩家移动时 这是一个半慢速移动游戏 想想每秒 1 5 帧 我就会用新位置更新哈希并删除旧位置键 跟踪活跃玩家的最佳
  • 在 Mac OS X 上使用 docx2txt 和 Git 对 Word .docx 文件进行版本控制

    On Git网站上有关于 Microsoft Word 版本控制的详细说明 doc文件与catdoc http git scm com book en Customizing Git Git Attributes http git scm
  • Android WebView LoadData - 土耳其字符

    如何在 webview 中将土耳其语字符 显示为 loadData 我的网络视图是 mWebView loadData detailsHtml text html UTF 8 thanks webView loadData htmlStr
  • 我需要进行哪些更改才能使 Magento 与 PHP 5.3 配合使用?

    我需要将我的服务器升级到 PHP 5 3 但安装了 Magento 而且我知道 Magento 不能很好地与 PHP 5 3 配合使用 我认为变化不会太大 根据我所读到的 但是 我想我应该向大众开放 有人成功做到这一点吗 如果是这样 你记好
  • 开关盒无法正常工作

    当我离开现场时 我发布了下面的代码ip空白 空并向其他字段赋值 toast总是给出消息KATimer is invalid or missing 我期望看到一个toast显示一条消息 指示空字段 但下面的代码 如果任何字段为空 它总是说KA
  • 有没有一种方法可以列出所有对象,例如使用同义词的视图?

    就像罐头上说的那样 如何确定哪些 SQL 对象正在使用给定的 SYNONYM 我想一种方法是搜索 PROC 或 VIEW 的所有内容并运行LIKE SYN NAME 但这看起来很笨拙 select from sys sql expressi
  • 如何在.NET MAUI 中创建可重用组件?

    我最近刚刚开始使用 Net MAUI 但现在我想知道如何使用一段代码 例如我的所有页面上都有一个自制的导航栏 因为在所有 10 个页面上编写相同的代码是没有意义的 我想知道是否有办法创建一个可以像 React 或 Angular 一样重用的
  • 使用 psycopg2 插入多行

    根据psycopg2 用一个查询插入多行 https stackoverflow com questions 8134602 psycopg2 insert multiple rows with one query 使用 psycopg2
  • Visual Studio Windows 窗体预览以不同的分辨率?

    我喜欢以 1680 x 1050 分辨率运行 Visual Studio 但是 我们的客户通常使用 1024 x 768 是否有某种方法可以使我的编辑器保持原始分辨率 但以不同的分辨率显示表单设计器 谢谢 Austin 我建议您购买一个额外
  • 如何获取 google+ 的公共数据以及如何使用 php 从我的网站发布到 google+

    我的网站和 google plus 之间的身份验证工作正常 但我的详细信息和我的活动现在没有显示 我们得到 Googleplus 的 getAccessToken 就像 access token ya29 AHES6ZSTauVKgrdDU
  • 继承后受保护的成员行为。

    我对受保护的标识符有一些疑问 在 K Sierra 编写的 Sun Certified Java Programmer Study Guide 的第一章中 我发现了以下信息 一旦包外子类继承了受保护的成员 该成员 由子类继承 就成为子类外部
  • 将 Pandas 数据帧转换为时间序列

    我有一个熊猫数据框 Out 57 lastrun rate 0 2013 11 04 12 15 02 0 1 2013 11 04 13 14 50 4 2 2013 11 04 14 14 48 10 3 2013 11 04 16 1
  • 迭代器的无限产量

    我正在尝试学习一些红宝石 想象一下 我正在循环并执行一个长时间运行的过程 在这个过程中 我希望获得一个旋转器 只要需要就可以 所以我可以这样做 a aNow 0 skip setup a big loop print a aNow aNow
  • 在 beautiful soup 中使用 lambda 函数

    尝试匹配包含某些文本的链接 我正在做 links soup find all a href lambda x org in x 但这会引发 TypeError argument of type NoneType is not iterabl
  • 如何设计一个同时安全的多用户 Ajax Web 应用程序

    我有一个网页 显示来自服务器的大量数据 通信是通过ajax完成的 每次用户交互并更改此数据 假设用户 A 重命名某些内容 时 它都会告诉服务器执行该操作 然后服务器返回新更改的数据 如果用户 B 同时访问该页面并创建一个新的数据对象 它将再