실시간으로 무거운 웹 소켓 기반 웹 응용 프로그램을 아키텍처하는 방법은 무엇입니까?


17

실시간 단일 페이지 응용 프로그램을 개발하는 과정에서 사용자에게 최신 데이터를 제공하기 위해 점진적으로 웹 소켓을 채택했습니다. 이 단계에서 앱 구조를 너무 많이 파괴하고 있다는 사실에 슬 sad 고이 현상에 대한 해결책을 찾지 못했습니다.

세부 사항에 들어가기 전에 약간의 컨텍스트 :

  • webapp는 실시간 SPA입니다.
  • 백엔드는 Ruby on Rails에 있습니다. 실시간 이벤트는 Ruby가 Redis 키로 푸시 한 다음, 마이크로 노드 서버가이를 철회하여 Socket.Io로 푸시합니다.
  • 프론트 엔드는 AngularJS에 있으며 Node의 socket.io 서버에 직접 연결됩니다.

서버 측에서는 실시간으로 처리하기 전에 컨트롤러와 모델 기반의 리소스 분리가 명확했습니다. 이 고전적인 MVC 디자인 은 웹 소켓을 통해 사용자에게 물건을 푸시하기 시작했을 때 완전히 파쇄되거나 적어도 우회되었습니다. 이제 모든 앱이 다소 구조화 된 데이터로 흘러가단일 파이프가 있습니다 . 그리고 나는 스트레스를 받는다.

프론트 엔드에서 주요 관심사는 비즈니스 로직의 복제입니다. 사용자가 페이지를로드 할 때 클래식 AJAX 호출을 통해 모델을로드해야합니다. 그러나 실시간 데이터 플러딩도 처리해야하며 클라이언트 측 모델의 일관성을 유지하기 위해 많은 클라이언트 측 비즈니스 로직을 복제하고 있습니다.

몇 가지 연구를 한 후 몇 가지 구체적인 주제를 염두에두고 현대 웹 응용 프로그램의 아키텍처를 디자인하고 디자인하는 방법에 대한 조언을 제공하는 좋은 게시물, 기사, 서적 또는 기타 내용을 찾을 수 없습니다.

  • 서버에서 사용자에게 전송되는 데이터를 구성하는 방법은 무엇입니까?
    • "이 리소스가 업데이트되었고 AJAX 호출을 통해 다시로드해야합니다"와 같은 이벤트 만 보내거나 업데이트 된 데이터를 푸시하고 초기 AJAX 호출을 통해로드 된 이전 데이터를 바꾸어야합니까?
    • 전송 된 데이터에 대해 일관되고 확장 가능한 스켈레톤을 정의하는 방법은 무엇입니까? 이것은 모델 업데이트 메시지이거나 "blahblahblah에 오류가있었습니다"메시지입니다
  • 백엔드 어디서나 모든 것에 대한 데이터를 보내지 않는 방법은 무엇입니까?
  • 서버와 클라이언트 쪽에서 비즈니스 논리 중복을 줄이는 방법은 무엇입니까?

Rails가 SPA에 가장 적합한 옵션입니까? Rails는 훌륭하지만 모놀리스 애플리케이션을 목표로합니다. 분리 문제가있는 모듈 식 백엔드를 원할 수도 있습니다 ... 필요에 따라 실시간 SPA를위한 대체 Ruby 프레임 워크를 고려할 것입니다.
Myst

1
나는 Rails가 최선의 선택이라고 확신하지 못하지만 적어도 백엔드에서 스택에 만족합니다 (아마도이 ​​특정 프레임 워크에 만족하기 때문입니다). 여기서 제가 염려 할 것은 기술적 불가지론 적 관점에서 SPA를 설계하는 방법에 대한 것입니다. 또한 단일 프로젝트에서 여러 언어, 프레임 워크 및 라이브러리를 곱하고 싶지 않습니다.
Philippe Durix

연결된 벤치 마크에 문제가있을 수 있지만 현재 ActiveRecord 구현에 약점이 있습니다. plezi.io 에 대해 편견이있을 수 있지만 벤치 마크의 이후 결과 에서 지적했듯이 클러스터링 및 Redis (테스트되지 않은) 이전에도 성능이 크게 향상되었습니다. node.js보다 성능이 더 좋다고 생각합니다. 상황이 바뀔 때까지 plezi.io를 사용합니다.
Myst

답변:


10

