基于TILE-GX实现快速数据包处理框架-netlib实现分析

NETLIB是一款专为TILE-GX众核处理器设计的软件开发套件,它能够显著提升数据包处理性能,并缩短产品上市时间。NETLIB通过在用户空间实现数据包处理过程,避免了传统内核空间处理所带来的开销,支持L2至L7层的网络功能。本文档详细介绍了NETLIB的组件、开发环境、目录结构及如何使用。


转载请注明出处出!

NETLIB FOR FAST PATHPACKET PROCESS

1.概述

网络功能,例如数据包转发、路由、DNS、IPS/IDS、DPI(deep packet inspection)、负载均衡、音频/视频网络服务off-loading等,上述涉及到数据包处理的网络功能会将数据面和网络面分离开来。

Ø 数据面数据包处理过程包括如下操作:

Ø 拷贝I/O数据到系统内存;

Ø 分析数据包;

Ø 执行关于包含多个表项的表查询操作;

Ø 基于表查询的结果将数据包进行分类;

Ø 根据需要的功能分析IP包头或载荷;

Ø 应用额外的功能,例如流量整形;

Ø 管理数据包队列;

Ø 尽可能快的将数据包拷贝到目的I/O设备;

在现代高速率传输的网络环境下,因为数据在I/O设备和系统内存之间频繁的拷贝会给系统总线和处理器带来较大的压力。

为了缓解该问题,数据面数据包处理过程被从控制面数据包处理过程中分离出来,在用户空间实现类似于OS的模块以消除上述网络处理给内核带来的开销。就像fast path processing处理,该种数据包的处理过程可以避免由内核抢占、线程切换、定时器等内核任务带来的开销。Slow path processing的数据包处理过程基于OS内核实现,其处理过程中会涉及到更为复杂的内核处理过程带来的开销。

Netlib为数据面的软件开发套件,其能够使运行于TILE-GX众核处理族上应用实现非常突出的性能表现,同时能够缩短产品上市的时间和数据包处理应用软件的开发周期。Netlib实现的模块涉及LinkLayer(l2)、Internet/IPLayer(L3)、Transport Layer(L4)甚至L5~L7。

Note:Netlib AN43 1.20开发套件完善了IPv4数据包的处理流程,但是不支持IPv6操作。通过简单的修改可以使Netlib支持IPv6数据包的接收和发送,后续详细介绍修改的方式。

Netlib作为一个例子框架,其在应用层实现了很多的功能项。你可以利用其中的某些组件已达到你的性能需求或者产品功能需求。

图1中,黄色tile表示运行于控制面模式上的完全SMP Linux,蓝色tiles表示运行于数据面模式上的支持Zero Overhead Linux(ZOL)用以高速率数据包的处理。SMP Linux处理大部分的控制面的操作。数据面运行于用户空间,没有被放进内核。

2.Netlib拥护者

 Netlib能够表现如此高的性能完全依赖于其特殊的数据包处理过程,该过程在用户空间实现数据包的处理,而不是传统意义上内核空间。其提供的基准测试模块展现了基于TILE-GX处理器族的数据包处理能力和可扩展性。

  网络应用开发工程师可以参考Netlib实现改善其数据包的处理性能。系统/架构设计师可以设计自己的系统时参考基础测试模块的结果。

3.Netlib v1.1组件

      Netlib V1.1实现的networking fast path组件包括:

Ø Layer-2网桥

Ø Layer-3最长前缀匹配(Longest Prefix Matching LPM)的IP数据报转发模块

Ø 基于flow table实现的数据包处理,包括:

ü Flow转发模块

ü IP路由缓存模块(Route Cache)

ü NAT模块

Ø 为了能够高效实用Tilera硬件资源的基础组件,包括:

ü CPU pool管理模块

ü 内存管理模块

ü mPIPI管理模块

       任何需要fast path数据面 L2/L3转发、NAT、处理的网络应用都可以直接使用其提供的API开发以达到高性能的数据包处理。

4.Netlib开发环境

      Netlib的开发需要Tilera MDE的支持,所以我们需要声明环境变量:TILERA_ROOT到MDE开发环境,tilera MDE 一般安装在/opt/tilera目录下。

      硬件方面,TILE-GX36芯片至少需要8GRAM以支持netlib v1.1能够运行其应用程序和达到预期的性能。

