# 開發前必讀
使用WorkerMan開發應用,你需要了解以下內容:
## 一、WorkerMan開發與普通PHP開發的不同之處
除了與HTTP協議相關的變量函數無法直接使用外,WorkerMan開發與普通PHP開發并沒有很大不同。
### 1、應用層協議不同
- 普通PHP開發一般是基于HTTP應用層協議,WebServer已經幫開發者完成了協議的解析
- WorkerMan支持各種協議,目前內置了HTTP、WebSocket等協議。WorkerMan推薦開發者使用更簡單的自定義協議通訊
*由于非HTTP協議的應用,所以```header()````setcookie()````session\_start```等函數無法直接使用,需要使用WorkerMan提供的方法,具體參考高級應用-WebServer部分*
### 2、請求周期差異
- PHP在Web應用中一次請求過后會釋放所有的變量與資源
- WorkerMan開發的應用程序在第一次載入解析后便常駐內存,使得類的定義、全局對象、類的靜態成員不會釋放,便于后續重復利用
### 3、注意避免類和常量的重復定義
- 由于WorkerMan會緩存編譯后的PHP文件,所以要避免多次require/include相同的類或者常量的定義文件。建議使用require\_once/include\_once加載文件。
### 4、注意單例模式的鏈接資源的釋放
- 由于WorkerMan不會在每次請求后釋放全局對象及類的靜態成員,在數據庫等單例模式中,往往會將數據庫實例(內部包含了一個數據庫socket鏈接)保存在數據庫靜態成員中,使得WorkerMan在進程生命周期內都復用這個數據庫socket鏈接。需要注意的是當數據庫服務器發現某個鏈接在一定時間內沒有活動后可能會主動關閉socket鏈接,這時再次使用這個數據庫實例時會報錯,(錯誤信息類似mysql gone away)。WorkerMan提供了[數據庫類](315205),有斷開重連的功能,開發者可以直接使用。
### 5、注意不要使用exit、die出語句
- WorkerMan運行在PHP命令行模式下,當調用exit、die退出語句時,會導致當前進程退出。雖然子進程退出后會立刻重新創建一個的相同的子進程繼續服務,但是還是可能對業務產生影響。
## 二、需要了解的基本概念
### 1、TCP傳輸層協議
TCP是一種面向連接的、可靠的、基于IP的傳輸層協議。TCP傳輸層協議一個重要特點是TCP是基于數據流的,客戶端的請求會源源不斷的發送給服務端,服務端收到的數據可能不是一個完整的請求,也有可能是多個請求連在一起。這就需要我們在這源源不斷的數據流中區分每個請求的邊界。而應用層協議主要是為請求邊界定義一套規則,避免請求數據混亂。
### 2、應用層協議
應用層協議(application layer protocol)定義了運行在不同端系統上(客戶端、服務端)的應用程序進程如何相互傳遞報文,例如HTTP、WebSocket都屬于應用層協議。例如一個簡單的應用層次協議可以如下```{"module":"user","action":"getInfo","uid":456}\\n"```。此協議是以```"\\n"```(注意這里```"\\n"```代表的是回車)標記請求結束,消息體是字符串。
### 3、短連接
短連接是指通訊雙方有數據交互時,就建立一個連接,數據發送完成后,則斷開此連接,即每次連接只完成一項業務的發送。像WEB網站的HTTP服務一般都用短鏈接。
*短鏈接應用程序開發可以參考基本開發流程一章*
### 4、長連接
長連接,指在一個連接上可以連續發送多個數據包。
注意:長鏈接應用必須加[心跳](315282),否則鏈接可能由于長時間不活躍而被路由節點防火墻斷開。
長連接多用于操作頻繁,點對點的通訊的情況。每個TCP連接都需要三步握手,這需要時間,如果每個操作都是先連接,再操作的話那么處理速度會降低很多。所以長連接在每個操作完后都不斷開,下次處理時直接發送數據包就OK了,不用建立TCP連接。例如:數據庫的連接用長連接,如果用短連接頻繁的通信會造成socket錯誤,而且頻繁的socket 創建也是對資源的浪費。
*當需要主動向客戶端推送數據時,例如聊天類、即時游戲類、手機推送等應用需要長連接。*
*長鏈接應用程序開發可以參考Gateway/Worker開發流程*
### 5、平滑重啟
一般的重啟的過程是把所有進程全部停止后,再開始創建全新的服務進程。在這個過程中會有一個短暫的時間內是沒有進程對外提供服務的,這就會導致服務暫時不可用,這在高并發時勢必會導致請求失敗。
而平滑重啟則不是一次性的停止所有進程,而是一個進程一個進程的停止,每停止一個進程后馬上重新創建一個新的進程頂替,直到所有舊的進程都被替換為止。
平滑重啟WorkerMan可以使用 ```php your\_file.php reload```命令,能夠做到在不影響服務質量的情況下更新應用程序。
**注意:只有在on{...}回調中載入的文件平滑重啟后才會自動更新,啟動腳本中直接載入的文件或者寫死的代碼運行reload不會自動更新。**
## 三、區分主進程和子進程
有必要注意下代碼是運行在主進程還是子進程,一般來說在```Worker::runAll();```調用前運行的代碼都是在主進程運行的,onXXX回調運行的代碼都屬于子進程。注意寫在```Worker::runAll();```后面的代碼永遠不會被執行。
例如下面的代碼
```php
require_once __DIR__ . '/Workerman/Autoloader.php';
use Workerman\Worker;
// 運行在主進程
$tcp_worker = new Worker("tcp://0.0.0.0:2347");
// 賦值過程運行在主進程
$tcp_worker->onMessage = function($connection, $data)
{
// 這部分運行在子進程
$connection->send('hello ' . $data);
};
Worker::runAll();
```
**注意:**不要在主進程中初始化數據庫、memcache、redis等連接資源,因為主進程初始化的連接可能會被子進程自動繼承(尤其是使用單例的時候),但是這個鏈接資源在子進程是無法使用的,因為服務端通過這個連接返回的數據在多個進程上都可讀,會導致數據錯亂。
- 序言
- 原理
- 開發必讀
- 入門指引
- 特性
- 簡單的開發示例
- 安裝
- 環境要求
- 下載安裝
- 啟動停止
- 開發流程
- 開發前必讀
- 目錄結構
- 開發規范
- 基本流程
- 通訊協議
- 通訊協議作用
- 定制通訊協議
- 一些例子
- Worker類
- 構造函數
- 屬性
- id
- count
- name
- protocol
- transport
- reusePort
- connections
- stdoutFile
- pidFile
- logFile
- user
- reloadable
- daemonize
- globalEvent
- 回調屬性
- onWorkerStart
- onWorkerReload
- onConnect
- onMessage
- onClose
- onBufferFull
- onBufferDrain
- onError
- 接口
- runAll
- stopAll
- listen
- TcpConnection類
- 屬性
- id
- protocol
- worker
- maxSendBufferSize
- defaultMaxSendBufferSize
- maxPackageSize
- 回調屬性
- onMessage
- onClose
- onBufferFull
- onBufferDrain
- onError
- 接口
- send
- getRemoteIp
- getRemotePort
- close
- destroy
- pauseRecv
- resumeRecv
- pipe
- AsyncTcpConnection類
- 構造函數
- connect
- reconnect
- transport
- Timer定時器類
- add
- del
- 定時器注意事項
- WebServer
- 調試
- 基本調試
- 查看運行狀態
- 網絡抓包
- 跟蹤系統調用
- 常用組件
- GlobalData數據共享組件
- GlobalDataServer
- GlobalDataClient
- Channel分布式通訊組件
- ChannelServer
- channelClient
- 例子-集群推送
- 例子-分組發送
- FileMonitor文件監控組件
- MySQL組件
- workerman/mysql
- swoole/mysql(異步)
- redis組件
- swoole/redis
- 異步http組件
- swoole/http-client
- 異步消息隊列組件
- react/zmq
- react/stomp
- 異步dns組件
- swoole/dns
- 常見問題
- 心跳
- 客戶端鏈接失敗原因
- 是否支持多線程
- 與其它框架整合
- 運行多個workerman
- 支持哪些協議
- 如何設置進程數
- 查看客戶端連接數
- 對象和資源的持久化
- 例子無法工作
- 啟動失敗
- 停止失敗
- 支持多少并發
- 更改代碼不生效
- 向指定客戶端發送數據
- 如何主動推送消息
- 在其它項目中推送
- 如何實現異步任務
- status里send_fail的原因
- Windows下開發Linux下部署
- 是否支持socket.io
- 終端關閉導致workerman關閉
- 與nginx apache的關系
- 禁用函數檢查
- 平滑重啟原理
- 為Flash開843端口
- 如何廣播數據
- 如何建立udp服務
- 監聽ipv6
- 關閉未認證的鏈接
- 傳輸加密-ssl/tsl
- 創建wss服務
- 創建https服務
- workerman作為客戶端
- 作為ws/wss客戶端
- PHP的幾種回調寫法
- 附錄
- Linux內核調優
- 壓力測試
- 安裝擴展
- websocket協議
- ws協議
- text協議
- frame協議
- 不支持的函數/特性
- 版權信息