自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(43)
  • 收藏
  • 关注

原创 DangerWind-RPC-framework---八、Netty网络通信

客户端与服务端之间发送数据需要进行网络通信,而Netty恰好是一款出色的网络通信框架,Netty底层实现依赖于NIO,其工作线程组EventLoopGroup中的每一个EventLoop可以理解为一个线程,与NIO类似,EventLoopGroup的工作也是基于事件响应的,可以理解为bossEventLoop负责监听连接事件,并建立连接,众多workEventLoop监听读事件,在channel可读时读入数据并进行处理。因此需要自定义rpc协议,这也是rpc与http的区别之一。

2024-07-15 00:12:19 664

原创 DangerWind-RPC-framework---七、序列化算法

Kryo序列化算法性能较好,但是是线程不安全的,因此需要进行线程隔离,通过ThreadLocal为每个线程维护一个Kryo对象,使用完毕后就remove掉,之后需要使用时再次在threadLocal中初始化一个Kryo对象,这也是withInitial懒加载的过程。三种序列化算法的实现过程中,主要是需要对线程不安全的隐患进行处理,比如进行隔离等操作,以保证序列化操作的正确执行。客户端与服务端的在交互过程中需要互相发送交换数据,这就需要对传输对象进行序列化与反序列化操作,此过程涉及到序列化算法的使用。

2024-07-14 18:47:07 259

原创 DangerWind-RPC-framework---六、负载均衡

首先是根据对应服务的服务列表来获取身份标识,身份标识的作用是判断可用机器实例是否有发生变化,如果发生变化,那么本地缓存中对存储的应服务的哈希环就失效了,使用本地缓存也是因为构造哈希环是对计算资源有一定损耗的。按上述方式就完成了使用一致性哈希算法来选择机器的过程,生产场景中也是经常需要使用一致性哈希算法进行负载均衡,比如服务端机器本地缓存了一些非常耗时的操作,或者是不同的参数需要依赖不同的文件,为了防止文件的重复下载,也需要使用一致性哈希来进行负载均衡。哈希值和机器的映射保存在TreeMap中。

2024-07-14 00:56:28 638 1

原创 DangerWind-RPC-framework---五、服务端的反射调用

getService就是从服务端注册时的本地缓存中取出Bean。得到Bean后接下来需要根据方法名和方法的参数类型来获取到对应方法,获取到之后再调用方法获取结果,并返回。当服务端接收并解析来自客户端发出的数据之后,该如何调用客户端希望调用的方法呢?这主要是通过反射实现的。获取到相应结果之后,再按照相应的通信规则进行包装处理,发送给客户端进行响应。

2024-07-13 18:19:17 413

原创 DangerWind-RPC-framework---四、SPI

参考Dubbo的SPI机制,来实现本RPC框架的SPI部分。每个SPI接口都有自身的ExtensionLoader,调用getExtensionLoader时,首先会进行一系列的合法检查操作,之后会尝试获取该接口的ExtensionLoader,先尝试本地缓存CHM中获取,获取不到的话再创建Loader对象。由于(类标识,Class对象)、(Class对象,对象实例)、(类标识,对象实例)这三个缓存的存在,后续可以直接传入标识获取到对应类的实例,也优化了RPC框架的性能。

2024-07-11 00:30:13 434

原创 DangerWind-RPC-framework---三、服务端下机

自定义关闭线程池的逻辑可以更加优雅的实现线程池资源的释放。可以达到停止接受新的任务,继续在一定等待时间范围内执行已提交的任务,超出等待时间在强制终止线程池。服务端机器的优雅下线需要使用ShutdownHook,这相当于添加了一个关闭钩子,这个钩子是一个线程,它在JVM关闭时(即程序结束时)被调用,清理资源,优雅下机。当一台机器下线时,面临很多问题:如何将其从注册中心下线?客户端拉取服务列表时也使用了本地缓存,如何及时更新本地缓存?

2024-07-10 22:49:14 289

原创 DangerWind-RPC-framework---二、动态代理

DangerWind-RPC-framework---二、动态代理