5.Netlib目录架构

      下面为Netlib v1.1整个源码的目录架构:

 

6 .开始使用Netlib

为了能够编写你自己的Netlib应用程序,你首先需要初始化Netlib的数据结构以及Netlib的任务。

Ø netlib_init_data()--初始化Netlib各个模块需要的数据结构。

Ø netlib_init_task()—为Netlib各个模块初始化、分配任务。

在进行任何操作之前你必须调用上述两个过程完成Netlib必要的初始化。关于data和task的具体处理流程,后续会详细的分析。

Netlib支持日志和调试模块,我们可以使用netlib_logger_on和netlib_debug_set(NL_DEBUG_ALL)完成日志模块的开启和配置需要调试的模块,默认打开所有模块的调试功能。

6.1基于Netlib实现IPv4路由和防火墙功能

基于TILE-GX处理器我们可以实现路由、防火墙、ACL应用,下面为整体的架构图

你需要自己实现控制面应用以支持路由协议(BGP、OSPF等),同时包括任何的管理工具(ssh、telnet、ftp等)。

    如图所示,Netlib提供了一些基础的组件,我们基于这些组件实现自己的应用,或则改善Netlib的实现已达到自己的需求。Netlib提供IPSec功能,Seclib(未实现)可以允许基于TRIO和MiCA(硬件加解密)加速用户应用的处理速度。

6.2如何基于Netlib移植路由器/防护墙应用到TILE-GX

    如果你想将自己已经实现好的路由器或防火墙移植到TILE-GX处理器架构平台上,你可以参考Netlib如何使用CPU、mPIPE,如何实现内存的管理以及如何管理flow tables以达到处理性能的优化。或者你可以移植Netlib的某些模块到自己的平台上。如果你需要基于Netlib移植自己的网络应用,你可能要考虑下面几问题:

Ø mPIPE classifier:使用mPIPE的分类器引擎将hash运算和数据包解码操作从CPU中剥离,以减轻CPU的负载

Ø ZOL模式:使Netlib运行于ZOL(dataplane)tiles模式下,以达到避免抖动带来的影响,将控制面和数据面的任务分别运行于不同的处理器上以避免不同任务之间的冲突。

Ø mPIPE rx/tx:Netlib经过对于mPIPE细致的调整以提高应用层的性能。关于mPIPE的管理你可以参考Netlib RX/TX Module的相关实现。

Ø 内存管理:huge pages和home cache的内存属性为低延迟、高性能的重要因素。

Ø Netlib利用了TILE-GX处理器的DDC(Dynamic Distributed Caching)和mPIPE引擎提供的优势。同时你也可以使用基于per-worker-tile-basedflow tables,secondaryflow hash,或者lock-lesstable的查询等手段以优化数据包的处理流程。

6.3基于Netlib构建网络设备

用户可以使用Netlib实现一个路由器或者防火墙。Netlib提供的基础组件可以让用户轻松地扩展和使用。

 如果你需要在用户空间实现自己的控制面的应用,你所需要的做的就是将用户自己的实现的任务加入到Netlib的任务管理模块中。然后使用Netlib的数据面特性,例如flow table、ACL模块、LPM路由模块、NAT模块等完成数据面的数据包处理操作,或则你可以重写数据包的特性,然后将其直接加入到Netlib的netlib/service/fwd_stack目录下,之后你就可以使用自己定制的数据面特性工作了。

 或者,你不希望移植自己的控制面应用到用户空间,而选择使用内核作为控制面,使用Linux内核网络协议栈处理控制面的操作。使用标准的Linux内核网络协议栈可以降低控制面的管理复杂度。Netlib提供了这一能力。

 用户可以使用定制化的classifier将控制数据包发送到Linux内核,而将其他的数据包直接发送到用户空间。Netlib同时提供了将用户空间的数据包发送内核网路协议栈的能力,其基于Linux内核实现的tap/tun机制实现。

 Netlib同样提供了将同步Linux内核路由表和ARP表到用户空间的能力,其基于netlink和netevent实现,所以用户空间可以获得一份路由表和arp表信息已完成用户空间的路由功能。

7.Netlib软件架构和代码集成

