1. 程式人生 > >ONOS:負載均衡路由算法及應用開發(二)

ONOS:負載均衡路由算法及應用開發(二)

lan group uil etc src reactive core 函數的調用 pty

ONOS:負載均衡路由算法及應用開發(二)

本文將為大家講述應用的實現,並進行必要的代碼分析。

本應用暫時以Maven作為項目的構建工具,並采用最簡單的single bundle的項目組織形式[1]。如果進行大項目的開發,推薦仿照onos.faultmanagement應用進行模塊劃分和項目feature組織。

雖然ONOS在最新的1.8.0-SNAPSHOT版本中強制引入了BUCK項目構建工具,但本應用開發時尚未有這個要求。大家在開發自己的應用時仍可使用Maven,但如果想要貢獻代碼,則必須添加兼容BUCK構建工具的配置信息。

一、Maven項目POM文件

為了便於各位理解,必要的講解已經寫在了註釋中。

1.App屬性信息

<!-- Mao: Application Identifier -->
    <groupId>org.mao</groupId>
    <artifactId>onos-app-mao-load-balance</artifactId>
    <packaging>bundle</packaging>
 
    <!-- Mao: Application Readable Info -->
    <description>Mao Load Balance Routing</
description> <url>http://maojianwei.github.io/</url> <properties> <!-- Mao: ONOS App Info for Jersey REST API & Swagger UI --> <web.context>/onos</web.context> <api.version>1.0.0</api.version> <api.title
>Mao Load Balance Routing REST API</api.title> <api.description> APIs for interacting with the Mao Load Balance application. </api.description> <api.package>org.fnl.rest</api.package> <!-- Mao: ONOS App Info for maven packaging --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <onos.version>1.8.0-SNAPSHOT</onos.version> <onos.app.name>org.onosproject.mao.loadbalance</onos.app.name> <onos.app.title>Mao Load Balance Routing</onos.app.title> <onos.app.origin>Mao Jianwei, FNLab, BUPT</onos.app.origin> <onos.app.category>Traffic Steering</onos.app.category> <onos.app.url>http://maojianwei.github.io/</onos.app.url> <onos.app.readme>Mao Load Balance Routing</onos.app.readme> </properties>

2.App依賴

<dependencies>
        <!-- REST API -->
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <!--<version>2.22.2</version>-->
        </dependency>
 
        <!-- PortStatisticsService -->
        <dependency>
            <groupId>org.onosproject</groupId>
            <artifactId>onos-incubator-api</artifactId>
        </dependency>
 
        <!-- DefaultTopology -->
        <dependency>
            <groupId>org.onosproject</groupId>
            <artifactId>onos-core-common</artifactId>
            <version>1.8.0-SNAPSHOT</version>
        </dependency>
</dependencies>

二、代碼分析

1.源碼目錄總覽

技術分享
主要包含如下兩個部分:

  • MaoRoutingManager:負載均衡Reactive Routing核心模塊
  • MaoRoutingService:預留供引用的模塊服務接口

2. 負載均衡核心模塊MaoRoutingManager

使用ONOS提供的基礎服務,加粗的是本應用重點使用的服務:

服 務

用 途

CoreService

註冊應用模塊,獲取ApplicationId

IntentService

下發/撤銷數據流的路由決策

TopologyService

獲取網絡當前拓撲

HostService

獲取客戶機的抽象對象Host

DeviceService

獲取設備端口的抽象對象Port,從中獲取端口的工作速率

PortStatisticsService

獲取鏈路連接點ConnectPoint的當前發送速率

PacketService

添加/移除數據包處理器;

註冊/撤銷網絡應上報的數據包的特征

從主模塊MaoRoutingManager的角度看,模塊內部設計和功能劃分如下:
技術分享
整體上劃分為兩個重要部分:

  • BandwidthLinkWeight:鏈路帶寬度量值計算器。符合ONOS選路算法的設計規範。
  • InternalPacketProcessor:數據包處理器。包含流量處理的入口函數,以及本文路由算法所需的函數

a) BandwidthLinkWeight
這是一個工具類,實現了ONOS定義的LinkWeight接口,主要服務於選路算法函數,作用是計算指定鏈路的權值(Weight)。LinkWeight接口定義如下:
技術分享
BandwidthLinkWeight的具體功能是計算鏈路當前剩余帶寬所占的百分比,以此作為鏈路的權值。計算過程中需要獲取鏈路的工作速率和當前速率。如果鏈路失效或鏈路容量已滿,則返回最大值100%,意指鏈路滿載。其實現如下:

/**
* Tool for calculating weight value for each Link(TopologyEdge).
*
* @author Mao.
*/
private class BandwidthLinkWeight implements LinkWeight {
 
    private static final double LINK_WEIGHT_DOWN = 100.0;
    private static final double LINK_WEIGHT_FULL = 100.0;
 
    @Override
    public double weight(TopologyEdge edge) {
 
        if (edge.link().state() == Link.State.INACTIVE) {
            return LINK_WEIGHT_DOWN;
        }
 
        long linkWireSpeed = getLinkWireSpeed(edge.link());
 
        long interLinkRestBandwidth = linkWireSpeed - getLinkLoadSpeed(edge.link());
 
        if (interLinkRestBandwidth <= 0) {
            return LINK_WEIGHT_FULL;
        }
 
        // 當前剩余帶寬百分比
        return 100 - interLinkRestBandwidth * 1.0 / linkWireSpeed * 100;
    }
 
