SwitNIO-接続Redis(一)

3689 ワード

先周、SwitNIOでプログラムを书いて、redisをキャッシュして、GitHubで探してみたらオープンソースライブラリがありましたが、使っていたらBugがあったので、作者にissueを提案してから今まで修复していません...だから自分で車輪を作って、ついでに勉強しました.
Redisクライアントを作るには、RESPプロトコルとredisコマンドの2つを理解する必要があります.

簡単な紹介RESP

  • 単一行文字列(Simple Strings)、先頭文字:'+'"+OK\r
    "
  • エラーメッセージ(Errors)、先頭文字は'-'"-Error message\r
    "
  • 整形数字(Integers)、先頭文字は':'":0\r
    "
  • 複数行文字列(Bulk Strings)、先頭文字:'$'"$6\r
    foobar\r
    "
  • 配列(Arrays)、先頭文字:'*'"*2\r
    $3\r
    foo\r
    $3\r
    bar\r
    "
  • 送信されたコマンドもredisサービス側からの返信も\r
    で終わることに注意してください.
    Javaの方はJedis、NettyでNedisと書いてあるので、これをSedisと名付けました!

    まず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 in
                        channel.pipeline.add(handler: RESPHandler())
                })
            
        }
        
        private func _connect() -> EventLoopFuture {
            assert(bootstrap != nil, "init failure")
            
            return bootstrap!.connect(host: options.host, port: options.prot)
        }
    }
    

    前にSwitNIOでUDP通信を確立することを書いたのですが、DatagramBootstrapを使っています.ここではクライアントとして接続する必要があるので、ClientBootstrapを使っています.

    最後にRESPHandlerを追加

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

    ここでサービス側の返信を受信した後、何の操作もせずに直接出力します.SedisClientinitメソッドの末尾にテストコードを追加して、正常に通信できるかどうかをテストします.
    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 in
                        channel.pipeline.add(handler: RESPHandler())
                })
            
            let channel = try? _connect().wait()
            let command = "set a 1\r
    ".utf8 var byteBuffer = ByteBufferAllocator().buffer(capacity: command.count) byteBuffer.write(bytes: command) channel?.writeAndFlush(byteBuffer, promise: nil) try? channel?.closeFuture.wait() }

    テスト実行
    let sdies = SedisClient(options: SedisOptions(prot: 6379, host: "127.0.0.1", password: nil, database: 0))
    

    コンソール出力が表示されます
    Optional("+OK\r
    ")

    これで第1部はfinish