Programing

다중 사용자 Ajax 웹 애플리케이션을 동시에 안전하게 설계하는 방법

lottogame 2020. 8. 28. 07:46
반응형

다중 사용자 Ajax 웹 애플리케이션을 동시에 안전하게 설계하는 방법


서버에서 많은 양의 데이터를 보여주는 웹 페이지가 있습니다. 통신은 ajax를 통해 이루어집니다.

사용자가 상호 작용하고이 데이터를 변경할 때마다 (사용자 A가 이름을 변경한다고 가정) 서버에 작업을 수행하도록 지시하고 서버는 변경된 새 데이터를 반환합니다.

사용자 B가 동시에 페이지에 액세스하고 새 데이터 객체를 생성하면 다시 ajax를 통해 서버에 알리고 서버는 사용자를 위해 새 객체를 반환합니다.

A의 페이지에는 이름이 변경된 개체가있는 데이터가 있습니다. 그리고 B의 페이지에는 새로운 객체가있는 데이터가 있습니다. 서버에서 데이터에는 이름이 변경된 개체와 새 개체가 모두 있습니다.

여러 사용자가 동시에 사용하는 경우 페이지를 서버와 동기화 상태로 유지하기위한 옵션은 무엇입니까?

전체 페이지를 잠 그거나 변경 될 때마다 사용자에게 전체 상태를 덤프하는 것과 같은 옵션은 오히려 피합니다.

도움이되는 경우이 특정 예제에서 웹 페이지는 데이터베이스에서 저장 프로 시저를 실행하는 정적 웹 메서드를 호출합니다. 저장 프로시 저는 변경된 데이터 만 반환합니다. 그런 다음 정적 웹 메서드는 저장 프로 시저의 반환을 클라이언트로 전달합니다.

바운티 편집 :

Ajax를 사용하여 서버와 통신하지만 동시성 문제를 방지하는 다중 사용자 웹 애플리케이션을 어떻게 설계합니까?

즉, 데이터 또는 상태 손상의 위험없이 데이터베이스의 기능 및 데이터에 대한 동시 액세스


개요 :

  • 소개
  • 서버 아키텍처
  • 클라이언트 아키텍처
  • 케이스 업데이트
  • 사례 커밋
  • 충돌 사례
  • 성능 및 확장 성

안녕 Raynos,

여기서는 특정 제품에 대해 논의하지 않겠습니다. 다른 사람들이 언급 한 것은 이미 살펴볼 좋은 도구 세트입니다 (해당 목록에 node.js를 추가 할 수도 있음).

아키텍처 관점에서 보면 버전 관리 소프트웨어에서 볼 수있는 것과 동일한 문제가있는 것 같습니다. 한 사용자가 개체에 대한 변경 사항을 확인하고 다른 사용자가 다른 방법으로 동일한 개체를 변경하려고합니다 => 충돌. 사용자 변경 사항을 개체에 통합하는 동시에 업데이트를 적시에 효율적으로 제공하고 위와 같은 충돌을 감지하고 해결해야합니다.

내가 당신의 입장이라면 나는 다음과 같은 것을 개발할 것입니다.

1. 서버 측 :

  • "원자 아티팩트"(페이지? 페이지의 개체? 개체 내부의 값?)라고 부르는 것을 정의 할 합리적인 수준을 결정합니다. 이것은 웹 서버, 데이터베이스 및 캐싱 하드웨어, 사용자 수, 개체 수 등에 따라 달라집니다. 쉬운 결정이 아닙니다.

  • 각 원자 아티팩트에는 다음이 있습니다.

    • 응용 프로그램 전체의 고유 ID
    • 증가하는 버전 ID
    • 쓰기 액세스를위한 잠금 메커니즘 (뮤텍스 가능)
    • 링 버퍼 내부의 작은 히스토리 또는 "변경 로그"(공유 메모리가 잘 작동 함). 확장 가능성은 낮지 만 단일 키-값 쌍도 괜찮을 수 있습니다. http://en.wikipedia.org/wiki/Circular_buffer 참조
  • 연결된 사용자 에게 관련 변경 로그를 효율적 으로 전달할 수있는 서버 또는 의사 서버 구성 요소입니다 . Observer-Pattern은 당신의 친구입니다.

