source

다중 사용자 Ajax 웹 응용 프로그램을 동시에 안전하게 설계하는 방법

myloves 2023. 4. 3. 21:54

다중 사용자 Ajax 웹 응용 프로그램을 동시에 안전하게 설계하는 방법

서버에서 대량의 데이터를 볼 수 있는 웹 페이지가 있습니다.통신은 에이잭스를 통해 이루어집니다.

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

사용자 B가 동시에 페이지에 액세스하여 새로운 데이터 개체를 생성하면 다시 Ajax를 통해 서버에 알립니다.서버는 사용자를 위해 새로운 개체를 가지고 돌아갑니다.

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

여러 사용자가 동시에 페이지를 사용할 때 서버와 페이지를 동기화하기 위한 옵션은 무엇입니까?

변경할 때마다 페이지 전체를 잠그거나 사용자에게 상태 전체를 덤프하는 등의 옵션은 피하는 것이 좋습니다.

도움이 될 경우 이 특정 예에서는 웹 페이지가 데이터베이스에 저장 프로시저를 실행하는 정적 웹 메서드를 호출합니다.저장 프로시저는 변경된 데이터만 반환하고 더 이상 반환하지 않습니다.그런 다음 static webmethod는 저장 프로시저의 반환을 클라이언트에 전송합니다.

현상금 편집:

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

즉, 데이터나 상태 손상의 위험이 없는 데이터베이스상의 기능과 데이터에 동시에 액세스 할 수 있습니다.

개요:

  • 도입부
  • 서버 아키텍처
  • 클라이언트 아키텍처
  • 업데이트 케이스
  • 커밋 케이스
  • 컨플릭트 케이스
  • 퍼포먼스와 확장성

안녕 레이노스

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

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

제가 당신 입장이라면 다음과 같이 발전시킬 것입니다.

1. 서버측:

  • 내가 '원자 유물'이라고 부르는 것을 정의할 수 있는 적절한 수준을 결정합니다(페이지).페이지상의 오브젝트?오브젝트 내부의 값?)이는 웹 서버, 데이터베이스 및 캐싱 하드웨어, 사용자 수, 개체 수 등에 따라 달라집니다.결정을 내리기가 쉽지 않다.

  • 각 원자 아티팩트에 대해 다음이 있다.

    • 응용 프로그램 전체의 일의 ID
    • 증분 버전 ID
    • 쓰기 액세스용 잠금 메커니즘(180x maybe)
    • 링 버퍼 내의 작은 이력 또는 "changelog"(공유 메모리)를 나타냅니다.단일 키-값 쌍으로도 충분할 수 있지만 확장성은 낮습니다.http://en.wikipedia.org/wiki/Circular_buffer 를 참조해 주세요.
  • 연결된 사용자에게 관련 변경 로그를 효율적으로 전달할 수 있는 서버 또는 의사 서버 구성 요소.Observer-Pattern은 이 문제의 친구입니다.

2. 클라이언트 측:

  • 위의 서버에 대한 장시간 HTTP-Connection을 사용할 수 있거나 경량 폴링을 사용하는 Javascript 클라이언트입니다.

  • 접속되어 있는 Javascript 클라이언트가 감시된 아티팩트 이력 변경을 통지했을 때 사이트의 콘텐츠를 갱신하는 Javascript 아티팩트 갱신 컴포넌트입니다.(또한 옵저버 패턴이 좋은 선택일 수 있습니다)

  • 원자 아티팩트 변경을 요청할 수 있는 Javascript 아티팩트 커밋 구성 요소로, 뮤텍스 잠금을 획득하려고 합니다.알려진 클라이언트 사이드 아티팩트 버전 ID와 현재 서버 사이드 아티팩트 버전 ID를 비교하여 불과 몇 초 전에 다른 사용자에 의해 아티팩트 상태가 변경되었는지 탐지합니다(javascript 클라이언트의 지연 시간 및 프로세스 팩터의 커밋).

  • 인간이 올바른 결정을 내릴 수 있는 Javascript 충돌 해결사입니다."누군가 당신보다 빨랐어요.거스름돈을 삭제했습니다.가서 울어라.기술적인 차이 또는 사용자 친화적인 솔루션에서 많은 옵션을 선택할 수 있을 것 같습니다.

