flask-SocketIO 中文翻譯(三)

logo-full-flask.png

上回我們說到了一些小功能涧尿,這次再對(duì)flask應(yīng)用做一個(gè)補(bǔ)充系奉,交代flask的上下文變量,身份認(rèn)證等功能姑廉。

當(dāng)我們的flask應(yīng)用做好之后就不得不涉及到部署缺亮,本文將會(huì)討論flask-socketio的三種不同類型的服務(wù)器部署,nginx的反向代理桥言,外部進(jìn)程消息的處理等

到今天為止萌踱,flask-socketio官方文檔,除了api限书,其它都已經(jīng)翻譯完畢虫蝶,感謝各位的賞臉。_

11.訪問flask上下文全局變量

SocketIO活動(dòng)處理不同于路由處理倦西,在于它引入了許多容易混淆的東西能真,圍繞著SocketIO什么可以做,什么不可以做扰柠。最主要的區(qū)別就是SocketIO活動(dòng)發(fā)生在單個(gè)長(zhǎng)期運(yùn)行在上下文的請(qǐng)求之中粉铐。

盡管有所不同,F(xiàn)lask-SocketIO將環(huán)境改造成類似于常規(guī)HTTP請(qǐng)求卤档,使SocketIO活動(dòng)處理更加輕松蝙泼。接下來的列表描述了什么將會(huì)生效,什么不會(huì)劝枣。

  • 在活動(dòng)處理函數(shù)之前推送應(yīng)用的上下文使得current_appg可以在處理函數(shù)中可用汤踏。

  • 這個(gè)請(qǐng)求的上下文同樣在回調(diào)處理函數(shù)前被啟用织鲸,也使requestsession可用。但是注意到WebSocket活動(dòng)與之并沒有獨(dú)立的聯(lián)系溪胶,因此為連接期間分派的所有事件推送啟動(dòng)連接的請(qǐng)求上下文搂擦。

  • request上下文全局變量隨一個(gè)sid成員增加,這個(gè)成員是為了給連接一個(gè)獨(dú)特的會(huì)話編號(hào)(session ID)哗脖。這個(gè)值在客戶端剛剛添加的時(shí)候瀑踢,就被最初的房間使用了。

  • request上下文全局變量由包含了當(dāng)前處理函數(shù)的命名空間和活動(dòng)參數(shù)的argumentevent來增加才避。這個(gè)活動(dòng)成員是一個(gè)包含了messageargs鍵值的字典橱夭。

  • session上下文全局變量表現(xiàn)得和通常的請(qǐng)求不一樣。在連接開始建立的時(shí)候桑逝,就會(huì)復(fù)制一份用戶的會(huì)話在這個(gè)連接上下文中給處理器調(diào)用棘劣。如果SocketIO處理器修改了這個(gè)會(huì)話,這個(gè)修改過的會(huì)話就會(huì)為未來的SocketIO處理器保留楞遏,但是正常的HTTP路由處理器不會(huì)察覺這些改變呈础。有效率的是,當(dāng)SocketIO處理器改變這個(gè)會(huì)話的時(shí)候橱健,會(huì)話就會(huì)為這些處理器創(chuàng)建一個(gè)“分支”(fork)而钞。這個(gè)限制的技術(shù)原因是用戶的會(huì)話cookie必須要發(fā)送到客戶端,這需要HTTP請(qǐng)求和應(yīng)答而不是SocketIO連接拘荡。在使用服務(wù)端的會(huì)話時(shí)臼节,比如那些由Flask-Session或者Flask-KVSession擴(kuò)展提供的會(huì)話,在HTTP處理器中的會(huì)話改變也可以在SocketIO處理器中可見珊皿,只要這個(gè)會(huì)話不是在SocketIO處理器中修改的网缝。

  • before_requestafter_request鉤子不會(huì)調(diào)用SocketIO活動(dòng)處理器。

  • SocketIO處理器可以使用自定義的裝飾器蟋定,但是大多數(shù)Flask裝飾器并不適于SocketIO處理器粉臊,考慮到SocketIO連接中沒有Response對(duì)象這一概念。

12.身份認(rèn)證