2024-07-10 00:04:46 905

原创 DangerWind-RPC-framework---一、服务注册与发现

RpcScan注解定义时,通过@Import(CustomScannerRegistrar.class)引入CustomScannerRegistrar,意味着当任何一个Spring配置类上使用了@RpcScan注解,CustomScannerRegistrar也会被自动导入到Spring的配置中。这样,CustomScannerRegistrar的逻辑就会被执行,这通常涉及到自定义的扫描逻辑,比如扫描某些特定的注解,并将扫描到的类注册为Spring容器中的bean。将服务发布到注册中心。

2024-07-09 00:51:55 751

原创 NIO之非阻塞模式

NIO支持非阻塞模式,以网络连接和网络数据传输为例。如果使用阻塞模式,ServerSocketChannel在调用accept等待客户端建立连接是阻塞的,没有连接就一直阻塞。从Channel中读取客户端传送的数据也是阻塞的,没有数据就一直阻塞。当我们开启非阻塞模式,等待连接建立时没有连接就返回null,等到数据时没有数据就返回0。

2024-04-21 23:10:56 453

原创 NIO之ByteBuffer

如写模式下随着字符的写入,position的位置会相应地后移,当切换为读模式时,position会一直最前方,此时其代表读取位置。另一种是compact,这种方式是保留没读过的元素,也就是读模式的position和读模式的limit之间的元素,切换写模式时会被保留,position会指向被保留元素的下一个。limit是限制位置,在读写模式切换后limit的位置也跟着切换,切换到读限制位置和写限制位置。在读模式下limit一般位于buffer中最后一个字符的位置,即在读的时候不超出存有内容的范围即可。

2024-04-21 20:11:21 428

原创 动态规划解决布尔运算

本题的核心在于分治思想,即将一整个大问题的求解分治为很多小问题,最后再根据逻辑将其合并。dp[i][j][k] [i,j]区间运算结果为0或1的方法数,k=0或1,可由递推得到,初始化是对单个运算数进行初始化,为0的话dp[i][j][0]取1,反之取0。具体进行操作时按子数组长度由小到大的顺序遍历,每轮求出每个长度作为窗口在数组上滑动可以取的所有值。具体的合并过程在于将两边的所有可能情况相乘求出笛卡尔积,两边的子数组的长度都是比当前子数组长度短的子数组,在之前的循环中就已经求出了,现在刚好可以用上。

2024-04-21 03:03:46 196

原创 优先队列解决滑动窗口最大值问题

Java的优先队列支持根据自定义的comparator来排序,我们先将值大的放在队首,值相同元素越靠后越需要排在前面,后续每轮在添加一个元素后,我们需要选取窗口中的最大值,先将队首元素不在窗口内的弹出,然后直接取队首元素即可,不用担心队列后面还有不在窗口内的过期元素,因为并不影响我们取队首窗口内的最大值。遍历添加完所有元素,窗口也就移到了最后,这样就可以避免每次都对 窗口内元素进行排序。

2024-04-20 23:14:10 237

原创 背包问题小结

2.组合与排序:核心区别在于内外层问题,即先遍历背包还是先遍历物品,组合问题需要先固定物品,在遍历背包容量,因为不需要在意先后顺序。而排列则是先固定背包容量,在遍历物品,因为序号靠后的物品能拿的话也要先拿,需要在意顺序,当然这是完全背包的排列问题,排列问题通常对应的是完全背包。1.01背包与完全背包,最好都是先固定物品,在遍历背包容量。核心区别在于01要到序遍历容量防止多拿,完全要正序遍历允许多拿。

2024-04-05 23:12:30 103

原创 快速排序(单边、双边两种)

2.2.基准元素自身位置不需遍历 ,最后pointer指向的位置一定是大于基准元素的(如果有的话,否则是基准元素自身),因为比基准元素小的都移动到比pointer低的位置了,交换即可。2.1.需要额外注意,基准元素只能先选最后一个位置,因为在遍历过程中,第一个元素(位置较小的元素)有可能与其他比基准元素小的元素发生交换,最后想把基准元素放到应该的位置上时,却发现基准元素被换到未知地方去了。1.2.需要注意最后ij重合的位置,需要考虑这个位置和基准元素的大小关系,因为都有可能。1.快排核心在于递归。

