SwiftNIO-鏈接Redis(一)

上星期用SwiftNIO寫了一套程序,而後須要用到redis作緩存,而後就去GitHub上找了一下發現有一個開源庫,可是用的時候發現有Bug,向做者提了issue以後至今沒有修復... 因此就本身動手造了個輪子,順便學習一下。redis

要作redis客戶端的話有兩個東西必需要了解,RESP協議redis命令bootstrap

簡單介紹RESP

  • 單行字符串(Simple Strings), 開頭字符爲:'+' "+OK\r\n"
  • 錯誤信息(Errors),開頭字符爲:'-' "-Error message\r\n"
  • 整形數字(Integers),開頭字符爲:':' ":0\r\n"
  • 多行字符串(Bulk Strings),開頭字符爲:'$' "$6\r\nfoobar\r\n"
  • 數組(Arrays),開頭字符爲:'*' "*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"

須要注意的是無論是發送的命令仍是收到來自redis服務端的回覆,都是以\r\n結尾。數組

Java那邊的叫Jedis,用Netty寫的叫Nedis,那麼我就把這個命名爲Sedis了!promise

首先建立一個struct,用來存儲鏈接信息:

struct SedisOptions {
    let prot: Int
    let host: String
    var password: String?
    var database: Int?
}
複製代碼

而後建立SedisClient類:

這個類裏面須要根據SedisOptions的信息來建立鏈接,包括身份驗證。緩存

class SedisClient {
    private let options: SedisOptions
    private var bootstrap: ClientBootstrap?
    private var loopGroup: EventLoopGroup!
    
    init(options: SedisOptions) {
        self.options = options
        
        loopGroup = MultiThreadedEventLoopGroup(numThreads: System.coreCount)
        bootstrap = ClientBootstrap(group: loopGroup)
            .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET),
                                                 SO_REUSEADDR), value: 1)
            .channelInitializer({ (channel) -> EventLoopFuture<Void> in
                    channel.pipeline.add(handler: RESPHandler())
            })
        
    }
    
    private func _connect() -> EventLoopFuture<Channel> {
        assert(bootstrap != nil, "init failure")
        
        return bootstrap!.connect(host: options.host, port: options.prot)
    }
}
複製代碼

以前寫了一遍用SwiftNIO創建UDP通信的,用的是DatagramBootstrap,咱們這裏須要看成客戶端鏈接,因此用的是ClientBootstrapbash

最後加上一個RESPHandler

class RESPHandler: ChannelDuplexHandler {
    typealias InboundIn = ByteBuffer
    
    func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
        var value = unwrapInboundIn(data)
        print(value.readString(length: value.writerIndex))
    }
}
複製代碼

這裏接收到服務端回覆後先不作任何操做直接輸出。socket

如今在SedisClientinit方法末尾加上一段測試代碼測試是否能正常通信oop

init(options: SedisOptions) {
        self.options = options
        
        loopGroup = MultiThreadedEventLoopGroup(numThreads: System.coreCount)
        bootstrap = ClientBootstrap(group: loopGroup)
            .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET),
                                                 SO_REUSEADDR), value: 1)
            .channelInitializer({ (channel) -> EventLoopFuture<Void> in
                    channel.pipeline.add(handler: RESPHandler())
            })
        
        let channel = try? _connect().wait()
        let command = "set a 1\r\n".utf8
        var byteBuffer = ByteBufferAllocator().buffer(capacity: command.count)
        byteBuffer.write(bytes: command)
        channel?.writeAndFlush(byteBuffer, promise: nil)
        try? channel?.closeFuture.wait()
    }

複製代碼

測試運行post

let sdies = SedisClient(options: SedisOptions(prot: 6379, host: "127.0.0.1", password: nil, database: 0))
複製代碼

能夠看到控制檯輸出學習

Optional("+OK\r\n")
複製代碼

至此,第一部分就已經finish

相關文章
相關標籤/搜索