서버에서 사용자에게 전송되는 데이터를 구성하는 방법은 무엇입니까?

메시징 패턴을 사용하십시오 . 글쎄, 이미 메시징 프로토콜을 사용하고 있지만 변경 사항을 메시지, 특히 이벤트로 구성하는 것을 의미합니다. 서버 측이 변경되면 비즈니스 이벤트가 발생합니다. 시나리오에서 고객보기는 이러한 이벤트에 관심이 있습니다. 이벤트에는 해당 변경과 관련된 모든 데이터가 포함되어야합니다 (모두보기 데이터 일 필요는 없음). 그런 다음 클라이언트 페이지는 이벤트 데이터로 유지 관리중인 부분을 업데이트해야합니다.

예를 들어, 주식 시세 표시기를 업데이트하고 AAPL이 변경된 경우 모든 주가를 낮추거나 AAPL에 대한 모든 데이터 (이름, 설명 등)를 푸시하지 않을 것입니다. AAPL, 델타 및 새로운 가격 만 푸시합니다. 클라이언트에서는 뷰에서 해당 주가 만 업데이트합니다.

"이 리소스가 업데이트되었고 AJAX 호출을 통해 다시로드해야합니다"와 같은 이벤트 만 보내거나 업데이트 된 데이터를 푸시하고 초기 AJAX 호출을 통해로드 된 이전 데이터를 바꾸어야합니까?

나는 둘 다 말하지 않을 것이다. 이벤트를 전송하는 경우 계속 진행하여 전체 데이터가 아닌 관련 데이터를 전송하십시오. 이벤트 종류의 이름을 지정하십시오. (이벤트의 이름과 데이터가 시스템의 기계적 작동 범위를 벗어납니다. 이는 비즈니스 로직이 모델링되는 방법과 더 관련이 있습니다.) 뷰 업데이트 프로그램은 각 특정 이벤트를 다음으로 변환하는 방법을 알아야합니다. 정확한 뷰 변경 (즉, 변경된 내용 만 업데이트)

전송 된 데이터에 대해 일관되고 확장 가능한 스켈레톤을 정의하는 방법은 무엇입니까? 이것은 모델 업데이트 메시지이거나 "blahblahblah에 오류가있었습니다"메시지입니다

나는 이것이 큰 개방형 질문이라고 말하고 다른 질문으로 나뉘어 별도로 게시해야합니다.

그러나 일반적으로 백엔드 시스템은 중요한 사건에 대한 이벤트를 작성하여 비즈니스에 전달해야합니다. 외부 피드 또는 백엔드 자체의 활동에서 발생할 수 있습니다.

백엔드 어디서나 모든 것에 대한 데이터를 보내지 않는 방법은 무엇입니까?

발행 / 구독 패턴을 사용하십시오 . SPA가 실시간 업데이트 수신에 관심이있는 새 페이지를로드 할 때 해당 페이지는 사용할 수있는 이벤트 만 구독하고 해당 이벤트가 들어올 때 뷰 업데이트 논리를 호출해야합니다. pub / sub 논리가 필요할 것입니다. 서버는 네트워크 부하를 줄입니다. Websocket pub / sub에 라이브러리가 있지만 Rails 생태계에 무엇이 있는지 잘 모르겠습니다.

서버와 클라이언트 쪽에서 비즈니스 논리 중복을 줄이는 방법은 무엇입니까?

클라이언트와 서버 모두에서보기 데이터를 업데이트 해야하는 것처럼 들립니다. 내 생각에는 실시간 클라이언트를 시작하기 위해 스냅 샷을 만들 수 있도록 서버 측 뷰 데이터가 필요하다는 것입니다. 관련된 두 가지 언어 / 플랫폼 (Ruby 및 Javascript)이 있으므로 뷰 업데이트 논리를 둘 다 작성해야합니다. 트랜스 필링 (자체적 인 문제가 있음)을 제외하고는 그 방법을 찾지 못했습니다.

기술 요점 : 데이터 조작 (보기 업데이트)은 비즈니스 로직이 아닙니다. 유스 케이스 유효성 검사를 의미하는 경우 클라이언트의 유효성 검사는 우수한 사용자 경험을 위해 필요하지만 궁극적으로는 서버에서 신뢰할 수 없기 때문에 피할 수없는 것처럼 보입니다.


다음은 그러한 구조가 잘 보이는 방법입니다.