2024-04-05 18:14:17 180

原创 非递归迭代前中后序遍历二叉树

3.使用一个指针记录上一个弹栈的节点,用以判断右子树是否遍历完毕。6.前中后序遍历共用一套模板,区别在于记录节点的时机不同。5.右子树没有遍历过需使用cur指向右子树继续遍历。2.设置stack栈记录遍历过的节点,方便回退。4.没有右子树或右子树遍历完才可弹栈。1.设置一个cur指向当前节点。

2024-04-05 16:43:56 203

原创 柱状图最大矩形——单调栈巧解

之前发的接雨水是在矩形外部,而这次是在内部,这次动态规划不便了,更方便的是单调栈。

2024-04-02 20:43:17 149

原创 接雨水DP速解及单调栈解法

解法一:DP动态规划速解,hard变easy中easy。

2024-04-02 20:23:52 210

原创 二叉树中的最大路径和——递归速解有感

然后我们思考,那这个路径是不是一定要有个根呢?求当前节点侧最大值时,先找到其左右节点的侧最大值,如果这个最值都小于0,那就没必要加子树了,直接返回当前节点值作为侧最大值即可,否则就返回当前节点值加上侧最值,并返回,在这个过程中,别忘了求以当前节点为根的最大路径值,这个是以当前跟节点值为基石,如果左右子树的侧最值大于0,就加上作为路径,求出这个值后,可以与当前Max比较置换,一次遍历即可求出最值!首先搞清楚“路径”的定义,即从一个节点出发,到达另一个节点,每个节点遍历一次即可遍历到所有节点。

2024-03-27 21:02:21 316

原创 递归速解——翻转等价二叉树

这再次体现了递归的强大,我们知道,二叉树问题除了广度遍历,树形dp,剩下的基本上都是递归解法。对于两个根节点,如果皆为空那肯定比较成功。如果都不为空,如果其值不等,那一定比较失败,如果相等,我们应该比较其子节点,这是因为可能出现翻转情况,我们进行两次比较,一次翻转,一次不翻转,有一个成功即为成功。这道题一看会很头疼,可翻转的节点数很多,根本没法比,可能性也太多啦!

2024-03-24 14:39:44 179

原创 二维数组的顺时针旋转

最关键的是要找到规律,即matrix[i][j]旋转之后要放到哪里,matrix[j][n-1-i],找到这个即可分成4份遍历其中一份。

2024-03-24 13:36:53 124

原创 股票问题动态规划解法核心思路

4.对于含手续费的,不限制买卖次数,实际上和1解法相同,只有两个状态,但别忘了,每卖一次减去手续费。注意这里初始化第一天dp[0][1],也就是第一天不持有股票的初始值,应该是0,不需要减手续费,你闲的你当天买入卖出交一次手续费?后续就是找到每天的这几个状态与前一天的这几个状态的递推关系,初始化正确,从前向后遍历即可,最后取最后一天,或最后一天的最大值即可。3.对于含冷冻期的,实际上每天只有4个状态,即买入,卖出,冷冻期,可买入这4个状态。1.对于不限买卖次数的,我们可以每天记录2个状态,即买入、卖出。

2024-03-20 20:48:41 117

原创 树形dp的入门

最为关键的是dp数组,每个节点只有两个状态,偷与不偷,我们可以用只有两个元素的一维dp数组来进行记录,而不是所有节点都需要记录,这是与普通dp不同之处。这也是关键之处,我们应该使用后序遍历来进行遍历,下层节点将状态传递到上层,上层根据下层传入的值进行处理,得到到达本层该节点时的状态,这样到了最上层跟节点,实际上已经考虑到了下层所有节点的状态。对于本题,具体而言就是如果偷当前节点,那左右必须取不偷,如果不偷当前节点,则可偷但不一定偷左右节点,这是我们应该取偷与不偷的最大值!这就是具体在每层进行递推的逻辑。

