API Gateway (REST) ​​+ Dịch vụ kính hiển vi hướng sự kiện


15

Tôi có một loạt các dịch vụ siêu nhỏ có chức năng mà tôi thể hiện thông qua API REST theo mẫu API Gateway. Vì các dịch vụ siêu nhỏ này là các ứng dụng Spring Boot, tôi đang sử dụng Spring AMQP để đạt được giao tiếp đồng bộ theo kiểu RPC giữa các dịch vụ siêu nhỏ này. Mọi thứ đã diễn ra suôn sẻ cho đến nay. Tuy nhiên, càng đọc về các kiến ​​trúc microservice hướng sự kiện và xem xét các dự án như Spring Cloud Stream, tôi càng tin rằng tôi có thể làm mọi thứ sai cách với RPC, cách tiếp cận đồng bộ (đặc biệt vì tôi sẽ cần điều này để mở rộng quy mô để đáp ứng hàng trăm hoặc hàng ngàn yêu cầu mỗi giây từ các ứng dụng khách).

Tôi hiểu điểm đằng sau một kiến ​​trúc hướng sự kiện. Điều tôi không hiểu lắm là làm thế nào để thực sự sử dụng một mẫu như vậy khi ngồi sau một mô hình (REST) ​​mong đợi phản hồi cho mọi yêu cầu. Ví dụ: nếu tôi có cổng API của mình dưới dạng microservice và một dịch vụ siêu nhỏ khác lưu trữ và quản lý người dùng, làm thế nào tôi có thể mô hình hóa một thứ như theo GET /users/1kiểu hoàn toàn theo sự kiện?

Câu trả lời:


9

Nhắc lại theo tôi:

Các sự kiện REST và không đồng bộ không phải là sự kiện thay thế. Chúng hoàn toàn trực giao.

Bạn có thể có một, hoặc cái kia, hoặc cả hai, hoặc không. Chúng là những công cụ hoàn toàn khác nhau cho các lĩnh vực vấn đề hoàn toàn khác nhau. Trong thực tế, giao tiếp đáp ứng yêu cầu mục đích chung hoàn toàn có khả năng không đồng bộ, hướng sự kiện và chịu lỗi .


Như một ví dụ tầm thường, giao thức AMQP gửi tin nhắn qua kết nối TCP. Trong TCP, mọi gói phải được người nhận thừa nhận . Nếu người gửi gói không nhận được ACK cho gói đó, nó sẽ tiếp tục gửi lại gói đó cho đến khi ACK'd hoặc cho đến khi lớp ứng dụng "từ bỏ" và từ bỏ kết nối. Đây rõ ràng là một mô hình phản hồi yêu cầu không chịu lỗi vì mỗi "yêu cầu gửi gói" phải có "phản hồi xác nhận gói" đi kèm và không thể trả lời kết quả trong toàn bộ kết nối bị lỗi. Tuy nhiên, AMQP, một giao thức được tiêu chuẩn hóa và được áp dụng rộng rãi cho nhắn tin chịu lỗi không đồng bộ, được truyền thông qua TCP! Đưa cái gì?

Khái niệm cốt lõi đang chơi ở đây là nhắn tin chịu lỗi kết nối lỏng lẻo có thể mở rộng được xác định bởi những tin nhắn bạn gửi chứ không phải cách bạn gửi chúng . Nói cách khác, khớp nối lỏng lẻo được xác định ở lớp ứng dụng .

