本文章是由機器翻譯。

StreamInsight

馴服事件串流:快速約計

Michael Meijer

下載代碼示例

 

你們得浩繁和潛力無窮的事件,如點擊流、 感應器資料、 信用卡交易資料或互聯網交通流。 它是不可行的要存儲的所有事件或分析他們在多個傳遞。 為什麼不求助於最近發生的事件,以簡化分析的視窗?

假設您想要的涵蓋最新 N 事件流的一個大視窗中的有趣事件進行計數。 盤點幼稚的做法要求要在記憶體和充分的反覆運算中對他們的所有 N 事件。 視窗幻燈片所到達的一個新的事件,其最早的事件到期,插入新的事件。 在新的視窗,從零開始計數廢物共用 N 2 事件的處理時間。 惡 ! 本文介紹了一種資料結構來減少記憶體空間的使用和處理時間需要使用該方法時,應同時支援事件速度超過每秒對商品化的硬體事件有成千上萬的一小部分。 這篇文章還演示了如何在 Microsoft 流式資料處理器,StreamInsight 2.1 使用者定義流運算子在 C# 中嵌入的資料結構。 中間的程式設計技能均須遵守沿,和一些經驗 StreamInsight 會非常有用。

一個故事的計數

潛入 StreamInsight 之前, 我會調查看似瑣碎問題的計數。 為簡單起見,假設流具有與有效載荷的 0 或 1 的事件 — — 枯燥無味和有趣的事件,分別 (無論什麼構成"有趣的"在您的特定情況下)。 包含最新的 N 事件 (固定大小) 基於計數的視窗上方數是 1 秒。 天真計數需要 o (n) 時間和空間。

作為一個精明的閱讀器,您可能出了保持連續的視窗之間的計數和新 1s 和遞減遞增它的想法它過期 1s,為分享 N 2 事件已處理。 良好的思維 ! O (1) 時間: 現在維持計數。 但是,應該你遞減為過期的事件或不? 除非您知道實際的事件,不能保持計數。 不幸的是,要知道直到他們已過期的事件需要在記憶體中的整個視窗 — — 也就是說,它佔用 o (n) 的空間。 另一種策略可能是篩選出不感興趣的事件和計數只剩下有趣的事件。 但這並不減少計算的複雜性,讓你可變大小的視窗。

可以被馴服的記憶體野獸嗎? 是的它可以 ! 但是,它需要的處理時間和記憶體空間犧牲準確性之間的妥協。 和領、 阿裡斯蒂德 Gionis、 彼得 · 迪克 Rajeev Motwani 的論文題為"維護流統計超過滑動視窗"(stanford.io/SRjWT0) 描述了一個稱為指數長條圖的資料結構。 它對與有界的相對誤差的最後 N 事件保持近似計數 ε。 這意味著,在所有時間:

|exact count – approximate count|  ≤ ε, where 0 < ε < 1 
       exact count

從概念上講,長條圖將事件存儲在存儲桶。 每桶最初包括一個事件,所以它有一個計數 1、 它涵蓋了該事件的時間戳記。 當事件到達時,過期水桶 (包括過期的事件) 被刪除。 一桶只創建一個有趣的事件。 隨著存儲桶的創建隨著時間的推移,他們正在合併,以節省記憶體。 水桶合併所以他們有呈指數級增長計數從最新到最後一桶,也就是說,1,1,......,2,2,......,4、 4、......、 8、 8 等。 這種方式,存儲桶的數目是對數的視窗大小 N. 更確切地說,它需要 O (1⁄ε 日誌 N) 的時間和空間進行維護。 但最後的鬥都涵蓋只非過期的事件。 最後的鬥涵蓋至少一個非過期的事件。 必須估計其計數,其中在逼近總體的計數將導致錯誤。 因此,最後的鬥必須保持足夠小,能夠尊重相對誤差上限。

在下一節中,與最低的數學討論是指數長條圖在 C# 中執行。 讀取上述檔的錯綜複雜的詳細資訊。 我將解釋代碼,並跟進用筆和紙的例子。 長條圖是為使用者定義的流運算子在本文稍後部分發達國家 StreamInsight 構建基塊。

鬥或不鬥

這裡是鬥類:

[DataContract]
public class Bucket
{
  [DataMember]
  private long timestamp;
  [DataMember]
  private long count;
  public long Timestamp {
    get { return timestamp; }
    set { timestamp = value; } }
  public long Count { get { return count; } set { count = value; } }
}

