上一篇文章已经实现了通过Netty整合protobuf进行简单的客户端给服务端发消息,功能上来说protobuf确实不太能看出有其他业务能力,但是性能上来说确实是提升了很多毕竟主要作用是序列化后和反序列化。但是一个问题是如果.proto里面有多个message,那么方法内该如何识别传输/接收哪个message属性呢???
接下来说一下解决这个问题的步骤:可以将多个message放在外部,然后一个主message,在里面通过DataType设立一个枚举组来调用他们,并且使用oneof保证一次只调用一个节省内存。
syntax = "proto2";
package com.gongda.protobuf;
option optimize_for = SPEED;
option java_package = "netty.protobuf";
option java_outer_classname = "DataInfo";
message MyMessage{
enum DataType{
StudentType = 1;
DogType = 2;
CatType = 3;
}
required DataType data_type = 1;
oneof dataBody{
Student student = 2;
Dog dog = 3;
Cat cat = 4;
}
}
message Student{
optional string name = 1;
optional int32 age = 2;
optional string address = 3;
}
message Dog{
optional string name = 1;
optional int32 age = 2;
}
message Cat{
optional string name = 1;
optional string city = 2;
}
oneof:有多个字段但是只想选择其中的一个字段的时候,oneof可以节省内存
既然选择了这个规则,有一个原则不能忘记,在调用的时候只能调用最外部的message去实现,就是MyMessage而且为了保证是可以去随意去调用的,要去修改Handler并且用一个随机函数来证明。。。
现在主要来说一下逻辑,先说说客户端的Handler要使用客户端给服务器端发消息,在SpringMVC中服务端虽然不用去写,是前端的工作但是必须要说一下还是建议去了解Servlet生命周期。。
Handler实现中要了解的事情:
- 显示出上面提到的oneof关键字的效果,需要用一个随机数体现出每次只实现其中一个message逻辑
- 发送信息的格式还是采用设计模式中的构造者模式,先实现枚举组中最大的DataType,然后由DataType去实现,内部包含三个Message
- 节省空间采用单例模式,保证只实例化一次。
TestClientHandler:
package netty.sixthexample;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import netty.protobuf.DataInfo;
import java.util.Random;
public class TestClientHandler extends SimpleChannelInboundHandler<DataInfo.MyMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DataInfo.MyMessage msg) throws Exception {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
int randomInt = new Random().nextInt(3);
DataInfo.MyMessage myMessage = null;
if (0 == randomInt) {
myMessage = DataInfo.MyMessage.newBuilder().setDataType(DataInfo.MyMessage.DataType.StudentType).setStudent(DataInfo.Student.newBuilder().setName("zxvc").setAge(18).setAddress("anhui").build()).build();
}else if (1 == randomInt){
myMessage = DataInfo.MyMessage.newBuilder().setDataType(DataInfo.MyMessage.DataType.DogType).setDog(DataInfo.Dog.newBuilder().setName("happy").setAge(1).build()).build();
}else {
myMessage = DataInfo.MyMessage.newBuilder().setDataType(DataInfo.MyMessage.DataType.CatType).setCat(DataInfo.Cat.newBuilder().setName("haha").setCity("上海").build()).build();
}
ctx.channel().writeAndFlush(myMessage);
}
}
现在来说一下服务端Handler 的实现思路:
- msg里面封装好了Client发送过来的信息,信息确实在msg但是确切说是在msg的dataType里面,因此首先要获取到dataType
- 获取到DataType后可以根据枚举组去获取到想要的信息了,但是注意的是因为之前用的是随机,因此这里肯定也是if-else去获取信息
来看一下TestServerHandler的具体实现:
package netty.sixthexample;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import netty.protobuf.DataInfo;
public class TestServerHandler extends SimpleChannelInboundHandler<DataInfo.MyMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DataInfo.MyMessage msg) throws Exception {
DataInfo.MyMessage.DataType dataType = msg.getDataType();
if (dataType == DataInfo.MyMessage.DataType.StudentType) {
DataInfo.Student student = msg.getStudent();
System.out.println(student.getName());
System.out.println(student.getAge());
System.out.println(student.getAddress());
}else if (dataType == DataInfo.MyMessage.DataType.DogType){
DataInfo.Dog dog = msg.getDog();
System.out.println(dog.getName());
System.out.println(dog.getAge());
}else {
DataInfo.Cat cat = msg.getCat();
System.out.println(cat.getName());
System.out.println(cat.getCity());
}
}
}