tech.guitarrapc.cóm

Technical updates

C# や PowerShell から Redis を直接操作する RespClient というクライアント

私が所属する謎社では、Cache として Redis をフルに活用しています。Redis大好き最高です。

そんなRedis ですが、 インタラクティブに操作するクライアントとして誰もがお世話になったことがあるのが redis-cli でしょう。

ビルトインで使えるとても有用なクライアントです。

しかしこのクライアントはLinux用で、Windowsで使えません。

Windowsから、かつ C#、PowerShellからRedis Commandをインタラクティブに叩きたい。そんなあなたに今回は 謎社で利用しているクライアントの紹介です。

目次

RespClient

ResClient

弊社で使っているのは、 弊社の neuecc が作った RespClientです。

このクライアントの特徴は、Redis Protocol specification つまり、RESP プロトコルを解釈してSocket経由で叩いていることです。

しかも、C# から呼ぶだけではなく、PowerShell Cmdletとしても実装されています。

MSOpenTech の Redisクローンはどうなのか

MSOpenTech 大センセーの cloneならWindowsでもRedisが使えます。

しかし、C#、PowerShell から呼び出して制御したいのであって、外部コマンドをラップするのがどう考えてもいやです。そういう意味でも、RespClientのRESPを解釈してSocketで叩けるのはPowerShellから呼び出すにあたって正にあってほしい機能となります。

PowerShell から Cmdlet として使ってみよう

PowerShell Cmdlet が実装されているということは、Moduleとしてdll提供されているということです。

PowerShell から呼び出す流れを見てみましょう。

Github

このクライアントは Github で公開されています。

まずはクローンなり、Zipをダウンロードするなりしてください。

ビルド

RespClient.slnを開いてさくっとビルドします。

VS2013 であれば何も問題なくビルドできるかと思います。

f:id:guitarrapc_tech:20140901061203p:plain

Binary Module (.dll) の配置

生成された、RespClient\bin\Release\RespClient.dll をモジュールパスに配置するか、任意のパスにおきましょう。

f:id:guitarrapc_tech:20140901061459p:plain

$env:USERPROFILE\Documents\WindowsPowerShell\Modules\RespClient\RespClient.dllに配置すれば、PowerShell V3 以降であれば、モジュールとして自動読み込まれるのでいい感じでしょう。

f:id:guitarrapc_tech:20140901061612p:plain

RespClient Module の読み込み

もし、モジュールパスに配置しなかった場合は、Import-Module を使って直接RespClient.dll を読み込んでください。

PowerShell ISE でも、PowerShell.exe でもいいのでお好きな方でどうぞ。

Import-Module RespClient.dll

モジュールパスに配置していた場合は不要です。

これで準備はできました。

RespClient で PowerShell から Redis を叩く

RespClient で公開されている Cmdlet

早速みてみましょう。

Get-Command -Module RespClient

これらのCmdlet が公開されています。

CommandType Name                   ModuleName
----------- ----                   ----------
Cmdlet      Begin-RedisPipeline    RespClient
Cmdlet      Connect-RedisServer    RespClient
Cmdlet      Disconnect-RedisServer RespClient
Cmdlet      Execute-RedisPipeline  RespClient
Cmdlet      Get-RedisCurrentInfo   RespClient
Cmdlet      Send-RedisCommand      RespClient

早速使ってみましょう。

Redis ServerへのSocket接続を生成する

この時利用するのは、次のCmdlet です。

Connect-RedisServer

ヘルプを見てみましょう。

help Connect-RedisServer -full
NAME
    Connect-RedisServer
    
SYNTAX
    Connect-RedisServer [[-Host] <string>] [[-Port] <int>] [[-IoTimeout] <int>]  [<CommonParameters>]
    
    
PARAMETERS
    -Host <string>
        
        Required?                    false
        Position?                    0
        Accept pipeline input?       false
        Parameter set name           (All)
        Aliases                      None
        Dynamic?                     false
        
    -IoTimeout <int>
        
        Required?                    false
        Position?                    2
        Accept pipeline input?       false
        Parameter set name           (All)
        Aliases                      None
        Dynamic?                     false
        
    -Port <int>
        
        Required?                    false
        Position?                    1
        Accept pipeline input?       false
        Parameter set name           (All)
        Aliases                      None
        Dynamic?                     false
        
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see 
        about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). 
    
    
INPUTS
    None
    
    
OUTPUTS
    System.Object
    
ALIASES
    None
    

REMARKS
    None

対象のRedisサーバーへのSocket接続を作ります。

Redisに親しんでいる方にはご存知の通り、HostアドレスとPortとIoTimeout が設定可能です。

デフォルトでは 127.0.0.1 に 6379 で接続しますが、10.0.0.10 の Port 7000 で接続したい場合は次のようにします。

Connect-RedisServer -Host 10.0.0.10 -Port 7000

Socket接続は、一度接続すれば任意のタイミングできるまで接続しっぱなしなので、コマンド実行ごとに接続を作ったり渡す必要がないのが特徴です。REPLには好ましい動作ですね。

現在のSocket接続を調べる

Socket接続で繋ぎっぱなしな以上、確認はしたくなりますね。

