Netty的protobuf的图书订购服务端开发

Netty的protobuf的图书订购服务端开发

一.Protobuf简介

Google的protobuf在业界非常流行,很多商业项目选择protobuf作为编解码框架。它是一个灵活,高效,结构化的数据序列化框架,相比于XML等传统的序列化工具,它更快,更小,更简单。支持数据结构化一次可以到处使用。

二. Protobuf编解码开发

package protobuf;

 

import java.util.List;

 

public class TestSubscribeReqProto {

        

         privatestatic byte[] encode(SubscribeReqproto.SubscribeReq req){

                   returnreq.toByteArray();

         }

        

         privatestatic SubscibeReqProto.SubscribeReq decode(byte[] body)

         throwsInvalidProtocolBufferException{

                   returnSubscribeReqProto.SubscribeReq.parseFrom(body);

         }

   

         privatestatic SubscribeReqProto.SubscribeReq createSubscribeReq(){

                   SubscribeReqProto.SubscribeReq.Builderbuilder=

                                     SubscribeReqProto.SubscribeReq.newBuilder();

                   builder.setSubReqID(1);

                   builder.setUserName("zhengguang");

                   builder.setProductName("NettyBook");

                   List<String>address=new ArrayList<>();

                   address.add("wuhan");

                   address.add("shanghai");

                   address.add("beijing");

                   builder.addAllAddress(address);

                   returnbuilder.build();

         }

        

         publicstatic void main(String[] args) throws InvalidprotocolBufferException{

                   SubscribeReqProto.SubscirbeReqreq=CreateSubscribeReq();

                   System.out.println("Beforeencode:"+req.toString());

                   SubscribeReqProto.SubscribeReqreq2=decode(encode(req));

                   System.out.println("Afterdecode:"+req.toString());

                   System.out.println("Assertequal: -->"+req2.equals(req));

         }

}

    通过SubscribeReqProto.SubscribeReq静态方法newBuilder创建builder实例,通过Builder构建器对SubscribeReq属性进行设置,对于集合类型,通过addAllxxx()方法可以将集合对象设置到对应属性中。

    编码时通过调用SubscribeReqProto.SubscribeReq实例的toByte即可,将SubscribeReq编码为byte数组,使用非常方便。

    解码时通过SubscribeReqProto.SubscribeReq的静态方法parseForm将二进制byte数组解码为原始的对象。

三. Netty的Protobuf服务端开发

3.1 SubReqServe实现

package protobuf;

 

import serializable.SubReqClient;

import io.netty.bootstrap.ServerBootstrap;

import io.netty.channel.socket.SocketChannel;

 

public class SubReqServer {

         publicvoid bind(int port) throws Exception{

                   //配置服务端的NIO线程组

                   EventLoopGroupbossGroup =new NioEventLoopGroup();

                   EventLoopGroupworkerGroup=new NioEventLoopGroup();

                   try{

                            ServerBootstrapb=new ServerBootstrap();

                            b.group(bossGroup,workerGroup)

                            .channel(NioServerSocketChannel.class)

                            .option(ChannelOption.SO_BACKLOG,100)

                            .handler(newLoggingHandler(LogLevel.INFO))

                            .childHandler(newChannelInitializer<SocketChannel>() {

                                     @Override

                                     publicvoid initChannel(SocketChannel ch){

                                               //用于半包处理

                                               ch.pipeline().addLast(

                                                                 newProtobufVarint32FrameDecoder());

                                               //添加解码器,参数是com.google.protobuf.MessageLite

                                               //实际上就是要告诉ProtobufDecoder需要解码的目标类是什么

                                               ch.pipeline().addLast(

                                                                 newProtobufDecoder(

                                                                                    SubscribeReqProto.SubscribeReq

                                                                                    .getDefaultInstance()));

                                               ch.pipeline().addLast(

                                                                 newProtobufVarint32LengthFieldPrepender());

                                               ch.pipeline().addLast(newprotobufEncoder());

                                               ch.pipeline().addLast(newSubReqServerHandler());

                                                                 )

                                     });

                                     //绑定端口,同步等待成功

                                     ChannelFuturef=b.bind(port).sync();

                                     //等待服务器监听端口关闭

                                     f.channel().closeFuture().sync();

                   }catch (Exception e) {

                            //TODO: handle exception

                   }finally{

                            //优雅退出,释放线程池资源

                            bossGroup.shutdownGracefully();

                            workerGroup.shutdownGracefully();

                   }

         }

                  

                   publicstatic void main(String[] args) throws Exception{

                            intport=8080;

                            if(args!=null&&args.length>0){

                                     try{

                                               port=Integer.valueOf(args[0]);

                                              

                                     }catch (NumberFormatException e) {

                                               //TODO: handle exception

                                              

                                     }

                                     newSubReqClient().connect(port,"127.0.0.1");

                            }

                   }

}

3.2 SubReqServeHandler实现

 

package protobuf;

 

public class SubReqServerHandler extendsChannelHandlerAdapter{

   

         @Override