2. 클라이언트 측 :

  • 위에 언급 된 서버에 장기 실행되는 HTTP 연결을 가질 수 있거나 가벼운 폴링을 사용하는 자바 스크립트 클라이언트.

  • 연결된 자바 스크립트 클라이언트가 감시 된 이슈 기록의 변경 사항을 알릴 때 사이트 콘텐츠를 새로 고치는 자바 스크립트 이슈 업데이트 프로그램 구성 요소입니다. (다시 관찰자 패턴이 좋은 선택 일 수 있습니다)

  • 뮤텍스 잠금 획득을 시도하면서 원자 아티팩트 변경을 요청할 수있는 자바 스크립트 아티팩트 커미터 구성 요소입니다. 알려진 클라이언트 측 artifact-version-id와 현재 서버 측 artifact-version-id를 비교하여 아티팩트의 상태가 몇 초 전에 다른 사용자에 의해 변경되었는지 감지합니다 (JavaScript 클라이언트 지연 및 커밋 프로세스 요인).

  • 사람이 올바른 결정을 내릴 수 있도록 해주는 자바 스크립트 충돌 해결사. 사용자에게 "누군가가 너보다 빠르다. 변경 사항을 삭제했습니다. 가서 울어"라고 말하고 싶지 않을 수도 있습니다. 다소 기술적 차이 또는 사용자 친화적 인 솔루션의 많은 옵션이 가능해 보입니다.

그래서 그것은 어떻게 굴러 갈까요 ...

사례 1 : 업데이트를위한 종류의 시퀀스 다이어그램 :

  • 브라우저 렌더링 페이지
  • 자바 스크립트는 각각 적어도 하나의 값 필드, 고유 및 버전 ID를 갖는 아티팩트를 "확인"합니다.
  • javascript 클라이언트가 시작되어 발견 된 버전에서 시작하여 발견 된 이슈 기록을 "감시"하도록 요청합니다 (이전 변경 사항은 흥미롭지 않음).
  • 서버 프로세스는 요청을 기록하고 지속적으로 이력을 확인 및 / 또는 전송합니다.
  • 히스토리 항목에는 클라이언트가 독립적으로 또는 전체 데이터 세트를 폴링 할 수 있도록 "아티팩트 x가 변경됨, 클라이언트 pls 요청 데이터"라는 간단한 알림이 포함될 수 있습니다. "아티팩트 x가 값 foo로 변경되었습니다"
  • javascript artifact-updater는 업데이트 된 것으로 알려지 자마자 새로운 값을 가져 오기 위해 할 수있는 일을합니다. 새로운 ajax 요청을 실행하거나 자바 스크립트 클라이언트에 의해 공급됩니다.
  • 페이지 DOM 콘텐츠가 업데이트되고 사용자에게 선택적으로 알림이 전송됩니다. 역사 관찰은 계속됩니다.

사례 2 : 이제 커밋합니다.

  • artifact-committer는 사용자 입력에서 원하는 새 값을 알고 서버에 변경 요청을 보냅니다.
  • 서버 측 뮤텍스가 획득 됨
  • 서버는 "이봐, 버전 123에서 아티팩트 x의 상태를 알고 있습니다. 값을 foo pls로 설정하겠습니다."
  • If the Serverside version of artifact x is equal (can not be less) than 123 the new value is accepted, a new version id of 124 generated.
  • The new state-information "updated to version 124" and optionally new value foo are put at the beginning of the artifact x's ringbuffer (changelog/history)
  • serverside mutex is released
  • requesting artifact committer is happy to receive a commit-confirmation together with the new id.
  • meanwhile serverside server component keeps polling/pushing the ringbuffers to connected clients. All clients watching the buffer of artifact x will get the new state information and value within their usual latency (See case 1.)

