Skip to content

Commit 5fef65e

Browse files
committed
Initial drop
1 parent e106fd7 commit 5fef65e

18 files changed

+2810
-3
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,8 @@ fastlane/report.xml
6565
fastlane/Preview.html
6666
fastlane/screenshots
6767
fastlane/test_output
68+
69+
*.xcodeproj
70+
.build-linux
71+
Package.resolved
72+

CONTRIBUTORS.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Helge Hess <me@helgehess.eu>
2+

LICENSE renamed to LICENSE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright [yyyy] [name of copyright owner]
189+
Copyright 2018 ZeeZide GmbH
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

Package.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// swift-tools-version:4.0
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "swift-nio-redis",
7+
products: [
8+
.library (name: "NIORedis", targets: [ "NIORedis" ]),
9+
.library (name: "Redis", targets: [ "Redis" ]),
10+
],
11+
dependencies: [
12+
.package(url: "https://github.com/apple/swift-nio.git",
13+
from: "1.3.1"),
14+
],
15+
targets: [
16+
.target(name: "NIORedis", dependencies: [ "NIO", "NIOFoundationCompat" ]),
17+
.target(name: "Redis", dependencies: [ "NIORedis" ])
18+
]
19+
)

README.md

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,145 @@
1-
# swift-nio-redis
2-
A high performance Redis protocol implementation for SwiftNIO
1+
# SwiftNIO Redis
2+
3+
SwiftNIO Redis is a Swift package that contains a high performance
4+
[Redis protocol](https://redis.io/topics/protocol)
5+
implementation for
6+
[SwiftNIO](https://github.com/apple/swift-nio).
7+
This is a **standalone project** and has no other dependencies but
8+
[SwiftNIO](https://github.com/apple/swift-nio).
9+
10+
Apart from the protocol implementation which can encode and decode
11+
[RESP](https://redis.io/topics/protocol) (REdis Serialization Protocol),
12+
we also provide a [Redis client module](Sources/Redis/README.md)
13+
build on top.
14+
15+
What is Redis?
16+
[Redis](https://redis.io/) is a highly scalable in-memory data structure store,
17+
used as a database, cache and message broker.
18+
For example it can be used to implement a session store backing a web backend
19+
using its "expiring keys" feature,
20+
or it can be used as a relay to implement a chat server using its builtin
21+
[PubSub](https://redis.io/topics/pubsub)
22+
features.
23+
24+
## Performance
25+
26+
This implementation is focused on performance.
27+
It tries to reuse NIO `ByteBuffer`s as much as possible to avoid copies.
28+
29+
The parser is based on a state machine, not on a buffering
30+
`ByteToMessageDecoder`/Encoder.
31+
That doesn't make it nice, but efficient ;-)
32+
33+
## Importing the module using Swift Package Manager
34+
35+
An example `Package.swift `importing the necessary modules:
36+
37+
```swift
38+
// swift-tools-version:4.0
39+
40+
import PackageDescription
41+
42+
let package = Package(
43+
name: "RedisTests",
44+
dependencies: [
45+
.package(url: "https://github.com/NozeIO/swift-nio-redis.git",
46+
from: "0.8.0")
47+
],
48+
targets: [
49+
.target(name: "MyProtocolTool",
50+
dependencies: [ "NIORedis" ]),
51+
.target(name: "MyClientTool",
52+
dependencies: [ "Redis" ])
53+
]
54+
)
55+
```
56+
57+
58+
## Using the SwiftNIO Redis protocol handler
59+
60+
The RESP protocol handler just implemented as a regular
61+
`ChannelHandler`, similar to `NIOHTTP1`.
62+
It takes incoming `ByteBuffer` data, parses that, and emits `RESPValue`
63+
items.
64+
Same the other way around, the user writes `RESPValue` (or `RESPEncodable`)
65+
objects, and the handler renders such into `ByteBuffer`s.
66+
67+
The [NIORedis module](Sources/NIORedis/README.md) has a litte more
68+
information.
69+
70+
To add the RESP handler to a NIO Channel pipeline, the `configureRedisPipeline`
71+
method is called, e.g.:
72+
73+
```swift
74+
bootstrap.channelInitializer { channel in
75+
channel.pipeline
76+
.configureRedisPipeline()
77+
.then { ... }
78+
}
79+
```
80+
81+
82+
## Using the Redis client module
83+
84+
The `Redis` module is modeled after the Node.js
85+
[node_redis](https://github.com/NodeRedis/node_redis)
86+
module,
87+
but it also supports NIO like Promise/Future based methods in addition
88+
to the Node.js `(err,result)` style callbacks. Choose your poison.
89+
90+
### Simple KVS use example:
91+
92+
```swift
93+
import Redis
94+
95+
let client = Redis.createClient()
96+
97+
client.set ("counter", 0, expire: 2)
98+
client.incr("counter", by: 10)
99+
client.get ("counter") { err, value in
100+
print("Reply:", value)
101+
}
102+
client.keys("*") { err, reply in
103+
guard let keys = reply else { return print("got no keys!") }
104+
print("all keys in store:", keys.joined(separator: ","))
105+
}
106+
```
107+
108+
Using NIO Promises:
109+
110+
```swift
111+
client
112+
.set ("counter", 0, expire: 2)
113+
.then {
114+
client.incr("counter", by: 10)
115+
}
116+
.then {
117+
client.get("counter")
118+
}
119+
.map {
120+
print("counter is:", $0)
121+
}
122+
```
123+
124+
125+
## Status
126+
127+
The
128+
[protocol implementation](Sources/NIORedis/)
129+
is considered complete. There are a few open ends
130+
in the `telnet` variant, yet the regular binary protocol is considered done.
131+
132+
The
133+
[Redis client module](Sources/Redis/)
134+
has a few more open ends, but seems to work fine.
135+
136+
137+
### Who
138+
139+
Brought to you by
140+
[ZeeZide](http://zeezide.de).
141+
We like
142+
[feedback](https://twitter.com/ar_institute),
143+
GitHub stars,
144+
cool [contract work](http://zeezide.com/en/services/services.html),
145+
presumably any form of praise you can think of.

Sources/NIORedis/README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# SwiftNIO Redis - Protocol Implementation
2+
3+
swift-nio-redis is a port of the
4+
[Noze.io redis module](https://github.com/NozeIO/Noze.io/tree/master/Sources/redis).
5+
6+
The NIO implementation has been optimized for performance.
7+
8+
This [Noze.io](http://noze.io/) `RedisParser` stream:
9+
10+
```swift
11+
let parser = RedisParser()
12+
13+
stream! | parser | Writable { values, done in
14+
handle(replies: values)
15+
done(nil)
16+
}
17+
```
18+
19+
This is essentially replaced by the
20+
[RESPChannelHandler](RESPChannelHandler.swift).
21+
Instead of piping via `|`, it can be injected into the
22+
Swift NIO channel pipleline like so:
23+
24+
```swift
25+
_ = bootstrap.channelInitializer { channel in
26+
channel.pipeline
27+
.configureRedisPipeline()
28+
.then {
29+
channel.pipeline.add(YourRedisHandler())
30+
}
31+
}
32+
```
33+
34+
Your handler will then receive
35+
[RESPValue](RESPValue.swift)
36+
enums as the "readable input",
37+
and it can emit
38+
[RESPEncodable](RESPEncodable.swift)
39+
values are the "writable output".
40+
41+
A `RESPValue` is just an enum with the on-the-write datatypes supported
42+
by RESP:
43+
44+
- `simpleString` (a `ByteBuffer`)
45+
- `bulkString` (a `ByteBuffer` or `nil`)
46+
- `integer`
47+
- `array` (a `ContiguousArray` of `RESPValue`s)
48+
- `error` (an error)
49+
50+
The primary `RESPEncodable` is again a `RESPValue`, but
51+
`Int`'s, `String`'s, `Data`'s etc can also be directly written
52+
w/o having to wrap them in a `RESPValue`.
53+
54+
## Example
55+
56+
For a full example on how to use the protocol implementation,
57+
a [Redis client module](../Redis/) is provided as part of this package.
58+
59+
## Telnet Mode
60+
61+
Besides the binary variant, the Redis protocol also supports a "Telnet mode".
62+
A basic implementation of that is included,
63+
the major piece lacking is quoted strings.

0 commit comments

Comments
 (0)