Um sistema simultâneo Go como bibliotecas de rede/http para Swift que funciona em Linux e Mac.
A razão pela qual comecei este projeto é porque senti que era muito difícil lidar com io assíncrono com Swift no projeto chamado Slimane que eu havia feito anteriormente. No paradigma assíncrono em Swift, muitas vezes precisamos usar bem a lista de captura para fechamentos e às vezes reter o objeto (conexão etc.) para evitar a liberação pelo ARC. Então pensei que o mecanismo simultâneo/paralelo e síncrono do Go é um modelo adequado para o estágio atual do Swift (se você quiser escrever o servidor na máquina MultiCore). Porque podemos facilmente fazer operações assíncronas sem cadeias de retorno de chamada, podemos usar Full Cores com a sintaxe simples e fácil de compartilhar a memória via Canal entre um Thread e um Thread.
(Prorsum não é Goroutine. Ele não possui Corotuines e a troca de contexto é feita no lado do sistema operacional. Ele apenas possui um mecanismo de memória compartilhada thread-safe (funciona no GCD) que é fortemente inspirado em Go.)
A arquitetura do servidor HTTP do Prorsum é mestre orientado a eventos + manipulador de solicitações multithreading. Em um DispatchQueue, você pode escrever E/S assíncrona com sintaxe síncrona com go()
+ Channel<Element>
.
Códigos fáceis de fazer resolvem C10K sem retornos de chamada.
+-----------------+
|-- | Request Handler |
| +-----------------+
+--------+ | +-----------------+
----- TCP ---- | master |---Dispatch Queue---|-- | Request Handler |
+--------+ | +-----------------+
| +-----------------+
|-- | Request Handler |
+-----------------+
Atualmente o Prorsum suporta apenas SPM.
import PackageDescription
let package = Package (
name : " MyApp " ,
dependencies : [
. Package ( url : " https://github.com/noppoMan/Prorsum.git " , majorVersion : 0 , minor : 1 )
]
)
Ainda não suportado
Ainda não suportado
go
go é um alias de DispatchQueue().async { }
func asyncTask ( ) {
print ( Thread . current )
}
go ( asyncTask ( ) )
go {
print ( Thread . current )
}
gomain {
print ( Thread . current ) // back to the main thread
}
WaitGroup
Um WaitGroup aguarda a conclusão de uma coleção de operações GCD. A operação GCD principal chama Add para definir o número de operações GCD a serem aguardadas. Em seguida, cada uma das operações do GCD é executada e chama Done quando concluída. Ao mesmo tempo, Wait pode ser usado para bloquear até que todas as operações do GCD sejam concluídas.
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>
Canais são os tubos que conectam a operação simultânea. Você pode enviar valores para canais de uma operação GCD e receber esses valores em outra operação 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
A instrução select permite que um BlockOperation
aguarde várias operações de comunicação.
let magicCh = Channel < String > . make ( capacity : 1 )
go {
try ! magicCh . send ( " Obliviate " )
}
select {
when ( magicCh ) {
print ( $0 )
}
otherwise {
print ( " otherwise " )
}
}
forSelect
Geralmente você precisa envolver o select dentro de um loop while. Para facilitar o trabalho com esse padrão Você pode usar forSelect
. forSelect fará um loop até que done()
seja chamado.
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
Aqui está um exemplo de servidor 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 é lançado sob a licença do MIT. Consulte LICENÇA para obter detalhes.