Created
August 21, 2015 04:02
-
-
Save jots/158e6e1934e7acc9dd40 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import asyncnet, asyncdispatch, strutils, threadpool | |
proc fib(n: int): int = | |
if n < 2: | |
result = n | |
else: | |
result = fib(n-1) + fib(n-2) | |
proc processClient(client: AsyncSocket) {.async.} = | |
while true: | |
let line = await client.recvLine() | |
#let res = await | |
#await client.send(intToStr(res) & "\n") | |
await client.send(intToStr( fib(line.parseInt()) ) & "\n") | |
proc serve() {.async.} = | |
var server = newAsyncSocket() | |
server.setSockOpt(OptReuseAddr, true) | |
server.bindAddr(Port(25000)) | |
server.listen() | |
while true: | |
let client = await server.accept() | |
discard processClient(client) | |
discard serve() | |
runForever() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# requests/sec of fast requests | |
import threadpool, net, os, selectors, strutils | |
let sock = newSocket() | |
sock.connect("localhost", Port(25000)) | |
var n = 0 | |
proc monitor() = | |
while true: | |
sleep(1000) | |
echo n.intToStr & " reqs/sec" | |
n = 0 | |
spawn monitor() | |
var buf = TaintedString"" | |
while true: | |
sock.send("1\n") | |
sock.readLine(buf) | |
n += 1 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Imports are by source, so this will import threadpool.nim, net.nim etc | |
# from ../lib relative to the location of the nim compiler. Everything | |
# will be compiled together into a single statically linked executable. | |
import threadpool, net, os, selectors, strutils | |
## A trivial spawning socket server where each connecting socket is handed | |
## over to the threadpool for handling and response. | |
## | |
## * Uses the new "threadpool" module to spawn off the actual handling of each request | |
## * Uses the new high level socket module called "net" which is above "rawsockets" | |
## * Uses the new abstract selector from module "selectors" to do efficient polling (epoll etc) | |
##= | |
## Compile using Nim "bigbreak" with: | |
## nim c --threads:on --d:release spawningserver.nim | |
## | |
## Run it and throw ab on it, 100 concurrent clients: | |
## ab -r -n 500000 -c 100 http://localhost:8099/ | |
## | |
## On my laptop for "bytes = 100" I get about: | |
## | |
## Requests per second: 17133.43 [#/sec] (mean) | |
## Time per request: 5.837 [ms] (mean) | |
## Time per request: 0.058 [ms] (mean, across all concurrent requests) | |
# Just a regular kind of "Nim class definition" - the type "Server" inheriting | |
# the default root object with a single instance variable "socket" of type | |
# Socket. | |
proc fib(n: int): int = | |
if n < 2: | |
result = n | |
else: | |
result = fib(n-1) + fib(n-2) | |
type | |
Server = ref object of RootObj | |
socket: Socket | |
# This is where we perform the response on the socket. | |
# This proc is spawned off in its own thread on the threadpool. | |
proc handle(client: Socket) = | |
# TaintedString is used for strings coming from the outside, security mechanism. | |
# The below is equivalent to TaintedString(r"") and TaintedString is a distinct type | |
# of the type string. The "r" means a raw string. | |
var buf = TaintedString"" | |
try: | |
while true: | |
client.readLine(buf) | |
let res = fib(buf.parseInt()).intToStr() & "\n" | |
client.send(res) | |
except: | |
client.close() | |
# Eternal loop where we use the new selectors API. | |
# If we get an event on the listening socket | |
# we create a new Socket and accept the connection | |
# into it. Then we spawn the handle proc. | |
proc loop(self: Server) = | |
# Create a Selector - cross platform abstraction for polling events. | |
var selector = newSelector() | |
# Register our listener socket's file descriptor, the events we want to wait for | |
# and an optional user object associated with this file descriptor - we just use nil | |
# since we are only listening on one Socket. | |
discard selector.register(self.socket.getFD, {EvRead}, nil) | |
while true: | |
# Ask selector to wait up to 1 second, did we actually get a connection? | |
if selector.select(1000).len > 0: | |
# Socket is a ref object, so "Socket()" will allocate it on the heap. | |
# Perhaps a bit needless since we will deepCopy it two lines down in spawn. | |
var client: Socket = Socket() | |
#client.setSockOpt(OptReuseAddr, true) | |
# Or like this, its equivalent: | |
# var client: Socket | |
# new(client) | |
accept(self.socket, client) | |
# Spawn it off into the new threadpool - nifty stuff. It is a self adapting | |
# thread pool that checks number of cores etc. The argument is deepCopied over | |
# ensuring threads do not share data. | |
spawn handle(client) | |
# We create a listening port and then call loop() which does not return | |
proc listen(self: Server, port: int) = | |
# First we create a Socket. newSocket is a convenient proc with good | |
# default values. | |
self.socket = newSocket() | |
# Hmmm, where is InvalidSocket defined in bigbreak? | |
#if self.socket == sockets.InvalidSocket: raiseOSError(osLastError()) | |
# Then we bind/listen and call the loop. Whichever way we exit the try: | |
# block (exception raised or a normal return) Nim will call the finally: | |
# block for cleanups where we make sure to close the socket. | |
try: | |
self.socket.setSockOpt(OptReuseAddr, true) | |
self.socket.bindAddr(port = Port(port)) | |
self.socket.listen() | |
echo("Server listening on port " & $port) | |
self.loop() | |
finally: | |
self.socket.close() | |
# Only compiled when this is not used as a module | |
when isMainModule: | |
# Type inference makes port an int | |
var port = 25000 | |
# Type inference makes server a Server, which is | |
# a "ref" to an "object", see type definition at top. | |
# If you call a ref object type like this - it acts | |
# like a constructor and will use new to allocate the | |
# type ref'ed on the heap. | |
var server = Server() | |
# The listen proc takes a Server as first param | |
server.listen(port) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment