使用Java搭建一个简单的Netty通信例子

看过dubbo源码的同学应该都清楚,使用dubbo协议的底层通信是使用的netty进行交互,而最近看了dubbo的Netty部分后,自己写了个简单的Netty通信例子。

准备

工程截图

模块详解

rpc-common

rpc-common作为各个模块都需使用的模块,工程中出现的是一些通信时请求的参数以及返回的参数,还有一些序列化的工具。

rpc-client

rpc-client中目前只是单单的一个NettyClient启动类。

rpc-server

rpc-client中目前也只是单单的一个NettyServer服务启动类。

需要的依赖

目前所有的依赖项都出现在 rpc-common 下的 pom.xml中。

io.netty

netty-all

4.1.10.Final

org.slf4j

slf4j-log4j12

1.7.25

com.dyuproject.protostuff

protostuff-core

1.0.9

com.dyuproject.protostuff

protostuff-runtime

1.0.9

org.objenesis

objenesis

2.1

com.alibaba

fastjson

1.2.38

实现

首先我们在common中先定义本次的Request和Response的基类对象。

public class Request {

private String requestId;

private Object parameter;

public String getRequestId() {

return requestId;

}

public void setRequestId(String requestId) {

this.requestId = requestId;

}

public Object getParameter() {

return parameter;

}

public void setParameter(Object parameter) {

this.parameter = parameter;

}

}

public class Response {

private String requestId;

private Object result;

public String getRequestId() {

return requestId;

}

public void setRequestId(String requestId) {

this.requestId = requestId;

}

public Object getResult() {

return result;

}

public void setResult(Object result) {

this.result = result;

}

}

使用fastJson进行本次序列化

Netty对象的序列化转换很好懂, ByteToMessageDecoder 和 MessageToByteEncoder 分别只要继承它们,重写方法后,获取到Object和Byte,各自转换就OK。

不过如果是有要用到生产上的同学,建议不要使用 fastJson,因为它的漏洞补丁真的是太多了,可以使用google的 protostuff。

public class RpcDecoder extends ByteToMessageDecoder {

// 目标对象类型进行解码

private Class> target;

public RpcDecoder(Class target) {

this.target = target;

}

@Override

protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {

if (in.readableBytes() < 4) { // 不够长度丢弃

return;

}

in.markReaderIndex(); // 标记一下当前的readIndex的位置

int dataLength = in.readInt(); // 读取传送过来的消息的长度。ByteBuf 的readInt()方法会让他的readIndex增加4

if (in.readableBytes() < dataLength) { // 读到的消息体长度如果小于我们传送过来的消息长度,则resetReaderIndex. 这个配合markReaderIndex使用的。把readIndex重置到mark的地方

in.resetReaderIndex();

return;

}

byte[] data = new byte[dataLength];

in.readBytes(data);

Object obj = JSON.parseObject(data, target); // 将byte数据转化为我们需要的对象

out.add(obj);

}

}

public class RpcEncoder extends MessageToByteEncoder {

//目标对象类型进行编码

private Class> target;

public RpcEncoder(Class target) {

this.target = target;

}

@Override

protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {

if (target.isInstance(msg)) {

byte[] data = JSON.toJSONBytes(msg); // 使用fastJson将对象转换为byte

out.writeInt(data.length); // 先将消息长度写入,也就是消息头

out.writeBytes(data); // 消息体中包含我们要发送的数据

}

}

}

NetyServer

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

@Override

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

Request request = (Request) msg;

System.out.println("Client Data:" + JSON.toJSONString(request));

Response response = new Response();

response.setRequestId(request.getRequestId());

response.setResult("Hello Client !");

// client接收到信息后主动关闭掉连接

ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);

}

@Override

public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

ctx.flush();

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

ctx.close();

}

}

public class NettyServer {

private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);

private String ip;

private int port;

public NettyServer(String ip, int port) {

this.ip = ip;

this.port = port;

}