它有一個計數 (有趣) 的事件,它涵蓋了、 它涵蓋的最新事件的時間戳記。 如前所述,只有最後的鬥可以涵蓋過期的事件,但它必須包括至少一個非過期的事件。 因此,所有但最後鬥計數是確切。 最後鬥計數必須由長條圖估計。 存儲桶含只過期的事件自己過期,可以從長條圖中刪除。

指數長條圖使用只是兩個操作,確保相對誤差上限 ε N 最近發生的事件感興趣的事件計數。 一次操作是更新新的和過期的事件,保持桶一桶的長條圖。 另一個是用於查詢來自桶一桶的近似數。 長條圖類大綱所示圖 1。 除了存儲桶的連結清單,其關鍵的變數是視窗大小 (n)、 相對誤差上限 (epsilon) 和緩存所有鬥計數 (總數) 的總和。 在建構函式中設置給定的視窗大小、 給定相對誤差上限和存儲桶的初始為空清單。

圖 1 指數長條圖類大綱

[DataContract]
public class ExponentialHistogram
{
  [DataMember]
  private long n;
  [DataMember]
  private double epsilon;
  [DataMember]
  private long total;
  [DataMember]
  private LinkedList<Bucket> buckets;
  public ExponentialHistogram(long n, double epsilon)
  {
    this.
n = n;
    this.epsilon = epsilon;
    this.buckets = new LinkedList<Bucket>();
  }
  public void Update(long timestamp, bool e) { ...
}
  protected void ExpireBuckets(long timestamp) { ...
}
  protected void PrependNewBucket(long timestamp) { ...
}
  protected void MergeBuckets() { ...
}
  public long Query() { ...
}
}

此更新方法進行維修的長條圖:

public void Update(long timestamp, bool eventPayload)
{
  RemoveExpiredBuckets(timestamp);
  // No new bucket required; done processing
  if (!eventPayload)
    return;
  PrependNewBucket(timestamp);
  MergeBuckets();
}

它接受一個離散的時間戳記,而不是牆上時鐘時間,以確定有哪些最新的 N 事件。 這用來查找並刪除已過期的存儲桶。 如果新的事件已為 0 (false) 的有效載荷,將停止處理。 當新的事件具有有效載荷的 1 (true) 時,新桶是創建和預置到存儲桶的清單。 在合併桶一桶的真正的煙花。 在序列中討論由 update 方法調用的方法。

這裡是去除水桶的代碼:

protected void RemoveExpiredBuckets(long timestamp)
{
  LinkedListNode<Bucket> node = buckets.Last;
  // A bucket expires if its timestamp
  // is before or at the current timestamp - n
  while (node != null && node.Value.Timestamp <= timestamp - n)
  {
    total -= node.Value.Count;
    buckets.RemoveLast();
    node = buckets.Last;
  }
}

遍歷從最古老的 (最後一個) 鬥開始,並在第一次非過期鬥結束。 事件的最新時間戳記過期的每個存儲桶 — — 也就是說,其時間戳記是不大於目前時間戳減去視窗大小 — — 從清單中刪除。 這是離散的時間戳記是哪裡來的。 所有鬥計數 (總數) 的總和是遞減計數每個過期的存儲桶。

後過期事件和水桶被占,處理新的事件:

protected void PrependNewBucket(long timestamp)
{
  Bucket newBucket = new Bucket()
  {
    Timestamp = timestamp,
    Count = 1
  };
  buckets.AddFirst(newBucket);
  total++;
}

新鬥為 1 (true) 有效載荷與事件創建帶有 1 和等於目前時間戳時間戳記的計數。 新的鬥預置到存儲桶的清單並遞增的所有鬥計數 (總數) 的總和。

記憶體節省空間和錯誤邊界魔術是並購的水桶中。 中列出的代碼圖 2。 水桶合併這樣連續水桶有呈指數級增長......,計數,也就是說,1,1,2,2,......,4、 4、......、 8、 8 等。 具有相同數量的存儲桶數,由選擇的相對誤差上限 ε。 總的存儲桶數對數隨著視窗 n,其中解釋了節省記憶體空間的大小。 合併後盡可能多桶,但最後鬥數保持在最小不足 (相比其他鬥計數的總和),確保相對誤差為界。