Case 3: for conflicts:

  • artifact committer knows desired new value from user input and sends a change-request to the server
  • in the meanwhile another user updated the same artifact successfully (see case 2.) but due to various latencies this is yet unknown to our other user.
  • So a serverside mutex is acquired (or waited on until the "faster" user committed his change)
  • Server receives "Hey, I know artifact x's state from version 123, let me set it to value foo."
  • On the Serverside the version of artifact x now is 124 already. The requesting client can not know the value he would be overwriting.
  • Obviously the Server has to reject the change request (not counting in god-intervening overwrite priorities), releases the mutex and is kind enough to send back the new version-id and new value directly to the client.
  • confronted with a rejected commit request and a value the change-requesting user did not yet know, the javascript artifact committer refers to the conflict resolver which displays and explains the issue to the user.
  • The user, being presented with some options by the smart conflict-resolver JS, is allowed another attempt to change the value.
  • Once the user selected a value he deems right, the process starts over from case 2 (or case 3 if someone else was faster, again)

Some words on Performance & Scalability

HTTP Polling vs. HTTP "pushing"

  • Polling creates requests, one per second, 5 per second, whatever you regard as an acceptable latency. This can be rather cruel to your infrastructure if you do not configure your (Apache?) and (php?) well enough to be "lightweight" starters. It is desirable to optimize the polling request on the serverside so that it runs for far less time than the length of the polling interval. Splitting that runtime in half might well mean lowering your whole system load by up to 50%,
  • Pushing via HTTP (assuming webworkers are too far off to support them) will require you to have one apache/lighthttpd process available for each user all the time. The resident memory reserved for each of these processes and your systems total memory will be one very certain scaling limit that you will encounter. Reducing the memory footprint of the connection will be necessary, as well as limiting the amount continuous CPU and I/O work done in each of these (you want lots of sleep/idle time)

backend scaling

  • Forget database and filesystem, you will need some sort of shared memory based backend for the frequent polling (if the client does not poll directly then each running server process will)
  • if you go for memcache you can scale better, but its still expensive
  • The mutex for commits has to work globaly even if you want to have multiple frontend servers to loadbalance.

frontend scaling

  • regardless if you are polling or receiving "pushes", try to get information for all watched artifacts in one step.

"creative" tweaks

  • If clients are polling and many users tend to watch the same artifacts, you could try to publish the history of those artifacts as a static file, allowing apache to cache it, nevertheless refreshing it on the serverside when artifacts change. This takes PHP/memcache out of the game some for requests. Lighthttpd is verry efficent at serving static files.
  • use a content delivery network like cotendo.com to push artifact history there. The push-latency will be bigger but scalability's a dream
  • write a real server (not using HTTP) that users connect to using java or flash(?). You have to deal with serving many users in one server-thread. Cycling through open sockets, doing (or delegating) the work required. Can scale via forking processes or starting more servers. Mutexes have to remain globaly unique though.
  • Depending on load scenarios group your frontend- and backend-servers by artifact-id ranges. This will allow for better usage of persistent memory (no database has all the data) and makes it possible to scale the mutexing. Your javascript has to maintain connections to multiple servers at the same time though.

Well I hope this can be a start for your own ideas. I am sure there are plenty more possibilities. I am more than welcoming any criticism or enhancements to this post, wiki is enabled.

Christoph Strasen


I know this is an old question, but I thought I'd just chime in.

OT (operational transforms) seem like a good fit for your requirement for concurrent and consistent multi-user editing. It's a technique used in Google Docs (and was also used in Google Wave):

There's a JS-based library for using Operational Transforms - ShareJS (http://sharejs.org/), written by a member from the Google Wave team.

And if you want, there's a full MVC web-framework - DerbyJS (http://derbyjs.com/) built on ShareJS that does it all for you.

