使用Spring Boot和GridDB进行实时事件追踪

事件追踪服务的定义

在今天的数码环境中,追踪和分析用户事件对于获得关于用户行为的洞察、优化性能以及提供个性化用户体验是极其重要的。

事件是指在网站或移动应用程序上以用户行为触发的交互。其中包括从按钮点击到订阅通讯、购买产品等各种情况。事件页面浏览本身就是一个事件。

通过使用事件追踪服务,企业可以捕捉和处理事件的发生,并获得最新的洞察。这使得企业能够迅速应对新趋势,实时识别问题,并立即进行基于数据驱动的决策。

本文将介绍如何利用Spring Boot和GridDB来实现实时事件跟踪服务。

功能要求

在深入技术细节之前,我先概述一下事件追踪服务的主要功能要求。这些要求将塑造系统的设计和实施。

    • リアルタイムでイベントを捕捉し、保存する。

 

    集計されたデータをダッシュボードで提供すること。

估计容量

让我们来查一下每天需要处理多少数据。

假设一天有1万活跃用户。

各ユーザーは1日あたり50クリック * 10K * 50 = 500K clicks per day * 1クリックのデータは0.1KBです。

1日に必要なストレージ 1日に必要なストレージ:0.1KB * 500K = 50メガバイト * 1日あたり

选择正确的数据库:是选择SQL还是NoSQL。

在选择数据库时,取决于应用程序的特定需求是选择SQL还是NoSQL。在速度和可扩展性至关重要的实时事件跟踪服务中,NoSQL架构超越了传统的SQL数据库。

像GridDB这样的NoSQL数据库提供了灵活的模式设计、水平扩展性和针对处理大容量实时事件数据进行优化的性能。

使用Spring Boot进行Java应用程序的设置

在深入了解Spring Boot实现的细节之前,拥有对Spring Boot框架的基本理解非常重要。Spring Boot是一种流行的基于Java的框架,它简化了独立开发和生产级Spring应用程序的开发。它提供了一套工具和库,以便提高开发过程的效率,开发者可以专注于编写业务逻辑,而不是处理模板代码。

那么,让我们使用Spring Boot来进行Java应用程序的设置。

    • Java Development Kit (JDK)をインストールし、開発環境をセットアップします。

 

    • 好みのビルドツール(MavenやGradleなど)を使って、新しいSpring Bootプロジェクトをセットアップします。https://start.spring.io/ を使ってSpring Bootプロジェクトを初期化します。

 

    GridDBやその他の必要なライブラリについて、Spring Bootの依存関係を設定します。このプロジェクトの主なライブラリは以下の通り:

1. spring-boot-starter-web:使用Spring MVC构建包含RESTful的Web应用程序的起始器。默认使用Tomcat作为内嵌容器。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

2. Gridstore: Java客户端适用于GridDB CE。

    <dependency>
        <groupId>com.github.griddb</groupId>
        <artifactId>gridstore</artifactId>
        <version>5.1.0</version>
    </dependency>

GridDB配置定义

在创建API端点之前,您需要先连接应用程序和GridDB集群数据库。在这里,我们将创建GridStore类的实例。

@Bean
public GridStore gridStore() throws GSException {
    // Acquiring a GridStore instance
    Properties properties = new Properties();
    properties.setProperty("notificationMember", "griddbserver:10001");
    properties.setProperty("clusterName", "dockerGridDB");
    properties.setProperty("user", "admin");
    properties.setProperty("password", "admin");
    GridStore store = GridStoreFactory.getInstance().getGridStore(properties);
    return store;
}

事件数据模型的实施

这是一个用于存储事件数据的Java类。它将时间戳列设置为主键。

import java.util.Date;
import com.toshiba.mwcloud.gs.RowKey;
import lombok.Data;

@Data
public class Event {
    @RowKey
    Date timestamp;
    String appId;
    String eventType;
    String externalId;
}
    • appId: Owner ID of the Mobile App or Website.

 

    • eventType: “ADD_CART”, “ADD_TO_WISHLIST”, “IN_APP_AD_CLICK”, “SEARCH”, “LOGIN”, “LAUNCH_APP”, “START_TRIAL”

 

    externalId: External userId

创建管理行的容器。

一旦配置数据库连接,就可以使用它来创建容器。在这里,我们将使用时间序列容器来存储一系列事件。

import com.toshiba.mwcloud.gs.TimeSeries;

@Bean
public TimeSeries<event> eventsContainer(GridStore gridStore) throws GSException {
    return gridStore.putTimeSeries("events", Event.class);
}
    com.toshiba.mwcloud.gs.TimeSeriesインタフェースは、TimeSeriesデータの操作に特化したコンテナです。

获取事件

需要创建一个用于捕获事件的 RESTful 端点。在此教程中,我们只提供一个端点。

要报告事件(例如:LaunchAPP、Purchase、Login、Add to Cart),需要向/app/track/终点发送POST请求。

| Request address | http://localhost:8080/api/track |
| -------- | -------- |
| Request method     | **POST**     |

Request Example
curl --request POST --url http://localhost:8080/api/track \
 --header 'content-type: application/json'  --header 'user-agent: vscode-restclient' \
   --data '{
    "appId":"APP_1",
    "eventType":"ADD_CART",
    "externalId":"2385254254",
    "timestamp":"2023-07-12T10:55:50.764Z"
}'
Response Example
HTTPS/1.1 200 OK
Saved
    このエンドポイントは、成功した場合は HTTP Status 200 を返し、エラーが発生した場合は例外を投げます。

使用GridDB的Java客户端库实现事件处理和保存逻辑。

要保存事件数据,首先需要获取先前创建的容器,然后调用append方法。