應(yīng)用的共同需要就是驗(yàn)證他們用戶的身份驶兜。自從SocketIO沒有使用HTTP請(qǐng)求和應(yīng)答扼仲,傳統(tǒng)的基于網(wǎng)頁表單和HTTP請(qǐng)求的機(jī)制不能用于SocketIO連接。如果需要的話抄淑,應(yīng)用可以實(shí)施自定義的登陸表單屠凶,當(dāng)用戶按下提交按鈕時(shí),它利用一個(gè)SocketIO消息將證書發(fā)送到服務(wù)器肆资。

然而矗愧,在大多數(shù)情況下,在SocketIO連接建立之前使用傳統(tǒng)的身份驗(yàn)證方式會(huì)更加方便郑原,用戶的身份信息可以被記錄下來作為用戶會(huì)話或者cookie唉韭,之后在SocketIO連接建立起來的時(shí)候夜涕,這些信息也可以被SocketIO活動(dòng)處理器得到。

13.使用Flask-SocketIO的Flask-Login模塊

Flask-SocketIO可以獲得由Flask-Login維護(hù)的登陸信息属愤。在一個(gè)正常的Flask-Login身份認(rèn)證被使用的時(shí)候钠乏,login_user()函數(shù)將會(huì)被調(diào)用去記錄用戶會(huì)話中的用戶,任何SocketIO連接都可以得到current_user上下文變量:

@socketio.on('connect')
def connect_handler():
    if current_user.is_authenticated:
        emit('my response',
            {'message':'{0} has joined'.format(current_user.name)},
            broadcast=True
        )
    else:
        return False # not allowed here

注意到login_required裝飾器不能和SocketIO活動(dòng)處理器一起使用春塌,但是一個(gè)自定義的關(guān)閉連接無身份認(rèn)證的裝飾器可以按下面的方式創(chuàng)建:

import functools
from flask import request
from flask_login import current_user
from flask_socketio import disconnect

def authenticated_only(f):
    @functools.wraps(f):
    def wraped(*args, **kwargs):
        if not current_user.is_authenticated:
            disconnect()
        else:
            return f(*args, **kwargs)
        return wraped

@socketio.on('my event')
@authenticated_only
def handle_my_custom_event(data)
    emit('my response',
        {'message': '{0} has joined'.format(current_user.name)},
        broadcast=True
    )

14.部署

我們有多種部署Flask-SocketIO服務(wù)器的選擇,從最簡(jiǎn)單到瘋狂地復(fù)雜簇捍。在這一章節(jié)里只壳,我們將會(huì)介紹最普遍的選擇映皆。

嵌入式服務(wù)器

最簡(jiǎn)單的策略是安裝eventlet或者gevent骑冗,并且就像前面章節(jié)的例子中引用socketio.run(app)的方式來啟動(dòng)網(wǎng)絡(luò)服務(wù)器歌憨。這個(gè)將會(huì)在eventlet或者gevent網(wǎng)絡(luò)服務(wù)器中啟動(dòng)這個(gè)應(yīng)用悯许,被嵌入的網(wǎng)絡(luò)服務(wù)器是哪一個(gè)取決于是安裝的是哪一個(gè)捂齐。

注意到socketio.run(app)運(yùn)行在eventlet或gevent已安裝上的生產(chǎn)服務(wù)器中热幔。如果它們中沒有一個(gè)被安裝玄帕,那么這個(gè)應(yīng)用運(yùn)行在Flask開發(fā)服務(wù)器中晨缴,這并不適于生產(chǎn)環(huán)境的使用驹愚。

不幸的是远搪,這個(gè)選擇并不能在帶有uWSGI的gevent服務(wù)器上使用,你可以在下面獲取更多有關(guān)這個(gè)選項(xiàng)的信息逢捺。

Gunicorn網(wǎng)絡(luò)服務(wù)器

作為Socketio.run(app)替代方法的就是使用gunicorn作為網(wǎng)絡(luò)服務(wù)器谁鳍,工作在eventlet或gevent下。這個(gè)選擇下劫瞳,除了gunicorn要安裝倘潜,eventlet或者gevent也是不可缺少的。這個(gè)條命令將會(huì)啟動(dòng)這個(gè)基于gunicorn的eventlet服務(wù)器:
gunnicorn --worker--class eventlet -w 1 module:app

如果你更傾向于使用gevent志于,啟動(dòng)服務(wù)器的命令如下:
gunicorn -k gevent -w 1 module:app

