Add a Example for Memcache Binary Codec

Motivation:
Currently, there exists no example which shows how to use the memcache binary
protocol.

Modifications:
Add an example client and client handler to show how to utilize the binary
protocol in a memcache client with a simple interactive shell.

Result:
Users looking for an example can now start off with the provided one.
This commit is contained in:
Matthew Leventi 2014-01-25 16:39:14 -08:00 committed by Michael Nitschinger
parent ad955fa8a4
commit 7a6fa73989
3 changed files with 195 additions and 0 deletions

View File

@ -53,6 +53,11 @@
<artifactId>netty-codec-http</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-codec-memcache</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-codec-socks</artifactId>

View File

@ -0,0 +1,108 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.example.memcache.binary;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.memcache.binary.BinaryMemcacheClientCodec;
import io.netty.handler.codec.memcache.binary.BinaryMemcacheObjectAggregator;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* Simple memcache client that demonstrates get and set commands against a memcache server.
*/
public class MemcacheClient {
private final String host;
private final int port;
public MemcacheClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new BinaryMemcacheClientCodec());
p.addLast(new BinaryMemcacheObjectAggregator(Integer.MAX_VALUE));
p.addLast(new MemcacheClientHandler());
}
});
// Start the connection attempt.
Channel ch = b.connect(host, port).sync().channel();
// Read commands from the stdin.
System.out.println("Enter commands (quit to end)");
System.out.println("get <key>");
System.out.println("set <key> <value>");
ChannelFuture lastWriteFuture = null;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (;;) {
String line = in.readLine();
if (line == null) {
break;
}
if ("quit".equals(line.toLowerCase())) {
ch.close().sync();
break;
}
// Sends the received line to the server.
lastWriteFuture = ch.writeAndFlush(line);
}
// Wait until all messages are flushed before closing the channel.
if (lastWriteFuture != null) {
lastWriteFuture.sync();
}
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
// Print usage if no argument is specified.
if (args.length != 2) {
System.err.println(
"Usage: " + MemcacheClient.class.getSimpleName() +
" <host> <port>");
return;
}
// Parse options.
String host = args[0];
int port = Integer.parseInt(args[1]);
new MemcacheClient(host, port).run();
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.example.memcache.binary;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.memcache.binary.BinaryMemcacheOpcodes;
import io.netty.handler.codec.memcache.binary.BinaryMemcacheRequest;
import io.netty.handler.codec.memcache.binary.DefaultBinaryMemcacheRequest;
import io.netty.handler.codec.memcache.binary.DefaultFullBinaryMemcacheRequest;
import io.netty.handler.codec.memcache.binary.FullBinaryMemcacheResponse;
import io.netty.util.CharsetUtil;
public class MemcacheClientHandler extends ChannelDuplexHandler {
/**
* Transforms basic string requests to binary memcache requests
*/
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
String command = (String) msg;
if (command.startsWith("get ")) {
String key = command.substring("get ".length());
BinaryMemcacheRequest req = new DefaultBinaryMemcacheRequest(key);
req.setOpcode(BinaryMemcacheOpcodes.GET);
req.setKeyLength((short) key.length());
req.setTotalBodyLength(key.length());
ctx.write(req, promise);
} else if (command.startsWith("set ")) {
String[] parts = command.split(" ", 3);
if (parts.length < 3) {
throw new IllegalArgumentException("Malformed Command: " + command);
}
String key = parts[1];
String value = parts[2];
ByteBuf content = Unpooled.wrappedBuffer(value.getBytes(CharsetUtil.UTF_8));
ByteBuf extras = ctx.alloc().buffer(8);
BinaryMemcacheRequest req = new DefaultFullBinaryMemcacheRequest(key, extras, content);
req.setOpcode(BinaryMemcacheOpcodes.SET);
req.setKeyLength((short) key.length());
req.setExtrasLength((byte) 8);
req.setTotalBodyLength(key.length() + 8 + value.length());
ctx.write(req, promise);
} else {
throw new IllegalStateException("Unknown Message: " + msg);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
FullBinaryMemcacheResponse res = (FullBinaryMemcacheResponse) msg;
System.out.println(res.content().toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}