그럼 어떻게 굴러갈까요?

케이스 1: 업데이트용 시퀀스 다이어그램 종류:

  • 브라우저 렌더링 페이지
  • 각각 하나 이상의 값 필드, 고유 및 버전 ID를 갖는 javascript "parents" 아티팩트
  • javascript 클라이언트가 시작되어 발견된 아티팩트 이력을 발견된 버전부터 "보기"를 요청합니다(변경은 흥미롭지 않습니다).
  • 서버 프로세스가 요청을 기록하고 이력을 지속적으로 확인 및/또는 전송합니다.
  • 이력 엔트리에는 단순한 알림 "artifact x has changed, client pls request data"가 포함되어 있어 클라이언트가 독립적으로 또는 전체 데이터셋을 폴링할 수 있습니다. "artifact x has changed to value foo"
  • javascript attribute-module은 갱신된 것으로 알려진 즉시 새로운 값을 가져오기 위해 할 수 있는 모든 것을 수행합니다.새로운 ajax 요청을 실행하거나 javascript 클라이언트에 의해 공급됩니다.
  • DOM 컨텐츠 페이지가 갱신되어 유저에게 통지됩니다(옵션).역사관람은 계속된다.

케이스 2: 이제 커밋을 실시합니다.

  • attact-committer는 사용자 입력에서 원하는 새 값을 알고 서버에 변경 요청을 보냅니다.
  • serverside mutex를 취득했다.
  • 서버가 "123 버전에서 아티팩트 x의 상태를 알고 있습니다. 값을 foopls로 설정합니다."를 수신합니다.
  • 아티팩트 x의 Serverside 버전이 123보다 작을 수 없는 경우 새 값이 허용되면 124의 새 버전 ID가 생성됩니다.
  • 새 상태 정보 "버전 124로 업데이트" 및 선택적으로 새 값 foo가 아티팩트 x의 링 버퍼 시작 부분에 배치됩니다(changelog/history).
  • serverside mutex가 해방되다
  • 요청된 아티팩트 커밋은 새 ID와 함께 커밋 확인을 받게 되어 기쁩니다.
  • 한편 서버사이드 서버 컴포넌트는 링 버퍼를 연결된 클라이언트에 폴링 또는 폴링합니다.아티팩트 x의 버퍼를 감시하는 모든 클라이언트는 통상적인 지연 시간 내에 새로운 상태 정보와 값을 가져옵니다(케이스 1 참조).

케이스 3: 경합의 경우:

  • 아티팩트 커밋은 사용자 입력에서 원하는 새 값을 알고 변경 요청을 서버로 보냅니다.
  • 한편, 다른 유저가 같은 아티팩트를 업데이트했습니다(케이스 2. 참조).다양한 지연 시간으로 인해 다른 유저에게는 아직 알 수 없습니다.
  • 따라서 서버사이드 뮤텍스가 취득됩니다(또는 "빠른" 사용자가 변경을 커밋할 때까지 대기합니다).
  • 서버가 "123 버전에서 아티팩트 x의 상태를 알고 있습니다. 값을 foo로 설정합니다."를 수신합니다.
  • Serverside에서 아티팩트 x의 버전은 이미 124입니다.요청 클라이언트는 덮어쓸 값을 알 수 없습니다.
  • 서버는 변경 요구(god-interven overwrite priority에 포함되지 않음)를 거부하고 뮤텍스를 해방하여 새로운 버전 ID와 새로운 값을 클라이언트에 직접 반환할 수 있습니다.
  • javascript 아티팩트커밋터는 거부된 커밋 요구와 변경 요구 사용자가 아직 알지 못한 값에 직면했을 때 사용자에게 문제를 표시하고 설명하는 충돌 해결사를 참조합니다.
  • 사용자는 스마트 충돌 해결기 JS로부터 몇 가지 옵션을 제공받으면 값을 변경할 수 있습니다.
  • 사용자가 옳다고 생각하는 값을 선택하면 프로세스가 케이스 2(또는 다른 사용자가 더 빠르면 케이스 3)부터 다시 시작됩니다.

