- 博客(162)
- 收藏
- 关注
原创 zk基础—2.架构原理和使用场景二
同理,如果Leader收到过半Follower的ACK响应后,在发送Commit消息的过程中,出现Leader宕机或者Leader和Follower的网络问题。那么Follower只要与它能找到的Leader(新选举的ZXID最大的Follower或者原来的Leader)进行数据同步,就可以保证新数据在该Follower中完成Commit。某个客户端连接到某个Follower时可以读取到刚刚Commit的数据,但有的客户端连接到另一个Follower时却没法读取到还没有Commit的数据。
2025-04-01 21:31:46
399
原创 zk基础—2.架构原理和使用场景一
集群启动后会自动选举一个Leader出来,只有Leader可以写,Follower只能同步数据和提供数据的读取。zk封装了分布式架构中很多核心和主流的需求功能,比如很多分布式系统都需要分布式锁、集中式存储分布式集群的元数据、Master选举、分布式协调,这些功能需求都可以使用zk来实现。Kafka里使用zk进行元数据管理、Master选举、分布式协调,Canal也一样使用zk进行元数据管理、Master选举(HA主备切换),HDFS的HA也是基于zk来做的。假设现在有3台机器,要进行元数据的存储。
2025-04-01 21:31:05
231
原创 zk基础—1.一致性原理和算法二
大纲1.分布式系统特点2.分布式系统的理论3.两阶段提交Two-Phase Commit(2PC)4.三阶段提交Three-Phase Commit(3PC)5.Paxos岛的故事来对应ZooKeeper6.Paxos算法推导过程7.Paxos协议的核心思想8.ZAB算法简述6.Paxos算法推导过程(1)Paxos的概念(2)问题描述(3)推导过程(4)Proposer生成提案(5)Acceptor批准提案(6)Paxos算法描述(7)Learner学习被选定的value(8)如何保证Paxos算法的活性
2025-03-31 22:46:13
616
原创 zk基础—1.一致性原理和算法一
紧接着他又收到了S2的提议,结果他一查记事本,发现这个提议的编号小于等于我的当前编号1,于是拒绝了这个提议:对不起,这个提议先前提过了。因此S2的提议被拒绝,S1的提议被S1和S3同意。每个议员(zk的Server)只会同意大于当前编号(zk的ZXID)的提议(Proposal),包括已生效和未生效的(分布式节点要排除由于网络延迟而晚收到的消息)。Chubby和ZooKeeper都是基于Paxos的理论来实现的,Paxos还被认为是到目前为止唯一的分布式一致性算法,其它的算法都是Paxos的改进或简化。
2025-03-31 22:41:50
845
原创 Netty源码—10.Netty工具之时间轮二
如果业务机器宕机了,那么续期的延时任务失效,也无法续期,锁会超时释放。每次发起RPC调用时创建一个Future,记录这个Future的创建时间与超时时间,后台有一个定时任务进行检测。在Dubbo中发起RPC调用时,通常会配置超时时间,当消费者调用服务提供者出现超时进行一定的逻辑处理。我们可以利用定时任务。心跳机制会每隔固定的时间发送一个心跳包来检测客户端和服务端的连接状态,客户端发送心跳包用来告诉服务器其还正常运行。对于精度要求特别高的调度任务可能不太适合,因为时间轮的精度取决于时间格的跨度大小。
2025-03-30 21:59:31
544
原创 Netty源码—10.Netty工具之时间轮一
Netty的HashedWheelTimer是一个粗略的定时器实现,之所以称为粗略的实现是因为该时间轮并没有严格准时地执行定时任务,而是在每隔一个时间间隔之后的时间节点执行,并执行当前时间节点之前到期的定时任务。步骤七:遍历延时任务队列timeouts,将其中的延时任务保存到对应的槽的链表中,根据延时时间计算对应的时间槽和remainingRounds圈数。时间轮由多个时间格组成,每个时间格代表当前时间轮的基本时间跨度ticketDuration,其中时间轮的时间格的个数是固定的。
2025-03-30 21:58:35
684
原创 Netty源码—9.性能优化和设计模式二
对应于Netty的ChannelHandlerContext,通过ChannelHandlerContext的channel()方法可以获取Channel,通过ChannelHandlerContext的executor()方法可以获取Reactor线程。在这个过程的处理中,每个对象都可以只处理它所关心的那一部分。也就是每一个具体的责任处理器它都有权终止这个事件继续传播,对应于Netty的ChannelHandler接口实现中,只要不显式调用ctx.fireXXX()便可以实现XXX事件传播的终止。
2025-03-28 23:40:59
936
原创 Netty源码—9.性能优化和设计模式一
Stack描述了整个对象池的构造,用于存储当前同线程回收的对象,这里同线程的意思就是和后续的异线程相对的。每个线程的Map表示的是对这个线程来说,不同的Stack对应着其他线程绑定的Stack,而由其他线程创建的对象会存放在其他线程绑定的Stack的WeakOrderQueue里。如果这个Stack里的elements数组没有对象,则从当前Stack关联的其他线程的WeakOrderQueue里的Link结点的elements数组中转移对象,到当前Stack里的elements数组里。
2025-03-28 23:40:26
872
原创 Netty源码—8.编解码原理二
其中head结点的write()方法会通过底层的unsafe进行如下处理:把当前的ByteBuf对象添加到unsafe维护的一个写缓冲区里,同时计算写缓冲区大小是否超过64KB。一般而言,只要每个ChannelHandler都往下传播write事件和flush事件,那么最后都会传播到HeadContext结点的write()方法和flush()方法,然后分别执行unsafe.write()和unsafe.flush()将数据通过底层的unsafe写到JDK底层的Channel。
2025-03-27 21:50:24
693
原创 Netty源码—8.编解码原理一
在计算出分隔符的长度之后,会通过移动字节容器的readerIndex指针把分隔符之前的数据全部丢弃,当然丢弃的数据也包括分隔符。Netty对各种用户协议的支持就体现在ByteToMessageDecoder的抽象方法decode()中,decode()方法的入参是当前读取到的未被处理的所有数据in和业务数据包容器out,所有拆包器都需要实现ByteToMessageDecoder的decoed()方法。如果当前读取的数据加上已读取的数据足够拼成完整的数据包,就将拼好的数据包往业务传递,而多余的数据则保留。
2025-03-27 21:49:50
1052
原创 Netty源码—7.ByteBuf原理四
PooledByteBufAllocator的heapBuffer()方法通过其newHeapBuffer()方法执行代码heapArena.allocate()时,首先会调用PoolArena的allocate()方法,然后会调用PoolArena的allocateNormal()方法,接着会调用PoolChunk的allocate()方法,并最终调用到PoolChunk的allocateRun()方法进行Page级别的内存分配。其中,allocateNode()方法会通过异或运算实现 + 1。
2025-03-26 23:00:39
1138
原创 Netty源码—7.ByteBuf原理三
这些固定大小分别有:tiny类型规则的是16B的整数倍直到498B,small类型规则的有512B、1K、2K、4K,normal类型规定的有8K、16K、32K。PoolSubpage中的chunk属性表示该SubPage从属于哪个PoolChunk,PoolSubpage中的elemSize属性表示该SubPage是以多大的数值进行划分的,PoolSubpage中的bitmap属性会用来记录该SubPage的内存分配情况,一个Page里的PoolSubpage会连成双向链表。
2025-03-26 22:57:24
1013
原创 Netty源码—6.ByteBuf原理二
以DirectArena的newByteBuf()方法为例,它会通过RECYCLER.get()拿到一个PooledByteBuf。PoolArena的allocate()方法分配内存的大体逻辑如下:首先通过由PoolArena子类实现的newByteBuf()方法获取一个PooledByteBuf对象,接着通过PoolArena的allocate()方法在线程私有的PoolThreadCache上分配内存,这个分配过程其实就是拿一块内存,然后初始化PooledByteBuf对象里的内存地址值。
2025-03-25 23:52:41
620
原创 Netty源码—6.ByteBuf原理一
slice()方法会从原始ByteBuf中截取一段,这段数据是从readIndex到writerIndex的,同时返回的新的ByteBuf的最大容量maxCapacity为原始ByteBuf的readableBytes()。答:对于Page级别的内存分配与释放是直接通过完全二叉树的标记来寻找某一个连续内存的。所有类型的ByteBuf最终都是通过Netty里的内存分配器分配出来的,Netty里的内存分配器都有一个最顶层的抽象ByteBufAllocator,用于负责分配所有类型的内存。
2025-03-25 23:51:40
939
原创 Netty源码—5.Pipeline和Handler二
然后触发调用AbstractChannelHandlerContext的fireChannelRead()方法,接着通过findContextInbound()方法找到HeadContext的下一个结点,然后通过invokeChannelRead()方法继续调用该结点的channelRead()方法,直到最后一个结点TailContext。异常在ChannelPipeline中的双向链表传播时,无论Inbound结点还是Outbound结点,都是向下一个结点传播,直到tail结点为止。
2025-03-24 23:43:00
1143
原创 Netty源码—5.Pipeline和Handler一
ChannelPipeline中保存了Channel的引用,ChannelPipeline中每个结点都是一个ChannelHandlerContext对象,每个ChannelHandlerContext结点都包裹着一个ChannelHandler执行器,每个ChannelHandlerContext结点都保存了它包裹的执行器ChannelHandler执行操作时所需要的上下文ChannelPipeline。在这个过程中,任何ChannelHandler都可以中断当前的流程,中断消息的传递。
2025-03-24 23:41:41
739
原创 Netty源码—4.客户端接入流程二
接着还会传播ChannelActive事件。其中javaChannel().register()会将新连接NioSocketChannel绑定到Reactor线程的Selector上,这样后续这个新连接NioSocketChannel所有的事件都由绑定的Reactor线程的Selector来轮询。然后,新连接接入调用到服务端Channel的Pipeline的fireChannelRead()方法时,便会触发调用ServerBootstrapAcceptor处理器的channelRead()方法。
2025-03-23 21:38:27
660
原创 Netty源码—4.客户端接入流程一
第一部分是逐层调用父类的构造方法,其中会设置这个客户端Channel的阻塞模式为false,然后再把感兴趣的读事件OP_READ保存到这个Channel的成员变量中以便后续注册到Selector,接着会创建一系列的组件,包括作为Channel唯一标识的Id组件、用来进行底层数据读写的unsafe组件、用来作为业务逻辑载体的pipeline组件。Netty中最核心的是两种类型的Reactor线程,这两种类型的Reactor线程可以看作Netty中的两组发动机,驱动着Netty整个框架的运转。
2025-03-23 21:36:59
1106
原创 Netty源码—3.Reactor线程模型四
这个Task的任务是一个"添加某定时任务"的任务,而不是添加某定时任务。定时的任务队列PriorityQueue则是在添加定时任务时创建的,然后在外部线程调用NioEventLoop的schedule()方法时,会调用scheduleTaskQueue().add()方法将Task保存到定时的任务队列里。普通的任务队列MpscQueue在创建NioEventLoop时创建的,然后在外部线程调用NioEventLoop的execute()方法时,会调用addTask()方法将Task保存到普通的任务队列里。
2025-03-21 21:52:29
840
原创 Netty源码—3.Reactor线程模型三
如果有就调用selector.selectNow()进行非阻塞式的select操作,如果都没有就调用selector.select(timeoutMillis)进行阻塞式select操作。Selector.select()操作每次都会把就绪状态的IO事件添加到Selector底层的两个HashSet成员变量中,而Netty会通过反射的方式将Selector中用于存放SelectionKey的HashSet替换成数组,使得添加SelectionKey的时间复杂度由HashSet的O(n)降为数组的O(1)。
2025-03-21 21:51:14
1129
原创 Netty源码—2.Reactor线程模型二
也就是executor.execute()方法会通过DefaultThreadFactory的newThread()方法,创建出一个FastThreadLocalThread线程来执行Runnable任务。二.NioEventLoop的execute()方法其实就是其父类SingleThreadEventExecutor的execute()方法,它会先判断当前调用execute()方法的线程是不是Netty的Reactor线程,如果不是就调用startThread()方法来创建一个Reactor线程。
2025-03-20 22:36:45
652
原创 Netty源码—2.Reactor线程模型一
外部线程在执行Netty的一些任务时,如果判断不是由NioEventLoop对应的线程执行的,就会直接放入一个任务队列里,然后由一个NioEventLoop对应的线程去执行。多生产者就是这个NioEventLoop对应的线程之外的线程,通常情况下就是我们的业务线程。一.每次执行ThreadPerTaskExecutor的execute()方法时,都会创建出一个FastThreadLocalThread的线程实体,所以Netty的线程实体都是由ThreadPerTaskExecutor创建的。
2025-03-20 22:35:58
1502
原创 Netty源码—1.服务端启动流程二
ServerBootstrap的bind()方法,首先执行AbstractBootstrap的initAndRegister()方法完成了服务端Channel的初始化和注册后,就会调用AbstractBootstrap的doBind0()方法绑定端口。AbstractUnsafe的bind()方法中所调用的doBind()方法是属于AbstractChannel的抽象接口,会由NioServerSocketChannel来进行具体的实现,即调用JDK底层NIO的bind()方法来绑定端口。
2025-03-19 23:24:41
1101
原创 Netty源码—1.服务端启动流程一
用户调用启动辅助类ServerBootstrap的bind()方法时,第一步通过反射创建服务端Channel会执行NioServerSocketChannel的默认构造方法,来创建一个NioServerSocketChannel对象,并且在创建过程中会创建Netty的一系列核心组件:如Channel、ChannelConfig、ChannelId、Unsafe、ChannelPipeline。Selector的轮询操作是由其绑定的EventLoop线程run()方法驱动的,在一个循环体内循环执行。
2025-03-19 23:23:38
1102
原创 Netty基础—8.Netty实现私有协议栈二
如果客户端非法跟服务端建立一个Netty物理连接后,却一直不发送握手请求,这会导致服务端的连接资源被非法占用。当连接刚建立通道刚被激活时,客户端需要发起握手请求,服务端则需要启动一个延时线程检查握手是否超时,比如通道激活1分钟后还没有会话ID。然后服务端收到握手请求后通过channelRead()方法进行处理,客户端收到握手响应也是通过channelRead()方法进行处理。客户端处理握手响应,服务端处理握手请求。服务端处理握手请求时,还要根据白名单IP看是否为非法请求,并根据会话缓存避免重复握手。
2025-03-18 22:02:58
429
原创 Netty基础—8.Netty实现私有协议栈一
例如链路建立的握手请求和响应消息、链路检测的心跳消息等。当链路处于空闲时(即链路已经长时间没有通信),客户端主动发送Ping消息给服务端,服务端收到Ping消息后发送应答消息(即Pong消息)给客户端。比如0是业务请求消息、1是业务响应消息、2是业务ONE WAY消息(既是请求又是响应消息)、3是握手请求消息、4是握手响应消息、5是心跳请求消息、6是心跳应答消息。此时客户端会发送握手请求消息给服务端,服务端收到握手请求消息后,如果通过IP白名单校验,则返回握手成功应答消息给客户端,应用层链路建立成功。
2025-03-18 22:02:01
921
原创 Netty基础—7.Netty实现消息推送服务二
首先需要一个运营系统能够基于NettyClient和PushServer建立WebSocket长连接,然后浏览器客户端也要和PushServer建立好WebSocket长连接,接着运营系统会让NettyClient发送Push推送消息给PushServer,最后PushServer再把推送消息发送给浏览器客户端。首先启动PushServer,然后打开多个网页客户端查看console,接着启动运营客系统在控制台输入消息,这样就可以完成一个完整的消息推送的交互了。
2025-03-17 21:38:32
364
原创 Netty基础—7.Netty实现消息推送服务一
首先添加HTTP请求消息解码器HttpRequestDecoder,因为浏览器会把按照HTTP协议组织起来的请求数据序列化成字节数组发送给服务器,而HttpRequestDecoder可以按照HTTP协议从接收到的字节数组中读取出一个完整的请求数据。浏览器里面运行的是HTML网页代码,WebSocket就是在HTML网页代码里嵌入WebSocket代码,可以让浏览器里的HTML网页代码跟NettyServer建立连接,并且是基于长连接发送数据。HTTP请求消息由三部分组成:请求行、请求头、请求体。
2025-03-17 21:37:35
602
原创 Netty基础—6.Netty实现RPC服务二
首先在编码数据包时,需要在数据包开头添加4个字节的int类型的bytes.length,之后任何一个数据包的读取,都必须从4个字节的int(bytes.length)值开始读取,再按照int值读取后续指定数量的字节数,都读取完才能证明读取到一个完整的字节数组。说明二:RPC调用其实就是通过调用remoteCall()方法,往Netty客户端的Channel的writeAndFlush()方法写入请求数据,同时也通过sync()方法进行同步阻塞,以便可以等到Netty服务端的响应,从而获得RPC调用结果。
2025-03-16 23:25:08
537
原创 Netty基础—6.Netty实现RPC服务一
比如在下面的例子中,如果还要代理IReceiver接口的实现类,那么还需要定义一个ProxyReceiver代理类去实现IReceiver接口。然后当客户端调用Proxy.newProxyInstance()方法,并传入被代理类的接口和一个封装了被代理对象的InvocationHandler对象时,便会动态生成一个代理类并返回一个实现了被代理类接口的代理对象。动态代理中的动态是针对静态代理而言的,动态代理的优势不在于省去了编写静态代理类时的代码量,而是实现了可以在被代理类未知的时候就确定代理类的代理行为。
2025-03-16 23:24:00
654
原创 Netty基础—5.Netty的使用简介
通过ByteBuf的readableBytes()方法可以获得缓冲区可读的字节数,然后就可以根据缓冲区可读的字节数创建byte数组,接着通过ByteBuf的readBytes()方法便可以将缓冲区的字节数组复制到新创建的byte数组中。通过ChannelHandlerContext的write()方法会把待发送的消息放到发送缓冲区中,通过ChannelHandlerContext的flush()方法会将发送缓冲区中的消息写入到SocketChannel中发送出去。这就是TCP粘包和拆包问题。
2025-03-14 22:50:26
856
原创 Netty基础—4.NIO的使用简介二
大纲1.Buffer缓冲区2.Channel通道3.BIO编程4.伪异步IO编程5.改造程序以支持长连接6.NIO三大核心组件7.NIO服务端的创建流程8.NIO客户端的创建流程9.NIO优点总结10.NIO问题总结4.伪异步IO编程(1)BIO的主要问题(2)BIO编程模型的改进(3)伪异步IO编程(4)伪异步IO的问题(5)伪异步IO可能引起的级联故障(1)BIO的主要问题BIO的主要问题在于每当有一个新的客户端请求接入时,服务端必须创建一个新的线程来处理新接入的客户端链路,一个线程只能处理一个客户端连
2025-03-13 23:20:25
1109
原创 Netty基础—4.NIO的使用简介一
一般比较少会遇到直接操作Buffer的position、limit和mark的场景,通常都会使用上述操作Buffer缓冲区的几个方法:首先执行put()方法往缓冲区里写入数据,然后打算复用时,就执行clear()方法再重新写数据。这样处理的好处是可以让磁盘写的性能比较高,但如果此时系统宕机,那么OS Cache里的数据可能就会丢失。通道与流的区别在于:通道是双向的,流是单向的,一个流必须是InputStream和OutputStream的子类。在NIO中,所有的数据都是通过使用Buffer缓冲区来处理的。
2025-03-13 23:18:33
1128
原创 Netty基础—3.基础网络协议二
步骤三:客户端收到消息后会验证证书的合法性,然后会生成一串随机数密码,并用证书里的公钥进行非对称加密,接着使用约定好的Hash算法生成握手消息的Hash值,然后用随机密码对消息进行对称加密,最后把加密数据、加密后的随机密码、Hash值发给服务端。其中连接的建立和释放,都是基于TCP三次握手和四次挥手来实现的。IO多路复用模型就是通过一种新的系统调用,让一个进程(用户线程)可以监视多个连接(文件描述符),一旦某个连接就绪(文件描述符可读可写),那么内核就会通知进程(用户线程)进行相应的IO系统调用。
2025-03-12 22:32:06
1047
原创 Netty基础—3.基础网络协议一
如果跨子网的机器需要进行通信,就要在数据包写上对方的IP地址,然后先通过mac地址广播到路由器,接着路由器再把数据包传输到路由器,最后让路由器再根据另外一个子网的IP地址转换为mac地址,然后通过另外一个子网的交换机广播到对方的机器上。通过IP协议,可以判断发送者和接收者的IP地址是否是在同一个子网的。在以太网里面,如果一台电脑发送一个数据包出去,会广播给局域网(子网)内的所有电脑的网卡,然后每台电脑都从数据包里获取接收者的mac地址,跟自己的mac地址进行对比,如果一样就说明这是发给自己的数据包。
2025-03-12 22:31:04
804
原创 Netty基础—2.网络编程基础四
并且多Reactor线程模式在海量的客户端并发请求的情况下,还可以通过subReactor线程池将海量的连接分发给多个subReactor线程,在多核的操作系统中这能大大提升应用的负载和吞吐量。接着Acceptor处理器通过accept()方法便能得到这个客户端对应的连接(SocketChannel),然后将该连接(SocketChannel)所关注的read事件及对应的read事件处理器注册到Reactor中,这样Reactor监听到该连接的read事件就会交给对应的read事件处理器进行处理。
2025-03-11 23:11:37
855
原创 Netty基础—2.网络编程基础三
Linux下可以做到的异步IO效果也只是通过使用IO复用下的epoll模型,而IO复用实际上使用的还是同步IO,所以在Linux下进行网络编程常用的是NIO。为了改进这种一个连接一个线程的模型,可以使用线程池来管理这些线程,从而实现1个或多个线程处理N个客户端的模型。但是正因为限制了线程数量,如果发生读取数据慢(比如数据量大、网络传输慢等)且存在大量并发的情况,那么对其他连接请求的处理就只能一直等待,这就是最大的弊端。如果类的名字只有Socket的,则表示这个类是负责具体的网络读写的。
2025-03-11 23:08:41
678
原创 Netty基础—1.网络编程基础二
单个线程不断的轮询select/epoll系统调用所负责的成百上千的Socket连接,当某个或某些Socket网络连接有数据到达了,就返回这些可以读写的连接。单个线程不断的轮询select/epoll系统调用所负责的成百上千的Socket连接,当某个或某些Socket网络连接有数据到达了,就返回这些可以读写的连接。但是当在高并发的场景下,需要大量的线程来维护大量的网络连接。与一条线程维护一个连接相比,IO多路复用技术的最大优势是:系统不必创建线程,也不必维护这些线程,从而大大减小了系统的开销。
2025-03-10 21:53:20
855
原创 Netty基础—1.网络编程基础一
首先作为发送端的客户端在应用层(HTTP协议)发出一个HTTP请求,然后在传输层(TCP协议)把从应用层处收到的数据(HTTP请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层,接着在网络层(IP协议)增加作为通信目的地的MAC地址后转发给链路层。在确认报文中,ACK=1,ack=w+1,序列号seq=u+1。物理层如果要形成适合数据传输所需的实体来为数据传输进行服务,那么一要保证数据能在其上正确通过,二要提供足够的带宽来减少信道上的拥塞,其中带宽指的是每秒能通过的比特(Bit)数。
2025-03-10 21:52:18
628
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人