類似 Go 的並發系統 + Swift 的網路/http 函式庫,可在 Linux 和 Mac 上運作。
之所以開始這個項目,是因為我覺得在我之前做的一個名為 Slimane 的項目中,用 Swift 處理非同步 io 是非常困難的。在 Swift 的非同步範例中,我們經常需要很好地使用捕獲清單來進行閉包,有時還需要保留物件(連接等)以避免被 ARC 釋放。然後我認為Go的並發/並行和同步機制是適合Swift現階段的模型(如果你想在多核心機器上編寫Server)。因為我們可以在沒有回調鏈的情況下輕鬆進行非同步操作,可以使用簡單語法的完整核心,並且可以輕鬆透過線程與線程之間的 Channel 共享記憶體。
(Prorsum 不是 Goroutine。它沒有協程,而且上下文切換是在作業系統端完成的。它只是具有線程安全共享記憶體機制(它在 GCD 上工作),這在很大程度上受到 Go 的啟發。)
Prorsum 的 HTTP 伺服器架構是事件驅動主機 + 多執行緒請求處理程序。在 DispatchQueue 中,您可以使用go()
+ Channel<Element>
使用同步語法編寫非同步 I/O。
輕鬆讓程式碼解決 C10K 問題,無需回呼。
+-----------------+
|-- | Request Handler |
| +-----------------+
+--------+ | +-----------------+
----- TCP ---- | master |---Dispatch Queue---|-- | Request Handler |
+--------+ | +-----------------+
| +-----------------+
|-- | Request Handler |
+-----------------+
目前 Prorsum 僅支援 SPM。
import PackageDescription
let package = Package (
name : " MyApp " ,
dependencies : [
. Package ( url : " https://github.com/noppoMan/Prorsum.git " , majorVersion : 0 , minor : 1 )
]
)
尚不支持
尚不支持
go
go 是DispatchQueue().async { }
的別名
func asyncTask ( ) {
print ( Thread . current )
}
go ( asyncTask ( ) )
go {
print ( Thread . current )
}
gomain {
print ( Thread . current ) // back to the main thread
}
WaitGroup
WaitGroup 等待一組 GCD 操作完成。主GCD操作呼叫Add來設定等待的GCD操作數。然後每個 GCD 操作都會運行並在完成時調用 Done。同時,Wait可以用來阻塞,直到所有GCD作業完成。
let wg = WaitGroup ( )
wg . add ( 1 )
go {
sleep ( 1 )
print ( " wg: 1 " )
wg . done ( )
}
wg . add ( 1 )
go {
sleep ( 1 )
print ( " wg: 2 " )
wg . done ( )
}
wg . wait ( ) // block unitle twice wg.done() is called.
print ( " wg done " )
Channel<Element>
通道是連接並發操作的管道。您可以將值從一個 GCD 操作傳送到通道,並將這些值接收到另一個 GCD 操作。
let ch = Channel < String > . make ( capacity : 1 )
func asyncSend ( ) {
try ! ch . send ( " Expecto patronum! " )
}
go ( asyncSend ( ) ) // => Expecto patronum!
go {
try ! ch . send ( " Accio! " )
}
try ! ch . receive ( ) // => Accio!
ch . close ( )
select
select 語句讓BlockOperation
等待多個通訊操作。
let magicCh = Channel < String > . make ( capacity : 1 )
go {
try ! magicCh . send ( " Obliviate " )
}
select {
when ( magicCh ) {
print ( $0 )
}
otherwise {
print ( " otherwise " )
}
}
forSelect
通常,您需要將 select 包裝在 while 循環內。為了更輕鬆地使用此模式,您可以使用forSelect
。 forSelect 將迴圈直到呼叫done()
為止。
let magicCh = Channel < String > . make ( capacity : 1 )
let doneCh = Channel < String > . make ( capacity : 1 )
go {
try ! magicCh . send ( " Crucio " )
try ! magicCh . send ( " Imperio " )
}
go {
try ! doneCh . send ( " Avada Kedavra! " )
}
forSelect { done in
when ( magicCh ) {
print ( $0 )
}
when ( doneCh ) {
done ( ) // break current loop
}
otherwise {
print ( " otherwise " )
}
}
import Prorsum
import Foundation
let server = try ! HTTPServer { ( request , writer ) in
do {
let response = Response (
headers : [ " Server " : " Prorsum Micro HTTP Server " ] ,
body : . buffer ( " hello " . data )
)
try writer . serialize ( response )
writer . close ( )
} catch {
fatalError ( " ( error ) " )
}
}
try ! server . bind ( host : " 0.0.0.0 " , port : 3000 )
print ( " Server listening at 0.0.0.0:3000 " )
try ! server . listen ( )
RunLoop . main . run ( ) //start run loop
import Prorsum
let url = URL ( string : " https://google.com " )
let client = try ! HTTPClient ( url : url! )
try ! client . open ( )
let response = try ! client . request ( )
print ( response )
// HTTP/1.1 200 OK
// Set-Cookie: NID=91=CPfJo7FsoC_HXmq7kLrs-e0DhR0lAaHcYc8GFxhazE5OXdc3uPvs22oz_UP3Bcd2mZDczDgtW80OrjC6JigVCGIhyhXSD7e1RA7rkinF3zxUNsDnAtagvs5pbZSjXuZE; expires=Sun, 04-Jun-2017 16:21:39 GMT; path=/; domain=.google.co.jp; HttpOnly
// Transfer-Encoding: chunked
// Accept-Ranges: none
// Date: Sat, 03 Dec 2016 16:21:39 GMT
// Content-Type: text/html; charset=Shift_JIS
// Expires: -1
// Alt-Svc: quic=":443"; ma=2592000; v="36,35,34"
// Cache-Control: private, max-age=0
// Server: gws
// X-XSS-Protection: 1; mode=block
// Vary: Accept-Encoding
// X-Frame-Options: SAMEORIGIN
// P3P: CP="This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info."
#if os(Linux)
import Glibc
#else
import Darwin . C
#endif
import Prorsum
import Foundation
let server = try ! TCPServer { clientStream in
while !clientStream . isClosed {
let bytes = try ! clientStream . read ( )
try ! clientStream . write ( bytes )
clientStream . close ( )
}
}
// setup client
go {
sleep ( 1 )
let client = try ! TCPSocket ( )
try ! client . connect ( host : " 0.0.0.0 " , port : 3000 )
while !client . isClosed {
try ! client . write ( Array ( " hello " . utf8 ) )
let bytes = try ! client . recv ( )
if !bytes . isEmpty {
print ( String ( bytes : bytes , encoding : . utf8 ) )
}
}
server . terminate ( ) // terminate server
}
try ! server . bind ( host : " 0.0.0.0 " , port : 3000 )
try ! server . listen ( ) //start run loop
RunLoop . main . run ( ) //start run loop
這是一個 Websocket Echo 伺服器範例。
#if os(Linux)
import Glibc
#else
import Darwin . C
#endif
import Foundation
import Prorsum
let server = try ! HTTPServer { ( request , writer ) in
do {
let response : Response
if request . isWebSocket {
response = try request . upgradeToWebSocket { request , websocket in
websocket . onText {
print ( " received: ( $0 ) " )
try ! websocket . send ( $0 )
}
}
} else {
response = Response (
headers : [ " Server " : " Prorsum Micro HTTP Server " ] ,
body : . buffer ( " hello " . data )
)
}
try writer . serialize ( response )
try response . upgradeConnection ? ( request , writer . stream )
writer . close ( )
} catch {
fatalError ( " ( error ) " )
}
}
try ! server . bind ( host : " 0.0.0.0 " , port : 8080 )
print ( " Server listening at 0.0.0.0:8080 " )
try ! server . listen ( )
RunLoop . main . run ( )
Prorsum 是在 MIT 許可下發布的。有關詳細信息,請參閱許可證。