Netty常量池_attributekey常量池(2),2024年最新体系面试技巧

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

public static final ChannelOption<Integer> SO_TIMEOUT = valueOf("SO_TIMEOUT");
public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF");

}


分析:  
 上面一共三个类, Constant是一个常量接口, 定义了id和name方法, AbstractConstant对Constant中的id和  
 name方法进行了实现, 其实就等于get方法而已, 可以看到, 每一个常量对象都有一个name, 这个name就是这个  
 常量对象的字符串表示形式, 更加具体一点, 之前我们设置到ServerSocketChannel中的receiveBufferSize这  
 个配置, 就对应一个常量对象, 常量对象的name就是receiveBufferSize, id是一个随机生成的值, 用来表示唯  
 一性, 在这里我们仅仅分析到了AbstractConstant, 抽象类, 定义了所有情况下的常量的公共信息id和name  
 再往后, 要配置Channel, 那么就出现了ChannelOption, 要配置数据库就可能会出现DatabaseOption(这一个  
 是为了让大家更好的理解常量的含义, 笔者臆想的)  
 于是ChannelOption表示用于Channel配置信息中的key, 我们看到这个ChannelOption的源码, 一开始出现了  
 一个pool, 池子, 就是本文的核心主题, 常量池, 这个池子的作用是用来保存常量的, 假设我有许多的Channel  
 配置, 那么我自然就需要有许多的ChannelOption, 由于配置的key一定是固定的, 那么在使用的时候, 如果要  
 更改配置信息, 总不能每次都new一个ChannelOption来表示配置信息的key吧, 于是, 这些key就被保存到了常量  
 池中, ConstantPool就是用来保存这些配置对应的key的, ConstantPool是一个抽象类, 里面就一个  
 ConcurrentMap来保存, ConcurrentMap的key一定是字符串, value是泛型, 泛型为ChannelOption的时候, 表  
 示这个常量池中保存的是ChannleOption, 泛型为DatabaseOption, 表示这个常量池中保存的是  
 DatabaseOption, 这个应该好理解, 不同类型的常量对象, 只需要在其类中创建一个ConstantPool就好了, 父  
 类的ConcurrentMap是公共需要的, 但是ConcurrentMap里面存的值却需要子类来定义, 同时实现newConstant  
 方法表示这个池子中常量的创建, 不知道说到这里大家会不会觉得有点绕, 我们以SO\_RCVBUF这个ChannelOption  
 的创建过程及存储过程的源码进行一下分析吧



public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF"), 通过valueOf作为
入口进行创建, 可以联想到, 这个SO_RCVBUF就是常量池中ConcurrentMap的key, 与此同时会创建一个
ChannelOption, 这个ChannelOption的name也是SO_RCVBUF, 我们再来看看ConstantPool的valueOf源码

public T valueOf(String name) {
    checkNotNullAndNotEmpty(name);
    return getOrCreate(name);
}

private T getOrCreate(String name) {
    T constant = constants.get(name);
    if (constant == null) {
        final T tempConstant = newConstant(nextId(), name);
        constant = constants.putIfAbsent(name, tempConstant);
        if (constant == null) {
            return tempConstant;
        }
    }

    return constant;
}

其实很简单, constants就是那个ConcurrentMap, 通过key为SO\_RCVBUF去这个map查找对应的ChannelOption  
 如果没找到, 那么就调用newConstant创建一个该常量, 并且放入到ConcurrentMap中, newConstant由子类来  
 觉得创建的是哪个类型的常量, ChannelOption有一个匿名内部类是ConstantPool的子类, 之前我们也看过了,  
 其创建的是ChannelOption


总结:  
 任何配置都是一个key-value, ConstantPool用来保存任何配置中的key的对象表示形式, 其实就是用一个  
 ConcurrentMap来保存的, ConstantPool的子类来觉得这个Map中的value存储的是什么类型值, Channel的配置  
 对应的key用ChannelOption来表示, ChannelOption里面有一个name用来存储这个配置的字符串表示,  
 ChannelOption中利用一个匿名内部类继承于ConstantPool, 利用泛型指明了ConcurrentMap中保存的是  
 ChannelOption类型, 对于Channel中的SO\_RCVBUF这个配置来说, 会创建一个ChannelOption,  
 ChannelOption中的name就是SO\_RCVBUF, 与此同时将这个ChannelOption保存到常量池中