퍼포먼스와 scalability에 관한 용어

HTTP 폴링과HTTP '푸시'

  • 폴링은 허용 지연으로 간주되는 모든 요구를 초당 1개, 초당 5개씩 생성합니다.(Apache?) 및 (php?)를 '경량'으로 설정할 수 있을 정도로 충분히 설정하지 않으면 인프라스트럭처에 상당히 가혹한 영향을 미칠 수 있습니다.폴링 간격의 길이보다 훨씬 짧은 시간 동안 실행되도록 서버 측에서 폴링 요구를 최적화하는 것이 좋습니다.이 런타임을 반으로 분할하면 전체 시스템 부하를 최대 50% 절감할 수 있습니다.
  • HTTP를 통해 푸시하려면(웹 워커가 너무 멀어서 지원할 수 없다고 가정함) 사용자별로 항상 1개의 Apache/Lighthttpd 프로세스를 사용할 수 있어야 합니다.이러한 각 프로세스용으로 예약된 상주 메모리와 시스템 전체 메모리는 매우 확실한 확장 제한 중 하나입니다.접속의 메모리 용량을 삭감할 필요가 있을 뿐만 아니라, 각 접속에서 실행되는 CPU 및 I/O 작업의 양을 제한할 필요가 있습니다(많은 sleep/idle 시간을 필요로 합니다).

백엔드 스케일링

  • 데이터베이스와 파일 시스템은 잊어버리고 빈번한 폴링에는 일종의 공유 메모리 기반의 백엔드가 필요합니다(클라이언트가 직접 폴링하지 않으면 실행 중인 각 서버 프로세스가 폴링됩니다).
  • memcache를 선택하면 확장성이 향상되지만 여전히 비용이 많이 듭니다.
  • 커밋용 뮤텍스는 로드밸런싱을 위해 여러 프런트엔드 서버를 사용하는 경우에도 글로벌하게 동작해야 합니다.

프런트 엔드 스케일링

  • 폴링 중이든 수신 중이든 상관없이 모든 감시된 아티팩트에 대한 정보를 한 번에 수집해 보십시오.

"수정"

  • 클라이언트가 폴링 중이고 많은 사용자가 동일한 아티팩트를 감시하는 경향이 있는 경우 아티팩트의 이력을 정적 파일로 게시하여 Apache가 아티팩트를 캐시할 수 있도록 하고 아티팩트가 변경되면 서버 측에서 새로 고칠 수 있습니다.이렇게 하면 PHP/memcache를 게임 밖으로 내보낼 수 있습니다.Lightttpd는 정적 파일 처리에 매우 효율적입니다.
  • cotendo.com과 같은 콘텐츠 전송 네트워크를 사용하여 아티팩트 기록을 푸시합니다.푸시 레이텐시는 더 커지지만 확장성이 꿈입니다.
  • 사용자가 Java 또는 flash(?)를 사용하여 접속하는 실제 서버(HTTP를 사용하지 않음)를 작성합니다.하나의 서버 스레드로 여러 사용자에게 서비스를 제공해야 합니다.오픈 소켓을 순환하여 필요한 작업을 수행(또는 위임)합니다.포킹 프로세스 또는 더 많은 서버 부팅을 통해 확장할 수 있습니다.단, 뮤텍스는 글로벌하게 고유해야 합니다.
  • 로드 시나리오에 따라 프런트 엔드 및 백엔드 서버를 아티팩트 ID 범위별로 그룹화합니다.이것에 의해, 영속적인 메모리를 보다 효율적으로 사용할 수 있게 되어(모든 데이터를 가지는 데이타베이스는 없습니다), 뮤텍스를 확장할 수 있게 됩니다.javascript는 동시에 여러 서버에 대한 연결을 유지해야 합니다.

이것이 당신의 아이디어의 시작이 되기를 바랍니다.나는 더 많은 가능성이 있다고 확신한다.이 투고에 대한 어떠한 비판이나 개선도 환영합니다.Wiki는 유효합니다.

크리스토프 스트라센

오래된 질문인 건 알지만, 그냥 끼어들려고 했어요.