圖 2 合併在長條圖中的存儲桶

protected void MergeBuckets()
{
  LinkedListNode<Bucket> current = buckets.First;
  LinkedListNode<Bucket> previous = null;
  int k = (int)Math.Ceiling(1 / epsilon);
  int kDiv2Add2 = (int)(Math.Ceiling(0.5 * k) + 2);
  int numberOfSameCount = 0;
  // Traverse buckets from first to last, hence in order of
  // descending timestamp and ascending count
  while (current != null)
  {
    if (previous != null && previous.Value.Count == current.Value.Count)
      numberOfSameCount++;
    else
      numberOfSameCount = 1;
    // Found k/2+2 buckets of the same count?
if (numberOfSameCount == kDiv2Add2)
    {
      // Merge oldest (current and previous) into current
      current.Value.Timestamp = previous.Value.Timestamp;
      current.Value.Count = previous.Value.Count + current.Value.Count;
      buckets.Remove(previous);
      // A merged bucket can cause a cascade of merges due to
      // its new count, continue iteration from merged bucket
      // otherwise the cascade might go unnoticed
      previous = current.Previous;
    }
    else
    {
      // No merge, continue iteration with next bucket 
      previous = current;
      current = current.Next;
    }
  }
}

更正式的水桶有是非遞減計數從第一個 (最新) 到清單中最後的 (最早) 鬥。 鬥計數被約束為 2 的冪。 讓 k = 1⁄ε和 k⁄2 是一個整數,否則,取代後者的。 除了最後鬥計數,讓至少有 k⁄2 和最 k⁄2 + 1 桶的相同的計數。 每當有 k⁄2 + 2 桶的相同的計數、 最古老的兩個被合併到一桶兩倍的最古老的鬥計數與最近的其時間戳記。 合併兩個水桶,每當從合併後的鬥繼續遍歷。 合併會導致一連串的合併。 否則會遍歷繼續從下一個桶裡。

若要獲取計數逼近的一種感覺,看看長條圖的查詢方法:

public long Query()
{
  long last = buckets.Last != null ?
buckets.Last.Value.Count : 0;
  return (long)Math.Ceiling(total - last / 2.0);
}

到最後的鬥鬥數的總和是確切的。 最後的鬥必須包含至少一個非過期的事件,否則為鬥是過期並刪除。 因為它可以包括過期的事件,必須估計其計數。 通過估計的最後一桶半最後鬥的計數作為實際計數,絕對錯誤的估計是不大於該鬥的半計數。 總體的計數被估計所有鬥計數 (總) 減去一半最後鬥的計數的總和。 要確保絕對的錯誤是相對誤差,最後鬥影響範圍內必須足夠小相比其他鬥數的總和。 所幸的是,這被確保合併過程。

做的代碼清單和這一點的解釋離開你疑惑的長條圖運作嗎? 下面的示例通過讀取。

假設您有新初始化的長條圖與視窗大小 n = 7 和相對誤差上限 ε = 0.5,所以 k = 2。 長條圖開發中所示圖 3,和中描述了此長條圖示意性概述 圖 4。 在圖 3,合併是在 5、 7 和 9 的時間戳記。 級聯的合併是 9 的時間戳記。 過期的鬥是 13 的時間戳記。 我將更詳細地介紹這去。

圖 3 的指數長條圖示例

Timestamp Event

水桶計數時間戳記)

最新 ...最古老

總數 查詢 精確

相對

Error

1 0   0 0 0 0
2 1 (2,1) 1 1 1 0
3 1 (3,1) “ (2,1) 2 2 2 0
4 0 (3,1) “ (2,1) 2 2 2 0

5

(合併)

1 (5,1) “ (3,1) “ (2,1) 3 2 3 0.333...
(5,1) “ (3,2)
6 1 (6,1) “ (5,1) “ (3,2) 4 3 4 0.25

7

(合併)

1 (7,1) “ (6,1) “ (5,1) “ (3,2) 5 4 5 0.2
(7,1) “ (6,2) “ (3,2)
8 1 (8,1) “ (7,1) “ (6,2) “ (3,2) 6 5 6 0.166...

9

(合併)

(級聯的合併)

