2012-02-10 30 views
22

Trong vài tháng qua, tôi và đồng nghiệp đã xây dựng thành công hệ thống phía máy chủ để gửi thông báo đẩy tới thiết bị iPhone. Về cơ bản, người dùng đăng ký các thông báo này thông qua một webservice RESTful (Spray-Server, gần đây được cập nhật để sử dụng Spray-can làm lớp HTTP) và logic lên lịch một hoặc nhiều thư để gửi đi trong tương lai bằng cách sử dụng công cụ lên lịch của Akka.Dịch vụ web dựa trên diễn viên - Cách thực hiện đúng cách?

Hệ thống này, như chúng ta đã xây dựng, chỉ hoạt động: có thể xử lý hàng trăm, thậm chí hàng nghìn yêu cầu HTTP một giây và có thể gửi thông báo với tốc độ 23.000 mỗi giây - thậm chí có thể nhiều hơn , thêm nhiều người gửi thông báo cho người gửi (và do đó có nhiều kết nối hơn với Apple) và có thể có một số tối ưu hóa được thực hiện trong thư viện Java mà chúng tôi sử dụng (java-apns).

Câu hỏi này là về cách thực hiện đúng (tm). Đồng nghiệp của tôi, hiểu biết nhiều hơn về Scala và các hệ thống dựa trên diễn viên nói chung, lưu ý cách ứng dụng không phải là hệ thống dựa trên diễn viên 'thuần khiết' - và anh ta đúng. Những gì tôi đang tự hỏi bây giờ là làm thế nào để làm điều đó đúng.

Hiện tại, chúng tôi có một diễn viên Spray HttpService duy nhất, không được phân lớp, được khởi tạo với một tập hợp các chỉ thị phác thảo logic dịch vụ HTTP của chúng tôi. Hiện nay, rất nhiều đơn giản, chúng ta có chỉ thị như thế này:

post { 
    content(as[SomeBusinessObject]) { businessObject => request => 
    // store the business object in a MongoDB back-end and wait for the ID to be 
    // returned; we want to send this back to the user. 
    val businessObjectId = persister !! new PersistSchedule(businessObject) 
    request.complete("/businessObject/%s".format(businessObjectId)) 
    } 
} 

Bây giờ, nếu tôi có được quyền này, 'chờ đợi một câu trả lời' từ một diễn viên là một không-không trong chương trình diễn dựa trên (cộng với! ! không được chấp nhận). Điều tôi tin là cách 'đúng' để thực hiện việc này là chuyển đối tượng request cho diễn viên persister trong một tin nhắn và gọi cho số request.complete ngay khi nhận được ID được tạo từ phía sau.

Tôi đã viết lại một trong các tuyến đường trong ứng dụng của mình để thực hiện điều này; trong thông báo được gửi đến tác nhân, đối tượng/tham chiếu yêu cầu cũng được gửi. Điều này dường như làm việc như nó là nghĩa vụ phải:

content(as[SomeBusinessObject]) { businessObject => request => 
    persister ! new PersistSchedule(request, businessObject) 
    } 

mối quan tâm chính của tôi ở đây là chúng ta dường như vượt qua các đối tượng request đến 'logic kinh doanh', trong trường hợp này persister. Hiện tại, persister có trách nhiệm bổ sung, tức là gọi số request.complete và hiểu biết về hệ thống mà nó chạy, nghĩa là nó là một phần của dịch vụ web.

Cách chính xác để xử lý tình huống như thế này là gì, sao cho diễn viên này trở nên không biết nó là một phần của dịch vụ http và không cần biết cách tạo ID đã tạo?

Tôi nghĩ rằng yêu cầu vẫn nên được chuyển cho diễn viên persister, nhưng thay vì diễn viên persister gọi request.complete, nó sẽ gửi một thông báo lại cho diễn viên HttpService (một thông báo SchedulePersisted(request, businessObjectId)), chỉ cần gọi request.complete("/businessObject/%s".format(businessObjectId)) . Về cơ bản:

def receive = { 
    case SchedulePersisted(request, businessObjectId) => 
    request.complete("/businessObject/%s".format(businessObjectId)) 
} 

val directives = post { 
    content(as[SomeBusinessObject]) { businessObject => request => 
    persister ! new PersistSchedule(request, businessObject) 
    } 
} 

Tôi có đi đúng hướng với phương pháp này không?

Một câu hỏi cụ thể nhỏ hơn spray-server, có thể phân lớp HttpService và ghi đè phương thức nhận hoặc tôi sẽ chia nhỏ mọi thứ theo cách đó?(Tôi không có đầu mối về các diễn viên lớp phụ, hoặc cách chuyển các thông điệp không được công nhận cho diễn viên 'phụ huynh')

Câu hỏi cuối cùng, được chuyển qua các thông điệp diễn viên có thể đi qua toàn bộ ứng dụng một cách tiếp cận ổn hoặc có cách nào tốt hơn để 'nhớ' yêu cầu nào sẽ được gửi phản hồi sau khi gửi yêu cầu thông qua ứng dụng?

Trả lời

3

Về câu hỏi đầu tiên của bạn, có, bạn đang đi đúng hướng. (Mặc dù tôi cũng muốn xem một số cách khác để xử lý loại sự cố này).

Một đề xuất tôi có là cách ly diễn viên persister để biết về yêu cầu. Bạn có thể chuyển yêu cầu dưới dạng loại Any. Trình phù hợp trong mã dịch vụ của bạn có thể tự động chuyển cookie trở lại thành một Request.

case class SchedulePersisted(businessObjectId: String, cookie: Any) 

// in your actor 
override def receive = super.receive orElse { 
    case SchedulePersisted(businessObjectId, request: Request) => 
    request.complete("/businessObject/%s".format(businessObjectId)) 
} 

Về câu hỏi thứ hai, các lớp diễn viên thực sự không khác với các lớp thông thường. Nhưng bạn cần phải chắc chắn rằng bạn gọi phương thức receive của siêu lớp, để nó có thể xử lý các thông điệp của riêng nó. Tôi đã có một số cách khác để làm in my original answer này, nhưng tôi nghĩ rằng tôi thích chaining partial functions like this:

class SpecialHttpService extends HttpService { 
    override def receive = super.receive orElse { 
    case SpecialMessage(x) => 
     // handle special message 
    } 
} 
0

Bạn cũng có thể sử dụng các chỉ thị sản phẩm. Nó cho phép bạn tách các marshalling thực tế từ kết thúc yêu cầu:

get { 
    produce(instanceOf[Person]) { personCompleter => 
    databaseActor ! ShowPersonJob(personCompleter) 
    } 
} 

Chỉ thị sản phẩm trong ví dụ này trích ra một chức năng Person => Đơn vị mà bạn có thể sử dụng để hoàn thành yêu cầu minh bạch sâu bên trong lớp logic kinh doanh, trong đó không nên biết về phun.

https://github.com/spray/spray/wiki/Marshalling-Unmarshalling

Các vấn đề liên quan