2024-03-19 20:32:59 130

原创 基于动态规划解决的子序列问题

到某个序号i为止,仅仅是以当前序号i为结尾的,以此进行dp。

2024-03-16 22:53:35 124

原创 最长递增子序列——动态规划题解思路分享

既然我们的关键是想知道的最长递增子序列是要以序号i-1为结尾,那不妨我们就直接设dp[i]为以i为结尾的最长递增子序列就好了!我们需要从序号0遍历到i-1,如果nums[i] > nums[j],我们就比较dp[i]和dp[j]+1的关系,如果dp[j]+1更大,那我们就更新dp[i],这样最后找到的就是最大的dp[i]。最后一个注意点,最大值一定是dp[n-1]吗,这时我们就思考dp[i]的含义,显然dp[i]是以序号i为结尾的最长递增子序列,所以我们需要从0遍历到n-1,取出最大的dp[i]!

2024-03-14 20:05:02 280

原创 双重检查锁实现懒汉式单例模式——一篇通透

那volatile是干啥的呢?其实是为了保证变量在线程间的可见性和有序性,可见性就是说,不要再线程的本地存储空间中去找,要找主存,去查看最新的数据!不加volatile的话这三个过程可能会颠倒为:开辟内存空间、将变量指向对应内存地址、初始化对象,这在单线程环境下是没问题的,但是多线程环境下,比如现在顺序是颠倒的顺序,执行到第二步时变量不为null,但是目前确实是一个未完全初始化的对象。首先,构造方法是私有的,这不用多说,然后我们开放一个公共的方法,这个方法是为了获取单例对象,为什么是static的呢?

2024-03-12 22:57:47 292 1

原创 实现LRU缓存

【代码】实现LRU缓存。

2024-03-12 22:14:52 193 1

原创 两个线程交替输出对应的数字字母(wait/notify)

那么我们接下来就需要实现Runnable接口了,使用Lambda表达式,首先不考虑计数,我们可以先设为无限循环while(true),然后抢锁,抢到之后,如果发现flag不对,就调wait释放锁,此线程就阻塞在wait那里,直到下次它再被唤醒,才会进入下一轮循环继续抢对象锁。我来描述下过程吧,我们抢到对象锁之后进入for循环,如果标识位正确,我们输出并切换标识位,之后唤醒等待的线程抢锁,而本线程则由于标识位不符,调用wait方法进入wait状态并释放锁,那另一个线程就一定可以抢到锁啦!

2024-03-11 23:59:17 750

原创 多线程交替输出奇数偶数1到1000(wait/notify)

类似,可先参考,解析稍后补充!

2024-03-11 22:52:57 195

原创 多线程交替输出abc100次(wait/notify)

那么我们接下来就需要实现Runnable接口了,使用Lambda表达式,首先不考虑计数,我们可以先设为无限循环while(true),然后抢锁,抢到之后,如果发现flag不对,就调wait释放锁,此线程就阻塞在wait那里,直到下次它再被唤醒,才会进入下一轮循环继续抢对象锁。设为while(true)是因为循环是不断重试的,有可能抢到锁但本轮并不应该是它输出,所以用了无限循环,这样也会一直交替打印下去,那我们怎么控制器打印次数呢?为此我引入了计数器,打完c之后加一,最后计数器的值小于一个固定值即可!

2024-03-11 22:37:01 291

原创 Kafka的高性能设计原理

Kafka通过上述设计原理实现了高吞吐量的消息处理能力。这些设计共同作用,使得Kafka成为了处理大规模实时数据流的理想选择。Kafka的一个主要设计特点是将主题(Topics)分成多个分区(Partitions),每个分区可以分布在不同的服务器上。Kafka将消息存储为不可变的日志结构,这使得消息的追加操作非常高效。Kafka支持以批的形式处理消息,这意味着可以一次性处理多条消息。Kafka利用操作系统级别的零拷贝技术来提高数据传输的效率。Kafka通过副本机制保证数据的可靠性和高可用性。