Hãy xem xét hai bên giao tiếp trực tiếp với RESTful HTTP hoặc gián tiếp với nhà môi giới tin nhắn AMQP. Giả sử Bên A muốn tải hình ảnh JPEG lên Bên B, người sẽ làm sắc nét, nén hoặc nâng cao hình ảnh. Bên A không cần hình ảnh được xử lý ngay lập tức, nhưng không yêu cầu tham chiếu đến nó để sử dụng và truy xuất trong tương lai. Đây là một cách có thể đi trong REST:

  • Bên A gửi POSTtin nhắn yêu cầu HTTP đến Bên B vớiContent-Type: image/jpeg
  • Bên B xử lý hình ảnh (trong một thời gian dài nếu nó lớn) trong khi Bên A chờ đợi, có thể làm những việc khác
  • Bên B gửi 201 Createdthông điệp phản hồi HTTP đến Bên A với Content-Location: <url>tiêu đề liên kết đến hình ảnh được xử lý
  • Bên A xem xét công việc của mình được thực hiện vì hiện tại có tham chiếu đến hình ảnh được xử lý
  • Thỉnh thoảng trong tương lai khi Bên A cần hình ảnh được xử lý, nó NHẬN nó bằng cách sử dụng liên kết từ Content-Locationtiêu đề trước đó

Các 201 Createdmã phản hồi nói với một khách hàng không chỉ là yêu cầu của họ thành công, nó cũng tạo ra một nguồn lực mới. Trong phản hồi 201, Content-Locationtiêu đề là một liên kết đến tài nguyên đã tạo. Điều này được quy định trong RFC 7231 Phần 6.3.2 và 3.1.4.2.

Bây giờ, hãy xem cách tương tác này hoạt động qua giao thức RPC giả định trên AMQP:

  • Bên A gửi cho nhà môi giới tin nhắn AMQP (gọi là Messenger) một tin nhắn có chứa hình ảnh và hướng dẫn định tuyến đến Bên B để xử lý, sau đó trả lời Bên A bằng một địa chỉ nào đó cho hình ảnh
  • Bên A chờ đợi, có thể làm những việc khác
  • Messenger gửi tin nhắn ban đầu của Bên A đến Bên B
  • Bên B xử lý tin nhắn
  • Bên B gửi cho Messenger một tin nhắn chứa địa chỉ cho hình ảnh được xử lý và hướng dẫn định tuyến tin nhắn đó đến Bên A
  • Messenger gửi cho bên A tin nhắn từ bên B chứa địa chỉ hình ảnh được xử lý
  • Bên A xem xét công việc của mình được thực hiện vì hiện tại có tham chiếu đến hình ảnh được xử lý
  • Thỉnh thoảng trong tương lai khi Bên A cần hình ảnh, nó sẽ lấy hình ảnh bằng địa chỉ (có thể bằng cách gửi tin nhắn cho một số bên khác)

Bạn có thấy vấn đề ở đây không? Trong cả hai trường hợp, Bên A không thể có được địa chỉ hình ảnh cho đến khi Bên B xử lý hình ảnh . Tuy nhiên, bên A không cần hình ảnh ngay lập tức và, bằng mọi quyền, không thể quan tâm ít hơn nếu việc xử lý kết thúc!

Chúng ta có thể khắc phục điều này khá dễ dàng trong trường hợp AMQP bằng cách yêu cầu Bên B nói với B rằng B chấp nhận hình ảnh để xử lý, cung cấp cho A địa chỉ nơi hình ảnh sẽ ở sau khi quá trình xử lý hoàn tất. Sau đó, bên B có thể gửi tin nhắn cho A đôi khi trong tương lai cho biết quá trình xử lý ảnh đã kết thúc. AMQP nhắn tin để giải cứu!

Ngoại trừ đoán những gì: bạn có thể đạt được điều tương tự với REST . Trong ví dụ AMQP, chúng tôi đã thay đổi thông báo "đây là hình ảnh được xử lý" thành thông báo "hình ảnh đang xử lý, bạn có thể lấy nó sau". Để làm điều đó trong RESTful HTTP, chúng tôi sẽ sử dụng 202 Acceptedmã và Content-Locationmột lần nữa:

  • Bên A gửi POSTtin nhắn HTTP đến Bên B vớiContent-Type: image/jpeg
  • Bên B ngay lập tức gửi lại 202 Acceptedphản hồi có chứa một số loại nội dung "hoạt động không đồng bộ" mô tả việc xử lý đã kết thúc hay chưa và hình ảnh sẽ khả dụng khi xử lý xong. Cũng bao gồm là một Content-Location: <link>tiêu đề, trong một 202 Acceptedphản hồi, là một liên kết đến tài nguyên được đại diện bởi bất kỳ cơ quan phản hồi nào. Trong trường hợp này, điều đó có nghĩa là đó là một liên kết đến hoạt động không đồng bộ của chúng tôi!
  • Bên A xem xét công việc của mình được thực hiện vì hiện tại có tham chiếu đến hình ảnh được xử lý
  • Thỉnh thoảng trong tương lai khi Bên A cần hình ảnh được xử lý, trước tiên, nó NHẬN tài nguyên hoạt động không đồng bộ được liên kết trong Content-Locationtiêu đề để xác định xem việc xử lý đã kết thúc chưa. Nếu vậy, Bên A sau đó sử dụng liên kết trong chính hoạt động không đồng bộ để NHẬN hình ảnh được xử lý.