下图展示Netlib的软架构以及其与Tilera的硬件之间的集成关系

7.1Netlib应用如何使用mPIPE和Tiles

 Netlib应用程序通过下面几种方式实现数据面和控制面Tiles和mPIPe之间的通信。Netlib应用程序使用mPIPE完成下面的功能:

Ø 依据预先配置好的模式将流量负载均衡到Tiles(round Robin、static affinity、dynamic flowaffinity)

Ø 在出方向上帮组计算TCP校验和

Ø 在出、入方向上帮助Tile执行计算IP头校验和验证和TCP校验和验证工作。

    

Netlib提供了如下几种mPIPE的负载均衡模式:

Ø Always pick load balancing: NETLIB_MPIPE_BUCKET_ROUND_ROBIN

Ø Fixed load balancing: NETLIB_MPIPE_BUCKET_STATIC_FLOW_AFFINITY

Ø Sticky:NETLIB_MPIPE_BUCKET_STICKY_FLOW_LOCALITY

Ø Dynamic flow affinity: NETLIB_MPIPE_BUCKET_DYNAMIC_FLOW_AFFINITY

Tilera的官方文档UG404的5.2.9.4节详细介绍了mPIPE负载均衡的实现方式。

 在上图中,蓝线表示大部分的数据包都是基于mPIPE绕过Linux内核直接发送给了fast path(数据面)Tile。

 绿线表示被发送给fast path的数据包需要被发送给slow path进行进一步的处理,例如一个数据包成功匹配了路由,但是没有没有匹配的ARP信息。Linux TAP Tunnel接口提供了该机制。

 红线表示控制面的数据包被直接发送给slow path,即Linux内核协议栈。

8.定制Netlib mPIPE classifer

 相比于MDE提供的默认的mPIPE classifier,Netlib提供了两种定制的功能:

Ø 基于五元组信息计算second flow hash以减缓CPU的开销,second hash用于flow模块以减少flow hashtable的冲突。

Ø 过滤发送给用户空间的IPv4 TCP/UDP数据包,其他的数据包都发送给Linux kernel

8.1 mPIPE classifier计算second hash

  为了减少flow hash table的冲突率,Netlib使用基于CRC实现的hash key计算算法能够大大降低五元组计算后hash key的冲突率,提高hash table的查询性能。该算法实现为:netlib_tuple_to_second_hash(),其源码实现位于netlib\inclue\netlib_classifier_

 ops.h文件中。编译classifier使用NETLIB_GEN_SECOND_HASH启用second_hash计算机制。

   该算法可以应用于其他需要对五元组进行hash key计算的场景。

8.2过滤发送给用户空间和Linux内核的数据包

   在某些网络应用场景,用户要求slow path流量被Linux内核处理,然后仅仅在用户空间是实现fast path数据面的特性,因此常常存在需要classifier具有过滤数据包的功能。

   Netlib使用该特性实现了将IPv4 TCP/UDP数据包发送给用户空间,而不其他的数据包发送给Linux内核。这种过滤机制基于特殊的VLAN规则实现,IPV4 TCP/UDP数据流拥有特殊的VLAN ID,所以其可以匹配VLAN规则配置选项。编译classifier时定义NETLIB_SPILIT_WITH_VLAN就是可以启用该特性。

   为了使用该过滤特性,用户的应用程序需要在mPIPE启动时配置VLAN规则。Netlib用户可以在netlib_config.h中定义NETLIB_MPIPE_TRAFFIC_SPILT_MODE为NETLIB_MPIPE_TRAFFIC_SPLIT_VLAN以启用过滤特性。

8.2.1如何过滤特殊端口号的TCP/UDP流量

   我们已经知道classifier可以过滤IPv4 TCP/UDP数据包并将其发送到用户空间,下面我们所以下如何定制过滤特殊端口号的TCP/UDP数据包。

   比如,某用户需要将端口号为80的TCP数据包过滤处掉,然后将其发送给Linux内核处理,想过要完成上述功能用户做一下的工作:

A.      修改netlib/mpipe/classifier.c文件如下:

Ø 将864行关于tcp端口判断语句修改为:if(dst_port != 80)表示将目的端口号为80的TCP数据包发送给linux内核,其他的数据包发送给用户空间。