         publicvoid channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{

                   SubscribeReqProto.SubscribeReqreq=(SubscribeReqProto.SubscribeReq) msg;

                   if("zhengguang".equalsIgnoreCase(req.getUserName())){

                            System.out.println("Serviceaccept client subscribe req:["+req.toString()+"]");

                            ctx.writeAndFlush(req.getSubReqID()));

                   }

         }

        

         privateSubscribeRespProto.SubscribeResp resp(int subReqID) {

                   SubscribeRespProto.SubscribeReq.Builderbuilder=SubscribeRespProto.SubscribeResp

                                     .newBuilder();

                   builder.setSubReqID(subReqID);

                   builder.setRespCode(0);

                   builder.setDesc("Nettybook order succeed,3 days later,sent "

                                     +"to the desianated address");

                   returnbuilder.build();

         }

        

         @Override

         publicviod exceptionCaught(ChannelHandlerContext ctx,Throwable ctx) {

                   cause.printStackTrace();

                   ctx.close();// 发送异常,关闭链路

         }

}

    由于ProtobufDecoder已经对消息进行了自动解码,因此接收到的订购请求消息可以直接使用。对用户名进行校验,校验通过后构造应答消息返回给客户端,由于使用了ProtobufEncoder,所以不需要对SubscribeRespProto.SubscribeReq进行手工编码。

四. Netty的Protobuf客户端开发 

4.1 SubReqClient实现

package protobuf;

import io.netty.bootstrap.Bootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

importio.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.codec.protobuf.ProtobufEncoder;

importio.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

 

/*

 *profobuf的图书订购客户端

 */

public class SubReqClient {

         publicvoid connect(int port,String host) throws Exception{

                   //配置客户端NIO线程组

                   EventLoopGroupgroup=new NioEventLoopGroup();

                   try{

                            Bootstrapb=new Bootstrap();

                            b.group(group).channel(NioSocketChannel.class)

                            .option(ChannelOption.TCP_NODELAY,true)

                            .handler(newChannelInitializer<SocketChannel>() {

                                     @Override

                                     publicvoid initChannel(SocketChannel ch) throws Exception{

                                               ch.pipeline().addLast(

                                                                 newProtobufVarint32LengthFieldPrepender());

                                               ch.pipeline().addLast(newProtobufEncoder());

                                               ch.pipeline().addLast(newProtobufEncoder());

                                               ch.pipeline().addLast(newSubReqClientHandler());

                                     }

                                     });

                            //发起异步连接操作

                                     ChannelFuturef=b.connect(host,port).sync();

                                     //等待客户端链路关闭

                                     f.channel().closeFuture().sync();

                   }catch (Exception e) {

                            //TODO: handle exception

                   }finally{

                            //优雅退出,释放NIO线程组

                            group.shutdownGracefully();

                   }

         }

                  

                   publicstatic void main(String[] args) throws Exception{

                            intport=8080;

                            if(args!=null&&args.length>0){

                                     try{

                                               port=Integer.valueOf(args[0]);

                                              

                                     }catch (NumberFormatException e) {

                                               //TODO: handle exception

                                              

                                     }

                                     newSubReqClient().connect(port,"127.0.0.1");

                            }

                   }

 

}

    需要指出的是客户端需要解码的对象是订购响应,所以使用SubscibeRespProto.SubscribeResp的实例做入参。

4.1 SubReqClientHandler实现

package protobuf;

 

import java.util.ArrayList;

import java.util.List;

 

importio.netty.channel.ChannelHandlerAdapter;

importio.netty.channel.ChannelHandlerContext;

 

public class SubReqClientHandler extendsChannelHandlerAdapter{

        

         publicSubReqClientHandler(){

                  

         }

        

         @Override

         publicvoid channelActive(ChannelHandlerContext ctx){

                   for(inti=0;i<10;i++){

                            ctx.write(subReq(i));

                   }

                   ctx.flush();

         }

        

         privateSubscribeReqProto.SubscribeReq subReq(int i){

                   SubscribeReqProto.SubscribeReq.Builderbuilder=SubscribeReqProto.SubscribeReq.newBuilder();

                   builder.setSubReqID(i);

                   builder.setUserName("zhengguang");

                   builder.setProductName("NettyBook For Protobuf");

                   List<String>address=new ArrayList<>();

                   address.add("wuhan");

                   address.add("shanghai");

                   address.add("beijing");

                   returnbuilder.build();

         }

        

         @Override

         publicvoid channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{

                   System.out.println("Receiveserver response:["+msg+"]");

         }

        

         @Override

         publicvoid channelReadComplete(ChannelHandlerContext ctx) throws Exception{

                   ctx.flush();

         }

        

         @Override

         publicvoid exceptionCaucht(ChannelHandlerContext ctx,Throwable cause){

                   cause.printStackTrace();

                   ctx.close();

         }

}

    客户端接收到服务端的应答消息之后会直接打印,按照设计,应该打印10次。利用netty提供的protobuf编解码能力,我们在不需要了解protobuf实现和使用细节情况下就能轻松支持protobuf编辑码,可以方便实现跨语言的远程服务调用与周边异构系统进行通信对接。

   

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值