使用Confluent Kafka,KSQL,Spring Boot和分散式SQL開發物聯網應用程式

banq發表於2019-05-28

展示瞭如何整合Confluent Kafka,KSQL,Spring Boot和YugaByte DB來開發用於管理物聯網(IoT)感測器資料的應用程式。

場景 - 支援物聯網的車隊管理

一家貨運公司希望跟蹤其在全國範圍內運送貨物的物聯網車輛。車輛屬於不同型別(例如18輪車,公共汽車,大型卡車),並遵循3條交付路線(Route-37,Route-82,Route-43)。特別是,該公司希望跟蹤:

  • 每條交付路線的車輛型別的總體分佈。
  • 最近(例如,在過去30秒內)每個貨物交付路線的這些車輛型別的子集。
  • 道路封閉附近的車輛清單,以便他們可以預測交貨延誤。

應用架構

除了Confluent Kafka作為流媒體平臺,該應用程式還具有以下元件:

  • 資料儲存: YugaByte DB用於儲存來自Kafka流的原始事件以及來自KSQL資料處理器的聚合。
  • 資料生成器:用於模擬寫入Kafka流的車輛事件的程式。
  • 資料處理器: 從Data Producer讀取KSQL,計算聚合並將結果儲存在Data Store中。
  • Data Dashboard: Spring Boot應用程式,使用Web套接字,jQuery和Bootstrap顯示資料處理器的輸出。

下面是顯示這些元件如何組合在一起的架構圖。我們稱之為Confluent Kafka,KSQL和YugaByte DB堆疊或CKY堆疊。

我們現在將詳細介紹這些元件中的每一個。

資料儲存

該層儲存所有使用者資料。YugaByte DB用作資料庫,YugaByte雲查詢語言(YCQL)用作資料庫API。所有資料都儲存在金鑰空間TrafficKeySpace中。有一個Origin_Table用於儲存原始事件的表。

CREATE TABLE TrafficKeySpace.Origin_Table (
   vehicleId text, 
   routeId text, 
   vehicleType text, 
   longitude text, 
   latitude text, 
   timeStamp timestamp, 
   speed double, 
   fuelLevel double, 
   PRIMARY KEY ((vehicleId), timeStamp)
) WITH default_time_to_live = 3600;

請注意default_time_to_live設定為3600秒的值,以確保原始事件在1小時後自動刪除。這是為了確保原始事件不會消耗資料庫中的所有儲存,並且在計算聚合後不久就會有效地從資料庫中刪除。

有三個表用於儲存用於面向使用者的顯示的資料:

  • Total_Traffic 交通訊息
  • Window_Traffic 最後30秒的流量和
  • poi_traffic 對於興趣點附近的交通(道路封閉)。

資料處理器不斷更新這些表,儀表板從中讀取。

以下是這些表:

CREATE TABLE TrafficKeySpace.Total_Traffic (
   routeId text, 
   vehicleType text, 
   totalCount bigint, 
   timeStamp timestamp, 
   recordDate text, 
   PRIMARY KEY (routeId, recordDate, vehicleType)
);

CREATE TABLE TrafficKeySpace.Window_Traffic (
   routeId text, 
   vehicleType text, 
   totalCount bigint, 
   timeStamp timestamp, 
   recordDate text, 
   PRIMARY KEY (routeId, recordDate, vehicleType)
);

CREATE TABLE TrafficKeySpace.Poi_Traffic(
   vehicleid text, 
   vehicletype text, 
   distance bigint, 
   timeStamp timestamp, 
   PRIMARY KEY (vehicleid)
);

資料生產者

這包含生成模擬測試資料並將其釋出到Kafka主題iot-data-event的程式。這模擬了使用現實世界中的訊息代理從連線的車輛接收的資料。

單個資料點是JSON有效負載,如下所示:

{
  "vehicleId":"0bf45cac-d1b8-4364-a906-980e1c2bdbcb",
  "vehicleType":"Taxi",
  "routeId":"Route-37",
  "longitude":"-95.255615",
  "latitude":"33.49808",
  "timestamp":"2017-10-16 12:31:03",
  "speed":49.0,
  "fuelLevel":38.0
}

消費者讀取上面的iot-data-event主題,將每個這樣的事件轉換為YCQL INSERT語句,然後呼叫YugaByte DB持久化到事件表TrafficKeySpace.Origin_Table。

資料處理器

KSQL是Apache Kafka的流式SQL引擎。它為Kafka上的流處理提供了一個易於使用但功能強大的互動式SQL介面,無需使用Java或Python等程式語言編寫程式碼。它支援廣泛的流操作,包括資料過濾,轉換,聚合,連線,視窗和會話。

使用KSQL的第一步是STREAM從原始事件建立一個iot-data-event如下所示。