It uses BrowserChannel for communication between the server and clients (and I believe WebSockets support should be in the works - it was in there previously via Socket.IO, but was taken out due to the developer's issues with Socket.io) Beginner docs are a bit sparse at the moment, however.


I would consider adding time-based modified stamp for each dataset. So, if you're updating db tables, you would change the modified timestamp accordingly. Using AJAX, you can compare the client's modified timestamp with the data source's timestamp - if the user is ever behind, update the display. Similar to how this site checks a question periodically to see if anyone else has answered while you're typing an answer.


You need to use push techniques (also known as Comet or reverse Ajax) to propagate changes to the user as soon as they are made to the db. The best technique currently available for this seems to be Ajax long polling, but it isn't supported by every browser, so you need fallbacks. Fortunately there are already solutions that handle this for you. Among them are: orbited.org and the already mentioned socket.io.

In the future there will be an easier way to do this which is called WebSockets, but it isn't sure yet when that standard will be ready for prime time as there are security concerns about the current state of the standard.

There shouldn't be concurrency problems in the database with new objects. But when a user edits an object the server needs to have some logic that checks whether the object has been edited or deleted in the meantime. If the object has been deleted the solution is, again, simple: Just discard the edit.

But the most difficult problem appears, when multiple users are editing the same object at the same time. If User 1 and 2 start editing an object at the same time, they will both make their edits on the same data. Let's say the changes User 1 made are sent to the server first while User 2 is still editing the data. You then have two options: You could try to merge User 1's changes into the data of User 2 or you could tell User 2 that his data is out of date and display him an error message as soon as his data gets send to the server. The latter isn't very user friendly option here, but the former is very hard to implement.

One of the few implementations that really got this right for the first time was EtherPad, which was acquired by Google. I believe they then used some of EtherPad's technologies in Google Docs and Google Wave, but I can't tell that for sure. Google also opensourced EtherPad, so maybe that's worth a look, depending on what you're trying to do.

It's really not easy to do this simultaneously editing stuff, because it's not possible to do atomic operations on the web because of the latency. Maybe this article will help you to learn more about the topic.


Trying to write all this yourself is a big job, and it's very difficult to get it right. One option is to use a framework that's built to keep clients in sync with the database, and with each other, in realtime.

I've found that the Meteor framework does this well (http://docs.meteor.com/#reactivity).

"Meteor embraces the concept of reactive programming. This means that you can write your code in a simple imperative style, and the result will be automatically recalculated whenever data changes that your code depends on."

"This simple pattern (reactive computation + reactive data source) has wide applicability. The programmer is saved from writing unsubscribe/resubscribe calls and making sure they are called at the right time, eliminating whole classes of data propagation code which would otherwise clog up your application with error-prone logic."


I can't believe that nobody has mentioned Meteor. It's a new and immature framework for sure (and only officially supports one DB), but it takes all the grunt work and thinking out of a multi-user app like the poster is describing. In fact, you can't NOT build a mult-user live-updating app. Here's a quick summary:

  • Everything is in node.js (JavaScript or CoffeeScript), so you can share stuff like validations between the client and server.
  • It uses websockets, but can fall back for older browsers
  • It focuses on immediate updates to local object (i.e. the UI feels snappy), with changes sent to the server in the background. Only atomic updates are allowed to make mixing updates simpler. Updates rejected on the server are rolled back.
  • As a bonus, it handles live code reloads for you, and will preserves user state even when the app changes radically.

Meteor is simple enough that I would suggest you at least take a look at it for ideas to steal.


These Wikipedia pages may help add perspective to learning about concurrency and concurrent computing for designing an ajax web application that either pulls or is pushed state event (EDA) messages in a messaging pattern. Basically, messages are replicated out to channel subscribers which respond to change events and synchronization requests.

There are many forms of concurrent web-based collaborative software.

There are a number of HTTP API client libraries for etherpad-lite, a collaborative real-time editor.

django-realtime-playground implements a realtime chat app in Django with various real-time technologies like Socket.io.

Both AppEngine and AppScale implement the AppEngine Channel API; which is distinct from the Google Realtime API, which is demonstrated by googledrive/realtime-playground.


Server-side push techniques are the way to go here. Comet is (or was?) a buzz word.

The particular direction you take depends heavily on your server stack, and how flexible you/it is. If you can, I would take a look at socket.io, which provides a cross-browser implementation of websockets, which provide a very streamline way to have bidirectional communication with the server, allowing the server to push updates to the clients.

In particular, see this demonstration by the library's author, which demonstrates almost exactly the situation you describe.

참고URL : https://stackoverflow.com/questions/4815226/how-to-design-a-multi-user-ajax-web-application-to-be-concurrently-safe

반응형