2024-03-09 00:27:55 175 1

原创 RocketMQ的消息存储机制

RocketMQ是一款分布式消息中间件,它提供了高吞吐量、高可用性和可扩展性。为了保证消息的可靠性,RocketMQ采用了同步双写和异步刷盘的机制来存储消息。

2024-03-09 00:26:22 198 1

原创 RocketMQ与Kafka的异同及优势

RocketMQ是一个开源的分布式消息中间件,由阿里巴巴团队开发并捐赠给Apache基金会。它提供了低延迟、高性能、高可靠性和高可扩展性的消息和流处理服务。Kafka是一个开源的流处理平台,由LinkedIn开发并捐赠给Apache基金会。它主要用于构建实时数据管道和流应用程序,具有高吞吐量、可扩展性和容错性。

2024-03-09 00:25:49 537 1

原创 RPC调用和HTTP调用的异同及优势

是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,以进行通信。是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网数据通信的基础。

2024-03-09 00:24:53 524 2

原创 period7-使用 Protobuf 通信

目前的最后一个迭代周期的目标就是使用传输前使用Protobuf 通信。使用protobuf 编码,接收方再进行解码,可以显著地降低二进制传输的大小。另外一方面,protobuf 可非常适合传输结构化数据,便于通信字段的扩展。好了,wind- cache目前的七个迭代阶段到此结束,后续会再考虑对项目进行重构引入更多的功能点,希望大家继续提出宝贵的建议吧!不难看出,引入protobuf后的变化就是响应前进行编码,拿到响应后进行解码,编码后再进行响应提高了响应效率。

2024-03-04 02:13:46 119

原创 Period6-防止缓存穿透与击穿

在上一个迭代周期中,我们发现了一个问题:同时大量请求请求同一个key,那会同一时间会发生多次网络调用,对于性能有较大影响。对于同时大量请求请求同一个key这种情况,我们应该只调用一次。这个过程中主要出现在本地没有查询到结果后的load与loadLocally这个过程中。为此,我们对调用逻辑进行封装,使用singleflight结构对这一过程进行支持。

2024-03-03 15:28:53 91

原创 Period5-分布式节点

解释下这个新的结构,一致性哈希的整体结构以及各个节点应该提前注册到HTTPPool上,HTTPPool同时兼作为peerpicker的实现类,注册到各group上。查询缓存大致过程基本如下:在本地查不到之后,先尝试从其他节点获取,因为HTTPPool注册到了group上,作为peerpicker接口的实现类,我们可以使用HTTPool的pickpeergetter方法选择到哈希环上对应的节点,并找到访问的peergetter,使用group名字和key查询缓存的value。

2024-03-01 01:53:56 159 1

原创 Period4-一致性哈希选择分布式缓存对应机器

不难看出,少了一台机器之后,如果key对应的hash值是3,4,5的话,查询的机器都会改变,那肯定会调用耗时的降级方法再次加载,所以直接取hash值是不可行的。对于同一个Key,我们肯定想将其打到同一台机器上。对于分布式缓存来说,当一个节点接收到请求,如果该节点并没有存储缓存值,那么它面临的难题是,从谁那获取数据?

2024-02-29 13:13:33 105

原创 Period3-实现http server

我们对URL格式作出规定,除了最基本的ip地址和端口号以外,还需要拼接三个部分,第一部分basepath是一个标识,使用一个固定值“_windcache”,剩下两个部分需要拼接group name和key,先查到组,在group中根据key取出缓存内容。

2024-02-28 12:39:34 123

原创 Period-2-实现单机并发缓存

另设置降级方法,如果缓存中没有值,应该调用什么方法查询,由使用者传入。比如降级查询MySQL数据库中的一张表,我们恰好可以将缓存的Group与此table对应。第二个迭代周期的目标是保证缓存的并发安全,并封装出核心结构Group,Group可以理解为MySQL中的table。

2024-02-25 20:59:12 139

原创 Period-1-实现基本的缓存淘汰策略-方案设计

3. 新增、修改流程。

2024-02-24 17:27:19 141

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除