辅助链接

Dubbo系列之 (一)SPI扩展

Dubbo系列之 (二)Registry注册中心-注册(1)

Dubbo系列之 (三)Registry注册中心-注册(2)

Dubbo系列之 (四)服务订阅(1)

Dubbo系列之 (五)服务订阅(2)

Dubbo系列之 (六)服务订阅(3)

Dubbo系列之 (七)链路层那些事(1)

在讲解dubboTCP端的设计时,先了解下一些类的关系图。它们是如何组织在一起的,每个功能又是什么,接着在进一步深入了解其内涵。

类简介

1、Exchangers(交换器工具类) 用来创建TCP服务(bind)和建立客户端连接(connect)辅助类

2、Transporters(数据流传输工具类)用来创建TCP服务(bind)和建立客户端连接(connect)辅助类,Exchangers的底层内容依赖于Transporters,并且Transporters会根据SPI扩展,来适配合适的tcp通讯框架,比如netty,mina等。

3、Exchanger(交换器) 用来创建TCP链接,通过工具类Exchangers完成,该接口是一个SPI扩展,目前唯一仅有就是HeaderExchanger。从名字的含义可以得到,该协议是具有自定义协议头的交换器,所以取名HeaderExchanger。

4、Transporter(数据传输层) 用来创建TCP连接,通过工具类Transporters完成。它也是一个SPI扩展,比如NettyTransporter,MinaTransporter。

5、ExchangeClient (交换器客户端),Exchanger的connect()方法返回,即建立了TCP连接后,返回的客户端,接着就是通过该客户端与服务端通信,实例有HeaderExchangeClient、LazyConnectExchangeClient、ReferenceCountExchangeClient。之后分别讲解这3个,Exchangers工具类建立的连接客户端是HeaderExchangeClient。

6、ExchangeServer (交换器服务端端) Exchanger的bind()方法返回,即服务端监听的服务端实例,它监听这某个具体的tcp端口。默认实现是HeaderExchangeServer。

7、RemotingServer(远程的TCP服务端),ExchangeServer类也实现了该接口,代表其也是一个远程服务器,具体的实现有NettyServer,由Transporter的bind()方法返回,具体的Transporter返回相应的远程服务端。比如NettyTransporter#bind()返回NettyServer。

8、Client(TCP客户端),ExchangeClient类也实现了该接口,代表其也是一个TCP客户端,具体实现有NettyClient,由Transporter的connect()方法返回,具体的Transporter返回相应的TCP客户端。比如NettyTransporter#connect()返回NettyClient。

9、Channel (通信通道) ,每建立一个TCP链接就相应创建一个Channel。比如Netty建立连接后,就有一个Channel。这里的Channel指的是dubbo自己定义的一个channel。它与netty的channel建立关联,通过NettyChannel类,框架操作的是NettyChannel,而NettyChannel内部持有一个netty的channel对象。

10、HeaderExchangeChannel(交换器Channel,ExchangeChannel属于交换器Channel),它被HeaderExchangeClient客户端所持有,客户端就是通过HeaderExchangeChannel进行通信的,HeaderExchangeChannel内部持有一个具体的Channel。

11、ChannelHandler (通道处理器) 用来处理建立连接、发送请求、结束请求等操作的具体抽象。

12、ChannelHandlers(通道处理器工具类) 主要用来包裹封装具体的Channel,它的作用是通过消息类型,根据Dispatcher返回不同的

13、Dispatcher(消息派发器)

类型 Dispatcher Channelhandler 作用
All AllDispatcher AllChannelHandler 所有的消息类型全部通过业务线程池处理
Connection ConnectionOrderedDispatcher ConnectionOrderedChannelHandler 连接、断开消息单独通过一个线程池池来处理,其他的读写等消息通过业务线程池处理
Direct DirectDispatcher DirectChannelHandler 所有的消息都通过IO线程池处理,不放到业务线程池中
Execution ExecutionDispatcher ExecutionChannelHandler 请求消息在业务线程池处理,其他消息在IO线程池。
Message MessageOnlyDispatcher MessageOnlyChannelHandler 请求和响应消息在业务线程池处理,其他心跳,连接等消息在IO线程池处理

类关系图

试一把,Netty操作--客户端多线程,单链路(TCP)

1、定义传输消息

@Data
@ToString
public class SampleMessage { private String threadName; private String id; private String desc;
}

2、编写编码器

public class SampleEncoder extends MessageToByteEncoder<SampleMessage> {

