LoginSignup
0
0

More than 5 years have passed since last update.

Scalaでyrmcdsキー一覧取得

Last updated at Posted at 2015-07-04

yrmcds1.1.4で、キー一覧を返す keys コマンドが追加された!
ということで、早速試してみる。
インストール方法はこちらとほぼ同じで、バージョンを 1.1.1 → 1.1.4 に読み替える。

telnet 192.168.xxx.yyy 11211

set foo 0 0 3
bar
STORED

set hoge 0 0 4
fuga
STORED

keys
VALUE foo
VALUE hoge
END

keys f
VALUE foo
END

keysで全キーを、keys <prefix>で前方一致で取得できるようだ。
やったね!

それではScalaで同じ事を…と思ったのだが、既存のmemcachedライブラリでは任意のコマンドを発行する事が(多分)できないっぽい…。
仕方がないのでtelnetクライアントを自作してみよう。
netty4系でこちらのサンプルを参考にすればよさそうだ。

import io.netty.bootstrap.Bootstrap
import io.netty.channel._
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.codec.string.{StringEncoder, StringDecoder}
import io.netty.handler.codec.{Delimiters, DelimiterBasedFrameDecoder}

import scala.collection.mutable.ListBuffer
import scala.concurrent.{Future, Promise}

class YrmcdsClient(host: String, port: Int) {
  def keys(prefix: String = ""): Future[Seq[String]] = {
    TelnetClient.call(host, port) { c =>
      c.writeAndFlush(s"keys $prefix\r\n").sync()
        .channel().pipeline.get(classOf[TelnetClientHandler]).result
    }
  }
}

object TelnetClient {
  def call[A](host: String, port: Int)(f: Channel => A): A = {
    val g = new NioEventLoopGroup()
    try {
      val b = new Bootstrap().group(g).channel(classOf[NioSocketChannel])
      val cf = b.handler(new TelnetClientInitializer()).connect(host, port).sync()
      val c = cf.channel()
      val r = f(c)
      c.closeFuture().sync()
      r
    } finally {
      g.shutdownGracefully()
    }
  }
}

class TelnetClientInitializer extends ChannelInitializer[SocketChannel] {
  private[this] val decoder = new StringDecoder()
  private[this] val encoder = new StringEncoder()
  private[this] val handler = new TelnetClientHandler()

  override def initChannel(ch: SocketChannel): Unit = {
    val pipeline = ch.pipeline()
    pipeline.addLast(
      new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter(): _*),
      decoder,
      encoder,
      handler)
  }
}

class TelnetClientHandler extends SimpleChannelInboundHandler[String] {
  private[this] val keys: ListBuffer[String] = ListBuffer.empty[String]
  private[this] val p = Promise[Seq[String]]()

  def result: Future[Seq[String]] = p.future

  private[this] val prefix = "VALUE "
  private[this] val prefixSize = prefix.size

  override def channelRead0(ctx: ChannelHandlerContext, v: String): Unit = {
    if (v.startsWith(prefix)) {
      keys += v.drop(prefixSize)
    }
    if (v == "END") {
      p success keys.toSeq
      ctx.close().sync()
    }
  }
}

こんな感じでキーが取れた!
やったね!

scala> val c = new YrmcdsClient("192.168.xxx.yyy", 11211)

scala> val r1 = c.keys()
scala> r1.onSuccess{ case keys => println(keys) }
List(foo, hoge)

scala> val r2 = c.keys("f")
scala> r2.onSuccess{ case keys => println(keys) }
List(foo)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0