Skip to content

Commit

Permalink
Initial drop
Browse files Browse the repository at this point in the history
  • Loading branch information
helje5 committed Apr 10, 2018
1 parent e106fd7 commit 5fef65e
Show file tree
Hide file tree
Showing 18 changed files with 2,810 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output

*.xcodeproj
.build-linux
Package.resolved

2 changes: 2 additions & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Helge Hess <[email protected]>

2 changes: 1 addition & 1 deletion LICENSE → LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]
Copyright 2018 ZeeZide GmbH

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
19 changes: 19 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// swift-tools-version:4.0

import PackageDescription

let package = Package(
name: "swift-nio-redis",
products: [
.library (name: "NIORedis", targets: [ "NIORedis" ]),
.library (name: "Redis", targets: [ "Redis" ]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git",
from: "1.3.1"),
],
targets: [
.target(name: "NIORedis", dependencies: [ "NIO", "NIOFoundationCompat" ]),
.target(name: "Redis", dependencies: [ "NIORedis" ])
]
)
147 changes: 145 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,145 @@
# swift-nio-redis
A high performance Redis protocol implementation for SwiftNIO
# SwiftNIO Redis

SwiftNIO Redis is a Swift package that contains a high performance
[Redis protocol](https://redis.io/topics/protocol)
implementation for
[SwiftNIO](https://github.com/apple/swift-nio).
This is a **standalone project** and has no other dependencies but
[SwiftNIO](https://github.com/apple/swift-nio).

Apart from the protocol implementation which can encode and decode
[RESP](https://redis.io/topics/protocol) (REdis Serialization Protocol),
we also provide a [Redis client module](Sources/Redis/README.md)
build on top.

What is Redis?
[Redis](https://redis.io/) is a highly scalable in-memory data structure store,
used as a database, cache and message broker.
For example it can be used to implement a session store backing a web backend
using its "expiring keys" feature,
or it can be used as a relay to implement a chat server using its builtin
[PubSub](https://redis.io/topics/pubsub)
features.

## Performance

This implementation is focused on performance.
It tries to reuse NIO `ByteBuffer`s as much as possible to avoid copies.

The parser is based on a state machine, not on a buffering
`ByteToMessageDecoder`/Encoder.
That doesn't make it nice, but efficient ;-)

## Importing the module using Swift Package Manager

An example `Package.swift `importing the necessary modules:

```swift
// swift-tools-version:4.0

import PackageDescription

let package = Package(
name: "RedisTests",
dependencies: [
.package(url: "https://github.com/NozeIO/swift-nio-redis.git",
from: "0.8.0")
],
targets: [
.target(name: "MyProtocolTool",
dependencies: [ "NIORedis" ]),
.target(name: "MyClientTool",
dependencies: [ "Redis" ])
]
)
```


## Using the SwiftNIO Redis protocol handler

The RESP protocol handler just implemented as a regular
`ChannelHandler`, similar to `NIOHTTP1`.
It takes incoming `ByteBuffer` data, parses that, and emits `RESPValue`
items.
Same the other way around, the user writes `RESPValue` (or `RESPEncodable`)
objects, and the handler renders such into `ByteBuffer`s.

The [NIORedis module](Sources/NIORedis/README.md) has a litte more
information.

To add the RESP handler to a NIO Channel pipeline, the `configureRedisPipeline`
method is called, e.g.:

```swift
bootstrap.channelInitializer { channel in
channel.pipeline
.configureRedisPipeline()
.then { ... }
}
```


## Using the Redis client module

The `Redis` module is modeled after the Node.js
[node_redis](https://github.com/NodeRedis/node_redis)
module,
but it also supports NIO like Promise/Future based methods in addition
to the Node.js `(err,result)` style callbacks. Choose your poison.

### Simple KVS use example:

```swift
import Redis

let client = Redis.createClient()

client.set ("counter", 0, expire: 2)
client.incr("counter", by: 10)
client.get ("counter") { err, value in
print("Reply:", value)
}
client.keys("*") { err, reply in
guard let keys = reply else { return print("got no keys!") }
print("all keys in store:", keys.joined(separator: ","))
}
```

Using NIO Promises:

```swift
client
.set ("counter", 0, expire: 2)
.then {
client.incr("counter", by: 10)
}
.then {
client.get("counter")
}
.map {
print("counter is:", $0)
}
```


## Status

The
[protocol implementation](Sources/NIORedis/)
is considered complete. There are a few open ends
in the `telnet` variant, yet the regular binary protocol is considered done.

The
[Redis client module](Sources/Redis/)
has a few more open ends, but seems to work fine.


### Who

Brought to you by
[ZeeZide](http://zeezide.de).
We like
[feedback](https://twitter.com/ar_institute),
GitHub stars,
cool [contract work](http://zeezide.com/en/services/services.html),
presumably any form of praise you can think of.
63 changes: 63 additions & 0 deletions Sources/NIORedis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# SwiftNIO Redis - Protocol Implementation

swift-nio-redis is a port of the
[Noze.io redis module](https://github.com/NozeIO/Noze.io/tree/master/Sources/redis).

The NIO implementation has been optimized for performance.

This [Noze.io](http://noze.io/) `RedisParser` stream:

```swift
let parser = RedisParser()

stream! | parser | Writable { values, done in
handle(replies: values)
done(nil)
}
```

This is essentially replaced by the
[RESPChannelHandler](RESPChannelHandler.swift).
Instead of piping via `|`, it can be injected into the
Swift NIO channel pipleline like so:

```swift
_ = bootstrap.channelInitializer { channel in
channel.pipeline
.configureRedisPipeline()
.then {
channel.pipeline.add(YourRedisHandler())
}
}
```

Your handler will then receive
[RESPValue](RESPValue.swift)
enums as the "readable input",
and it can emit
[RESPEncodable](RESPEncodable.swift)
values are the "writable output".

A `RESPValue` is just an enum with the on-the-write datatypes supported
by RESP:

- `simpleString` (a `ByteBuffer`)
- `bulkString` (a `ByteBuffer` or `nil`)
- `integer`
- `array` (a `ContiguousArray` of `RESPValue`s)
- `error` (an error)

The primary `RESPEncodable` is again a `RESPValue`, but
`Int`'s, `String`'s, `Data`'s etc can also be directly written
w/o having to wrap them in a `RESPValue`.

## Example

For a full example on how to use the protocol implementation,
a [Redis client module](../Redis/) is provided as part of this package.

## Telnet Mode

Besides the binary variant, the Redis protocol also supports a "Telnet mode".
A basic implementation of that is included,
the major piece lacking is quoted strings.
Loading

0 comments on commit 5fef65e

Please sign in to comment.