package com.chicm.cmraft.rpc;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.util.List;
import com.chicm.cmraft.core.RaftRpcService;
import com.chicm.cmraft.protobuf.generated.RaftProtos.RequestHeader;
import com.chicm.cmraft.protobuf.generated.RaftProtos.ResponseHeader;
import com.chicm.cmraft.protobuf.generated.RaftProtos.TestRpcResponse;
import com.google.protobuf.BlockingService;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.Message;
import com.google.protobuf.MessageLite;
import com.google.protobuf.Descriptors.MethodDescriptor;
import com.google.protobuf.Message.Builder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
public class TestNettyServer {
static final EventExecutorGroup rpcgroup = new DefaultEventExecutorGroup(16);
private int port;
public TestNettyServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder(100000000,0,4,0,4));
ch.pipeline().addLast("encoder", new LengthFieldPrepender(4));
ch.pipeline().addLast("msgDecoder", new MyProtobufDecoder() );
ch.pipeline().addLast("msgencoder", new MyRpcEncoder());
ch.pipeline().addLast("handler", new MyRcpCallHandler());
/*
* static final EventExecutorGroup group = new DefaultEventExecutorGroup(16);
...
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new MyProtocolDecoder());
pipeline.addLast("encoder", new MyProtocolEncoder());
// Tell the pipeline to run MyBusinessLogicHandler's event handler methods
// in a different thread than an I/O thread so that the I/O thread is not blocked by
// a time-consuming task.
// If your business logic is fully asynchronous or finished very quickly, you don't
// need to specify a group.
pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
*/
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
//.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // (7)
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
System.out.println("server started");
f.channel().closeFuture().sync();
} finally {
System.out.println("shutdown");
// workerGroup.shutdownGracefully();
// bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 18080;
}
new TestNettyServer(port).run();
}
public class TestServerHandler extends ChannelInboundHandlerAdapter { // (1)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
// Discard the received data silently.
// System.out.println("msg:" + ((ByteBuf) msg).capacity());
//System.out.println("isdirect:" + ((ByteBuf) msg).isDirect());
((ByteBuf) msg).release(); // (3)
// System.out.println("channel read");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace(System.out);
ctx.close();
}
}
public class MyRcpCallHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
// Discard the received data silently.
RpcCall call = (RpcCall)msg;
// System.out.println("MyRcpCallHandler: callid:" + call.getCallId() );
try {
Thread.sleep(50);
} catch(Exception e){};
ctx.writeAndFlush(buildResponse(call));
//ctx.write(buildResponse(call));
//System.out.println("WRITE done");
}
public RpcCall buildResponse(RpcCall requestCall) {
ResponseHeader.Builder builder = ResponseHeader.newBuilder();
builder.setId(requestCall.getCallId());
builder.setResponseName(requestCall.getMd().getName());
ResponseHeader header = builder.build();
requestCall.setHeader(header);
//call.setMessage(response);
TestRpcResponse.Builder tbuilder = TestRpcResponse.newBuilder();
byte[] bytes = new byte[50];
tbuilder.setResult( ByteString.copyFrom(bytes));
requestCall.setMessage(tbuilder.build());
//RpcCall call = new RpcCall();
return requestCall;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
System.out.println("catched:" + cause.getMessage());
//cause.printStackTrace(System.out);
ctx.close();
}
}
public class MyProtobufDecoder extends MessageToMessageDecoder<ByteBuf> {
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)
throws Exception {
//System.out.println("size:" + msg.capacity());
long t = System.currentTimeMillis();
// System.out.println("ispooled:" + msg.)
ByteBufInputStream in = new ByteBufInputStream(msg);
RequestHeader.Builder hbuilder = RequestHeader.newBuilder();
hbuilder.mergeDelimitedFrom(in);
RequestHeader header = hbuilder.build();
//System.out.println("header:" + header);
BlockingService service = RaftRpcService.create().getService();
MethodDescriptor md = service.getDescriptorForType().findMethodByName(header.getRequestName());
Builder builder = service.getRequestPrototype(md).newBuilderForType();
Message body = null;
if (builder != null) {
if(builder.mergeDelimitedFrom(in)) {
body = builder.build();
//System.out.println("body parsed");
} else {
//System.out.println("parse failed");
}
}
RpcCall call = new RpcCall(header.getId(), header, body, md);
// System.out.println("Parse Rpc request from socket: " + call.getCallId()
// + ", takes" + (System.currentTimeMillis() -t) + " ms");
out.add(call);
}
}
public class MyOutboundHandler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
// System.out.println("MyOutboundHandler: ENCODING");
RpcCall call = (RpcCall) msg;
int totalSize = PacketUtils.getTotalSizeofMessages(call.getHeader(), call.getMessage());
ByteBuf encoded = ctx.alloc().buffer(totalSize);
//encoded.writeInt((int)m.value());
// (1)
////////////
//System.out.debug("total size:" + totalSize);
long t = System.currentTimeMillis();
ByteBufOutputStream os = new ByteBufOutputStream(encoded);
//writeIntToStream(totalSize, os);
try {
call.getHeader().writeDelimitedTo(os);
if (call.getMessage() != null)
call.getMessage().writeDelimitedTo(os);
ctx.write(encoded, promise);
} catch(Exception e) {
e.printStackTrace(System.out);
}
//System.out.println("MyOutboundHandler: DONE");
}
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
System.out.println("MyOutboundHandler:Closing ..");
ctx.close(promise);
}
}
public class MyRpcEncoder extends MessageToMessageEncoder<RpcCall> {
@Override
//public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
protected void encode(ChannelHandlerContext ctx, RpcCall call, List<Object> out) throws Exception {
// System.out.println("MyOutboundHandler: ENCODING");
//RpcCall call = (RpcCall) msg;
int totalSize = PacketUtils.getTotalSizeofMessages(call.getHeader(), call.getMessage());
ByteBuf encoded = ctx.alloc().buffer(totalSize);
//encoded.writeInt((int)m.value());
// (1)
////////////
//System.out.debug("total size:" + totalSize);
long t = System.currentTimeMillis();
ByteBufOutputStream os = new ByteBufOutputStream(encoded);
//writeIntToStream(totalSize, os);
try {
call.getHeader().writeDelimitedTo(os);
if (call.getMessage() != null)
call.getMessage().writeDelimitedTo(os);
//ctx.write(encoded, promise);
out.add(encoded);
} catch(Exception e) {
e.printStackTrace(System.out);
}
//System.out.println("MyOutboundHandler: DONE");
}
}
}