Cmdlet一発で取得できます。

Get-RedisCurrentInfo

結果です。

Host                                 Port                               IoTimeout
----                                 ----                               ---------
10.0.0.10                            6379                                      -1
Redis Serverへコマンドを送信する

Socket接続を作ったら早速コマンドを送信してみましょう。

これもCmdlet一発です。

Send-RedisCommand

ヘルプです。

NAME
    Send-RedisCommand
    
SYNTAX
    Send-RedisCommand [-Command] <string>  [<CommonParameters>]
    
    
PARAMETERS
    -Command <string>
        
        Required?                    true
        Position?                    0
        Accept pipeline input?       true (ByValue)
        Parameter set name           Command
        Aliases                      None
        Dynamic?                     false
        
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see 
        about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). 
    
    
INPUTS
    System.String
    
    
OUTPUTS
    System.Object
    
ALIASES
    None
    

REMARKS
    None

Redis のコマンドは さすが Linuxで String です。なので、実行したいコマンドを文字列で渡して実行するだけです。簡単。

たとえば、 info cpu コマンドででCPU情報を取得してみるなら、こうです。

Send-RedisCommand -Command "info cpu"

結果がとれますね。

# CPU
used_cpu_sys:14346.90
used_cpu_user:1883.09
used_cpu_sys_children:90018.57
used_cpu_user_children:681373.81

もちろん Pipeline 入力にも対応しているのでこう書くこともできます。

"info cpu" | Send-RedisCommand

戻り値はUTF8String で、デコードされています。PowerShell的にも UTF8は望ましいのでいい感じでしょう。

パイプライン実行

Redis といえば パイプライン実行です。

猫も杓子もパイプライン。パイプラインによるアトミック性担保と高速処理すぎょい。

Cmdletでも対応していてくれて、最高です。

こんなかんじですね。

Begin-RedisPipeline
Send-RedisCommand "set test fghijk"
Send-RedisCommand "incr testb"
Send-RedisCommand "incr testc"
Send-RedisCommand "get test"
Execute-RedisPipeline

結果です。

OK
1
1
fghijk

パイプラインで実行されました。

流れを簡単に説明しましょう。まず、パイプラインの開始は、Begin-RedisPipeline で行います。

Begin-RedisPipeline

その間実行された、Send-RedisComamnd は、すべて即時実行されずパイプラインにキューされます。

Send-RedisCommand "set test fghijk"
Send-RedisCommand "incr testb"
Send-RedisCommand "incr testc"
Send-RedisCommand "get test"

最後にExecute-RedisPipelien でパイプライン実行されます。

Execute-RedisPipeline

なかなか redis-cli でパイプライン実行といわれてもすぐにピンと来なくても PowerShell 経由であれば直観的に操作できることが分かるかと思います。

Redis Serverとの Socket接続を切断する

Socket 接続は、新しい Connect-RedisServer を行うことで前の接続は切断されます。

あるいは任意のタイミングで切断するには、Disconnect-RedisServerを使います。

Disconnect-RedisServer

接続されていない状態で、Send-RedisCommandを行ってもエラーがでるのでわかりやすいでしょう。

Send-RedisCommand "set test fghijk"
Send-RedisCommand : Server is not connecting
発生場所 行:1 文字:1
+ Send-RedisCommand "set test fghijk" | clip
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Send-RedisCommand], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException,Redis.PowerShell.Cmdlet.SendCommand

.NET呼び出し

もちろん PowerShell Cmdlet ではなく、.NET呼び出しも可能です。

ふつーにRespClientモジュールをインポートすれば準備は完了です。

Import-Module RespClient
サンプルC# コード

例えば、C# の次のコードをPowerShell で同様に書いてみましょう。

using (var client = new Redis.Protocol.RespClient())
{
    // string command
    client.SendCommand("set a 1", Encoding.UTF8.GetString);
 
    // binary safe command
    client.SendCommand("set", new[] { Encoding.UTF8.GetBytes("test"), Encoding.UTF8.GetBytes("abcde") }, Encoding.UTF8.GetString);
 
    // use pipeline
    var results = client.UsePipeline()
        .QueueCommand("incr a")
        .QueueCommand("incrby b 10")
        .QueueCommand("get a", Encoding.UTF8.GetString)
        .Execute();
} // disconnect on dispose

PowerShell に using はないのでお察しですが。また、単純には binary safeな書き方ができないので、さっくり String Command と use pipelineで見てみましょう。*1

# connection
$client = New-Object Redis.Protocol.RespClient -ArgumentList ("10.0.0.10", 6379)

# string command
$client.SendCommand("set a 1")
$client.SendCommand("get a")

# use pipeline
$result = $client.UsePipeline().
    QueueCommand("incr a").
    QueueCommand("incrby b 10").
    QueueCommand("get a").
    Execute()

# disconnect and dispose
$client.Dispose()
$client = $null

後置き記法ができなくてしょぼんですね。

まとめ

RespClient を使えば PowerShell から Redis が自在に操作できます。PowerShell に限らずさくっと C#からという用途でも是非ご活用ください。

*1:[System.Text.Encoding]::UTF8.GetString ではすまないわけですよ。匿名コンストラクタでもほげ