一般在使用netty时,数据传输的时候都会选择对传输的数据进行编解码,编码后的数据变小, 有利于在有限的带宽下传输更多的数据。

由于java本身序列化的缺点较多(无法跨语言,序列化后的码流太大,序列化的性能太低等),业界主流的编解码框架主要有如下三个:

  1. google的protobuf
  2. facebook的thrift
  3. jboss的marshalling

今天我们简单介绍一下netty结合google的protobuf框架进行数据的编解码。

1. 什么是protobuf?

protobuf全称是google protocol buffers, 它是谷歌公司开源的一个序列化框架。

它将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的pojo对象和protobuf相关的方法和属性。

它的特点如下:

  1. 结构化数据存储格式
  2. 高效的编解码性能
  3. 语言无关、平台无关、扩展性好
  4. 官方支持多个语言(java,c++,python,c#等)

2. 下载安装

protobuf已经托管在github上,可以在页面下载,本案例使用的是v3.6(要下载后缀为-win32.zip的)。

下载后,将压缩包进行解压。这里主要用到bin目录下的protoc.exe。

3. 定义好proto文件

subscribereq.proto

// 区分不同的protobuf版本,必须有
syntax = "proto2";
 
package netty;
// 生成的目标类的包路径
option java_package = "cn.ddlover.nettystudy.protobuf";
// 生成的目标类的名字
option java_outer_classname = "subscribereqproto";
 
message subscribereq{
    required int32 subreqid = 1;
    required string username = 2;
    required string productname = 3;
    repeated string address = 4;
}

subscriberesp.proto

syntax = "proto2";
package netty;
 
option java_package = "cn.ddlover.nettystudy.protobuf";
option java_outer_classname = "subscriberespproto";
 
message subscriberesp{
    required int32 subreqid = 1;
    required int32 respcode = 2;
    required string desc = 3;
}

此时项目结构如下

重点关注一下两个proto文件的位置,是位于项目的根路径下

4. 分别执行以下命令

d:\xhb\protoc-3.6.1-win32\bin\protoc.exe –java_out=.\src\main\java subscriberesp.proto 

d:\xhb\protoc-3.6.1-win32\bin\protoc.exe –java_out=.\src\main\java subscribereq.proto 

这里需要把protoc.exe的位置换成自己的, –java_out命令设置的是proto文件中java_package指令的父目录。

5. 此时项目结构如下

下面开始代码方面的开发,

1. 先贴一波maven的配置

<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/pom/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
         xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>
 
    <groupid>cn.ddlover</groupid>
    <artifactid>nettystudy</artifactid>
    <version>1.0-snapshot</version>
 
 
    <properties>
        <java.version>1.8</java.version>
    </properties>
 
    <dependencies>
        <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
        <dependency>
            <groupid>io.netty</groupid>
            <artifactid>netty-all</artifactid>
            <version>4.1.33.final</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
        <dependency>
            <groupid>com.google.protobuf</groupid>
            <artifactid>protobuf-java</artifactid>
            <version>3.6.1</version>
        </dependency>
 
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <version>1.18.4</version>
        </dependency>
    </dependencies>
</project>

2. 这里贴上完整代码的项目结构

3. subreqserver.java

 
import cn.ddlover.nettystudy.handler.subreqserverhandler;
import cn.ddlover.nettystudy.protobuf.subscribereqproto;
import io.netty.bootstrap.serverbootstrap;
import io.netty.channel.channelfuture;
import io.netty.channel.channelinitializer;
import io.netty.channel.channeloption;
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.protobuf.protobufdecoder;
import io.netty.handler.codec.protobuf.protobufencoder;
import io.netty.handler.codec.protobuf.protobufvarint32framedecoder;
import io.netty.handler.codec.protobuf.protobufvarint32lengthfieldprepender;
import io.netty.handler.logging.loglevel;
import io.netty.handler.logging.logginghandler;
 
/**
 * protobuf版本图书订购代码
 */
public class subreqserver {
    private static final int port = 8080;
 
    public static void main(string[] args) {
        bind(port);
    }
 
    private static void bind(int port) {
        eventloopgroup bossgroup = new nioeventloopgroup();
        eventloopgroup workergroup = new nioeventloopgroup();
 
        serverbootstrap b = new serverbootstrap();
        try {
            b.group(bossgroup, workergroup)
                    .channel(nioserversocketchannel.class)
                    .option(channeloption.so_backlog, 100)
                    .handler(new logginghandler(loglevel.info))
                    .childhandler(new channelinitializer<socketchannel>() {
                        @override
                        protected void initchannel(socketchannel socketchannel) throws exception {
                            // 半包处理
                            socketchannel.pipeline().addlast(new protobufvarint32framedecoder());
                            socketchannel.pipeline().addlast(new protobufdecoder(subscribereqproto.subscribereq.getdefaultinstance()));
                            socketchannel.pipeline().addlast(new protobufvarint32lengthfieldprepender());
                            socketchannel.pipeline().addlast(new protobufencoder());
                            socketchannel.pipeline().addlast(new subreqserverhandler());
                        }
                    });
            channelfuture future = b.bind(port).sync();
            future.channel().closefuture().sync();
        } catch (interruptedexception e) {
            e.printstacktrace();
        }finally {
            bossgroup.shutdowngracefully();
            workergroup.shutdowngracefully();
        }
    }
}

4. subreqserverhandler.java

import cn.ddlover.nettystudy.protobuf.subscribereqproto;
import cn.ddlover.nettystudy.protobuf.subscriberespproto;
import io.netty.channel.channelhandlercontext;
import io.netty.channel.channelinboundhandleradapter;
 
public class subreqserverhandler extends channelinboundhandleradapter {
    @override
    public void channelread(channelhandlercontext ctx, object msg) throws exception {
        subscribereqproto.subscribereq req = (subscribereqproto.subscribereq)msg;
 
        if ("张三".equals(req.getusername())) {
            system.out.println("server accept clietn subscribe req : ["+req.tostring()+"]");
            ctx.writeandflush(resp(req.getsubreqid()));
        }
    }
 
    private subscriberespproto.subscriberesp resp(int subreqid) {
        subscriberespproto.subscriberesp.builder builder = subscriberespproto.subscriberesp.newbuilder();
        builder.setsubreqid(subreqid);
        builder.setrespcode(0);
        builder.setdesc("netty书籍下单成功,3天后将会送到你的住处");
        return builder. build();
    }
 
    @override
    public void exceptioncaught(channelhandlercontext ctx, throwable cause) throws exception {
        cause.printstacktrace();
        ctx.close();
    }
}

5. subreqclient.java

import cn.ddlover.nettystudy.handler.subreqclienthandler;
import cn.ddlover.nettystudy.protobuf.subscriberespproto;
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;
import io.netty.channel.nio.nioeventloopgroup;
import io.netty.channel.socket.socketchannel;
import io.netty.channel.socket.nio.niosocketchannel;
import io.netty.handler.codec.protobuf.protobufdecoder;
import io.netty.handler.codec.protobuf.protobufencoder;
import io.netty.handler.codec.protobuf.protobufvarint32framedecoder;
import io.netty.handler.codec.protobuf.protobufvarint32lengthfieldprepender;
 
public class subreqclient {
    private static final string host = "localhost";
    private static final int port = 8080;
 
    public static void main(string[] args) {
        connect(host, port);
    }
 
    private static void connect(string host, int port) {
        eventloopgroup group = new nioeventloopgroup();
 
        bootstrap b = new bootstrap();
        try {
            b.group(group).channel(niosocketchannel.class)
                    .option(channeloption.tcp_nodelay, true)
                    .handler(new channelinitializer<socketchannel>() {
                        @override
                        protected void initchannel(socketchannel socketchannel) throws exception {
 
                            // 半包处理
                            socketchannel.pipeline().addlast(new protobufvarint32framedecoder());
                            socketchannel.pipeline().addlast(new protobufdecoder(subscriberespproto.subscriberesp.getdefaultinstance()));
                            socketchannel.pipeline().addlast(new protobufvarint32lengthfieldprepender());
                            socketchannel.pipeline().addlast(new protobufencoder());
                            socketchannel.pipeline().addlast(new subreqclienthandler());
                        }
                    });
            channelfuture f = b.connect(host, port).sync();
            f.channel().closefuture().sync();
        } catch (interruptedexception e) {
            e.printstacktrace();
        }finally {
            group.shutdowngracefully();
        }
    }
}

6. subreqclienthandler.java

import cn.ddlover.nettystudy.protobuf.subscribereqproto;
import io.netty.channel.channelhandlercontext;
import io.netty.channel.channelinboundhandleradapter;
 
import java.util.arraylist;
import java.util.list;
 
public class subreqclienthandler extends channelinboundhandleradapter {
 
    @override
    public void channelactive(channelhandlercontext ctx) throws exception {
        for (int i =0;i<10;i++) {
            ctx.write(subreq(i));
        }
        ctx.flush();
    }
 
    private subscribereqproto.subscribereq subreq(int i) {
        subscribereqproto.subscribereq.builder builder = subscribereqproto.subscribereq.newbuilder();
        builder.setsubreqid(i);
        builder.setusername("张三");
        builder.setproductname("netty book");
        list<string> address = new arraylist<>();
        address.add("nanjing yuhuatai");
        address.add("beijing liulichang");
        address.add("shenzhen hongshulin");
        builder.addalladdress(address);
        return builder.build();
    }
 
    @override
    public void channelread(channelhandlercontext ctx, object msg) throws exception {
        system.out.println("receive server response : ["+ msg +"]");
    }
 
 
    @override
    public void exceptioncaught(channelhandlercontext ctx, throwable cause) throws exception {
        cause.printstacktrace();
        ctx.close();
    }
}

代码部分到此就结束了,然后分别运行subreqserversubreqclient, 就可以发现运行成功了。

这里要注意的事,调用tostring的时候,中文在控制台打印的是字节,而调用对应属性的getter方法的时候,是可以做中文的判断的,显然这里protobuf的tostring没有被修改好呀。

当然,我们也可以发现,netty本身已经封装好了对谷歌的protobuf的支持。netty还是很强大的。

到此这篇关于netty结合protobuf进行编解码的文章就介绍到这了,更多相关netty结合protobuf编解码内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!