1 (9,1) “ (8,1) “ (7,1) “ (6,2) “ (3,2) 7 5 6 0.166...
(9,1) “ (8,2) “ (6,2) “ (3,2)
(9,1) “ (8,2) “ (6,4)
10 0 (9,1) “ (8,2) “ (6,4) 7 5 5 0
11 0 (9,1) “ (8,2) “ (6,4) 7 5 5 0
12 0 (9,1) “ (8,2) “ (6,4) 7 5 4 0.25
13 0 (9,1) “ (8,2) 3 2 3 0.333...

A Schematic Overview of the Histogram Depicted in Figure 3
圖 4 圖 3 所示的長條圖圖表概述

第一個事件沒有任何影響。 在第五個活動,合併最古老鬥的發生是因為有文字方塊中:k⁄2 + 2 桶與 1 相同的計數。 再次,合併發生在第七屆的事件。 在第九屆的事件中,合併到另一個合併級聯。 注意在第七屆的事件之後, 的第一個事件到期。 沒有鬥攜帶 13 事件直到過期時間戳記。 13 事件,在時間戳記 6 不再為鬥涵蓋至少一個非過期事件並因此過期。 請注意觀察到相對誤差是明顯小於相對誤差上限。

圖 4,虛線的框是視窗大小該點 ; 它包含水桶,意味著所涉及的事件的範圍。 實心框是一桶上頂部的時間戳記與上底部的計數。 情況 A 在帶有箭頭的計數的事件的時間戳記 7 顯示長條圖。 情況 B 在 9 的時間戳記顯示長條圖。 最後的鬥涵蓋過期的事件。 情況 C 在 13 時間戳記顯示長條圖。 在時間戳記為 6 桶過期。

後放在一起,我寫了指數長條圖 (簽出這篇文章的來原始程式碼下載) 小示範工程。 所得的結果顯示在 [圖 5] 中。 它類比 1 億 100 萬的事件計數基於視窗大小 N 的事件流。 每個事件都有一個有效載荷的 0 或 1 與 50%的機會。 它估計的近似數與任意選擇的相對誤差上限 1s ε 的 1%或 99%的準確性。 記憶體儲蓄的長條圖是巨大的相比 windows ; 存儲桶的數目是遠遠低於在視窗中的事件數。 每秒幾個 10 萬事件事件率是與英特爾 2.4 g h z 雙核心處理器和 3 GB 的 RAM 運行 Windows 7 的電腦上實現的。

Empirical Results for the Exponential Histogram
圖 5 實證結果指數長條圖

稱為 StreamInsight 美

也許你想知道微軟 StreamInsight 是什麼以及它。 本節提供了一些基本的東西。 StreamInsight 是一個強健、 高性能、 低開銷、 接近零延遲和極其靈活的引擎,溪流上的處理。 這是目前在 2.1 版。 完整版需要SQL Server許可證,雖然試用版本。 它具有運行或者作為一個獨立的服務或嵌入過程中。

在流式資料處理的核心是與時空流的事件模型。 從概念上講,它是潛在的無限和浩繁集合的資料隨著時間的推移抵港。 認為股票交易所價格、 天氣遙測、 電力監控、 Web 點擊,互聯網流量,收費亭等。 流中的每個事件都具有中繼資料和資料的有效負載的標頭。 在事件的標題中,保留時間戳,在最低限度。 事件可以到達穩步、 間歇性或也許在每秒數千掃射。 事件有三種類型:一個事件可以限於點時間 ; 其有效期為一定的間隔 ; 或有效期,不限成員名額的時間間隔 (邊緣)。 從流的事件,除了特殊標點事件是由引擎調用公共時間增量 (CTI) 發出的。 事件不能插入流中時間戳記小於 CTI 的時間戳記。 有效,CTI 事件確定事件可以到達順序的程度。 所幸的是,StreamInsight 照顧這一點。

異類源的輸入和輸出流的匯必須以某種方式加以修改以適應到此模型。 (可查詢) 時空流中的事件是在 IQStreamable <TPayload> 中捕獲的。 事件有效載荷從概念上講枚舉由拉或推入流的觀察。 因此,基本資料可以通過公開 IEnumerable <T> / IQueryable <T> (無功副檔名) 或 IObservable <T> / IQbservable <T> (無功副檔名),分別與公開的資料類型參數化。 他們留給處理引擎的時態方面的維護。 從和到的各種介面的轉換是可能的。