當(dāng)使用gunicorn作為gevent的工作站并且websocket支持也被提供的時(shí)候涮因,上述命令就必須被改成選擇一個(gè)自定義的gevent網(wǎng)絡(luò)服務(wù)器來支持websocket協(xié)議。修改后的命令如下:
gunicorn -k geventwebsocket.gunicorn.worker.GeventWebSocketWorker -w 1 module:app

在上述這些命令中伺绽,module是python模塊或者是定義了應(yīng)用實(shí)例的包养泡,此外,app是應(yīng)用實(shí)例本身奈应。

Gunicorn 18.0版本是被推薦和Flask-SocketIO搭配的版本瓤荔。19.x版本已知在帶有WebSocket的一些特定部署場(chǎng)景下存在不兼容的情況。

gunicorn由于使用了有限的負(fù)載均衡算法钥组,不可能在使用這種網(wǎng)絡(luò)服務(wù)器時(shí)調(diào)用兩個(gè)以上工作進(jìn)程因?yàn)檫@個(gè)原因输硝,上面的所有例子中都包含了-w 1的可選參數(shù)。

15.uWSGI網(wǎng)絡(luò)服務(wù)器

當(dāng)使用uWSGI網(wǎng)絡(luò)服務(wù)器搭配geventd的時(shí)候程梦,Socket.IO服務(wù)器的時(shí)候点把,可以利用uWSGI原生的WebSocket支持橘荠。

一個(gè)配置和運(yùn)用uWSGI服務(wù)器完整的解釋超出了本文的論述范圍。uWSGI服務(wù)器確實(shí)是一個(gè)比較復(fù)雜的郎逃,它提供了大量而又詳盡的設(shè)置選項(xiàng)哥童。它必須使用Websocket和SSL編譯才能支持WebSocket傳輸。作為介紹褒翰,下面的命令啟動(dòng)了一個(gè)uWSGI服務(wù)器作為范例贮懈,這個(gè)應(yīng)用app.py運(yùn)行在端口5000:
uwsgi --http :5000 --gevent 1000 --http-websockets --master --wsgi-file app.py --callable app

16.使用nginx作為反向代理服務(wù)器

使用nginx作為前端的反向代理將請(qǐng)求傳遞給應(yīng)用是可行的。然而优训,只有nginx 1.4版本以上才支持WebSocket協(xié)議朵你。下面是nginx代理HTTP和WebSocket請(qǐng)求的一個(gè)最基本的配置:

    server {
        listen 80;
        server_name _;
        
        location / {
            include proxy_params;
            proxy_pass http://127.0.0.1:5000;
        }
        
        location /socket.io {
            include proxy_params;
            proxy_http_version 1.1;
            proxy_buffering off;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_pass http://127.0.0.1:5000/socket.io;
        }
    }

下面的例子增加了對(duì)負(fù)載平衡多個(gè)服務(wù)器的支持:

    upstream socketio_nodes {
        ip_hash;
        
        server 127.0.0.1:5000;
        server 127.0.0.1:5001;
        server 127.0.0.1:5002;
        # to scale the app, just add more nodes here!
    }
    
    server {
        listen 80;
        server_name _;
        
        location / {
            include proxy_params;
            proxy_pass http://127.0.0.1:5000;
        }
        
        location /socket.io {
            include proxy_params;
            proxy_http_version 1.1;
            proxy_buffering off;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_pass http://socketio_nodes/socket.io;
        }
    }

雖然上面的例子可以作為最初的配置工作,要知道生產(chǎn)環(huán)境安裝的nginx需要一個(gè)完整的配置揣非,包括部署的其它方面抡医,例如服務(wù)于靜態(tài)文件的assert和SSL支持。

17.使用多個(gè)工作站

Flask-SocketIO從2.0版本起帶有負(fù)載均衡器支持多個(gè)工作站早敬。部署多個(gè)工作站給了使用Flask-SocketIO的應(yīng)用程序有能力在多進(jìn)程和多主機(jī)之間傳播客戶端鏈接忌傻,這種方式的擴(kuò)展支持極大規(guī)模的并發(fā)客戶端。

