Go like concurrent system + networking/http libraries สำหรับ Swift ที่ทำงานบน Linux และ Mac
เหตุผลที่ฉันเริ่มโปรเจ็กต์นี้เพราะฉันรู้สึกว่ามันยากมากที่จะจัดการ asynchronous io ด้วย Swift ในโปรเจ็กต์ชื่อ Slimane ที่ฉันเคยสร้างไว้ก่อนหน้านี้ ในกระบวนทัศน์แบบอะซิงโครนัสใน Swift เราจำเป็นต้องใช้รายการจับภาพบ่อยครั้งเพื่อการปิดและบางครั้งก็คงวัตถุไว้ (การเชื่อมต่อ ฯลฯ ... ) เพื่อหลีกเลี่ยงการเผยแพร่โดย ARC จากนั้นฉันก็คิดว่ากลไกการทำงานพร้อมกัน / ขนานและซิงโครนัสของ Go เป็นโมเดลที่เหมาะสมสำหรับขั้นตอนปัจจุบันของ Swift (หากคุณต้องการเขียนเซิร์ฟเวอร์บนเครื่อง MultiCore) เนื่องจากเราสามารถดำเนินการอะซิงก์ได้อย่างง่ายดายโดยไม่ต้องมีสายการเรียกกลับ สามารถใช้ Full Cores ด้วยไวยากรณ์ง่ายๆ และแชร์หน่วยความจำได้อย่างง่ายดายผ่าน Channel ระหว่าง Thread และ Thread
(Prorsum ไม่ใช่ Goroutine มันไม่มี Corotuines และ Context Switch ทำได้บนฝั่งระบบปฏิบัติการ มันแค่มีกลไกหน่วยความจำที่ใช้ร่วมกันของเธรดที่ปลอดภัย (ใช้งานได้บน GCD) ที่ได้รับแรงบันดาลใจอย่างมากจาก Go)
สถาปัตยกรรมเซิร์ฟเวอร์ HTTP ของ Prorsum คือ Event Driven master + Multithreading Request Handler ใน DispatchQueue คุณสามารถเขียน I/O แบบอะซิงโครนัสด้วยไวยากรณ์ซิงโครนัสด้วย go()
+ Channel<Element>
สร้างโค้ดง่าย ๆ แก้ C10K โดยไม่ต้องโทรกลับ
+-----------------+
|-- | Request Handler |
| +-----------------+
+--------+ | +-----------------+
----- TCP ---- | master |---Dispatch Queue---|-- | Request Handler |
+--------+ | +-----------------+
| +-----------------+
|-- | Request Handler |
+-----------------+
Currenty 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 แต่ละรายการจะทำงานและเรียกเสร็จสิ้นเมื่อเสร็จสิ้น ในเวลาเดียวกัน สามารถใช้ 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>
Channels คือท่อที่เชื่อมต่อการทำงานพร้อมกัน คุณสามารถส่งค่าไปยังช่องจากการดำเนินการ 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
โดยทั่วไป คุณจะต้องล้อมส่วนที่เลือกไว้ภายในลูป 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 ดูใบอนุญาตสำหรับรายละเอียด