2015-07-02 13 views
5

Tôi đang chơi với gevent và websockets. Đây là một máy chủ đơn giản vang:tại sao gevent-websocket đồng bộ?

from gevent.pywsgi import WSGIServer 
from geventwebsocket.handler import WebSocketHandler 
from gevent import sleep 
from datetime import datetime 
def app(environ, start_response): 
    ws = environ['wsgi.websocket'] 
    while True: 
     data = ws.receive() 
     print('{} got data "{}"'.format(
      datetime.now().strftime('%H:%M:%S'), data)) 
     sleep(5) 
     ws.send(data) 

server = WSGIServer(("", 10004), app, 
    handler_class=WebSocketHandler) 
server.serve_forever() 

và khách hàng:

<html> 
    <body> 
     <button type="button" id="push_data">Push</button> 
    </body> 
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.js"></script> 
    <script> 
     var ws = new WebSocket("ws://localhost:10004"); 
     ws.onmessage = function(evt) { 
      console.log(evt) 
     }; 
     $('#push_data').click(function(){ 
      console.log('sending data...'); 
      ws.send('sample data'); 
     }); 
    </script> 
</html> 

Do gevent Tôi đã hy vọng có một vài greenlets phục vụ dữ liệu không đồng bộ; đó là, nếu tôi đã đẩy một số dữ liệu vào websocket vài lần (nhanh chóng bấm vào nút Push), tôi đã mong đợi để có nó tất cả trở lại đồng thời sau 5 giây chờ đợi.

Tuy nhiên, không có vấn đề nhanh như thế nào tôi bấm nút Push, đây là những gì tôi nhận được trong giao diện điều khiển:

18:28:07 got data "sample data" 
18:28:12 got data "sample data" 
18:28:17 got data "sample data" 
18:28:22 got data "sample data" 
18:28:27 got data "sample data" 

tại sao nó nhận được dữ liệu của tôi đồng bộ, tạm dừng mỗi 5 giây? Làm thế nào để biến nó thành một máy chủ không đồng bộ?

Trả lời

6

Hành vi này đồng bộ vì mã của riêng bạn là đồng bộ. gevent chỉ là một thư viện coroutine sử dụng một vòng lặp sự kiện. Nó không kỳ diệu biến mã đồng bộ thành mã không đồng bộ.

hãy có một cái nhìn tại các tài liệu tại địa chỉ: http://www.gevent.org/servers.html

Người ta nói rằng các máy chủ đẻ trứng một greenlet mỗi kết nối (không theo yêu cầu). Do đó, việc thực hiện nhiều yêu cầu cho cùng một kết nối được nối tiếp.

Nếu bạn muốn đồng thời xử lý nhiều yêu cầu cho cùng một kết nối, bạn cần tạo ra các greenlet mới hoặc ủy quyền xử lý cho một nhóm các greenlet.

Dưới đây là một ví dụ (đẻ trứng một greenlet tại mỗi yêu cầu):

import gevent 
from gevent.pywsgi import WSGIServer 
from gevent.lock import Semaphore 
from geventwebsocket.handler import WebSocketHandler 
from datetime import datetime 

def process(ws,data,sem): 
    print('{} got data "{}"'.format(datetime.now().strftime('%H:%M:%S'), data)) 
    gevent.sleep(5) 
    with sem: 
     ws.send(data) 

def app(environ, start_response): 
    ws = environ['wsgi.websocket'] 
    sem = Semaphore() 
    while True: 
     data = ws.receive() 
     gevent.spawn(process,ws,data,sem) 

server = WSGIServer(("", 10004), app,handler_class=WebSocketHandler) 
server.serve_forever() 

Lưu ý sự hiện diện của các semaphore. Bởi vì quá trình xử lý đồng thời, cần thiết để ngăn hai greenlet đồng thời ghi cùng một lúc trên socket, dẫn đến các thông báo bị hỏng.

Điểm cuối cùng, với việc triển khai này, không đảm bảo rằng các thư trả lời sẽ được gửi theo thứ tự các yêu cầu.

+1

Khỉ có gắn gevent không "biến đổi mã đồng bộ một cách kỳ diệu thành mã không đồng bộ" không? – FullStack

+2

Chỉ khi bạn có nhiều greenlet và kết nối. Nếu mã được giao dịch với một kết nối duy nhất từ ​​một greenlet đơn, cơ chế coroutine của gevent sẽ không giúp ích gì.Nói cách khác, phép thuật chỉ xảy ra khi bạn tin vào nó, và cấu trúc mã của bạn cho phù hợp ;-) –

0

Vấn đề thực tế là thế này: data = ws.receive()

gì đang xảy ra ở đây là WebSocket của bạn bây giờ chờ đợi một kết nối duy nhất trong khi toàn bộ ứng dụng chỉ treo ra.

Bạn có hai giải pháp này, hãy thêm một thời gian chờ để ws.receive() HOẶC thiết lập nó như một ứng dụng cao cấp:

from geventwebsocket import WebSocketServer, WebSocketApplication, Resource 

class EchoApplication(WebSocketApplication): 
    def on_open(self): 
     print "Connection opened" 

    def on_message(self, message): 
     self.ws.send(message) 

    def on_close(self, reason): 
     print reason 

WebSocketServer(('', 8000), Resource({'/': EchoApplication}).serve_forever() 

như exampled đây: https://pypi.python.org/pypi/gevent-websocket/

này sau đó sẽ thiết lập của bạn quá trình hoàn toàn không đồng bộ, do đó việc gửi và nhận sẽ không cạnh tranh cho cùng một tài nguyên.

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