public void server() throws Exception {

EventLoopGroup bossGroup = new NioEventLoopGroup();

EventLoopGroup workerGroup = new NioEventLoopGroup();

try {

final ServerBootstrap serverBootstrap = new ServerBootstrap();

serverBootstrap.group(bossGroup, workerGroup)

.channel(NioServerSocketChannel.class)

.option(ChannelOption.SO_BACKLOG, 1024)

.option(ChannelOption.SO_SNDBUF, 32 * 1024)

.option(ChannelOption.SO_RCVBUF, 32 * 1024)

.option(ChannelOption.SO_KEEPALIVE, true)

.childHandler(new ChannelInitializer() {

protected void initChannel(SocketChannel socketChannel) throws Exception {

socketChannel.pipeline().addLast(new RpcDecoder(Request.class))

.addLast(new RpcEncoder(Response.class))

.addLast(new NettyServerHandler());

}

});

serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); // 开启长连接

ChannelFuture future = serverBootstrap.bind(ip, port).sync();

// if (future.isSuccess()) {

//

// new Register().register("/yanzhenyidai/com.yanzhenyidai.server", ip + ":" + port);

// }

future.channel().closeFuture().sync();

} finally {

bossGroup.shutdownGracefully();

workerGroup.shutdownGracefully();

}

}

public static void main(String[] args) throws Exception {

new NettyServer("127.0.0.1", 20000).server();

}

}

关键名词:

EventLoopGroup

workerGroup

bossGroup

Server端的EventLoopGroup分为两个,一般workerGroup作为处理请求,bossGroup作为接收请求。

ChannelOption

SO_BACKLOG

SO_SNDBUF

SO_RCVBUF

SO_KEEPALIVE

以上四个常量作为TCP连接中的属性。

ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);

NettyServerHandler中出现的 ChannelFutureListener.CLOSE ,作为Server端主动关闭与Client端的通信,如果没有主动Close,那么NettyClient将会一直处于阻塞状态,得不到NettyServer的返回信息。

NettyClient

public class NettyClient extends SimpleChannelInboundHandler {

private final String ip;

private final int port;

private Response response;

public NettyClient(String ip, int port) {

this.ip = ip;

this.port = port;

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

ctx.close();

}

@Override

protected void channelRead0(ChannelHandlerContext channelHandlerContext, Response response) throws Exception {

this.response = response;

}

public Response client(Request request) throws Exception {

EventLoopGroup group = new NioEventLoopGroup();

try {

// 创建并初始化 Netty 客户端 Bootstrap 对象

Bootstrap bootstrap = new Bootstrap();

bootstrap.group(group);

bootstrap.channel(NioSocketChannel.class);

bootstrap.handler(new ChannelInitializer() {

@Override

public void initChannel(SocketChannel channel) throws Exception {

ChannelPipeline pipeline = channel.pipeline();

pipeline.addLast(new RpcDecoder(Response.class));

pipeline.addLast(new RpcEncoder(Request.class));

pipeline.addLast(NettyClient.this);

}

});

bootstrap.option(ChannelOption.TCP_NODELAY, true);

// String[] discover = new Discover().discover("/yanzhenyidai/com.yanzhenyidai.server").split(":");

// 连接 RPC 服务器

ChannelFuture future = bootstrap.connect(ip, port).sync();

// 写入 RPC 请求数据并关闭连接

Channel channel = future.channel();

channel.writeAndFlush(request).sync();

channel.closeFuture().sync();

return response;

} finally {

group.shutdownGracefully();

}

}

public static void main(String[] args) throws Exception {

Request request = new Request();

request.setRequestId(UUID.randomUUID().toString());

request.setParameter("Hello Server !");

System.out.println(JSON.toJSONString(new NettyClient("127.0.0.1", 30000).client(request)));

}

}

测试

如果以上所有内容都准备就绪,那么就可以进行调试了。

启动顺序,先启动NettyServer,再启动NettyClient。

总结

记得刚出来工作时,有工作很多年的同事问我了不了解Netty,当时工作太短,直说听过Putty,现在回想起来真的挺丢人的,哈哈。