Ø UDP端口的修改方式类似

Ø 修改custom_tcp_vlan、custom_udp_vlan、netlib_app_vlan的定义使其与netlib_config.h中配置相符。Netlib_config.h配置文件中对应的值为:

NETLIB_MPIPE_SPLIT_VLAN_TCP_ID

NETLIB_MPIPE_SPLIT_VLAN_UDP_ID

注意这个变量的任意两个的值不能相同。

B.       Classifier的加载方式有两种:

Ø Bootrom加载方式:将编译好的classifier二进制文件集成到bootrom文件中,然后通过tile-monitor将其烧写到ROM中,这样当Tilera设备每次启动时都会加载该classifier。

Ø 动态加载classifier:使用tilera提供的gxio_mpipe_classifier_load_from_file接口将classifier动态的加载到mpipe中。加载classifier的位置为netlib/framework/rx_tx/目录下的netlib_if_gx.c文件的netlib_mpipe_rules_i

nit函数。

C.       Classifier规则添加:在netlib_mpipe_rules_init函数中我们还需要添加mpipe的classifier规则,即把netlib_config.h文件中定义的netlib_mpipe_split_vlan_XXX_ID添加到mpipe中。使用的接口、数据结构如下:

ü Gxio_mpipe_rules_t:mpipe的过滤规则

ü Gxio_mpipe_rules_add_vlan:将vlan_id添加到mpipe规律规则中。

具体实现参考/home/lhl/NIC/trunk/netlib/下的mpipe classifier tarball中的实现。

9.如何使用Netlib“同步”模块

      Netlib内核同步模块提供了Linux内核表的用户空间同步能力,Netlib提供了两种表的同步能力:

Ø 路由表(FIB)

Ø 邻居表(ARP)

Netlib内核表同步模块基于netlink和netevent事件处理机制实现。

Ø 用户应用程创建netlink套接字

Ø Linux内核路由或邻居表更新时都会触发netevent事件

Ø Linux内核netlink模块将更新信息通过netlink消息发送到用户空间