    ...
 
}


其中使用到以下四個輔助函數:

  • getLinkWireSpeed:返回鏈路的工作速率;暫定以兩端工作速率的最小值作為鏈路工作速率。
  • getLinkLoadSpeed:返回鏈路的當前速率;暫定以兩端發送速率的最大值作為鏈路當前速率。
  • getPortWireSpeed:獲取端口的工作速率;
  • getPortLoadSpeed:獲取端口的當前發送速率。
private long getLinkWireSpeed(Link link) {
 
    long srcSpeed = getPortWireSpeed(link.src());
    long dstSpeed = getPortWireSpeed(link.dst());
 
    return min(srcSpeed, dstSpeed);
}
 
private long getLinkLoadSpeed(Link link) {
 
    long srcSpeed = getPortLoadSpeed(link.src());
    long dstSpeed = getPortLoadSpeed(link.dst());
 
    return max(srcSpeed, dstSpeed);
}
 
/**
* Unit: bps
*/
private long getPortLoadSpeed(ConnectPoint port) {
 
//data source: Bps
    return portStatisticsService.load(port).rate() * 8;
}
 
/**
* Unit bps
*/
private long getPortWireSpeed(ConnectPoint port) {
    assert port.elementId() instanceof DeviceId;
 
    //data source: Mbps
    return deviceService.getPort(port.deviceId(), port.port()).portSpeed() * 1000000;
}

b)InternalPacketProcessor
為了便於展示,暫時將負載均衡路由算法的入口函數和算法四步驟的相關函數[2]都移到了數據包處理器中。源碼庫中的包處理器只保留了process主函數,其余的都移到了主模塊中。由於本算法可以作為獨立的路由算法使用,因此可將負載均衡路由功能獨立出來,作為本應用對外提供的一項服務,即添加相應的API在預留的MaoRoutingService模塊服務接口中。

包處理器內部分為兩部分。第一部分是流量處理的入口,以下省略了數據檢查和並發同步的部分,只保留了關鍵邏輯的代碼,完整源碼可瀏覽Github倉庫。

@Override
public void process(PacketContext context) {
    Ethernet pkt = context.inPacket().parsed();
    if (pkt.getEtherType() == Ethernet.TYPE_IPV4) {
 
        // 根據MAC生成主機標識ID
        HostId srcHostId = HostId.hostId(pkt.getSourceMAC());
        HostId dstHostId = HostId.hostId(pkt.getDestinationMAC());
 
        // 為兩個主機的該條流量選擇一條路由路徑
        Set<Path> paths = getLoadBalancePaths(srcHostId, dstHostId);
 
        // 構造流量匹配域
        IPv4 ipPkt = (IPv4) pkt.getPayload();
        TrafficSelector selector = DefaultTrafficSelector.builder()
                .matchEthType(Ethernet.TYPE_IPV4)
                .matchIPSrc(IpPrefix.valueOf(ipPkt.getSourceAddress(), 32))
                .matchIPDst(IpPrefix.valueOf(ipPkt.getDestinationAddress(), 32))
                .build();
 
        // 使用任意一條路徑結果
        Path result = paths.iterator().next();
 
        // 構造PathIntent路徑意圖
        PathIntent pathIntent = PathIntent.builder()
                .path(result)
                .appId(appId)
                .priority(40123)
                .selector(selector)
                .treatment(DefaultTrafficTreatment.emptyTreatment())
                .build();
 
        // 提交流量路徑決策
        intentService.submit(pathIntent);
    }
}


第二部分是路由算法實現部分。以下是算法實現中所有函數的調用關系和算法流程圖,請先關註右上方的圖例。
技術分享

圖 1負載均衡路由算法的函數調用關系及算法流程圖

先回憶一下在上一篇文章(見上文鏈接)中,筆者提到算法過程中幾種結果集的名稱:

可選路由路徑 → 優選路由路徑 → 最優路由路徑

首先,本實現提供了兩個便利的算法入口,可默認采用ONOS感知的實時拓撲進行路由計算,也可根據自定義的拓撲進行計算。

其次,在第一步的探路過程中,暫時使用DFS深度優先查找算法,進行遞歸查找,同時在算法實現中考慮了路由環路的預防。此處使用到ONOS對拓撲圖的三個抽象TopologyGragh、TopologyEdge和TopologyVertex,分別表示圖、邊和頂點。

第二步,算權值。借助BandwidthLinkWeight計算路徑中每一條鏈路的權值,然後以最大的鏈路權值作為該條路徑的權值。利用表征鏈路的各個TopologyEdge對象和算出的路徑權值,生成ONOS中的路由路徑抽象對象Path。

第三步,選路。如下圖,首先通過getMinCostPath選出“優選路由路徑”,再通過getMinHopPath選出“最優路由路徑”。
技術分享

圖 2選路過程中的“優選” 和“選定”

第四步,鋪路。在buildEdgeToEdgePath中將源點的第一跳鏈路與目的的第一跳鏈路分別接在最優路由路徑的前後兩端,並更新其路徑權值。

參考文獻

[1]ONOS 實戰分享(一):項目建立、調試到熱部署【http://www.sdnlab.com/15197.html】

註:本項目開源在筆者的

ONOS:負載均衡路由算法及應用開發(二)