CREATE STREAM traffic_stream (
           vehicleId varchar,
           vehicleType varchar,
           routeId varchar,
           timeStamp varchar,
           latitude varchar,
           longitude varchar)
    WITH (
           KAFKA_TOPIC='iot-data-event',
           VALUE_FORMAT='json',
           TIMESTAMP='timeStamp',
           TIMESTAMP_FORMAT='yyyy-MM-dd HH:mm:ss');

現在可以在上面的流上執行各種聚合/查詢,每種型別的查詢的結果儲存在它自己的新Kafka主題中。此應用程式使用3個此類查詢/主題。此後,Kafka Connect YugaByte DB Sink Connector讀取這3個主題,並將結果儲存到YugaByte DB中的3個相應表中。

CREATE TABLE total_traffic
     WITH ( PARTITIONS=1,
            KAFKA_TOPIC='total_traffic',
            TIMESTAMP='timeStamp',
            TIMESTAMP_FORMAT='yyyy-MM-dd HH:mm:ss') AS
     SELECT routeId,
            vehicleType,
            count(vehicleId) AS totalCount,
            max(rowtime) AS timeStamp,
            TIMESTAMPTOSTRING(max(rowtime), 'yyyy-MM-dd') AS recordDate
     FROM traffic_stream
     GROUP BY routeId, vehicleType;

CREATE TABLE window_traffic
     WITH ( TIMESTAMP='timeStamp',
            KAFKA_TOPIC='window_traffic',
            TIMESTAMP_FORMAT='yyyy-MM-dd HH:mm:ss',
            PARTITIONS=1) AS
     SELECT routeId,
            vehicleType,
            count(vehicleId) AS totalCount,
            max(rowtime) AS timeStamp,
          TIMESTAMPTOSTRING(max(rowtime), 'yyyy-MM-dd') AS recordDate
     FROM traffic_stream
     WINDOW HOPPING (SIZE 30 SECONDS, ADVANCE BY 10 SECONDS)
     GROUP BY routeId, vehicleType;

CREATE STREAM poi_traffic
      WITH ( PARTITIONS=1,
             KAFKA_TOPIC='poi_traffic',
             TIMESTAMP='timeStamp',
             TIMESTAMP_FORMAT='yyyy-MM-dd HH:mm:ss') AS
      SELECT vehicleId,
             vehicleType,
             cast(GEO_DISTANCE(cast(latitude AS double),cast(longitude AS double),33.877495,-95.50238,'KM') AS bigint) AS distance,
             timeStamp
      FROM traffic_stream
      WHERE GEO_DISTANCE(cast(latitude AS double),cast(longitude AS double),33.877495,-95.50238,'KM') < 30;

所有Kafka Connect YugaByte DB Sink Connector用於儲存兩個原始事件以及集合資料(這是使用KSQL生成)。它計算如下:

  • 到目前為止所有車輛和貨物的車輛型別和裝運路線細分。
  • 僅針對有效貨件計算上述細分。這是通過計算最近30秒的車輛型別和裝運路線的細分來完成的。
  • 檢測距離給定興趣點(POI)20英里範圍內的車輛,這表示道路封閉。

資料儀表板

這是一個Spring Boot應用程式,它從YugaByte DB查詢資料,並使用Web SocketsjQuery將資料推送到網頁。資料以固定間隔推送到網頁,因此資料將自動重新整理。主UI頁面使用bootstrap.js顯示包含圖表和表格的儀表板。

我們為三個表建立實體類Total_Traffic,Window_Traffic並poi_traffic為所有實體擴充套件建立資料訪問物件(DAO)介面CassandraRepository。例如,我們為TotalTrafficData實體建立DAO類,如下所示。

@Repository
public interface TotalTrafficDataRepository extends CassandraRepository<TotalTrafficData>{

     @Query("SELECT * FROM traffickeyspace.total_traffic WHERE recorddate = ?0 ALLOW FILTERING")
     Iterable<TotalTrafficData> findTrafficDataByDate(String date);

為了連線到YugaByte資料庫叢集並獲得資料庫操作的連線,我們還編寫了一個DatabaseConfig類。

請注意,目前儀表板不訪問原始事件表,僅依賴於儲存在聚合表中的資料。

總結

此應用程式是使用Confluent Kafka,KSQL,Spring Boot和YugaByte DB構建IoT應用程式的藍圖。雖然這篇文章專注於本地叢集部署,但Kafka代理和YugaByte資料庫節點可以在真正的叢集部署中進行水平擴充套件,以獲得更多的應用程式吞吐量和容錯能力。可以在yb-iot-fleet-managementGitHub倉庫中找到構建和執行應用程式的說明以及原始碼。​​​​​​​

相關文章