netty实现简单时事通讯_简单的Java实现Netty进行通信相关推荐

  1. netty实现简单时事通讯_使用 RSocket 进行反应式数据传输

    在微服务架构中,不同服务之间通过应用协议进行数据传输.典型的传输方式包括基于 HTTP 协议的 REST 或 SOAP API 和基于 TCP 字节流的 gRPC 等.HTTP 协议的优势在于其广泛的 ...

  2. python简单的编程_简单的Python2.7编程初学经验总结

    如果你从来没有使用过Python,我强烈建议你阅读Python introduction,因为你需要知道基本的语法和类型. 包管理 Python世界最棒的地方之一,就是大量的第三方程序包.同样,管理这 ...

  3. python简单圣诞树手工折纸_简单立体手工折纸圣诞树的视频教程

    简单立体手工折纸圣诞树的视频教程 简单折纸的创意手工折叠制作教程能够教会我们如何通过折叠塑形完成我们想要的制作来.这里纸艺网向大家推荐的这个手工折纸制作教程为一个简单的立体折纸圣诞树的制作哦.圣诞树作 ...

  4. python简单圣诞树手工折纸_简单手工折纸圣诞树折纸大全图解教程

    简单手工折纸圣诞树折纸大全图解教程 有许多复杂的手工折纸圣诞树教程正在折磨着喜欢圣诞节手工纸艺DIY的朋友们,为了能够让更多喜欢手工DIY,但是排斥过于复杂的手工折纸制作的朋友也能够在圣诞节即将到来之 ...

  5. 计算机的数学知识的手抄报图片大全,【数学小报图片大全】数学小报简单又漂亮_简单数学小报图片大全_亲亲宝贝网...

    数学小报图片大全:数学的定义与结构 定义 亚里士多德把数学定义为"数量科学",这个定义直到18世纪.从19世纪开始,数学研究越来越严格,开始涉及与数量和量度无明确关系的群论和投影几 ...

  6. 简单人物画像_简单的素描画人物图片

    简单的素描画人物图片 人物素描是素描中最常见的,所以我们一定要掌握好关于人物素描的一些方法技巧才行.下面给大家整理了简单的素描画人物图片,欢迎阅读! 简单的素描画人物图片1 简单的素描画人物图片2 简 ...

  7. 简单视频会议软件_简单而免费的视频会议

    简单视频会议软件 video conferencing. Like most of our services, it is free of charge. It is built on a relia ...

  8. python简单圣诞树手工折纸_简单折纸圣诞树手工制作教程教你如何折简单的圣诞树...

    简单折纸圣诞树手工制作教程教你如何折简单的圣诞树 圣诞节到来之际,各种有趣的折纸手工制作开始受到大家的关注和喜爱哦.纸艺网也开始给大家推荐一些有趣的手工折纸制作.这里纸艺网推荐的这个折纸制作是一个折纸 ...

  9. python简单圣诞树手工折纸_简单折纸圣诞树教你如何制作折纸圣诞树

    简单折纸圣诞树教你如何制作折纸圣诞树 圣诞树作为圣诞节中最为重要的装饰性元素在圣诞节到来的时候肯定是不能够少的哦.喜欢手工制作的同学相信对于圣诞树的期待还是比较高的.纸艺网当然了解大家的想法啦.所以在 ...

最新文章

  1. PHP使用curl_multi_add_handle并行处理
  2. 数据结构(七)---循环队列的实现---java版
  3. [剑指offer]面试题18:树的子结构
  4. K8S中手动扩容云盘数据卷
  5. SpringBoot整合Shiro实现权限管理,经典实战教程
  6. 年底促销海报还没准备好?电商美工看这里
  7. strstr区分大小写吗_ONU、机顶盒、路由器,遇到问题你会处理了吗?
  8. 红帽linux64系统下载,红帽rhel6.5下载
  9. win10怎么把c盘锁住_win10怎样锁住c盘 win10删除c盘无用文件
  10. AcWing 1402. 星空之夜(Flood Fill/哈希/DFS)
  11. 三维实时云渲染平台解决方案
  12. 程序员夏天穿格子衫,那么冬天穿什么?
  13. ccf认证--201809-1 卖菜(100分)
  14. ServiceNow主要模块介绍
  15. 计算机显示另外一个用户登录,电脑两个用户怎么在欢迎界面只显示一个?
  16. 治愈系英语笔记-2-一般、否定疑问句,现表将来
  17. 【cudaMemcpy】
  18. SEO人员,做好SEO的三大要素有哪些?
  19. Mvc 微信企业号开发之ACCESS_TOKE 获取
  20. 安装VS2003出现“FrontPage 2000 WEB 扩展客户端”安装失败时

热门文章

  1. ProtoBuf协议详解
  2. Linux修改文件句柄数与vm.max_map_count参数
  3. SQL中不建议使用 where 1=1 的说法,是错误的
  4. java 不定长参数实现原理
  5. 小雨的矩阵(DFS三参数模板题)
  6. 8.0强行转换后变成了7_如何在服务器上安装SSL证书,让你的网站变成https
  7. 面向对象开发方法概述
  8. JS中setInterval、setTimeout不能传递带参数的函数的解决方法
  9. ORACLE获取某个时间段之间的月份列表和日期列表
  10. 帐户当前被锁定,所以用户 sa 登录失败。系统管理员无法将该帐户解锁 解决方法...