使用多個(gè)Flask-SocketIO工作站需要兩個(gè)依賴:

  • 負(fù)載均衡器必須要配置成總是將所有的HTTP請(qǐng)求從一個(gè)給定的客戶端轉(zhuǎn)發(fā)到同樣的工作站中搞监。這有時(shí)會(huì)作為"sticky session"被提及水孩。對(duì)于nginx,使用這個(gè)ip_bash指示來達(dá)到上述要求琐驴。Gunicorn不能用于多工作站荷愕,因?yàn)樗呢?fù)載均衡算法并不支持粘性會(huì)話(sticky session)。
  • 一旦每個(gè)服務(wù)器只擁有一個(gè)客戶端連接棍矛,在Redis安疗、RabbitMQ等例子中,消息隊(duì)列將會(huì)被使用够委,來協(xié)調(diào)復(fù)雜的操作荐类,比如:廣播和房間。

當(dāng)使用消息隊(duì)列的時(shí)候茁帽,有許多額外的依賴包需要被安裝:

  • 對(duì)于Redis玉罐,redis包必須被安裝(pip install redis)
  • 對(duì)于RabbitMQ,kombu包必須要被安裝(pip install kombu)
  • 對(duì)于其它Kombu支持的消息隊(duì)列潘拨,Kombu documentation里可以找到需要的依賴
  • 如果使用了eventlet或者gevent吊输,那么通常需要使用猴子(Monkey)修補(bǔ)Python標(biāo)準(zhǔn)庫來強(qiáng)制消息隊(duì)列包使用協(xié)同友好的函數(shù)和類。

為了啟動(dòng)多個(gè)Flask-SocketIO服務(wù)器铁追,你必須首先確保消息隊(duì)列服務(wù)正在運(yùn)行季蚂。為了開啟一個(gè)Socket.IO服務(wù)器,使他連接到一個(gè)消息隊(duì)列,扭屁,需要添加參數(shù)message_queue到構(gòu)造函數(shù)SockIO:
socketio=SocketIO(app,message_queue='redis://')
參數(shù)message_queue的值就是隊(duì)列服務(wù)所使用的連接URL算谈。對(duì)于一個(gè)運(yùn)行在同一個(gè)作為服務(wù)器的主機(jī)中的Redis隊(duì)列來說,可以使用'redis://'這樣的URL料滥。同樣然眼,對(duì)于一個(gè)默認(rèn)的RabbitMQ隊(duì)列可以使用'amqp://'開頭的URL。Kombu包有一個(gè)文檔章節(jié)闡述了對(duì)于所有支持隊(duì)列的URL格式葵腹。

18.外部進(jìn)程消息

對(duì)于許多類型的應(yīng)用高每,從非服務(wù)端創(chuàng)建會(huì)話活動(dòng)很有必要,例如一個(gè)Celery工作站践宴。如果SocketIO服務(wù)器并沒有按照前面章節(jié)那樣配置監(jiān)聽隊(duì)列鲸匿,那么所有其它的進(jìn)程可以像服務(wù)器那樣創(chuàng)建它自己的SocketIO實(shí)例來創(chuàng)建消息活動(dòng)。

例如浴井,一個(gè)運(yùn)行在eventlet網(wǎng)絡(luò)服務(wù)器上的應(yīng)用,使用了Redis消息隊(duì)列霉撵,下面的Python腳本將向所有的客戶端廣播一個(gè)消息活動(dòng):

socketio=SocketIO(message_queue='redis://')
socketio.emit('my event', {'data': 'foo'}, namespace='/test')

當(dāng)使用這種方法引用SocketIO實(shí)例磺浙,F(xiàn)lask應(yīng)用實(shí)例將不會(huì)傳遞到構(gòu)造函數(shù)

當(dāng)SocketIO通過消息隊(duì)列使用參數(shù)channel來選擇一個(gè)具體channel的對(duì)話。當(dāng)很多獨(dú)立的SocketIO服務(wù)公用一個(gè)隊(duì)列的時(shí)候徒坡,使用一個(gè)自定義的channel名稱將是很有必要的撕氧。

Flask-SocketIO并沒有在使用eventlet或者gevent時(shí)應(yīng)用猴子(monkey)來修補(bǔ)。但是當(dāng)使用消息隊(duì)列的時(shí)候喇完,如果Python標(biāo)準(zhǔn)庫沒有使用猴子來修補(bǔ)伦泥,那么消息隊(duì)列服務(wù)的Python包很可能會(huì)掛起。

