Swift-NIO
RxHttpClient
orecon_ios(day: 2)
Date(“2018-09-13(THU)”)
(@mike_neck
: “L is B”,
: )
• try! Swift 2018 (1) -
• try! Swift 2018 (2) - Swift-NIO
• Swift-NIO RxHttpClient
try! Swift 2018 (1)
Swift-NIO
• try! Swift 2018
• HTTP TCP/UDP
• Norman Maurer Java
Netty
• Swift-NIO Netty
• Swift Netty
Netty NIO
• java.nio Netty (2004 6
version 2.1.0)
• Java 1.3(J2SE 1.3) I/O read/write Blocking I/
O ( Old blocking I/O=OIO )
• Java 1.4(J2SE 1.4) select (New) I/O(IO)
(2002)
• select ( epoll/kqueue) I/O
Non-blocking I/O
• NIO( NIO Non-blocking I/O )
OIO
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(socket_fd, (struct sockaddr *)&addr, sizeof(addr));
listen(socket_fd, 20);
while(1) {
len = sizeof(client);
sock = accept(socket_fd, (struct sockaddr *)&client, &len);
memset(buf, 0, sizeof(buf));
recv(sock, buf, sizeof(buf), 0); // read
send(sock, buf, (int)strlen(buf), 0); // write
close(buf);
}
fork/
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(socket_fd, (struct sockaddr *)&addr, sizeof(addr));
listen(socket_fd, 20);
while(1) {
len = sizeof(client);
sock = accept(socket_fd, (struct sockaddr *)&client, &len);
memset(buf, 0, sizeof(buf));
recv(sock, buf, sizeof(buf), 0); // read
send(sock, buf, (int)strlen(buf), 0); // write
close(buf);
}
/
Pool
OIO fork
/
Thread/proc
Thread/proc
Thread/proc
accept
DispatchQueue
64
OIO recv/
write I/O
recv write recv write
write recv write
recv write recv
block block
block
block block
block
OIO CPU I/O
OIO
• CPU
•
• ( )
•
• AWS/Azure/GCP
I/O
kqueue(BSD)
epoll(Linux)
I/O
(I/O Multiplexing)
•
• read/write
• OIO -> Thread/process
read/write
• I/O -> read/write
read/write
• I/O -> Non-Blocking I/O
I/O ( kqueue)
/// … socket/socketsetopt/bind/listen
int kq = kqueue();
struct kevent events;
EV_SET(&event, socket_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
kevent(kq, &event, 1, NULL, 0, NULL);
while(1) {
kevent(kq, NULL, 0, &event, 1, &timeout);
if (event.ident == spcket_fd) {
int client = accept_client(event.ident); // accept
EV_SET(&event, client, EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, NULL);
kevent(kq, &event, 1, NULL, 0, NULL);
} else if (event.flags & EVFILT_READ) {
char buf[8192];
read_message(event.ident, &buf); // read
prepare_write(&event, &buf); // EV_SET EVFILT_WRITE
kevent(kq, &event, 1, NULL, 0, NULL);
} else {
write_message(&event); // write
}
}
epoll(Linux)
socket
I/O
EVFILT_READ
EVFILT_WRITE
kevent()
accept()/read()
write()/close()
read()/write()
( )OIO
recv write recv write
write recv write
recv write recv
block block
block
block block
block
OIO CPU I/O
I/O
recv write recv writewrite recv writerecv write recv
I/O
•
• CPU
• 1
I/O
•
• OIO
• read/write
Swift-NIO
• I/O Swift
•
• API
• Linux(Ubuntu)/MacOSX
• API Non-Blocking I/O
•
• MacOS 14 Network (swift-nio-transport-
services)
• SSL/TLS (swift-nio-ssl)
try! Swift 2018
(2)
EventLoopGroup
Swift-NIO
EventLoop
ChannelPipeline
EventLoop
Channel
Channel
Channel
ChannelHandler
Bootstrap
EventLoop &
EventLoopGroup
//
while true {
let events = multiplexer.findEvents()
for event in events {
event.channel.handleEvent(event)
}
}
EventLoop &
EventLoopGroup
• EventLoop
• Swift-NIO
• EventLoop Thread
• Channel/ChannelHandler
Thread
• EventLoopGroup
• Channel EventLoop
Channel
•
• read/write/close/connect/bind
•
ChannelFuture
• 1 ChannelPipeline
/
ChannelPipeline &
ChannelHandler
Channel
read
channelRead channelRead
write
write
writewriteToSocket
ChannelInboundHandler /ChannelOutboundHandler
ChannelPipeline
•
• ChannelHandler
• ( ) head
tail
• head -> tail
• tail -> head
ChannelHandler
• ( )
• ChannelInboundHandler - (read)
• ChannelOutboundHandler - (write)
• Handler
ByteBuffer
String
HTTP Header
HTTP bodyByteBuffer
SSLHandler HTTPDecoder
Bootstrap
•
•
• (bind/listen)
• (connect)
• EventLoopGroup
• ChannelOption(socket option)
• ChannelHandler
…
Swift-NIO
GitHub
https://github.com/mike-neck/swift-nio-showcase
Swift-NIO HTTP
Swift-NIO Http Client
1. ClientBootstrap host
2. ChannelPipeline ChannelHandler
• https host
OpenSSLHandler
• HTTPRequestEncoder/HTTPResponseDecoder/
ChannelHandler
3. HTTPRequestPart Channel
write
4. OpenSSLHandler SSL/TLS
Swift-NIO Http Client
5. HTTPRequestEncoder write HTTPRequestPart
ByteBuffer
6. OpenSSLHandler write ByteBuffer
Channel
7. OpenSSLHandler (ByteBuffer) channelRead
8. HTTPResponseDecoder channelRead ByteBuffer
HTTPResponsePart
9. ChannelHandler HTTPResponsePart
• Bootstrap
EventLoopGroup( 1 2 )
• ChannelOption /ChannelInitializer
• Bootstrap host
• connect EventLoopFuture<Channel>
• ( ) EventLoopFuture
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let bootstrap = ClientBootstrap(group: eventLoopGroup)
.channelOption(soReuseeAddr, 1)
//
let future = bootstrap.connect(host: “example.com”, port: 443)
1.ClientBootstrap host
• ClientBootstrap channelInitializer ChannelHandler ChannelPipeline
• ChannelPipeline (head) (tail)
• HttpClient
• https OpenSSLHandler
• HttpRequestEncoder/HttpResponseDecoder
• ChannelInboundHandler
.channelInitializer { channel in
if https {
let openSslHandler = OpenSSLHandler(
context: sslContext, serverHostname: “example.com”)
channel.pipeline.add(handler: openSslHandler)
}
_ = channel.pipeline.addHTTPClientHandlers()
return channel.pipeline.add(handler: HttpResponseHandler())
}
2. ChannelPipeline
• http / HTTPClientRequestPart
• .head(HTTPRequestHead)
• .body(ByteBuffer)
• .end(HTTPRequestHead?)
• HTTPRequestHead
• HTTPVersion / HTTPMethod / url
• HTTPHeaders(name value (String,String) )
• channel write writeAndFlush EventLoopFuture<Void>
var request = HTTPRequestHead(
version: HTTPVersion(major:1,minor:1), method: HTTPMethod.GET,
url: “https://example.com/api/1/foo?query=bar”)
request.headers = HTTPHeaders([
(“Host”,”example.com”), (“User-Agent”,”swift-nio”),
(“Accept-Encoding”,”identity”),(“Accept”,”application/json”),])
_ = channel.write(HTTPClientRequestPart.head(request))
let future = channel.writeAndFlush(HTTPClientRequestPart.end(nil))
3. HTTPRequestPart( ) Channel
Swift-NIO Http Client
4. OpenSSLHandler SSL/TLS
5. HTTPRequestEncoder write HTTPRequestPart
ByteBuffer
6. OpenSSLHandler write ByteBuffer
Channel
7. OpenSSLHandler (ByteBuffer) channelRead
8. HTTPResponseDecoder channelRead ByteBuffer
HTTPResponsePart
Swift-NIO
• http / HTTPClientRequestPart
• .head(HTTPRequestHead)
• .body(ByteBuffer)
• .end(HTTPRequestHead?)
• HTTPRequestHead
• HTTPVersion / HTTPMethod / url
• HTTPHeaders(name value (String,String) )
• channel write writeAndFlush EventLoopFuture<Void>
typealias InboundIn = HTTPClientResponsePart
func channelRead(ctx:ChannelHandlerContext,data:NIOAny) {
let responsePart = unwrapInboundIn(data)
switch responsePart {
case .head(let header): //
case .body(let byteBuffer): //
case .end(_): //
9. HttpResponseHandler
Swift-NIO
•
• ChannelHandler
• channelRead -> channelReadComplete ->
channelRead
• ChannelOption
RxHttpClient
• EventLoopFuture ChannelOption
Channel Swift-NIO
• HTTP URL
• (? ) RxSwift
let eventLoopGroup = …
let client: HttpClient = RxHttpClient.newClient(
share: eventLoopGroup)
let response: Single<Response> = httpClient
.get(.https(“example.com”))
.path(“/api/1/team/users”)
.authorization(.bearer(“XXXX”))
.header(“Accept”, “application/json”)
.asSingle()
response.flatMap { $0.bodyAs(UserList.Type) }
.subscribe { print($0) }
RxHttpClient
https://github.com/mike-neck/RxHttpClient
For further study…
• swift-nio(GitHub)
• https://github.com/apple/swift-nio
• README
• SwiftNIO Docs
• https://apple.github.io/swift-nio/docs/current/NIO/index.html
• API
• Netty in Action(Norman Maurer/Marvin Allen Wolfthal)
• https://amzn.to/2QgrAZj
• Netty
• ( ) Netty(@mike_neck)
• https://www.slideshare.net/mikeneck/jjug-ccc-2018-spring-i7-netty
• Netty &
•
• https://www.irasutoya.com/

swift-nio のアーキテクチャーと RxHttpClient