<think>我们使用mapbox-vector-tile-java库来处理矢量切片数据,并实现轨迹点的抽稀(即轨迹压缩)。抽稀算法可以选择道格拉斯-普克算法(Douglas-Peucker),这是一种常用的轨迹压缩算法。 步骤: 1. 添加mapbox-vector-tile-java依赖。 2. 读取矢量切片数据,提取轨迹点(LineString几何对象)。 3. 应用道格拉斯-普克算法对轨迹点进行抽稀。 4. 将抽稀后的轨迹点重新编码为矢量切片。 注意:mapbox-vector-tile-java库用于编码和解码Mapbox矢量切片(MVT)。我们这里假设已经有一个包含轨迹(线)的矢量切片。 由于矢量切片以图层(Layer)和要素(Feature)的形式组织,每个要素包含几何图形和属性。我们的目标是处理几何图形中的线。 道格拉斯-普克算法原理:递归地找到偏离线段最远的点,如果该点到线段的距离大于阈值,则保留该点,然后以该点为界将线段分成两段,递归处理。否则,舍弃所有中间点。 我们将编写一个DouglasPeuckerSimplifier类来实现该算法,然后使用mapbox-vector-tile-java的API来读取和写入MVT。 以下是代码示例:</think>### 使用 mapbox-vector-tile-java 实现轨迹点抽稀 轨迹点抽稀(轨迹压缩)的核心是**道格拉斯-普克算法**(Douglas-Peucker),该算法通过保留关键拐点来减少冗余点。以下是基于 `mapbox-vector-tile-java` 的实现步骤: #### 1. 添加 Maven 依赖 ```xml <dependency> <groupId>com.wdtinc</groupId> <artifactId>mapbox-vector-tile</artifactId> <version>3.1.0</version> </dependency> ``` #### 2. 道格拉斯-普克算法实现 ```java import java.util.ArrayList; import java.util.List; public class DouglasPeucker { /** 执行轨迹抽稀 */ public static List<VectorTile.Tile.Geom> simplify( List<VectorTile.Tile.Geom> points, double epsilon) { if (points.size() < 3) return points; // 找到离首尾连线最远的点 int index = 0; double maxDistance = 0; Point lineStart = toPoint(points.get(0)); Point lineEnd = toPoint(points.get(points.size()-1)); for (int i = 1; i < points.size()-1; i++) { double d = perpendicularDistance(toPoint(points.get(i)), lineStart, lineEnd); if (d > maxDistance) { maxDistance = d; index = i; } } // 递归分割 if (maxDistance > epsilon) { List<VectorTile.Tile.Geom> result1 = simplify(points.subList(0, index+1), epsilon); List<VectorTile.Tile.Geom> result2 = simplify(points.subList(index, points.size()), epsilon); List<VectorTile.Tile.Geom> result = new ArrayList<>(result1); result.addAll(result2.subList(1, result2.size())); // 避免重复点 return result; } else { return List.of(points.get(0), points.get(points.size()-1)); } } /** 计算点到线段的垂直距离 */ private static double perpendicularDistance(Point p, Point lineStart, Point lineEnd) { double x = p.x, y = p.y; double x1 = lineStart.x, y1 = lineStart.y; double x2 = lineEnd.x, y2 = lineEnd.y; double numerator = Math.abs((y2-y1)*x - (x2-x1)*y + x2*y1 - y2*x1); double denominator = Math.sqrt(Math.pow(y2-y1, 2) + Math.pow(x2-x1, 2)); return numerator / denominator; } /** 几何对象转坐标点 */ private static Point toPoint(VectorTile.Tile.Geom geom) { return new Point(geom.getX(), geom.getY()); } static class Point { double x, y; Point(double x, double y) { this.x = x; this.y = y; } } } ``` #### 3. 处理矢量切片中的轨迹 ```java import com.wdtinc.mapbox_vector_tile.VectorTile; import com.wdtinc.mapbox_vector_tile.adapt.jts.MvtReader; import com.wdtinc.mapbox_vector_tile.adapt.jts.MvtWriter; import org.locationtech.jts.geom.*; public class TrajectorySimplifier { /** 抽稀处理入口 */ public static VectorTile.Tile processTile(byte[] mvtData, double epsilon) throws Exception { // 1. 解码MVT VectorTile.Tile tile = VectorTile.Tile.parseFrom(mvtData); // 2. 遍历图层和要素 VectorTile.Tile.Builder builder = tile.toBuilder(); for (VectorTile.Tile.Layer layer : builder.getLayersList()) { VectorTile.Tile.Layer.Builder layerBuilder = layer.toBuilder(); for (int i = 0; i < layer.getFeaturesCount(); i++) { VectorTile.Tile.Feature feature = layer.getFeatures(i); // 3. 仅处理线型轨迹 if (feature.getType() == VectorTile.Tile.GeomType.LINESTRING) { List<VectorTile.Tile.Geom> simplified = DouglasPeucker.simplify( feature.getGeometryList(), epsilon ); // 4. 更新几何数据 VectorTile.Tile.Feature.Builder featureBuilder = feature.toBuilder(); featureBuilder.clearGeometry(); featureBuilder.addAllGeometry(simplified); layerBuilder.setFeatures(i, featureBuilder.build()); } } builder.setLayers(i, layerBuilder); } return builder.build(); } /** 示例:使用JTS几何库读取外部数据 */ public static Geometry readJtsGeometry(String geojson) { // 实际实现需使用GeoTools等库解析GeoJSON return new LineString(new Coordinate[]{ new Coordinate(0, 0), new Coordinate(10, 10), new Coordinate(20, 15) }, new PrecisionModel(), 4326); } } ``` #### 关键说明: 1. **抽稀阈值** `epsilon`:控制压缩强度(单位:像素),值越大压缩率越高 2. **几何类型处理**:仅针对 `LINESTRING` 类型轨迹 3. **坐标参考**:实际应用需处理坐标系转换(如:经纬度转Web墨卡托) 4. **性能优化**:对于长轨迹,可分段并行处理 #### 应用场景 - 移动端地图显示:减少传输数据量 - 历史轨迹存储:降低存储成本 - 实时轨迹渲染:提升绘制效率 > 矢量切片以点、线和多边形的形式存储数据,支持直接查询和样式操作,适用于交互式地图场景[^1]。开源工具链(如TileMill)可辅助构建完整地图服务[^2]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值