OT(Operational Transforms)는 동시적이고 일관된 다중 사용자 편집 요건에 적합한 것으로 보입니다.Google 문서(Google Wave)에서 사용되는 기술입니다.

Operational Transforms - ShareJs를 사용하기 위한 JS 기반 라이브러리(http://sharejs.org/),는 Google Wave 팀의 구성원이 작성했습니다.

원한다면 완전한 MVC 웹 사이트인 DerbyJS(ShareJS를 기반으로 구축된 http://derbyjs.com/)를 이용할 수 있습니다.

서버와 클라이언트 간의 통신에 브라우저 채널을 사용합니다(WebSockets 지원은 진행 중이어야 한다고 생각합니다.이전에는 소켓을 통해 지원되었습니다).I/O는 Socket.io에서 개발자의 문제로 삭제되었습니다.단, 현재 초보자용 문서는 다소 희박합니다.

데이터 세트마다 시간 기반 수정 스탬프를 추가하는 것을 검토하고 있습니다.따라서 DB 테이블을 업데이트할 경우 수정된 타임스탬프를 적절히 변경합니다.AJAX를 사용하여 클라이언트의 수정된 타임스탬프를 데이터 원본의 타임스탬프와 비교할 수 있습니다. 사용자가 늦으면 디스플레이를 업데이트하십시오.이 사이트에서 사용자가 답변을 입력하는 동안 다른 사용자가 답변을 했는지 확인하기 위해 정기적으로 질문을 확인하는 방법과 유사합니다.

변경 내용이 DB에 추가되는 즉시 사용자에게 전달하려면 푸시 기술(Comet 또는 Reverse Ajax라고도 함)을 사용해야 합니다.현재 이용 가능한 가장 좋은 방법은 Ajax의 긴 폴링으로 보이지만 모든 브라우저에서 지원되는 것은 아니기 때문에 폴백이 필요합니다.다행히 이 문제를 해결할 수 있는 솔루션이 이미 있습니다.그 중에는 orbited.org과 이미 언급된 socket.io이 있습니다.

미래에는 웹소켓이라고 불리는 더 쉬운 방법이 있을 것이지만, 표준의 현재 상태에 대한 보안상의 우려가 있기 때문에 표준이 언제 황금 시간대에 준비될지는 아직 확실치 않다.

새 개체를 사용하는 데이터베이스에 동시성 문제가 있으면 안 됩니다.단, 사용자가 객체를 편집할 때는 그 사이에 객체가 편집 또는 삭제되었는지 확인하는 논리가 서버에 필요합니다.오브젝트가 삭제된 경우 해결 방법은 간단합니다.편집 내용을 파기하기만 하면 됩니다.

그러나 여러 사용자가 동시에 동일한 개체를 편집하는 경우 가장 어려운 문제가 발생합니다.사용자 1과 사용자 2가 동시에 오브젝트 편집을 시작하면 둘 다 동일한 데이터를 편집합니다.사용자 1이 변경한 내용이 사용자 2가 데이터를 편집하는 동안 먼저 서버로 전송된다고 가정합니다.다음 두 가지 옵션이 있습니다.사용자 1의 변경 내용을 사용자 2의 데이터로 병합하거나 데이터가 서버로 전송되는 즉시 사용자 2에게 데이터가 만료되었음을 알리고 오류 메시지를 표시할 수 있습니다.후자는 사용자 친화적인 옵션이 아니지만 전자는 구현하기가 매우 어렵습니다.

처음으로 이 기능을 제대로 구현한 몇 안 되는 구현 중 하나는 EtherPad로, 구글이 인수했습니다.구글 독스와 구글 웨이브에서 EtherPad의 기술을 사용했다고 생각합니다만, 확실히는 말할 수 없습니다.구글은 또한 EtherPad를 오픈하고 있기 때문에, 당신이 무엇을 하려고 하는가에 따라서는, 그것을 볼 가치가 있을지도 모른다.

웹에서는 대기 시간 때문에 원자적인 작업을 수행할 수 없기 때문에 편집 작업을 동시에 수행하는 것은 정말 쉽지 않습니다.기사가 주제에 대해 더 많이 알아가는 데 도움이 될 도 있습니다.

이 모든 것을 직접 쓰려고 하는 것은 큰 일이며, 그것을 제대로 쓰기는 매우 어렵다.하나의 옵션은 클라이언트가 데이터베이스 및 서로 실시간으로 동기화되도록 구축된 프레임워크를 사용하는 것입니다.

Meteor 프레임워크가 이 기능을 잘 수행한다는 것을 알게 되었습니다(http://docs.meteor.com/#http://http://docs.meteor.com/).

Meteor는 리액티브 프로그래밍의 개념을 수용합니다.즉, 심플한 명령형으로 코드를 작성할 수 있습니다.코드에 의존하는 데이터가 변경될 때마다 결과가 자동으로 재계산됩니다.

"이 단순한 패턴(반응형 계산 + 반응형 데이터 소스)은 적용 범위가 넓습니다.프로그래머는 등록 취소/등록 취소 콜을 작성하고 적절한 타이밍에 호출하는 것을 피할 수 있으며, 그렇지 않으면 오류가 발생하기 쉬운 논리로 애플리케이션을 방해할 수 있는 데이터 전파 코드 클래스를 모두 제거할 수 있습니다."

아무도 운석에 대해 언급하지 않았다니 믿을 수 없다.확실히 새롭고 미숙한 프레임워크(그리고 공식적으로 하나의 DB만 지원)이지만, 포스터가 설명하는 것처럼 여러 사용자 앱의 모든 고된 작업과 생각을 필요로 합니다.사실 다중 사용자 라이브 업데이트 앱을 구축하지 않을 수 없습니다.다음은 간단한 요약입니다.

  • 모든 것이 node.js(JavaScript 또는 CoffeeScript)에 있으므로 클라이언트와 서버 간에 검증 등의 정보를 공유할 수 있습니다.
  • 웹소켓을 사용하지만 오래된 브라우저에서는 폴백할 수 있습니다.
  • 로컬 오브젝트에 대한 즉각적인 업데이트(즉 UI가 빠르게 느껴짐)에 초점을 맞추고 변경 사항을 백그라운드에서 서버로 전송합니다.혼합 업데이트를 간소화하기 위해 사용할 수 있는 것은 원자적인 업데이트뿐입니다.서버에서 거부된 업데이트가 롤백됩니다.
  • 또한 라이브 코드 새로고침에 대응하여 앱이 급격하게 변경되어도 사용자 상태를 유지할 수 있습니다.

운석은 간단해서 최소한 훔쳐볼 만한 아이디어라도 얻으라고 제안하고 싶군요.

이러한 Wikipedia 페이지는 동시성동시 컴퓨팅에 대한 학습에 도움이 될 수 있습니다.이는 메시지 패턴에서 Push State Event(EDA; 푸시 상태 이벤트) 메시지가져오거나 푸시되Ajax응용 프로그램을 설계하기 위한 것입니다.기본적으로 메시지는 변경 이벤트 및 동기화 요구에 응답하는 채널 사용자에게 복제됩니다.

동시 웹 기반 협업 소프트웨어에는 여러 가지 형태가 있습니다.

협업 실시간에디터etherpad-lite에는 다수의 HTTP API 클라이언트 라이브러리가 있습니다.

django-http-playgroundSocket.io과 같은 다양한 실시간 기술을 사용하여 장고에 실시간 채팅 앱을 구현합니다.

AppEngine과 AppScale은 둘 AppEngine Channel API를 구현합니다. 이는 구글 실시간 API와는 다릅니다.Google Realtime API는 구글 드라이브/실시간 플레이그라운드로 구현됩니다.

서버측 푸시 기술이 이 방법을 위한 방법입니다.혜성은 유행어이다.

특정 방향은 서버 스택과 그 유연성에 따라 크게 달라집니다.가능하다면 웹소켓의 크로스오버 실장을 제공하는 socket.io을 살펴보겠습니다.이것에 의해, 서버와 쌍방향 통신을 실시할 수 있어 서버가 클라이언트에 업데이트를 푸시 할 수 있게 됩니다.

특히, 라이브러리 작성자의 이 데모를 참조하십시오.이 데모는 당신이 설명하는 상황을 거의 정확하게 보여줍니다.

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