You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UnetAgent邻居发现实现报错:MAC预留请求被拒绝问题排查

问题分析与解决方案

你的代码存在的核心问题

  1. 错误注册服务:你在setup()里调用register(Services.MAC)等方法,这是完全错误的逻辑。register()是用来让你的Agent对外提供指定服务的,但Unet已经内置了MAC、DATAGRAM、ROUTE_MAINTENANCE的官方Agent。你注册这些服务会导致系统服务冲突,这就是"mac reservation request was denied: null"错误的根源——系统无法确定该使用哪个MAC服务来处理资源预约。

  2. 同步阻塞的事件处理:在startup()里直接调用discoverNodes(),而方法里用while(receive(...))做同步阻塞循环。Unet Agent是事件驱动模型,所有消息处理依赖单线程事件循环,这个阻塞操作会占用线程,导致MAC等服务的初始化消息无法及时处理,进一步加剧预约失败的问题。

  3. 邻居实现方式不够优雅:手动构造RouteDiscoveryReq发送到不存在的节点来触发邻居发现,属于绕路的做法,Unet本身提供了更直接的邻居维护机制。

修正后的代码(解决错误版本)

先修正核心错误,保留你原有的发现逻辑,但修复注册和阻塞问题:

@Override
protected void setup() {
    this.nodeList = new ArrayList<>();
    this.currentNodeIndex = 0;
    // 移除所有register调用——你是要使用服务,不是提供服务
}

@Override
protected void startup() {
    this.phy = agentForService(Services.PHYSICAL);
    if (this.phy == null) {
        this.log.warning("No PHY found, carrier sensing disabled!");
    } else {
        this.subscribe(this.topic(this.phy, "snoop"));
    }

    // 异步触发邻居发现,避免阻塞事件循环
    addTask(this::discoverNodes);
}

private void discoverNodes() {
    int attempts = 2;
    int phantom = 132;
    int timeout = 10000;

    AgentID rdp = agentForService(Services.ROUTE_MAINTENANCE);
    if (rdp == null) {
        log.warning("Route maintenance service not available!");
        return;
    }
    this.subscribe(this.topic(rdp), RouteDiscoveryNtf.class);

    RouteDiscoveryReq req = new RouteDiscoveryReq(phantom);
    req.setCount(attempts);
    rdp.send(req);

    Message ntf;
    while ((ntf = receive(RouteDiscoveryNtf.class, timeout)) != null) {
        int neighbor = ((RouteDiscoveryNtf) ntf).getNextHop();
        this.log.warning("  Discovered neighbor: " + neighbor);
        nodeList.add(neighbor);
    }

    nodeList = nodeList.stream().distinct().collect(Collectors.toList());
    this.log.warning("Neighbors are " + nodeList);
    NUM_NODES = nodeList.size();
}

更简便的实现方式(利用Unet内置能力)

Unet的RouteMaintenanceAgent本身会自动维护邻居列表,你只需要订阅相关通知即可,无需手动发起发现请求:

@Override
protected void setup() {
    this.nodeList = new ArrayList<>();
    this.currentNodeIndex = 0;
}

@Override
protected void startup() {
    AgentID routeAgent = agentForService(Services.ROUTE_MAINTENANCE);
    if (routeAgent == null) {
        log.warning("Route maintenance service not found!");
        return;
    }

    // 订阅邻居列表更新通知
    subscribe(topic(routeAgent, "neighborList"), NeighborListNtf.class, this::updateNeighborList);
    // 订阅新邻居发现通知
    subscribe(topic(routeAgent, "neighborDiscovery"), NeighborDiscoveryNtf.class, this::onNewNeighbor);

    // 主动请求一次当前的邻居列表
    routeAgent.send(new GetNeighborListReq());
}

private void updateNeighborList(NeighborListNtf ntf) {
    nodeList.clear();
    nodeList.addAll(ntf.getNeighbors());
    nodeList = nodeList.stream().distinct().collect(Collectors.toList());
    log.warning("Updated neighbor list: " + nodeList);
    NUM_NODES = nodeList.size();
}

private void onNewNeighbor(NeighborDiscoveryNtf ntf) {
    int neighbor = ntf.getNeighbor();
    if (!nodeList.contains(neighbor)) {
        nodeList.add(neighbor);
        log.warning("New neighbor found: " + neighbor);
        NUM_NODES = nodeList.size();
    }
}

为什么单独Groovy脚本可以运行

Shell里的Groovy脚本不会注册冲突的服务,而且脚本的receive操作是在Shell独立的线程中执行,不会阻塞Unet内置Agent的事件循环,因此MAC服务能正常完成资源预约并处理邻居发现请求。

内容的提问来源于stack exchange,提问作者Bhavya Mehta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:43:21