关闭

Netty5入门学习笔记004-使用Netty传输POJO对象(上)

231人阅读 评论(0) 收藏 举报

使用Netty传输POJO对象,重点在于对象的序列化,序列化后的对象可以通过TCP流进行网络传输,结合Netty提供的对象编解码器,可以做到远程传输对象。

下面我们来看一个例子:模拟订票

首先Java序列化的POJO对象需要实现java.io.Serializable接口。

说明:还有很多种序列化的方式要比JDK自带的序列化要好 体积小利于保存和传输 例如google的protobuf和jboss的Marshalling 

火车车次和余票量POJO:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package bookticket;
 
import java.io.Serializable;
/**
 * 火车pojo对象
 * @author xwalker
 */
public class Train implements Serializable {
    private static final long serialVersionUID = 1510326612440404416L;
    private String number;//火车车次
    private int ticketCounts;//余票数量
    public Train(String number,int ticketCounts){
        this.number=number;
        this.ticketCounts=ticketCounts;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
    public int getTicketCounts() {
        return ticketCounts;
    }
    public void setTicketCounts(int ticketCounts) {
        this.ticketCounts = ticketCounts;
    }
 
}

车票POJO:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package bookticket;
 
import java.io.Serializable;
import java.util.Date;
/**
 * 订票POJO对象
 * @author xwalker
 */
public class Ticket implements Serializable {
    private static final long serialVersionUID = 4228051882802183587L;
    private String trainNumber;//火车车次
    private int carriageNumber;//车厢编号
    private String seatNumber;//座位编号
    private String number;//车票编号
    private User user;//订票用户
    private Date bookTime;//订票时间
    private Date startTime;//开车时间
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
 
    public Date getBookTime() {
        return bookTime;
    }
    public void setBookTime(Date bookTime) {
        this.bookTime = bookTime;
    }
    public Date getStartTime() {
        return startTime;
    }
    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public String getTrainNumber() {
        return trainNumber;
    }
    public void setTrainNumber(String trainNumber) {
        this.trainNumber = trainNumber;
    }
    public int getCarriageNumber() {
        return carriageNumber;
    }
    public void setCarriageNumber(int carriageNumber) {
        this.carriageNumber = carriageNumber;
    }
    public String getSeatNumber() {
        return seatNumber;
    }
    public void setSeatNumber(String seatNumber) {
        this.seatNumber = seatNumber;
    }
}

用户POJO:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package bookticket;
 
import java.io.Serializable;
/**
 * 用户POJO对象
 * @author xwalker
 */
public class User implements Serializable {
    private static final long serialVersionUID = -3845514510571408376L;
    private String userId;//身份证
    private String userName;//姓名
    private String phone;//电话
    private String email;//邮箱
    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
}

请求指令集:通讯使用的固定指令集 服务器和客户端统一

?
1
2
3
4
5
6
7
8
9
10
11
12
package bookticket;
 
/**
 * 指令集
 * @author xwalker
 *
 */
public class Code {
    public static final int CODE_SEARCH=1;//查询车票余量
    public static final int CODE_BOOK=2;//订票
    public static final int CODE_NONE=-1;//错误指令 无法处理
}

客户端发送的请求信息:客户端发送一条请求信息 根据code属性确定此消息需要服务器做出如何响应 依据Code.java中定义的查票还是订票等

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package bookticket;
 
import java.io.Serializable;
import java.util.Date;
/**
 * 订票人发送查询余票和订票使用的请求信息
 * @author xwalker
 *
 */
public class BookRequestMsg implements Serializable {
    private static final long serialVersionUID = -7335293929249462183L;
    private User user;//发送订票信息用户
    private String trainNumber;//火车车次
    private int code;//查询命令
    private Date startTime;//开车时间
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public String getTrainNumber() {
        return trainNumber;
    }
    public void setTrainNumber(String trainNumber) {
        this.trainNumber = trainNumber;
    }
    public Date getStartTime() {
        return startTime;
    }
    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
 
}

服务器接收订票和查票后处理完业务反馈客户端的信息:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package bookticket;
 
import java.io.Serializable;
import java.util.Date;
/**
 * 订票成功与否反馈信息
 * @author xwalker
 */
public class BookResponseMsg implements Serializable {
    private static final long serialVersionUID = -4984721370227929766L;
    private boolean success;//是否操作成功
    private User user;//请求用户
    private String msg;//反馈信息
    private int code;//请求指令
    private Train train;//火车车次
    private Date startTime;//出发时间
    private Ticket ticket;//订票成功后具体出票票据
    public boolean getSuccess() {
        return success;
    }
    public void setSuccess(boolean success) {
        this.success = success;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Ticket getTicket() {
        return ticket;
    }
    public void setTicket(Ticket ticket) {
        this.ticket = ticket;
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public Train getTrain() {
        return train;
    }
    public void setTrain(Train train) {
        this.train = train;
    }
    public Date getStartTime() {
        return startTime;
    }
    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
     
}

订票服务器:主要是配置对象解码器 netty会自动将序列化的pojo对象编码 解码 无需自己额外处理 只需要依据配置即可

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package bookticket;
 
import java.util.ArrayList;
import java.util.List;
 
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.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
 
/**
 * 订票服务器端
 * @author xwalker
 *
 */
public class BookTicketServer {
    public static List<Train> trains;
    /**
     * 初始化 构造车次和车票余数
     */
    public BookTicketServer() {
        trains=new ArrayList<Train>();
        trains.add(new Train("G242",500));
        trains.add(new Train("G243",200));
        trains.add(new Train("D1025",100));
        trains.add(new Train("D1235",0));
    }
    public void bind(int port) throws Exception{
        //配置NIO线程组
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        try{
            //服务器辅助启动类配置
            ServerBootstrap b=new ServerBootstrap();
            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 ch) throws Exception {
                    //添加对象解码器 负责对序列化POJO对象进行解码 设置对象序列化最大长度为1M 防止内存溢出
                    //设置线程安全的WeakReferenceMap对类加载器进行缓存 支持多线程并发访问  防止内存溢出 
                    ch.pipeline().addLast(new ObjectDecoder(1024*1024,ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
                    //添加对象编码器 在服务器对外发送消息的时候自动将实现序列化的POJO对象编码
                    ch.pipeline().addLast(new ObjectEncoder());
                    ch.pipeline().addLast(new BookTicketServerhandler());
                }
            });
            //绑定端口 同步等待绑定成功
            ChannelFuture f=b.bind(port).sync();
            //等到服务端监听端口关闭
            f.channel().closeFuture().sync();
        }finally{
            //优雅释放线程资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
 
    public static void main(String[] args) throws Exception {
        int port =8000;
        new BookTicketServer().bind(port);
    }
 
}

服务器端网络IO处理器,查票订票业务处理和反馈:根据客户端请求信息中的code指令 确定是查票还是订票

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51