很重要的一點(diǎn)是:外部進(jìn)程想連接到SocketIO服務(wù)器并不需要像主服務(wù)器那樣使用eventlet或者gevent锦溪。使一個(gè)服務(wù)器使用了協(xié)同框架不脯,外部進(jìn)程不是一個(gè)阻力。例如刻诊,Celery工作站并不需要配置使用eventlet或者gevent防楷,是因?yàn)橹鞣?wù)器已經(jīng)有了。但是则涯,如果你的外部進(jìn)程因?yàn)槟撤N原因
使用了協(xié)同框架复局,那么monkey修復(fù)就很可能是需要的,那么消息隊(duì)列就可以獲得協(xié)同友好的函數(shù)和類粟判。

19.從Flask-SocketIO 0.x 升級(jí)到 1.x 和 2.x 版本

老版本的Flask-SocketIO有完全不同的一系列依賴包亿昏。老版本依賴gevent-socketio和gevent-websocket,這些包 1.0 版本都不需要了档礁。

盡管依賴的改變角钩,但是 1.0 版本卻沒有太多重要的改變。下面是一個(gè)實(shí)際改變的詳細(xì)的清單:

  • 1.0 版本放棄支持Python 2.6,增加了對(duì)Python 3.3, Python 3.4 和 pypy 的支持彤断。
  • 0.x 版本需要老版本的Socket.IO javascript客戶端野舶。從 1.0 版本開始,支持新發(fā)布的Socket.IO和Engin.IO宰衙。1.0版本以前的Socket.IO將不再被支持平道。Swift和C++官方的Socket.IO客戶端也被支持。
  • 0.x 版本依賴gevent供炼,gevent-socketio和gevent-websocket.1.0 版本以后將不再使用一屋。在Flask開發(fā)的網(wǎng)絡(luò)服務(wù)器中,gevent是三種后端網(wǎng)絡(luò)服務(wù)器選擇之一袋哼,另外兩個(gè)是eventlet和其它常規(guī)多線程WSGI服務(wù)器冀墨。
  • Socket.IO服務(wù)器選項(xiàng)在 1.0 版本中也有所改變。它們可以由SocketIO構(gòu)造函數(shù)來提供涛贯,或者由run()調(diào)用诽嘉。這些選項(xiàng)在使用前在這兩者中被合并。
  • 0.x 版本暴露了gevent-socketio在連接中作為request.namespace弟翘。在 1.0 版本中它不再被使用虫腋。這個(gè)請(qǐng)求對(duì)象定義了request.namespace作為待處理的命令空間。并且增加了request.aid稀余,為客戶端連接定義了一個(gè)獨(dú)有的會(huì)話ID悦冀,request.event包含了活動(dòng)名稱和參數(shù)。
  • 為了獲得房間列表睛琳,0.x版本需要應(yīng)用使用私有g(shù)event-socketio結(jié)構(gòu)盒蟆,包含request.namespace.rooms表達(dá)式。這是在 1.0 版本中將不再出現(xiàn)师骗,因?yàn)樗艘粋€(gè)合適的room()函數(shù)历等。
  • 這個(gè)推薦的“把戲(trick)”發(fā)送消息到一個(gè)獨(dú)立的客戶端將消息分發(fā)到每個(gè)客戶端所在的獨(dú)立的房間內(nèi),這個(gè)地址消息對(duì)應(yīng)著目的房間(desired room)辟癌。這個(gè)特性在 1.0 版本中被正式化了募闲,當(dāng)客戶端連接到服務(wù)器時(shí),它會(huì)立即自動(dòng)地被分配到一個(gè)特定的房間內(nèi)愿待。
  • 全局命名空間的connect活動(dòng)在 1.0 版本之前并沒有被觸發(fā)浩螺。這bug已經(jīng)被修復(fù)了并且按照預(yù)期觸發(fā)。
  • 在 1.0 版本增加了對(duì)客戶端的回調(diào)函數(shù)的支持仍侥。