고객 조회수 :

  • 뷰 스냅 샷 및 뷰의 마지막으로 본 이벤트 번호를 요청합니다.
    • 클라이언트가 처음부터 빌드 할 필요가 없도록 뷰를 미리 채 웁니다.
    • 단순성을 위해 HTTP GET을 사용할 수 있습니다.
  • 웹 소켓 연결을 만들고보기의 마지막 이벤트 번호부터 특정 이벤트를 구독합니다.
  • 웹 소켓을 통해 이벤트를 수신하고 이벤트 유형 / 데이터에 따라보기를 업데이트합니다.

클라이언트 명령 :

  • 데이터 변경 요청 (HTTP PUT / POST / DELETE)
    • 응답은 성공 또는 실패 + 오류입니다
    • (변경으로 생성 된 이벤트는 웹 소켓을 통해보고 업데이트를 트리거합니다.)

서버 쪽은 실제로 제한된 책임으로 여러 구성 요소로 나눌 수 있습니다. 들어오는 요청을 처리하고 이벤트를 생성하는 것입니다. 다른 고객은 클라이언트 가입을 관리하고, 이벤트 (예 : 처리 중)를 듣고 적절한 이벤트를 가입자에게 전달할 수 있습니다. 이벤트를 수신하고 서버 측보기를 업데이트하는 세 번째가있을 수 있습니다. 가입자가 이벤트를 받기 전에 발생할 수도 있습니다.

내가 설명한 것은 CQRS + Messaging 의 형태이며 현재 직면하고있는 문제를 해결하기위한 일반적인 전략입니다.

나는 안 가져 왔어요 이벤트 소싱을 당신이 반드시 필요한 경우가 먹고 싶어 그것의 어떤 경우 잘 모르겠어요으로이 설명에 나. 그러나 관련 패턴입니다.


나는 그 주제에 관해 많은 진전을 보였고 당신이 준 포인터는 매우 유용했습니다. 나는 모든 조언을 사용하지 않더라도 많은 조언을 사용했기 때문에 대답을 받아 들였습니다. 다른 대답에서 따라 간 길을 설명하겠습니다.
Philippe Durix

4

백엔드에서 몇 달 동안 일한 후 주로 플랫폼이 직면 한 문제를 해결하기 위해 몇 가지 조언을 사용할 수있었습니다.

백엔드를 다시 생각할 때의 주요 목표는 가능한 한 CRUD에 충실하는 것이 었습니다. 여러 경로에 흩어져있는 모든 작업, 메시지 및 요청이 생성, 업데이트, 읽기 또는 삭제 된 리소스로 다시 그룹화되었습니다 . 지금은 분명하게 들리지만 신중하게 적용하는 것은 매우 어려운 생각이었습니다.

모든 것이 리소스로 구성된 후에는 실시간 메시지를 모델에 첨부 할 수있었습니다.

  • 작성은 새로운 자원이있는 메시지를 트리거합니다.
  • 업데이트는 업데이트 된 속성과 UUID 만있는 메시지를 트리거합니다.
  • 삭제는 삭제 메시지를 트리거합니다.

Rest API에서 모든 작성, 업데이트, 삭제 메소드는 헤드 전용 응답, 성공 또는 실패를 알리는 HTTP 코드 및 웹 소켓으로 푸시되는 실제 데이터를 생성합니다.

프런트 엔드에서 각 리소스는 초기화시 트로프 HTTP를로드 한 다음 업데이트를 구독하고 시간이 지남에 따라 상태를 유지 관리하는 특정 구성 요소에 의해 처리됩니다. 그런 다음 뷰는 이러한 구성 요소에 바인딩하여 동일한 구성 요소를 통해 리소스를 표시하고 해당 리소스에 대한 작업을 수행합니다.


CQRS + 메시징 및 이벤트 소싱 읽기가 매우 흥미 롭다는 것을 알았지 만 문제가 약간 복잡 해져서 중앙 데이터베이스에 데이터를 커밋하는 것이 비싼 고급 응용 프로그램에 더 적합하다고 생각했습니다. 그러나 나는이 접근법을 분명히 기억할 것입니다.

이 경우 응용 프로그램에는 동시 클라이언트가 거의 없으며 데이터베이스에 많은 의존을하는 당사자가되었습니다. 가장 변화가 많은 모델은 Redis에 저장되어 초당 수백 건의 업데이트를 처리합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.