# Swoole協程之旅-前篇
[TOC]
### 寫在最前
??Swoole協程經歷了幾個里程碑,我們需要在前進的道路上時常總結與回顧自己的發展歷程,所謂溫故而知新,本系列文章將分為協程之旅前、中、后三篇。
1. 前篇主要介紹協程的概念和Swoole幾個版本協程實現的主要技術;
2. 中篇主要深入Zend分析PHP部分的原理和實現;
3. 后篇主要補充和分析協程4.x的實現。
### 軟文正式開始~
??協程是什么?
??概念其實很早就出現了,摘wiki一段:*According to Donald Knuth, the term coroutine was coined by Melvin Conway in 1958, after he applied it to construction of an assembly program.The first published explanation of the coroutine appeared later, in 1963.*協程要比c語言的歷史還要悠久,究其概念,協程是子程序的一種, 可以通過yield的方式轉移程序控制權,協程之間不是調用者與被調用者的關系,而是彼此對稱、平等的。協程完全有用戶態程序控制,所以也被成為用戶態的線程。協程由用戶以非搶占的方式調度,而不是操作系統。正因為如此,沒有系統調度上下文切換的開銷,協程有了輕量,高效,快速等特點。(大部分為非搶占式,但是,比如golang在1.4也加入了搶占式調度,其中一個協程發生死循環,不至于其他協程被餓死。需要在必要的時刻讓出CPU,Swoole后續也會增加這個特性)。
??協程近幾年如此火爆,很大一部分原因歸功與golang在中國的流行和快速發展,受到很多開發的喜愛。目前支持協程的語言有很多,例如: golang、lua、python、c#、javascript等。大家也可以用很短的代碼用c/c++擼出協程的模型。當然PHP也有自己的協程實現,也就是生成器,我們這里不展開討論。
### Swoole1.x
?? Swoole最初以高性能網絡通訊引擎的姿態進入大家視線,Swoole1.x的編碼主要是異步回調的方式,雖然性能非常高效,但很多開發都會發現,隨著項目工程的復雜程度增加,以異步回調的方式寫業務代碼是和人類正常思維相悖的,尤其是回調嵌套多層的時候,不僅開發維護成本指數級上升,而且出錯的幾率也大幅增加。大家理想的編碼方式是:同步編碼得到異步非阻塞的效果。所以Swoole很早的時候就開始了協程的探索。
??最初的協程版本是基于PHP生成器Generators\\Yield的方式實現的,可以參考PHP大神Nikita的早期博客的關于[協程](https://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html "協程")介紹。PHP和Swoole的事件驅動的結合可以參考騰訊出團隊開源的[TSF](https://github.com/Tencent/tsf "TSF")框架,我們也在很多生產項目中使用了該框架,確實讓大家感受到了,以同步編程的方式寫異步代碼的快感,然而,現實總是很殘酷,這種方式有幾個致命的缺點:
* 所有主動讓出的邏輯都需要yield關鍵字。這會給程序員帶來極大的概率犯錯,導致大家對協程的理解轉移到了對Generators語法的原理的理解。
* 由于語法無法兼容老的項目,改造老的項目工程復雜度巨大,成本太高。
這樣使得無論新老項目,使用都無法得心應手。
### Swoole2.x
?? 2.x之后的協程都是基于內核原生的協程,無需yield關鍵字。2.0的版本是一個非常重要的里程碑,實現了php的棧管理,深入zend內核在協程創建,切換以及結束的時候操作PHP棧。在Swoole的文檔中也介紹了很多關于每個版本實現的細節,我們這篇文章只對每個版本的協程驅動技術做簡單介紹。**原生協程都有對php棧的管理,后續我們會單獨拿一片文章來深入分析PHP棧的管理和切換。**
?? 2.x主要使用了setjmp/longjmp的方式實現協程,很多C項目主要采用這種方式實現try-catch-finally,大家也可以參考Zend內核的用法。setjmp的首次調用返回值是0,longjmp跳轉時,setjmp的返回值是傳給longjmp的value。 setjmp/longjmp由于只有控制流跳轉的能力。雖然可以還原PC和棧指針,但是無法還原棧幀,因此會出現很多問題。比如longjmp的時候,setjmp的作用域已經退出,當時的棧幀已經銷毀。這時就會出現未定義行為。假設有這樣一個調用鏈:
> func0() -> func1() -> ... -> funcN()
只有在func{i}()中setjmp,在func{i+k}()中longjmp的情況下,程序的行為才是可預期的。
### Swoole3.x
3.x是生命周期很短的一個版本,主要借鑒了[fiber-ext](https://github.com/fiberphp/fiber-ext "fiber-ext")項目,使用了PHP7的VM interrupts機制,該機制可以在vm中設置標記位,在執行一些指令的時候(例如:跳轉和函數調用等)檢查標記位,如果命中就可以執行相應的hook函數來切換vm的棧,進而實現協程。
#### Swoole4.x
4.x協程我們放在最后。
協程之旅前篇結束,下一篇文章我們將深入Zend分析Swoole原生協程PHP部分的實現。
- 序言
- 入門指引
- 環境依賴
- 編譯安裝
- 編譯參數
- 常見錯誤
- Cygwin
- Linux二進制包
- 快速起步
- 創建TCP服務器
- 創建UDP服務器
- 創建Web服務器
- 創建WebSocket服務器
- 設置定時器
- 執行異步任務
- 創建同步TCP客戶端
- 創建異步TCP客戶端
- 網絡通信協議設計
- 使用異步客戶端
- 多進程共享數據
- 使用協程客戶端
- 協程:并發 shell_exec
- 協程:Go + Chan + Defer
- 協程:實現 Go 語言風格的 defer
- 協程:實現 sync.WaitGroup 功能
- 編程須知
- sleep/usleep的影響
- exit/die函數的影響
- while循環的影響
- stat緩存清理
- mt_rand隨機數
- 進程隔離
- 版本更新記錄
- 4.3.1
- 4.3.0 [大版本]
- 4.2.13
- 4.2.12
- 4.2.11
- 4.2.10
- 4.2.9
- 4.2.8
- 4.2.7
- 4.2.0
- 4.1.0
- 4.0.1
- 4.0.0
- 向下不兼容改動
- 新特性使用
- 4.3.0 在 Process 中使用協程
- 4.3.0 延時事件機制改進
- 2.1.2 進程池模塊的使用
- 1.9.24 調度支持 Stream 模式
- 1.9.24 異步客戶端自動解析域名
- 1.9.17 支持異步安全重啟特性
- 1.9.14 使用異步客戶端超時機制
- 1.8.0 使用內置Http異步客戶端
- 1.7.16 使用迭代器遍歷Server所有連接
- 1.7.5 在Server中使用swoole_table
- 1.7.5 swoole_client支持sendfile接口
- 1.7.4 SSL隧道加密TCP-Server
- 1.7.4 task進程中使用毫秒定時器
- 1.7.3 固定包頭+包體協議自動分包
- 1.7.3 onTask直接return取代finish函數
- 1.7.2 swoole_process多進程模塊的使用
- 1.7.2 task進程使用消息隊列
- 項目路線圖
- php.ini選項
- 內核參數調整
- 開發者列表
- 衍生開源項目
- 框架
- 工具
- 分布式
- 通信協議
- 用戶與案例
- 物聯網項目
- 網絡游戲
- 騰訊(Tencent)
- 百度(Baidu.com)
- 閱文集團
- BiliBili(嗶哩嗶哩)
- 車輪互聯(chelun.com)
- (撈月狗) 游戲社區
- 儒博(roobo.com)
- 提交錯誤報告
- 常見問題
- 升級swoole版本的常見問題
- 生成可分發的二進制swoole版本
- 在phpinfo中有在php -m中沒有
- Connection refused是怎么回事
- Resource temporarily unavailable [11]
- Cannot assign requested address [99]
- swoole與node.js相比有哪些優勢
- swoole與golang相比有哪些優勢
- pcre.h: No such file or directory
- undefined symbol: __sync_bool_compare_and_swap_4
- 學習Swoole需要掌握哪些基礎知識
- 同步阻塞與異步非阻塞適用場景
- PHP7環境下出現zend_mm_heap corrupted
- Swoole 項目起源和名字由來
- '__builtin_saddl_overflow' was not declared in this scope
- Server
- 函數列表
- Server::__construct
- Server->set
- Server->on
- Server->addListener
- Server->addProcess
- Server->listen
- Server->start
- Server->reload
- Server->stop
- Server->shutdown
- Server->tick
- Server->after
- Server->defer
- Server->clearTimer
- Server->close
- Server->send
- Server->sendfile
- Server->sendto
- Server->sendwait
- Server->sendMessage
- Server->exist
- Server->pause
- Server->resume
- Server->getClientInfo
- Server->getClientList
- Server->bind
- Server->stats
- Server->task
- Server->taskwait
- Server->taskWaitMulti
- Server->taskCo
- Server->finish
- Server->heartbeat
- Server->getLastError
- Server->getSocket
- Server->protect
- Server->confirm
- 屬性列表
- Server::$setting
- Server::$master_pid
- Server::$manager_pid
- Server::$worker_id
- Server::$worker_pid
- Server::$taskworker
- Server::$connections
- Server::$ports
- 配置選項
- reactor_num
- worker_num
- max_request
- max_conn (max_connection)
- task_worker_num
- task_ipc_mode
- task_max_request
- task_tmpdir
- dispatch_mode
- dispatch_func
- message_queue_key
- daemonize
- backlog
- log_file
- log_level
- heartbeat_check_interval
- heartbeat_idle_time
- open_eof_check
- open_eof_split
- package_eof
- open_length_check
- package_length_type
- package_length_func
- package_max_length
- open_cpu_affinity
- cpu_affinity_ignore
- open_tcp_nodelay
- tcp_defer_accept
- ssl_cert_file
- ssl_method
- ssl_ciphers
- user
- group
- chroot
- pid_file
- pipe_buffer_size
- buffer_output_size
- socket_buffer_size
- enable_unsafe_event
- discard_timeout_request
- enable_reuse_port
- enable_delay_receive
- open_http_protocol
- open_http2_protocol
- open_websocket_protocol
- open_mqtt_protocol
- open_websocket_close_frame
- reload_async
- tcp_fastopen
- request_slowlog_file
- enable_coroutine
- max_coroutine
- task_enable_coroutine
- ssl_verify_peer
- 監聽端口
- 可選參數
- 可選回調
- 連接迭代器
- 預定義常量
- 事件回調函數
- onStart
- onShutdown
- onWorkerStart
- onWorkerStop
- onWorkerExit
- onConnect
- onReceive
- onPacket
- onClose
- onBufferFull
- onBufferEmpty
- onTask
- onFinish
- onPipeMessage
- onWorkerError
- onManagerStart
- onManagerStop
- 高級特性
- 改變Worker進程的用戶/組
- 回調函數中的 reactor_id 和 fd
- Length_Check 和 EOF_Check 的使用
- Worker與Reactor通信模式
- TCP-Keepalive死連接檢測
- TCP服務器心跳維持方案
- 多端口監聽的使用
- 捕獲Server運行期致命錯誤
- Server內存管理機制
- Server的兩種運行模式介紹
- Server中對象的4層生命周期
- 在worker進程內監聽一個Server端口
- 在php-fpm/apache中使用task功能
- 常見問題
- 為什么不要send完后立即close
- 如何在回調函數中訪問外部的變量
- 是否可以共用1個redis或mysql連接
- 關于onConnect/onReceive/onClose順序
- 4種PHP回調函數風格
- 不同的Server程序實例間如何通信
- 錯誤信息:ERROR (9006)
- eventLoop has already been created. unable to create swoole_server
- 壓力測試
- 并發10萬TCP連接的測試
- PHP7+Swoole/Nginx/Golang性能對比
- 全球Web框架權威性能測試 Techempower Web Framework Benchmarks
- Coroutine
- Coroutine
- Coroutine::set
- Coroutine::stats
- Coroutine::create
- Coroutine::exist
- Coroutine::getCid
- Coroutine::getPcid
- Coroutine::getContext
- Coroutine::defer
- Coroutine::list
- Coroutine::getBackTrace
- Coroutine::yield
- Coroutine::resume
- Coroutine::fread
- Coroutine::fgets
- Coroutine::fwrite
- Coroutine::sleep
- Coroutine::gethostbyname
- Coroutine::getaddrinfo
- Coroutine::exec
- Coroutine::readFile
- Coroutine::writeFile
- Coroutine::statvfs
- Coroutine\Channel
- Coroutine\Channel->__construct
- Coroutine\Channel->push
- Coroutine\Channel->pop
- Coroutine\Channel->stats
- Coroutine\Channel->close
- Coroutine\Channel->length
- Coroutine\Channel->isEmpty
- Coroutine\Channel->isFull
- Coroutine\Channel->$capacity
- Coroutine\Channel->$errCode
- Coroutine\Client
- Coroutine\Client->connect
- Coroutine\Client->send
- Coroutine\Client->recv
- Coroutine\Client->close
- Coroutine\Client->peek
- Coroutine\Http\Client
- 屬性列表
- Coroutine\Http\Client->get
- Coroutine\Http\Client->post
- Coroutine\Http\Client->upgrade
- Coroutine\Http\Client->push
- Coroutine\Http\Client->recv
- Coroutine\Http\Client->addFile
- Coroutine\Http\Client->addData
- Coroutine\Http\Client->download
- Coroutine\Http2\Client
- Coroutine\Http2\Client->__construct
- Coroutine\Http2\Client->set
- Coroutine\Http2\Client->connect
- Coroutine\Http2\Client->send
- Coroutine\Http2\Client->write
- Coroutine\Http2\Client->recv
- Coroutine\Http2\Client->close
- Coroutine\Redis
- Coroutine\Redis::__construct
- Coroutine\Redis::setOptions
- 屬性列表
- 事務模式
- 訂閱模式
- Coroutine\Socket
- Coroutine\Socket::__construct
- Coroutine\Socket->bind
- Coroutine\Socket->listen
- Coroutine\Socket->accept
- Coroutine\Socket->connect
- Coroutine\Socket->send
- Coroutine\Socket->sendAll
- Coroutine\Socket->recv
- Coroutine\Socket->recvAll
- Coroutine\Socket->sendto
- Coroutine\Socket->recvfrom
- Coroutine\Socket->getsockname
- Coroutine\Socket->getpeername
- Coroutine\Socket->close
- Coroutine\MySQL
- 屬性列表
- Coroutine\MySQL->connect
- Coroutine\MySQL->query
- Coroutine\MySQL->prepare
- Coroutine\MySQL->escape
- Coroutine\MySQL->begin
- Coroutine\MySQL->commit
- Coroutine\MySQL->rollback
- Coroutine\MySQL\Statement->execute
- Coroutine\MySQL\Statement->fetch
- Coroutine\MySQL\Statement->fetchAll
- Coroutine\MySQL\Statement->nextResult
- Coroutine\PostgreSQL
- Coroutine\PostgreSQL->connect
- Coroutine\PostgreSQL->query
- Coroutine\PostgreSQL->fetchAll
- Coroutine\PostgreSQL->affectedRows
- Coroutine\PostgreSQL->numRows
- Coroutine\PostgreSQL->fetchObject
- Coroutine\PostgreSQL->fetchAssoc
- Coroutine\PostgreSQL->fetchArray
- Coroutine\PostgreSQL->fetchRow
- Coroutine\PostgreSQL->metaData
- Coroutine\PostgreSQL->prepare
- Server
- 并發調用
- setDefer 機制
- 子協程+通道
- 實現原理
- 協程與線程
- 發送數據協程調度
- 協程內存開銷
- 4.0 協程實現原理
- 協程客戶端超時規則
- 協程執行流程
- 常見問題
- 運行中出現 Fatal error: Maximum function nesting level of '1000' reached, aborting!
- 為什么只能在回調函數中使用協程客戶端
- 支持協程的回調方法列表
- 錯誤信息: XXXX client has already been bound to another coroutine
- Swoole4 協程與 PHP 的 Yield/Generator 協程有什么區別
- Swoole4 協程與 Go 協程有哪些區別
- 編程須知
- 在多個協程間共用同一個協程客戶端
- 禁止使用協程 API 的場景(2.x 版本)
- 使用類靜態變量/全局變量保存上下文
- 退出協程
- 異常處理
- 擴展組件
- MongoDB
- 編程調試
- Runtime
- 文件操作
- 睡眠函數
- 開關選項
- 嚴格模式
- Timer
- swoole_timer_tick
- swoole_timer_after
- swoole_timer_clear
- Memory
- Lock
- swoole_lock->__construct
- swoole_lock->lock
- swoole_lock->trylock
- swoole_lock->unlock
- swoole_lock->lock_read
- swoole_lock->trylock_read
- swoole_lock->lockwait
- Buffer
- swoole_buffer->__construct
- swoole_buffer->append
- swoole_buffer->substr
- swoole_buffer->clear
- swoole_buffer->expand
- swoole_buffer->write
- swoole_buffer->read
- swoole_buffer->recycle
- Table
- Table->__construct
- Table->column
- Table->create
- Table->set
- Table->incr
- Table->decr
- Table->get
- Table->exist
- Table->count
- Table->del
- Atomic
- swoole_atomic->__construct
- swoole_atomic->add
- swoole_atomic->sub
- swoole_atomic->get
- swoole_atomic->set
- swoole_atomic->cmpset
- swoole_atomic->wait
- swoole_atomic->wakeup
- mmap
- swoole_mmap::open
- Channel
- Channel->__construct
- Channel->push
- Channel->pop
- Channel->stats
- Serialize
- swoole_serialize::pack
- swoole_serialize::unpack
- Http\Server
- Http\Server
- Http\Server->on
- Http\Server->start
- Http\Request
- Http\Request->$header
- Http\Request->$server
- Http\Request->$get
- Http\Request->$post
- Http\Request->$cookie
- Http\Request->$files
- Http\Request->rawContent
- Http\Request->getData
- Http\Response
- Http\Response->header
- Http\Response->cookie
- Http\Response->status
- Http\Response->gzip
- Http\Response->redirect
- Http\Response->write
- Http\Response->sendfile
- Http\Response->end
- Http\Response->detach
- Http\Response::create
- 配置選項
- upload_tmp_dir
- http_parse_post
- document_root
- http_compression
- 常見問題
- CURL發送POST請求服務器端超時
- 使用Chrome訪問服務器會產生2次請求
- GET/POST請求的最大尺寸
- WebSocket\Server
- 回調函數
- onHandShake
- onOpen
- onMessage
- 函數列表
- WebSocket\Server->push
- WebSocket\Server->exist
- WebSocket\Server::pack
- WebSocket\Server::unpack
- WebSocket\Server->disconnect
- WebSocket\Server->isEstablished
- 預定義常量
- 常見問題
- 配置選項
- WebSocket\Frame
- Redis\Server
- 方法
- Redis\Server->setHandler
- Redis\Server::format
- 常量
- Process
- Process::__construct
- Process->start
- Process->name
- Process->exec
- Process->write
- Process->read
- Process->setTimeout
- Process->setBlocking
- Process->useQueue
- Process->statQueue
- Process->freeQueue
- Process->push
- Process->pop
- Process->close
- Process->exit
- Process::kill
- Process::wait
- Process::daemon
- Process::signal
- Process::alarm
- Process::setAffinity
- Process::exportSocket
- Process\Pool
- Process\Pool::__construct
- Process\Pool->on
- Process\Pool->listen
- Process\Pool->write
- Process\Pool->start
- Process\Pool->getProcess
- Client
- 方法列表
- swoole_client::__construct
- swoole_client->set
- swoole_client->on
- swoole_client->connect
- swoole_client->isConnected
- swoole_client->getSocket
- swoole_client->getSockName
- swoole_client->getPeerName
- swoole_client->getPeerCert
- swoole_client->send
- swoole_client->sendto
- swoole_client->sendfile
- swoole_client->recv
- swoole_client->close
- swoole_client->sleep
- swoole_client->wakeup
- swoole_client->enableSSL
- 回調函數
- onConnect
- onError
- onReceive
- onClose
- onBufferFull
- onBufferEmpty
- 屬性列表
- swoole_client->errCode
- swoole_client->sock
- swoole_client->reuse
- 并行
- swoole_client_select
- TCP客戶端異步連接
- SWOOLE_KEEP建立TCP長連接
- 常量
- 配置選項
- ssl_verify_peer
- ssl_host_name
- ssl_cafile
- ssl_capath
- package_length_func
- http_proxy_host
- 常見問題
- Event
- swoole_event_add
- swoole_event_set
- swoole_event_isset
- swoole_event_write
- swoole_event_del
- swoole_event_exit
- swoole_event_defer
- swoole_event_cycle
- swoole_event_wait
- swoole_event_dispatch
- 常見問題
- epoll_wait 偶爾會用很長時間
- 異步回調
- 異步文件系統IO
- swoole_async_readfile
- swoole_async_writefile
- swoole_async_read
- swoole_async_write
- swoole_async_dns_lookup
- swoole_async::exec
- 異步MySQL客戶端
- swoole_mysql->__construct
- swoole_mysql->on
- swoole_mysql->connect
- swoole_mysql->escape
- swoole_mysql->query
- swoole_mysql->begin
- swoole_mysql->commit
- swoole_mysql->rollback
- swoole_mysql->close
- 異步Redis客戶端
- swoole_redis->__construct
- swoole_redis->on
- swoole_redis->connect
- swoole_redis->__call
- swoole_redis->close
- 異步Http/WebSocket客戶端
- swoole_http_client->__construct
- swoole_http_client->set
- swoole_http_client->setMethod
- swoole_http_client->setHeaders
- swoole_http_client->setCookies
- swoole_http_client->setData
- swoole_http_client->addFile
- swoole_http_client->get
- swoole_http_client->post
- swoole_http_client->upgrade
- swoole_http_client->push
- swoole_http_client->execute
- swoole_http_client->download
- swoole_http_client->close
- 異步Http2.0客戶端
- swoole_http2_client->__construct
- swoole_http2_client->get
- swoole_http2_client->post
- swoole_http2_client->setHeaders
- swoole_http2_client->setCookies
- 高級
- Swoole的實現
- Reactor線程
- Manager進程
- Worker進程
- Reactor、Worker、TaskWorker的關系
- Task/Finish特性的用途
- 在php-fpm或apache中使用swoole
- Swoole異步與同步的選擇
- TCP/UDP壓測工具
- swoole服務器如何做到無人值守100%可用
- MySQL的連接池、異步、斷線重連
- PHP中哪些函數是同步阻塞的
- 守護進程程序常用數據結構
- 隊列(Queue)
- 堆(Heap)
- 定長數組(SplFixedArray)
- 使用jemalloc優化swoole內存分配性能
- C開發者如何使用Swoole
- C++開發者如何使用Swoole
- 使用systemd管理swoole服務
- 網卡中斷設置
- 將Swoole靜態編譯內嵌到PHP
- 異步回調程序內存管理
- 日志等級控制
- 使用 asan 內存檢測
- Windows編譯
- Swoole協程之旅-前篇
- Swoole協程之旅-中篇
- Swoole協程之旅-后篇
- 協程CPU密集場景調度實現
- 其他
- 函數列表
- swoole_set_process_name
- swoole_version
- swoole_strerror
- swoole_errno
- swoole_get_local_ip
- swoole_clear_dns_cache
- swoole_get_local_mac
- swoole_cpu_num
- swoole_last_error
- Swoole社區
- Swoole技術會議
- 工作組(Working Groups)
- 參與開源項目指引
- 捐贈Swoole項目
- 加入Swoole開發組
- 非協程特性獨立擴展 (swoole_async)
- 附錄:Linux信號列表
- 附錄:Linux錯誤碼(errno)列表
- 附錄:Swoole錯誤碼列表
- 附錄:TCP連接的狀態
- 附錄:tcpdump抓包工具的使用
- 附錄:strace工具的使用
- 附錄:gdb工具的使用
- 附錄:lsof工具的使用
- 附錄:perf工具的使用
- 附錄:編譯PHP擴展的相關工具
- 備用:已移除的歷史特性
- swoole_server->handler
- task_worker_max
- swoole_server->addtimer
- swoole_server->deltimer
- onTimer
- swoole_timer_add
- swoole_timer_del
- swoole_get_mysqli_sock
- swoole_mysql_query
- onMasterConnect
- onMasterClose
- Nginx/Golang/Swoole/Node.js的性能對比
- Coroutine::call_user_func
- Coroutine::call_user_func_array
- Coroutine\Channel::select
- task_async
- 歷史:版本更新記錄(1.x)
- 1.10.3
- 1.10.2
- 1.10.1
- 1.10.0
- 1.9.23
- 1.9.22
- 1.9.19
- 1.9.18
- 1.9.17
- 1.9.16
- 1.9.15
- 1.9.14
- 1.9.12
- 1.9.11
- 1.9.9
- 1.9.7
- 1.9.6
- 1.9.5
- 1.9.4
- 1.9.3
- 1.9.2
- 1.9.1
- 1.9.0
- 1.8.13
- 1.8.12
- 1.8.11
- 1.8.10
- 1.8.9
- 1.8.8
- 1.8.7
- 1.8.6
- 1.8.5
- 1.8.4
- 1.8.3
- 1.8.2
- 1.8.1
- 1.8.0
- 1.7.22
- 1.7.21
- 1.7.20
- 1.7.19
- 1.7.18
- 1.7.17
- 1.7.16
- 1.7.15
- 1.7.14
- 1.7.13
- 1.7.12
- 1.7.11
- 1.7.10
- 1.7.9
- 1.7.8
- 1.7.7
- 1.7.6
- 1.7.5
- v1.5
- v1.6
- v1.7
- 歷史:版本更新記錄(2.x)
- 2.0.1-Alpha
- 2.0.5
- 2.0.9
- 1.9.21
- 2.0.10
- 2.0.11
- 2.0.12
- 2.0.13
- 2.1.1
- 2.1.2
- 2.2.0
- 3.0.0
- 歷史:版本更新記錄(4.x)
- 4.0.3
- 4.0.2
- 4.0.4
- 4.1.1
- 4.1.2
- 4.2.1
- 4.2.2
- 4.2.3
- 4.2.4
- 4.2.5
- 4.2.6
- 4.2.7
- 4.2.9
- 4.2.8
- 社區文檔版權申明
- 社區文檔編輯條例