為了升級(jí)到新的Flask-SocketIO版本要出,你需要升級(jí)你的Socket.IO客戶端到兼容Socket.IO 1.0 協(xié)議。對(duì)于Javascript客戶端农渊,1.3.x和1.4.x版本經(jīng)過充分地測(cè)試患蹂,發(fā)現(xiàn)是兼容的或颊。

在服務(wù)端,有一些要點(diǎn)是要被考慮到的:

  • 如果你想繼續(xù)使用gevent传于,那么gevent-socketio需要從你的虛擬環(huán)境中卸載囱挑,因?yàn)檫@個(gè)包將不再需要并且可能會(huì)與它的替代——python-socketio相沖突。
  • 如果你想輕微地提高性能和穩(wěn)定性沼溜,那么推薦你轉(zhuǎn)而使用eventlet平挑。為了做到這一點(diǎn),需要卸載gevent系草、gevent-socketio和gevent-websocket通熄,然后安裝eventlet。
  • 如果你的應(yīng)用使用了猴子修復(fù)了并轉(zhuǎn)向了eventlet找都,需要調(diào)用eventlet.monkey_patch()來代替gevent中的monkey.patch_all()唇辨。此外,任何對(duì)gevent的調(diào)用必須被同等條件下的對(duì)eventlet調(diào)用替代能耻。
  • 任何使用request.namespace需要被直接調(diào)用Flask-SocketIO函數(shù)替代赏枚。例如,request.namespace.rooms要用rooms()函數(shù)替換晓猛。
  • 任何使用內(nèi)置的gevent-socketio的對(duì)象都必須被去除饿幅,當(dāng)這個(gè)包不再是所需的依賴的時(shí)候。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鞍帝,一起剝皮案震驚了整個(gè)濱河市诫睬,隨后出現(xiàn)的幾起案子煞茫,更是在濱河造成了極大的恐慌帕涌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件续徽,死亡現(xiàn)場(chǎng)離奇詭異蚓曼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)钦扭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門纫版,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人客情,你說我怎么就攤上這事其弊。” “怎么了膀斋?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵梭伐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我仰担,道長(zhǎng)糊识,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮赂苗,結(jié)果婚禮上愉耙,老公的妹妹穿的比我還像新娘。我一直安慰自己拌滋,他們只是感情好朴沿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸠真,像睡著了一般悯仙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吠卷,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天锡垄,我揣著相機(jī)與錄音,去河邊找鬼祭隔。 笑死货岭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疾渴。 我是一名探鬼主播千贯,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼搞坝!你這毒婦竟也來了搔谴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤桩撮,失蹤者是張志新(化名)和其女友劉穎敦第,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體店量,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芜果,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了融师。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片右钾。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖旱爆,靈堂內(nèi)的尸體忽然破棺而出舀射,到底是詐尸還是另有隱情,我是刑警寧澤怀伦,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布脆烟,位于F島的核電站,受9級(jí)特大地震影響空镜,放射性物質(zhì)發(fā)生泄漏浩淘。R本人自食惡果不足惜捌朴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望张抄。 院中可真熱鬧砂蔽,春花似錦、人聲如沸署惯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽极谊。三九已至诡右,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間轻猖,已是汗流浹背帆吻。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咙边,地道東北人猜煮。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像败许,于是被迫代替她去往敵國(guó)和親王带。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 作者:詹聰聰 序言: 本人工作中需要用到flask-socketio市殷,在學(xué)習(xí)英文文檔時(shí)發(fā)現(xiàn)愕撰,flask-socke...
    Python中文社區(qū)閱讀 12,617評(píng)論 6 39
  • 筆者最近在學(xué)習(xí)flask-SocketIO,由于找不到中文文檔醋寝,就選擇了自己翻譯官方的英文文檔搞挣,并分享給大家,希望...
    天涯待歸客閱讀 11,542評(píng)論 9 7
  • # Python 資源大全中文版 我想很多程序員應(yīng)該記得 GitHub 上有一個(gè) Awesome - XXX 系列...
    aimaile閱讀 26,443評(píng)論 6 428
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理甥桂,服務(wù)發(fā)現(xiàn)柿究,斷路器邮旷,智...
    卡卡羅2017閱讀 134,600評(píng)論 18 139
  • 平淡的日子 論文第一遍翻譯完成 晚上去跑了步 好爽 挺好的 晚安(?? . ??)
    文寧寧兒閱讀 100評(píng)論 0 0