源和匯剛剛討論過生活的邊界,而實際處理發生在查詢中。 基本單元組成編寫LINQ中的查詢。 它不斷處理事件從一個或多個資料流程,並輸出另一個流。 查詢是用於專案、 篩選器、 組適用、 多播,操作/聚合,聯接、 聯盟和視窗的事件。 營辦商可由使用者定義。 當他們到達時,他們工作事件 (增量) 或者基於 windows (非增量)。

關於視窗說明的順序。 視窗化分區分成有限子集的連續的視窗之間可能重疊的事件流。 視窗化有效地產生的 windows 中,反映在 IQWindowedStreamable <TPayload> 的流 在 StreamInsight。 目前,支援三種不同的視窗化構造:基於計數的、 基於時間和快照視窗。 基於計數的視窗覆蓋了最新的 N 事件和幻燈片所到達的一個新的事件,過期最古老和最新插入。 基於時間的 windows 蓋除最近發生的事件中最新­val 的時間以及一些間隔狀態 (也稱為跳躍或翻滾) 幻燈片。 快照視窗由事件的開始時間和結束時間 ; 這,對於每對最近事件的開始和結束時間,創建視窗。 與基於時間的 windows 驅動的間隔沿時間表,無論事件,快照視窗沿時間表不被固定。

這僅僅是表面。 從幾個來源,包括連線開發人員指南 》 的詳細資訊 (bit.ly/T7Trrx),"銀河系 StreamInsight 2.1 查詢指南"(bit.ly/NbybvY),CodePlex 例子,StreamInsight 團隊博客 (blogs.msdn.com/b/streaminsight) 和其他人。

整體回顧

是奠定基礎。 在這一點上,你可能不知道如何近似計數提請 StreamInsight 中生活。 總之,需要在時間點事件,攜帶有效載荷的 0 或 1,一些 (時空) 源流。 它被送入 N 最最近發生的事件使用的指數長條圖計算近似的 1s 計數的查詢。 查詢將產生一些 (時間) 時間點在事件流 — — 攜帶的近似計數 — — 這送入一個接收器。

讓我們從一個使用者定義的運算子近似計數。 你可能會忍不住要捕獲使用基於計數的視窗化構造 N 最新的事件。 再想一想 ! 這會違抗指數長條圖的節省記憶體的好處。 原因何在? 構建部隊整個 windows 要保存在記憶體中的事件。 它不需要由指數長條圖,因為它具有等效的隱式概念,通過存儲桶的維護視窗化。 此外,有一個運算子對 windows 是非增量的就是,與沒有處理的事件到達時,但只有當 (下) 視窗時可用。 解決方案是一個使用者定義的流運算子沒有顯式視窗化構造查詢。 中列出的代碼圖 6

圖 6 使用者定義流運算子執行

[DataContract]
public class ApproximateCountUDSO : CepPointStreamOperator<bool, long>
{
  [DataMember]
  private ExponentialHistogram histogram;
  [DataMember]
  private long currentTimestamp;  // Current (discrete) timestamp
  public ApproximateCountUDSO(long n, double epsilon)
  {
    histogram = new ExponentialHistogram(n, epsilon);
  }
  public override IEnumerable<long> ProcessEvent(
    PointEvent<bool> inputEvent)
  {
    currentTimestamp++;
    histogram.Update(currentTimestamp, inputEvent.Payload);
    yield return histogram.Query();
  }
  public override bool IsEmpty
  {
    get { return false; }
  }
}

操作員從抽象的 CepPointStreamOperator < TInputPayload、 TOutputPayload > 派生。 它具有指數長條圖的執行個體變數。 請注意 DataContract 和 DataMember 屬性與裝飾。 這會通知 StreamInsight 如何序列化該運算子 — — 例如,對於恢復能力的目的。 運算子重寫空空如也運算子,以指示它為非空,該運算子是有狀態的即。 這可以防止 StreamInsight 玩弄運算子時儘量減少記憶體使用率。 ProcessEvent 方法是運營商的核心。 它遞增 (離散) 目前時間戳,並將其與事件裝載中傳遞給長條圖的 update 方法。 長條圖處理了瓢潑大雨和近似計數查詢。 請確保使用產量返回語法,這使得該運算子可枚舉。 營辦商一般包裝一些隱藏在一個實用程式類中的擴充方法中。 此代碼演示如何做:

public static partial class Utility
{
  public static IQStreamable<long> ApproximateCount(
    this IQStreamable<bool> source, long n, double epsilon)
  {
    return source.Scan(() => new ApproximateCountUDSO(n, epsilon));
  }
}

