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)