Sự khác biệt duy nhất ở đây là trong mô hình AMQP, Bên B nói với Bên A khi quá trình xử lý ảnh được thực hiện. Nhưng trong mô hình REST, Bên A kiểm tra xem việc xử lý có được thực hiện ngay trước khi nó thực sự cần hình ảnh hay không. Những cách tiếp cận này có khả năng mở rộng tương đương . Khi hệ thống trở nên lớn hơn, số lượng tin nhắn được gửi trong cả chiến lược REST không đồng bộ và chiến lược REST không đồng bộ tăng lên với độ phức tạp tiệm cận tương đương. Sự khác biệt duy nhất là máy khách đang gửi thêm một tin nhắn thay vì máy chủ.

Nhưng cách tiếp cận REST có thêm một vài mánh khóe: khám phá năng động và đàm phán giao thức . Xem xét cách cả hai tương tác REST đồng bộ hóa và không đồng bộ bắt đầu. Bên A đã gửi cùng một yêu cầu chính xác cho Bên B, với sự khác biệt duy nhất là loại thông điệp thành công cụ thể mà Bên B đã phản hồi. Điều gì xảy ra nếu Bên A muốn chọn xử lý hình ảnh là đồng bộ hay không đồng bộ? Điều gì xảy ra nếu Bên A không biết liệu Bên B thậm chí có khả năng xử lý không đồng bộ không?

Chà, HTTP thực sự đã có một giao thức chuẩn hóa cho việc này rồi! Nó được gọi là Tùy chọn HTTP, cụ thể là respond-asynctùy chọn của RFC 7240 Mục 4.1. Nếu Bên A mong muốn có phản hồi không đồng bộ, nó sẽ bao gồm một Prefer: respond-asynctiêu đề với yêu cầu POST ban đầu. Nếu Bên B quyết định tôn trọng yêu cầu này, nó sẽ gửi lại 202 Acceptedphản hồi bao gồm a Preference-Applied: respond-async. Nếu không, Bên B chỉ đơn giản là bỏ qua các Prefertiêu đề và gửi lại 201 Creatednhư bình thường.

Điều này cho phép Bên A đàm phán với máy chủ, tự động thích ứng với bất kỳ triển khai xử lý hình ảnh nào mà nó đang nói đến. Hơn nữa, việc sử dụng các liên kết rõ ràng có nghĩa là Bên A không cần biết về bất kỳ bên nào ngoài B: không có nhà môi giới tin nhắn AMQP, không có Bên C bí ẩn nào biết cách thực sự biến địa chỉ hình ảnh thành dữ liệu hình ảnh, không có B-Async thứ hai bên nếu cả hai yêu cầu đồng bộ và không đồng bộ cần phải được thực hiện, v.v ... Nó chỉ đơn giản mô tả những gì nó cần, những gì nó sẽ tùy chọn, và sau đó phản ứng với mã trạng thái, nội dung phản hồi và liên kết. Thêm vào trongCache-Controltiêu đề cho các hướng dẫn rõ ràng về thời điểm giữ bản sao dữ liệu cục bộ và bây giờ các máy chủ có thể thương lượng với khách hàng mà tài nguyên khách hàng có thể giữ bản sao cục bộ (hoặc thậm chí ngoại tuyến!). Đây là cách bạn xây dựng các dịch vụ siêu nhỏ chịu lỗi kết hợp lỏng lẻo trong REST.