public void create(EventRequest request) {
    Event event = new Event();
    event.setAppId(request.getAppId());
    event.setEventType(request.getEventType());
    event.setExternalId(request.getExternalId());
    event.setTimestamp(request.getTimestamp());
    try {
        eventsContainer.append(event);
    } catch (GSException e) {
        e.printStackTrace();
    }
}

报告网页接口

现在,我们需要提供一个实时的仪表盘。在本教程中,我们将创建两个图表,分别按日期和事件类型显示事件的计数。

生成图表

为了在Thymeleaf模板上显示图表,我们使用Chartjs。

Chart.js 提供了常用的图表类型、插件和自定义选项。除了合理的内置图表类型集外,您还可以使用由社区管理的附加图表类型。此外,还可以组合多种图表类型,创建混合图表(基本上是将多种图表类型混合为一个并放置在同一画布上的操作)。

创建柱状图来表示单一数据集,并将其渲染到HTML页面上。

<script th:inline="javascript">
    Chart.register(ChartDataLabels);
    const ctx = document.getElementById('charts');

    const data = /*[[${aggregates}]]*/[
        { timeLabel: "2023-07-01", count: 10 }
    ];

    new Chart(ctx, {
        type: 'bar',
        data: {
            labels: data.map(row => row.timeLabel),
            datasets: [{
                label: 'Count by Date',
                data: data.map(row => row.count),
                borderWidth: 1
            }]
        },
        options: {
            scales: {
                y: {
                    beginAtZero: true
                }
            },
            plugins: {
                datalabels: {
                    color: 'white',
                    font: {
                        weight: 'bold'
                    }
                }
            }
        }
    });

显示按日期分类的事件数量

按照事件类型显示事件数量。

事件统计

为了向chartjs提供数据集,需要使用时间序列行的聚合处理方法。

public List getEvents() {
    List views = new ArrayList<>();

    LocalDateTime startDate = LocalDateTime.parse("2023-07-01T00:00:00"),
            endDate = LocalDateTime.now();
    for (LocalDateTime date = startDate; date.isBefore(endDate); date = date.plusDays(1)) {
        try {
            java.util.Date start =
                    convertToDateViaInstant(date.withHour(0).withMinute(0).withSecond(0));
            java.util.Date end =
                    convertToDateViaInstant(date.withHour(23).withMinute(59).withSecond(59));
            AggregationResult aggregationResult =
                    eventsContainer.aggregate(start, end, "appId", Aggregation.COUNT);
            Long count = aggregationResult.getLong();
            if (count.compareTo(0L) > 0) {
                views.add(new EventAggregateView(convertToDateViaInstant(date), count,
                        date.format(dateTimeFormatter)));
            }
        } catch (GSException e) {
            e.printStackTrace();
        }
    }
    return views;
}

使用TimeSeries容器的聚合函数。

AggregationResult aggregate(java.util.Date start,
                          java.util.Date end,
                          java.lang.String column,
                          Aggregation aggregation)
                            throws GSException

根据指定的开始时间和结束时间,对行集或特定列执行汇总处理。参数列可能会被参数汇总忽略。与开始时间或结束时间相匹配的时间戳将包含在操作范围内。如果指定的开始时间晚于结束时间,则所有行都将从操作范围中排除。

为了模拟事件生成,每分钟创建一个启动的定时任务。

@Scheduled(fixedDelay = 1, initialDelay = 1, timeUnit = TimeUnit.MINUTES)
public void scheduleEventCreation() throws IOException, InterruptedException {
    log.info("scheduleEventCreation()");

    HttpClient client = HttpClient.newHttpClient();
    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

    for (int i = 0; i <= 10; i++) {
        Runnable runnableTask = () -> {
            try {
                postEvent(client);
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        };
        executorService.schedule(runnableTask, i, TimeUnit.SECONDS);
    }
}

private void postEvent(HttpClient client)
        throws JsonProcessingException, IOException, InterruptedException {
    Instant instant = Instant.now();
    var values = new HashMap<String, String>() {
        {
            put("appId", "APP_1");
            put("eventType", getRandomElement(EventTrackerConstants.EVENT_TYPE_LIST));
            put("externalId", faker.idNumber().valid());
            put("timestamp", instant.toString());
        }
    };

    var objectMapper = new ObjectMapper();
    String requestBody = objectMapper.writeValueAsString(values);

    HttpRequest request =
            HttpRequest.newBuilder().uri(URI.create("http://localhost:8080/api/track"))
                    .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                    .header("Content-type", "application/json").build();

    client.send(request, HttpResponse.BodyHandlers.ofString());
}

利用Docker Compose执行项目.

要启动该项目,需要使用docker和docker-compose。你只需要安装docker就可以了。然后执行以下命令:

1. 构建 Docker 镜像。

docker compose build

2. 执行Docker镜像:

docker compose up

请打开http://localhost:8080/events查看所有活动。

请打开 http://localhost:8080/aggregate 查看图表。

最佳实践

要充分利用Spring Boot实现实时事件追踪,遵循一些最佳实践非常重要。以下是为了最大限度地发挥实时事件追踪的潜力而推荐的建议:

    • 関連するすべてのユーザーアクションとインタラクションをキャプチャする、明確で一貫性のあるイベントスキーマの定義に時間をかけましょう。また、ユーザーの状態や環境に関する洞察を提供する関連するコンテキスト情報をキャプチャすることも重要です。

 

    • システムがデータ保護規制に準拠していることを確認し、適切なセキュリティ対策を実施します。

 

    • ストリーム処理エンジンを使用して分析パイプラインを実装します。

 

    イベント・ドリブン・アーキテクチャーとApache KafkaやRabbitMQのようなメッセージング・テクノロジーを組み合わせて、イベント・プロデューサーとコンシューマーをデカップリングします。
bannerAds