    protected void encode(ChannelHandlerContext channelHandlerContext, SampleMessage sampleMessage, ByteBuf byteBuf) throws Exception {

        String threadName = sampleMessage.getThreadName();
String id = sampleMessage.getId();
String desc = sampleMessage.getDesc(); byteBuf.writeInt(threadName.getBytes().length);
byteBuf.writeBytes(threadName.getBytes()); byteBuf.writeInt(id.getBytes().length);
byteBuf.writeBytes(id.getBytes()); byteBuf.writeInt(desc.getBytes().length);
byteBuf.writeBytes(desc.getBytes());
String str = sampleMessage.getThreadName() + ":" + sampleMessage.getDesc() + ":" + sampleMessage.getId(); System.out.println(str);
}
}

3、编写解码器

public class SampleDecoder extends ByteToMessageDecoder {

    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {

        byteBuf.markReaderIndex();

        String threadName = read(byteBuf);
if (threadName == null) {
byteBuf.resetReaderIndex();
return;
} String id = read(byteBuf);
if (id == null) {
byteBuf.resetReaderIndex();
return;
} String desc = read(byteBuf);
if (desc == null) {
byteBuf.resetReaderIndex();
return;
} SampleMessage sampleMessage = new SampleMessage();
sampleMessage.setId(id);
sampleMessage.setThreadName(threadName);
sampleMessage.setDesc(desc);
list.add(sampleMessage);
} private String read(ByteBuf byteBuf) {
if (canReadInt(byteBuf)) {
int readInt = byteBuf.readInt();
if (canReadN(byteBuf, readInt)) {
byte[] bytes = new byte[readInt];
byteBuf.readBytes(bytes);
return new String(bytes);
}
}
return null;
} private boolean canReadInt(ByteBuf byteBuf) {
return canReadN(byteBuf, 4);
} private boolean canReadN(ByteBuf byteBuf, int n) {
if (!byteBuf.isReadable()) {
return false;
}
return byteBuf.readableBytes() >= n;
}
}

4、编写消息处理器

public class PrintChannelHandlers extends ChannelInboundHandlerAdapter {

    @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof SampleMessage) {
SampleMessage sampleMessage = (SampleMessage) msg;
System.out.println(sampleMessage.getThreadName() + ":" + sampleMessage.getId() + ":" + sampleMessage.getDesc());
}
} }

5、编写服务端

public class NettyServerMain {

    public static void main(String[] args) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(new NioEventLoopGroup(1), new NioEventLoopGroup(12))
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
// .addLast("log",new LoggingHandler(LogLevel.INFO))
.addLast("decoder", new SampleDecoder())
.addLast("encoder", new SampleEncoder())
.addLast("handler", new PrintChannelHandlers());
}
}); ChannelFuture channelFuture = serverBootstrap.bind(8888);
channelFuture.syncUninterruptibly();
System.out.println("链接前");
Channel channel = channelFuture.channel();
System.out.println("链接后");
}
}

6、编写客户端

public class NettyClientMain {
public static void main(String[] args) {
NettyClientMain nettyClientMain = new NettyClientMain();
nettyClientMain.open();
} public void open() {
Bootstrap bootstrap = new Bootstrap();
bootstrap = new Bootstrap();
bootstrap.group(new NioEventLoopGroup(10))
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.channel(NioSocketChannel.class); bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000);
bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override
protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
.addLast("decoder", new SampleDecoder())
.addLast("encoder", new SampleEncoder());
//.addLast("handler", new PrintChannelHandlers()); }
}); SocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8888); ChannelFuture future = bootstrap.connect(socketAddress);
boolean ret = future.awaitUninterruptibly(3000, MILLISECONDS); if (ret && future.isSuccess()) {
Channel newChannel = future.channel();
doProcess(newChannel);
}
} private void doProcess(Channel channel) { AtomicLong atomicLong = new AtomicLong();
for (int i = 0; i < 15; i++) {
final char ch = (char) (i + 65);
final String id = "id" + i;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
SampleMessage sampleMessage = new SampleMessage();
sampleMessage.setThreadName(Thread.currentThread().getName());
sampleMessage.setDesc(getdes(ch));
sampleMessage.setId("id" + sampleMessage.getDesc().length() + "-" + atomicLong.getAndIncrement());
channel.writeAndFlush(sampleMessage);
}
}
});
t.start();
}
} private String getdes(char a) {
Random random = new Random();
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < random.nextInt(500) + 1; i++) {
buffer.append(a);
}
return buffer.toString();
}
}

7、测试结果

结果符合预期,dubbo 也是通过服务底层公用一条TCP链接,多线程进行调用该链路channel。