1

Tất nhiên, bạn có cần phải hoàn toàn hướng sự kiện hay không, tất nhiên, tùy thuộc vào kịch bản cụ thể của bạn. Giả sử rằng bạn thực sự cần phải như vậy, thì bạn có thể giải quyết vấn đề bằng cách:

Lưu trữ một địa phương, read-only sao chép các dữ liệu bằng cách lắng nghe cho các sự kiện khác nhau và nắm bắt được thông tin trong những trọng lượng. Trong khi điều này cho phép bạn đọc nhanh dữ liệu đó, được lưu trữ ở dạng phù hợp với ứng dụng chính xác đó, điều đó cũng có nghĩa là dữ liệu của bạn cuối cùng sẽ nhất quán trên các dịch vụ.

Để mô hình hóa GET /users/1với phương pháp này, người ta có thể lắng nghe UserCreatedUserUpdatedcác sự kiện và lưu trữ tập hợp con hữu ích của dữ liệu người dùng trong dịch vụ. Khi bạn cần lấy thông tin người dùng đó, bạn chỉ cần truy vấn kho dữ liệu cục bộ của mình.

Trong một phút, hãy giả sử rằng dịch vụ hiển thị /users/điểm cuối không xuất bản bất kỳ loại sự kiện nào. Trong trường hợp này, bạn có thể đạt được điều tương tự bằng cách đơn giản lưu các phản hồi vào các yêu cầu HTTP bạn thực hiện, do đó phủ nhận nhu cầu thực hiện nhiều hơn 1 yêu cầu HTTP cho mỗi người dùng trong một số khung thời gian.


Tôi hiểu. Nhưng những gì về xử lý lỗi (và báo cáo) cho khách hàng trong kịch bản này?
Tony E. Stark

Ý tôi là, làm thế nào để tôi báo cáo lại các lỗi máy khách REST xảy ra khi xử lý UserCreatedsự kiện (ví dụ: trùng lặp tên người dùng hoặc email hoặc cơ sở dữ liệu).
Tony E. Stark

Nó phụ thuộc vào nơi bạn đang thực hiện hành động. Nếu bạn ở trong hệ thống người dùng, bạn có thể thực hiện tất cả xác thực của mình, ghi vào kho lưu trữ dữ liệu ở đó, sau đó xuất bản sự kiện. Mặt khác, tôi thấy việc thực hiện một yêu cầu HTTP tiêu chuẩn đến /users/điểm cuối là hoàn toàn chấp nhận được và cho phép hệ thống đó xuất bản sự kiện của mình nếu nó thành công và đáp ứng yêu cầu với thực thể mới
Andy Hunt

0

Với một hệ thống có nguồn gốc sự kiện, các khía cạnh không đồng bộ thường xuất hiện khi một cái gì đó đại diện cho trạng thái, có thể là cơ sở dữ liệu hoặc chế độ xem tổng hợp của một số dữ liệu, bị thay đổi. Sử dụng ví dụ của bạn, một cuộc gọi tới GET / api / người dùng chỉ có thể trả về phản hồi từ một dịch vụ có đại diện cập nhật của danh sách người dùng trong hệ thống. Trong một trường hợp khác, yêu cầu GET / api / người dùng có thể khiến một dịch vụ sử dụng luồng sự kiện kể từ lần chụp cuối cùng của người dùng để tạo một ảnh chụp nhanh khác và chỉ cần trả về kết quả. Một hệ thống hướng sự kiện không nhất thiết hoàn toàn không đồng bộ từ Yêu cầu đến Phản hồi, nhưng có xu hướng ở mức độ mà các dịch vụ cần tương tác với các dịch vụ khác. Thông thường, sẽ không có ý nghĩa khi trả lại một cách không đồng bộ yêu cầu GET và do đó bạn chỉ có thể trả về phản hồi của dịch vụ,

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.