Netty 4 实现一个 NettyClient
本文章为作者原创,有问题的地方请提出指正。
1、类继承Diagram

2、定义EndPoint类
目前仅仅定义了2个方法,分别用来获取本地或远程服务器的地址。
package netty; import java.net.InetSocketAddress; /**
* @author xfyou
* @date 2019/8/28
*/
public interface EndPoint { /**
* Return the local Inet address
*
* @return The local Inet address to which this <code>EndPoint</code> is bound, or <code>null</code>
* if this <code>EndPoint</code> does not represent a network connection.
*/
InetSocketAddress getLocalAddress(); /**
* Return the remote Inet address
*
* @return The remote Inet address to which this <code>EndPoint</code> is bound, or <code>null</code>
* if this <code>EndPoint</code> does not represent a network connection.
*/
InetSocketAddress getRemoteAddress(); }
2、定义AbstractClass类
主要是定义几个抽象方法:
- doOpen - 创建引导类Bootstrap;
- doConnect - 创建Channel并连接远程服务器;
- getChannel - 获取已创建的Channel
另外,提供了2个公共的方法给外部调用:
- send - 发送消息(OutBound)
- receive - 接收消息 (InBound)
内部私有的write()方法。write方法负责在connect成功后,把消息写到远程peer。翻阅源码,我们可以看到如下的调用栈:
- channel.writeAndFlush
- pipeline.writeAndFlush (pipleline为channel实例所关联的pipleline实例)
- AbstractChannelHandlerContext.writeAndFlush (每个ChannelHanlder都有一个对应的ChannelHandlerContext,可以从这个ChannelHanlderConext获取Channel、ChannelHanlder和ChannelPipeline)
- AbstractChannelHandlerContext.write(在这个方法里面有一个executor.inEventLoop()的判断,这个地方很重要,它主要是判断当前线程是否是EventLoop分配的线程,如果是则直接使用EventLoop分配的线程执行,否则会将当前要执行的任务封装成一个Task,然后塞到一个LinkedBlockQueue里面去等待后续的调度执行。这样做的目的主要是就是把用户线程的操作封装成Task放入队列,统一由I/O线程来处理)
- AbstractChannelHandlerContext.writeAndFlush (每个ChannelHanlder都有一个对应的ChannelHandlerContext,可以从这个ChannelHanlderConext获取Channel、ChannelHanlder和ChannelPipeline)
- pipeline.writeAndFlush (pipleline为channel实例所关联的pipleline实例)
package netty; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.NotImplementedException; import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; /**
* @author xfyou
* @date 2019/8/29
*/
@Slf4j
@RequiredArgsConstructor
abstract class AbstractClient implements EndPoint { @NonNull
private String hostName; @NonNull
private int port; @NonNull
@Getter(value = AccessLevel.PROTECTED)
private int connectionTimeout; protected final CountDownLatch countDownLatch = new CountDownLatch(1);
protected String respMsg; @SneakyThrows
public void send(Object message) {
doOpen();
doConnect();
write(message);
} @SneakyThrows
public String receive() {
boolean b = countDownLatch.await(getConnectionTimeout(), TimeUnit.MILLISECONDS);
if (!b) {
log.error("Timeout(" + getConnectionTimeout() + "ms) when receiving response message");
}
return respMsg;
} private void write(Object message) {
Channel channel = getChannel();
if (null != channel) {
ChannelFuture f = channel.writeAndFlush(byteBufferFrom(message)).syncUninterruptibly();
if (!f.isSuccess()) {
log.error("Failed to send message to " + getRemoteAddress() + f.cause().getMessage());
}
}
} private ByteBuf byteBufferFrom(Object message) {
return message instanceof String ? Unpooled.copiedBuffer((String) message, StandardCharsets.UTF_8) : Unpooled.copiedBuffer((byte[]) message);
} @Override
public InetSocketAddress getRemoteAddress() {
return new InetSocketAddress(hostName, port);
} @Override
public InetSocketAddress getLocalAddress() {
throw new NotImplementedException("This method is not need to be implemented");
} /**
* Open client.
*
* @throws Throwable
*/
protected abstract void doOpen() throws Throwable; /**
* Connect to server.
*
* @throws Throwable
*/
protected abstract void doConnect() throws Throwable; /**
* Get the connected channel.
*
* @return channel
*/
protected abstract Channel getChannel(); }
4、定义NettyClient类
NettyClient类继承了AbstractClient类,主要是实现了doOpen、doConnect、getChannel类;同时实现了一个自定义的ChannelHander用来在ChannelActive时获取Channel以及有消息返回时读取消息。
- doOpen方法的实现。创建引导类并在引导类上注册相关属性;
- 注册NioEventLoopGroup,基于java NIO传输的一个线程池,线程池的默认大小为:CPU核数*2。当一个新的Channel被创建后,Netty会从这个NioEventLoopGroup中选择一个线程来为此Channel创建一个关联的EventLoop(用来监听关联Channel的所有的I/O事件,比如连接、断开连接、读、写等);
- 注册NioSocketChannel类类型,这个类型说明将要创建的Channel的实例的类型,客户端为:NioSocketChannel,服务器端为:NioServerSocketChannel;Bootstrap会根据这个class来创建一个BootstrapChannelFactory<NioSocketChannel>实例(Channel工厂类,用于将来在connect时创建Channel);
- 设置相关Option选项
- 注册自定义的ChannelHandler,这些ChannelHandler会被注册到与Channel相关联的ChannelPipleline中,用来拦截消息并做相应的处理。
- doConnect方法的实现。通过已创建的Channel来连接到远程服务器。前面我们已经在Bootstrap中设置的超时时间,所以connect时可以使用忽略线程中断阻塞的方式去连接,直到超时。connect时会先通过BootstrapChannelFactory<NioSocketChannel>来创建一个NioSocketChannel实例,并把这个NioSocketChannel实例注册到NioEventGroup中去(从线程池中按某种算法选择一个EventLoop来和当前的Channel建立对应关系,可以是1:N,即一个EventLoop可以对应多个Channel )。EventLoop同时也是一个EventLoopExecutor,EventLoop和Channel对应起来后就可以处理所有这个Channel的I/O操作了。一句话,某个Channel的所有I/O操作都是线程池(NioEventGroup)中的某个I/O线程(EventLoopExecutor)来异步处理的。
package netty; import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j; import java.nio.charset.StandardCharsets; /**
* @author xfyou
* @date 2019/8/28
*/
@Slf4j
public class NettyClient extends AbstractClient { private Bootstrap bootstrap; private volatile Channel channel; private static final NioEventLoopGroup NIO_GROUP = new NioEventLoopGroup(); public NettyClient(String hostName, int port, int connectionTimeout) {
super(hostName, port, connectionTimeout);
} private class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
channel = ctx.channel();
} @Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
try {
respMsg = msg.toString(StandardCharsets.UTF_8);
} finally {
countDownLatch.countDown();
ctx.close();
}
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.error("An exception was thrown, cause:" + cause.getMessage());
ctx.close();
}
} @Override
protected void doOpen() throws Throwable {
bootstrap = new Bootstrap();
bootstrap
.group(NIO_GROUP)
.remoteAddress(getRemoteAddress())
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getConnectionTimeout())
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ClientHandler());
}
});
} @Override
public void doConnect() {
ChannelFuture f = bootstrap.connect().syncUninterruptibly();
if (!f.isSuccess() && null != f.cause()) {
log.error("The client failed to connect the server:" + getRemoteAddress() + ",error message is:" + f.cause().getMessage());
}
} @Override
protected Channel getChannel() {
return channel;
} }
5、测试类
package netty; import lombok.SneakyThrows; /**
* Test
*
* @author xfyou
*/
public class Test { @SneakyThrows
public static void main(String[] args) {
NettyClient client = new NettyClient("127.0.0.1", 8080, 45000);
client.send("aaa".getBytes());
// maybe do something else
System.out.println(client.receive());
} }
Netty 4 实现一个 NettyClient的更多相关文章
- Netty实现的一个异步Socket代码
本人写的一个使用Netty实现的一个异步Socket代码 package test.core.nio; import com.google.common.util.concurrent.ThreadF ...
- Netty(1):第一个netty程序
为什么选择Netty netty是业界最流行的NIO框架之一,它的健壮型,功能,性能,可定制性和可扩展性都是首屈一指的,Hadoop的RPC框架Avro就使用了netty作为底层的通信框架,此外net ...
- 【Netty】第一个Netty应用
一.前言 前面已经学习完了Java NIO的内容,接着来学习Netty,本篇将通过一个简单的应用来了解Netty的使用. 二.Netty应用 2.1 服务端客户端框架图 下图展示了Netty中服务端与 ...
- java使用netty模拟实现一个类dubbo的分布式服务调用框架
本文较长,如果想直接看代码可以查看项目源码地址: https://github.com/hetutu5238/rpc-demo.git 要想实现分布式服务调用框架,我们需要了解分布式服务一般需要的功能 ...
- netty系列之:一个价值上亿的网站速度优化方案
目录 简介 本文的目标 支持多个图片服务 http2处理器 处理页面和图像 价值上亿的速度优化方案 总结 简介 其实软件界最赚钱的不是写代码的,写代码的只能叫马龙,高级点的叫做程序员,都是苦力活.那么 ...
- 使用netty的第一个Hello World
server端 package com.netty.test;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.Cha ...
- netty(二) 创建一个netty服务端和客户端
服务端 NettyServer package com.zw.netty.config; import com.zw.netty.channel.ServerInitializer;import io ...
- Netty+SpringBoot写一个基于Http协议的文件服务器
本文参考<Netty权威指南> NettyApplication package com.xh.netty; import org.springframework.boot.SpringA ...
- 使用Netty实现的一个简单HTTP服务器
1.HttpServer,Http服务启动类,用于初始化各种线程和通道 public class HttpServer { public void bind(int port) throws Exce ...
随机推荐
- Java开发环境之Tomcat
查看更多Java开发环境配置,请点击<Java开发环境配置大全> 壹章:Tomcat安装教程 1)去官网下载安装包 http://tomcat.apache.org/ 建议下载压缩包(zi ...
- 服务器架构前面加了防火墙,Nginx如何获取客户端真实ip???
在大部分实际业务场景中,网站访问请求并不是简单地从用户(访问者)的浏览器直达网站的源站服务器,中间可能经过所部署的CDN.高防IP.WAF等代理服务器.例如,网站可能采用这样的部署架构:用户 > ...
- Python 大文件处理
非内存资源可以使用with 在python中逐行读取大文件 在我们日常工作中,难免会有处理日志文件的时候,当文件小的时候,基本不用当心什么,直接用file.read()或readlines()就可以了 ...
- zabbix--基本操作
zabbix 快速上手 示例一些zabbix的最基本的配置: 添加主机群组:添加主机:创建监控项:创建触发器 添加主机群组 参考官档:https://www.zabbix.com/documentat ...
- commix 命令注入工具
关于系统命令注入,可以参考这篇文章:命令攻击介绍 系统命令注入场景 在对企业进行安全测试时候,很少会发现系统注入漏洞.这是因为大部分情况下代码业务主要是数据操作.文件操作.逻辑处理和api接口调用等, ...
- Pycharm中设置默认头注释
在编写Python项目时,我们可能需要添加一些默认的信息,比如添加文件创建的时间,比如添加文件作者,等等,这些信息可以自己在python脚本中添加,但是也可以在Pycharm中配置模板,每次创建文件的 ...
- qt 静态库中貌似不能使用静态属性
今天一个问题搞的很郁闷,原本好好的工程,这两天加了几个类之后链接不通过了. 发现过程略去不说,最后去掉了类中的static属性,编译通过了.具体原因没时间仔细查了,反正尽量避免在静态链接库里使用sta ...
- js 用 hasOwnProperty() 判定属性是来自该对象成员,还是原型链
var People=function(){ this.name='liujinyu'; }; People.prototype={ ...
- MySQL中列别名为中文时,Order by 子句中使用别名时不要加引号
暂时还不清楚原因 1.按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩 SC表: 这里,当做总成绩处理 select sid, sum(score) as '总成绩', avg(score) ...
- kuma 学习一 minikube 安装
官方文档提供了比较全的环境安装说明 我使用的系统是mac,同时使用minikube 运行 安装kumactl 下载地址: https://kong.bintray.com/kuma/kuma-0.1. ...