Netty In Action Chinese version, nettyaction
Http://blog.csdn.net/abc_key/article/details/38388031
This chapter describes how Netty easily implements custom codecs. Due to the flexibility of the Netty architecture, these codecs are easy to reuse and test. For easier implementation, Memcached is used as an example of the Protocol because it is more convenient for us to implement.
Memcached is a free, open-source, high-performance, and distributed memory object cache system. It aims to accelerate the response of dynamic Web applications and reduce database load; memcache is actually a small memory block that stores arbitrary data with key-value. Someone may ask "Why Memcached ?", Because the Memcached protocol is very simple and easy to explain.
14.1 decoder range
We will only implement a subset of the Memcached protocol, which is sufficient for adding, retrieving, and deleting objects. In Memcached, we execute the SET, GET, and DELETE commands. Memcached supports many other commands, but we only need to use three of them to better understand what is simple. Memcached has a binary and plain text protocol that can be used to communicate with the Memcached server. The type of protocol used depends on the protocol supported by the server. This chapter focuses on implementing the binary protocol, because binary is the most commonly used in network programming. 14.2 implement Memcached codecs
To implement a codec for a given protocol, we should spend some time learning how it works. Generally, the Protocol itself has some detailed records. How many details will you find here? Fortunately, Memcached's binary protocol can be well extended. The RFC provides the Memcached binary protocol http://code.google.com/p/memcached/wiki/binaryprotocolrevamped. We will not execute all Memcached commands, but will only execute three operations: SET, GET, and DELETE. In this way, to make things easier. 14.3 understand the Memcached binary Protocol
You can. However, this website does not seem to be accessible if it does not go through the wall. 14.4 Netty encoder and decoder 14.4.1 implement Memcached Encoder
First, define the memcached operation code (Opcode) and response Status code (Status ):
package netty.in.action.mem;/** * memcached operation codes * @author c.king * */public class Opcode {public static final byte GET = 0x00;public static final byte SET = 0x01;public static final byte DELETE = 0x04;}
package netty.in.action.mem;/** * memcached response statuses * @author c.king * */public class Status {public static final short NO_ERROR = 0x0000;public static final short KEY_NOT_FOUND = 0x0001;public static final short KEY_EXISTS = 0x0002;public static final short VALUE_TOO_LARGE = 0x0003;public static final short INVALID_ARGUMENTS = 0x0004;public static final short ITEM_NOT_STORED = 0x0005;public static final short INC_DEC_NON_NUM_VAL = 0x0006;}
Continue to write the memcached request message body:
package netty.in.action.mem;import java.util.Random;/** * memcached request message object * @author c.king * */public class MemcachedRequest {private static final Random rand = new Random();private int magic = 0x80;// fixed so hard codedprivate byte opCode; // the operation e.g. set or getprivate String key; // the key to delete, get or setprivate int flags = 0xdeadbeef; // randomprivate int expires; // 0 = item never expiresprivate String body; // if opCode is set, the valueprivate int id = rand.nextInt(); // Opaqueprivate long cas; // data version check...not usedprivate boolean hasExtras; // not all ops have extraspublic MemcachedRequest(byte opcode, String key, String value) {this.opCode = opcode;this.key = key;this.body = value == null ? "" : value;// only set command has extras in our examplehasExtras = opcode == Opcode.SET;}public MemcachedRequest(byte opCode, String key) {this(opCode, key, null);}public int getMagic() {return magic;}public byte getOpCode() {return opCode;}public String getKey() {return key;}public int getFlags() {return flags;}public int getExpires() {return expires;}public String getBody() {return body;}public int getId() {return id;}public long getCas() {return cas;}public boolean isHasExtras() {return hasExtras;}}
Finally, write the memcached request Encoder:
package netty.in.action.mem;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToByteEncoder;import io.netty.util.CharsetUtil;/** * memcached request encoder * @author c.king * */public class MemcachedRequestEncoder extends MessageToByteEncoder<MemcachedRequest> {@Overrideprotected void encode(ChannelHandlerContext ctx, MemcachedRequest msg, ByteBuf out)throws Exception {// convert key and body to bytes arraybyte[] key = msg.getKey().getBytes(CharsetUtil.UTF_8);byte[] body = msg.getBody().getBytes(CharsetUtil.UTF_8);// total size of body = key size + body size + extras sizeint bodySize = key.length + body.length + (msg.isHasExtras() ? 8 : 0);// write magic intout.writeInt(msg.getMagic());// write opcode byteout.writeByte(msg.getOpCode());// write key length (2 byte) i.e a Java shortout.writeShort(key.length);// write extras length (1 byte)int extraSize = msg.isHasExtras() ? 0x08 : 0x0;out.writeByte(extraSize);// byte is the data type, not currently implemented in Memcached// but requiredout.writeByte(0);// next two bytes are reserved, not currently implemented// but are requiredout.writeShort(0);// write total body length ( 4 bytes - 32 bit int)out.writeInt(bodySize);// write opaque ( 4 bytes) - a 32 bit int that is returned// in the responseout.writeInt(msg.getId());// write CAS ( 8 bytes)// 24 byte header finishes with the CASout.writeLong(msg.getCas());if(msg.isHasExtras()){// write extras// (flags and expiry, 4 bytes each), 8 bytes totalout.writeInt(msg.getFlags());out.writeInt(msg.getExpires());}//write keyout.writeBytes(key);//write valueout.writeBytes(body);}}
14.4.2 implement Memcached Decoder
Compile the memcached Response Message Body:
package netty.in.action.mem;/** * memcached response message object * @author c.king * */public class MemcachedResponse {private byte magic;private byte opCode;private byte dataType;private short status;private int id;private long cas;private int flags;private int expires;private String key;private String data;public MemcachedResponse(byte magic, byte opCode, byte dataType, short status,int id, long cas, int flags, int expires, String key, String data) {this.magic = magic;this.opCode = opCode;this.dataType = dataType;this.status = status;this.id = id;this.cas = cas;this.flags = flags;this.expires = expires;this.key = key;this.data = data;}public byte getMagic() {return magic;}public byte getOpCode() {return opCode;}public byte getDataType() {return dataType;}public short getStatus() {return status;}public int getId() {return id;}public long getCas() {return cas;}public int getFlags() {return flags;}public int getExpires() {return expires;}public String getKey() {return key;}public String getData() {return data;}}
Compile the memcached response decoder:
package netty.in.action.mem;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.ByteToMessageDecoder;import io.netty.util.CharsetUtil;import java.util.List;public class MemcachedResponseDecoder extends ByteToMessageDecoder {private enum State {Header, Body}private State state = State.Header;private int totalBodySize;private byte magic;private byte opCode;private short keyLength;private byte extraLength;private byte dataType;private short status;private int id;private long cas;@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)throws Exception {switch (state) {case Header:// response header is 24 bytesif (in.readableBytes() < 24) {return;}// read headermagic = in.readByte();opCode = in.readByte();keyLength = in.readShort();extraLength = in.readByte();dataType = in.readByte();status = in.readShort();totalBodySize = in.readInt();id = in.readInt();cas = in.readLong();state = State.Body;break;case Body:if (in.readableBytes() < totalBodySize) {return;}int flags = 0;int expires = 0;int actualBodySize = totalBodySize;if (extraLength > 0) {flags = in.readInt();actualBodySize -= 4;}if (extraLength > 4) {expires = in.readInt();actualBodySize -= 4;}String key = "";if (keyLength > 0) {ByteBuf keyBytes = in.readBytes(keyLength);key = keyBytes.toString(CharsetUtil.UTF_8);actualBodySize -= keyLength;}ByteBuf body = in.readBytes(actualBodySize);String data = body.toString(CharsetUtil.UTF_8);out.add(new MemcachedResponse(magic, opCode, dataType, status,id, cas, flags, expires, key, data));state = State.Header;break;default:break;}}}
14.5 test the decoder
Netty-based codecs are all written. Let's write a class to test it:
package netty.in.action.mem;import io.netty.buffer.ByteBuf;import io.netty.channel.embedded.EmbeddedChannel;import io.netty.util.CharsetUtil;import org.junit.Assert;import org.junit.Test;/** * test memcached encoder * @author c.king * */public class MemcachedRequestEncoderTest {@Testpublic void testMemcachedRequestEncoder() {MemcachedRequest request = new MemcachedRequest(Opcode.SET, "k1", "v1");EmbeddedChannel channel = new EmbeddedChannel(new MemcachedRequestEncoder());Assert.assertTrue(channel.writeOutbound(request));ByteBuf encoded = (ByteBuf) channel.readOutbound();Assert.assertNotNull(encoded);Assert.assertEquals(request.getMagic(), encoded.readInt());Assert.assertEquals(request.getOpCode(), encoded.readByte());Assert.assertEquals(2, encoded.readShort());Assert.assertEquals((byte) 0x08, encoded.readByte());Assert.assertEquals((byte) 0, encoded.readByte());Assert.assertEquals(0, encoded.readShort());Assert.assertEquals(2 + 2 + 8, encoded.readInt());Assert.assertEquals(request.getId(), encoded.readInt());Assert.assertEquals(request.getCas(), encoded.readLong());Assert.assertEquals(request.getFlags(), encoded.readInt());Assert.assertEquals(request.getExpires(), encoded.readInt());byte[] data = new byte[encoded.readableBytes()];encoded.readBytes(data);Assert.assertArrayEquals((request.getKey() + request.getBody()).getBytes(CharsetUtil.UTF_8), data);Assert.assertFalse(encoded.isReadable());Assert.assertFalse(channel.finish());Assert.assertNull(channel.readInbound());}}
package netty.in.action.mem;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.embedded.EmbeddedChannel;import io.netty.util.CharsetUtil;import org.junit.Assert;import org.junit.Test;/** * test memcached decoder * * @author c.king * */public class MemcachedResponseDecoderTest {@Testpublic void testMemcachedResponseDecoder() {EmbeddedChannel channel = new EmbeddedChannel(new MemcachedResponseDecoder());byte magic = 1;byte opCode = Opcode.SET;byte dataType = 0;byte[] key = "Key1".getBytes(CharsetUtil.UTF_8);byte[] body = "Value".getBytes(CharsetUtil.UTF_8);int id = (int) System.currentTimeMillis();long cas = System.currentTimeMillis();ByteBuf buffer = Unpooled.buffer();buffer.writeByte(magic);buffer.writeByte(opCode);buffer.writeShort(key.length);buffer.writeByte(0);buffer.writeByte(dataType);buffer.writeShort(Status.KEY_EXISTS);buffer.writeInt(body.length + key.length);buffer.writeInt(id);buffer.writeLong(cas);buffer.writeBytes(key);buffer.writeBytes(body);Assert.assertTrue(channel.writeInbound(buffer));MemcachedResponse response = (MemcachedResponse) channel.readInbound();assertResponse(response, magic, opCode, dataType, Status.KEY_EXISTS, 0,0, id, cas, key, body);}private static void assertResponse(MemcachedResponse response, byte magic,byte opCode, byte dataType, short status, int expires, int flags,int id, long cas, byte[] key, byte[] body) {Assert.assertEquals(magic, response.getMagic());Assert.assertArrayEquals(key,response.getKey().getBytes(CharsetUtil.UTF_8));Assert.assertEquals(opCode, response.getOpCode());Assert.assertEquals(dataType, response.getDataType());Assert.assertEquals(status, response.getStatus());Assert.assertEquals(cas, response.getCas());Assert.assertEquals(expires, response.getExpires());Assert.assertEquals(flags, response.getFlags());Assert.assertArrayEquals(body,response.getData().getBytes(CharsetUtil.UTF_8));Assert.assertEquals(id, response.getId());}}
14.6 Summary
This chapter describes how to use netty to simulate the memcached binary protocol. For details about the memcached binary protocol, you can understand it separately.
Error Message
What is the difference between Struts In Action and Struts In Action Chinese Version 2?
Apache Struts 2 is known as WebWork 2. After several years of development, the WebWork and Struts communities decided to merge into one, that is, Struts 2.
Action class:
Struts1 requires the Action class to inherit an abstract base class. A common problem with Struts1 is the use of abstract class programming rather than interfaces.
Struts 2 Action class can implement an Action interface or other interfaces to make optional and customized services possible. Struts2 provides an ActionSupport base class to implement common interfaces. The Action interface is not required. Any POJO object with the execute identifier can be used as the Action object of Struts2.
Thread mode:
Struts1 Action is a singleton mode and must be thread-safe, because only one instance of Action is used to process all requests. The Singleton policy limits what Struts1 actions can do and requires caution during development. Action resources must be thread-safe or synchronized.
The Struts2 Action object generates an instance for each request, so there is no thread security problem. (In fact, the servlet container generates many discarded objects for each request without causing performance and garbage collection problems)
Servlet dependency:
Struts1 Action depends on the Servlet API, because when an Action is called, HttpServletRequest and HttpServletResponse are passed to the execute method.
Struts 2 Action does not depend on the container, allowing the Action to be tested independently from the container. If necessary, Struts2 Action can still access the initial request and response. However, other elements reduce or eliminate the need to directly access HttpServetRequest and HttpServletResponse.
Testability:
One major problem in testing Struts1 Action is that the execute method exposes the servlet API (which makes the test dependent on the container ). A third-party extension, Struts TestCase, provides a set of Struts1 simulated objects for testing ).
Struts 2 Action can be tested through initialization, setting properties, and calling methods. "dependency injection" also makes testing easier.
Capture input:
Struts1 uses the ActionForm object to capture input. All actionforms must inherit a base class. Because other JavaBean cannot be used as an ActionForm, developers often create redundant class capture inputs. Dynamic beans (DynaBeans) can be used as an option to create a traditional ActionForm. However, developers may re-describe (create) the existing JavaBean (which will still lead to redundant javabean ).
Struts 2 directly uses the Action attribute as the INPUT attribute, eliminating the need for the second input object. The INPUT attribute may be a rich object type with its own (sub) attribute. The Action attribute can be accessed through taglibs on the web page. Struts2 also supports the ActionForm mode. Rich object type, including business objects, which can be used as input/output objects. This ModelDriven feature simplifies taglib's reference to POJO input objects.
Expression Language:
Struts1 integrates JSTL, so jstl el is used. This kind of EL has basic object graph traversal, but the support for set and index attributes is weak.
Struts2 can use JSTL, but also supports a stronger and more flexible Expression Language-"Object Graph Notation Language" (OGNL ).
Bind the value to the page (view ):
S... the remaining full text>