Dubbo系列之 (七)链路层那些事(1)的更多相关文章

  1. Dubbo系列之 (七)网络层那些事(2)

    辅助链接 Dubbo系列之 (一)SPI扩展 Dubbo系列之 (二)Registry注册中心-注册(1) Dubbo系列之 (三)Registry注册中心-注册(2) Dubbo系列之 (四)服务订 ...

  2. dubbo系列十一、dubbo transport层记录

    前言 在dubbo接口方法重载且入参未显式指定序列化id导致ClassCastException分析时候用到了dubbo的通信层和编解码,dubbo有个transport层,默认使用netty4进行网 ...

  3. Android网络编程系列 一 TCP/IP协议族之链路层

    这篇借鉴的文章主要是用于后续文章知识点的扩散,在此特作备份和扩散学习交流. 数据链路层有三个目的: 为IP模块发送和 接收IP数据报. 为ARP模块发送ARP请求和接收ARP应答. 为RARP发送RA ...

  4. TCP/IP中最高大上的链路层简介(二)

    引言 对于程序猿来讲,似乎越接近底层,就越显得高大上.这也算是程序猿们的共同认知吧,虽然不是所有人.今天LZ就和各位一起探讨一下TCP/IP中最高大上的一层,也就是最底层的链路层. 这一层LZ了解的还 ...

  5. CRL快速开发框架系列教程七(使用事务)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  6. TCP/IP协议学习(六) 链路层详解

    学习知识很简单,但坚持不懈却又是如此的困难,即使一直对自己说"努力,不能停下"的我也慢慢懈怠了... 闲话不多说,本篇将讲述TCP/IP协议栈的链路层.在本系列第一篇我讲到,TCP ...

  7. Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7497260 更多请看专栏,地址 ...

  8. tcp/ip 卷一 读书笔记(2)物理层和链路层网络

    物理层和链路层网络 术语 链路 是一对相邻结点间的物理线路,中间没有任何其他的交换结点. 数据链路 除了物理线路外,还必须有通信协议来控制这些数据的传输. 帧 数据链路层的协议数据单元(PDU) 串行 ...

  9. [HTTP] tcp/ip详解 链路层 网络层 传输层 应用层

    1.可以把七层协议简化成四层协议链路层 网络层 传输层 应用层 2.通过路由器连接的两个网络网络层ip提供的是一个逐跳协议,提供了一种不可靠的服务,中间有可能会丢传输层tcp在ip的基础上提供了可靠的 ...

随机推荐

  1. Nginx的Gzip功能

    什么是HTTP压缩 有时候客户端和服务器之间会传输比较大的报文数据,这时候就占用较大的网络带宽和时长.为了节省带宽,加速报文的响应速速,可以将传输的报文数据先进行压缩,然后再进行传输. HTTP支持多 ...

  2. 非确定性有穷状态决策自动机练习题Vol.3 D. Dp搬运工3

    非确定性有穷状态决策自动机练习题Vol.3 D. Dp搬运工3 题目描述 给定两个长度为 \(n\) 的排列,定义 \(magic(A,B)=∑_{i=1}^nmax(Ai,Bi)\) . 现在给定 ...

  3. Linux上通过curl发送PUT和POST请求

    通常而言,我们都使用curl发送get请求,但是还是可以使用它发送一些其他类型的请求的,如PUT/POST 只需要使用-X参数即可:

  4. CSS 段落/换行/缩进

    CSS 段落/换行/缩进 1.盒模型 box-sizing: content-box|border-box|inherit; content-box(默认) : 实际宽度/高度 = width/hei ...

  5. Java面试题(多线程篇)

    多线程 35.并行和并发有什么区别? 1.并行是指两个或者多个事件在同一时刻发生:而并发是指两个或多个事件在同一时间间隔发生. 2.并行是在不同实体上的多个事件,并发是在同一实体上的多个事件. 3.在 ...

  6. [CSP-S2019]划分 题解

    CSP-S2 2019 D2T2 考场上读完题感觉是DP就直接扔了开T3了,考完才发现部分分好像不难拿,枯了 题意分析 给出一个数列,要求将其分成几段,使每段的和非严格递增,求最小的每段的和的平方和. ...

  7. soso官方:基于相关排序的判断

    http://www.wocaoseo.com/thread-186-1-1.html 议程 概述 检索词 用户的信息需求 网页的自有信息 网页的附属信息 相关性的计算框架 概述 相关性的表象 检索词 ...

  8. JS - 有趣的面试题

    for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 1000); } //这个例子执行完输出什么结果 ...

  9. C++ Templates (2.2 使用Stack类模板 Use of Class Template Stack )

    返回完整目录 目录 2.2 使用Stack类模板 Use of Class Template Stack 2.2 使用Stack类模板 Use of Class Template Stack 在C++ ...

  10. Python爬虫实战点触验证码, 模拟登陆bilibili

    爬虫思路如下: 利用自动化爬虫工具 Selenium 模拟点击输入等操作来进行登录 分析页面,获取点触验证码的点触图片,通过将图片发送给超级鹰打码平台识别后获取坐标信息 根据超级鹰返回的数据,模拟坐标 ...