就是這個 ! 該運算子插入查詢通過擴充方法。 需要一點額外的代碼實際上證明其使用。 這裡是一個微不足道的源流:

public static partial class Utility
{
  private static Random random = new Random((int)DateTime.Now.Ticks);
  public static IEnumerable<bool> EnumeratePayloads()
  {
    while (true)  // ad infinitum
    {
      bool payload = random.NextDouble() >= 0.5;
      yield return payload;
    }
  }
}

這將生成隨機的 0 和 1 的有效載荷。 產量返回語法把它變成一個可枚舉的來源。 把它放在實用程式類中,如果你願意的話。

臭名昭著的程式類所示圖 7。 它創建的過程中嵌入的 StreamInsight 伺服器實例。 創建名為 ApproximateCountDemo 的所謂應用程式實例的如流式處理 (中繼資料) 的容器,例如,對於命名的流,查詢等。 使用前面所述的有效負載生成實用程式方法,定義可枚舉的時間點在事件源。 它變成一個時空的時間點在事件流。 查詢使用LINQ定義並選擇通過源流計算運算子近似計數。 這是遲早要用到擴充方法的使用者定義的運算子的位置。 它被引導與視窗的大小和相對誤差上限。 下一步,查詢輸出轉換成一個可枚舉的接收器,剝離的時空屬性。 最後,接收器反覆運算結束,從而積極地拉動通過管道事件。 執行該程式,並享有其在螢幕上的數位計算輸出。

圖 7 嵌入和在 StreamInsight 中執行

class Program
{
  public const long N = 10000;
  public const double Epsilon = 0.05;
  static void Main(string[] args)
  {
    using (Server server = Server.Create("StreamInsight21"))
    {
      var app = server.CreateApplication("ApproximateCountDemo");
      // Define an enumerable source
      var source = app.DefineEnumerable(() =>
        Utility.EnumeratePayloads());
      // Wrap the source in a (temporal) point-in-time event stream
      // The time settings determine when CTI events
      // are generated by StreamInsight
      var sourceStream = source.ToPointStreamable(e =>
        PointEvent.CreateInsert(DateTime.Now, e),
        AdvanceTimeSettings.IncreasingStartTime);
      // Produces a stream of approximate counts
      // over the latest N events with relative error bound Epsilon
      var query =
        from e in sourceStream.ApproximateCount(N, Epsilon) select e;
      // Unwrap the query's (temporal) point-in-time
      // stream to an enumerable sink
      var sink = query.ToEnumerable<long>();
      foreach (long estimatedCount in sink)
      {
        Console.WriteLine(string.Format(
          "Enumerated Approximate count: {0}", estimatedCount));
      }
    }
  }
}

簡單回顧一下,這篇文章解釋了如何高層樓宇的錯誤使用指數長條圖資料結構與計數的事件視窗上方近似對數時間和空間。 長條圖被嵌入在 StreamInsight 使用者定義的運算子。

長條圖和運算子可以進行擴展以支援可變大小的視窗,例如基於時間的 windows。 這就需要知道視窗間隔/時間跨度,而不是視窗大小的長條圖。 存儲桶被過期之前新事件時間戳記減去視窗 timespan 其時間戳記時。 擴展來計算方差在檔中,提出"維護方差與 k–Medians 在流資料視窗"的Brian巴布科克、 和領、 Rajeev 瓦尼和 Liadan O'Callaghan (stanford.io/UEUG0i)。 長條圖,除了文學仲介紹其他所謂簡介結構。 你能想到的隨機樣本、 重量級、 分位數,等等。

本文附帶的原始程式碼編寫 C# 4.0Visual Studio2010 中,需要 StreamInsight 2.1。 該代碼是免費的微軟公共許可證 (Ms PL) 下使用。 請注意它發展為教育目的是既不優化也生產環境的測試。

Michael Meijer 是作為 CIMSOLUTIONS BV,其中他提供 IT 諮詢服務和向整個荷蘭公司軟體發展解決方案的軟體工程師。期間他溫特大學,荷蘭恩斯赫德獲科學碩士學位電腦 Science–Information 工程系統的研究開始他 StreamInsight 和流資料處理中的利益。

感謝以下技術專家對本文的審閱:埃裡克 · 赫格曼、 羅馬 Schindlauer 和 Bas 斯泰默丁克