![](https://img-blog.csdnimg.cn/img_convert/e4cd23fdfe2db77dc8f3eece93d1d2d4.png)


#### ChannelConfig


ChannelConfig, 故名思意了, 保存了Channel的配置, 比如说接收缓冲区的大小等, Netty中的Channel有很多类型,  
 对于Nio的是NioServerSocketChannel以及NioSocketChannel, 对于Bio的OioServerSocketChannel以及  
 OioSocketChannel, 不同类型的Channel配置自然就不一样, 如下图所示, 就是ChannelConfig的简单类图, 可以清  
 晰的看到, 在红线的两边刚好分为了两块, 一个是Nio的ChannelConfig, 一个是Bio的ChannelConfig, 都是用来存  
 储对应的Channel的配置信息的, 那到底怎么存呢?其实也没那么复杂, 就用一个Map存就好了, 比如说对于服务端需要  
 存储SO\_RCVBUF这个配置信息, 正常情况应该是map.put( “SO\_RCVBUF”, 1024 ), 但是在Netty中, 用  
 ChannelOption对象的方式来表示这个配置的key, 于是就变成了map.put( ChannelOption.SO\_RCVBUF, 1024 ),  
 到此为止, 我们就将ChannelConfig - ChannelOption - ConstantPool的关系给描述完毕了


![](https://img-blog.csdnimg.cn/img_convert/3b4a8d5454d71a5561605218521692cf.png)


#### DefaultAttributeMap及AttributeKey


前面我们熟悉了ConstantPool与ChannelOption之间的关系, 以及ChannelConfig与ChannelOption之间的关系, 此  
 时再来看ConstantPool-AttributeKey-DefaultAttributeMap就会非常轻松了


我们在使用Netty的时候, 对ChannelHandler肯定是不会陌生的, 一个客户端与服务器的数据交互, 当数据到达了服  
 务端Netty程序的时候, 必然是通过多个ChannelHandler进行处理的, 比如先由LengthFieldPrepender这个  
 ChannelHandler对数据进行解码, 然后传给下一层级的ChannelHandler, 当数据写回客户端的时候, 同样会经过多  
 个ChannelHandler, 最后经过LengthFieldBasedFrameDecoder这个ChannelHandler进行编码并write回客户端,  
 这一整个生命周期中, 都是对一个SocketChannel进行操作, 那么假设想要从前一个ChannelHandler传递一些数据到  
 后一个ChannelHandler, 就需要将放置的数据存在到一个地方中, 所有的ChannelHandler都能够访问这块空间


DefaultAttributeMap就是这一块空间, 我们使用的NioServerSocketChannel就是这个类的子类,  
 NioServerSocketChannel之后的文章我们会进行分析, 那知道这两者关系的情况下, 我们就可以想到, 所有的  
 ChannelHandler中, 获取到对应的Channel, 就能访问这个Channel的共享空间DefaultAttributeMap, 可以往里面  
 写入key-value, 或者根据key读取value, 而这个key就是AttributeKey, value是我们自定义的值


先来说说这个AttributeKey吧, 跟ChannelOption是类似的, ChannelOption用来表示配置的key, 而AttributeKey  
 则是用来表示DefaultAttributeMap中的key, 联想ChannelOption, 可以知道AttributeKey中一定也会有一个匿名  
 内部类实现了ConstantPool接口, 即:



public final class AttributeKey<T> extends AbstractConstant<AttributeKey<T>> {

    private static final ConstantPool<AttributeKey<Object>> pool 
                                            = new ConstantPool<AttributeKey<Object>>() {
        @Override
        protected AttributeKey<Object> newConstant(int id, String name) {
            return new AttributeKey<Object>(id, name);
        }
    };
}

再来看看DefaultAttributeMap, 虽然说是Map, 其实里面用的确实数组来存储值的, 值为一个个的  
 AtomicReference对象, 利用一个AtomicReferenceArray来存储这些一个个的AtomicReference对象, 我们先来看  
 看DefaultAttributeMap的源码:



public class DefaultAttributeMap implements AttributeMap {
private volatile AtomicReferenceArray<DefaultAttribute<?>> attributes;

private static int index(AttributeKey<?> key) {
    return key.id() & MASK;
}

// 为了让大家知道这里面做了什么事情, 简略的大量的代码, 只抽离了核心的两行
public <T> Attribute<T> attr(AttributeKey<T> key) {
    int i = index(key);
    DefaultAttribute<?> head = attributes.get(i);
}

private static final class DefaultAttribute<T> 
                                    extends AtomicReference<T> implements Attribute<T> {

    private final AttributeKey<T> key;
    ..................
}

}


分析:  
 这样一看就很清晰了, DefaultAttributeMap中有一个AtomicReferenceArray的数组, 里面放置的是一个个的  
 DefaultAttribute对象, DefaultAttribute继承了AtomicReference, attr方法就是根据AttributeKey获取  
 这个数组中对应的值, 通过AttributeKey的id能够获取索引, 从而取出值, 而DefaultAttributeMap是所有  
 Channel的父类, 这些Channel利用DefaultAttributeMap这个类实现了所有ChannelHandler能够共享一块空间  
 从而实现了在ChannelHandler执行的过程中能够实现数据的传递


#### 总结


ChannelConfig存储Channel的配置, Channel的配置的key为ChannelOption, 为了能够缓存ChannelOption, 引入  
 了ConstantPool常量池, 为了能够在Channel数据交互处理的生命周期中, 不同的ChannelHandler能够共享空间, 实  
 现参数的传递等功能, 所有的Channel会继承于DefaultAttributeMap, DefaultAttributeMap利用一个数组来保存  
 所有的值, 通过AttributeKey来计算索引, AttributeKey跟ChannelOption类似, 也是常量类型  
 ##最后  
 大家看完有什么不懂的可以在下方留言讨论.  
 谢谢你的观看。  
 觉得文章对你有帮助的话记得关注我点个赞支持一下!


l会继承于DefaultAttributeMap, DefaultAttributeMap利用一个数组来保存  
 所有的值, 通过AttributeKey来计算索引, AttributeKey跟ChannelOption类似, 也是常量类型  
 ##最后  
 大家看完有什么不懂的可以在下方留言讨论.  
 谢谢你的观看。  
 觉得文章对你有帮助的话记得关注我点个赞支持一下!


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值