Xây dựng đầu HTTP tracker hứng log bằng Vertx.io

Nội dung

Xin chào các bạn, ở bài viết này chúng ta sẽ làm quen với việc lập trình xử lý tiếp nhận data từ đầu HTTP (Hypertext Transfer Protocol) hứng log từ phía client.

Đặt vấn đề

Vì sao lại có nhu cầu này? Đặt bài toán, chúng ta mong muốn ghi nhận một sự kiện gì đó ở phía client, client ở đây có thể là một browser, một mobile application hay đơn giản là một desktop application. Như vậy chúng ta có thể lập trình ở phía client tạo log và gửi về server thông qua HTTP để server có thể lưu trữ log một cách độc lập mà không cần lưu lại ở phía client.

Chúng ta sẽ làm một ví dụ đơn giản để có thể dễ dàng bước chân vào thế giới lập trình nhé. Giả sử chúng ta mong muốn ghi nhận sự kiện Đăng Ký một buổi seminar trên website, dữ liệu đã được gửi qua một đầu dịch vụ backend khác (ví dụ: CRM), tuy nhiên Tech Lead của bạn mong muốn có một 3rd-party có thể tracking độc lập sự kiện này, để hiểu rõ hơn về trải nghiệm của người dùng ở client là như thế nào.

Mô tả dữ liệu được trao đổi giữa Backend của app, app và đầu HTTP chúng ta sẽ tiếp cận.
Mô tả dữ liệu được trao đổi giữa Backend của app, app và đầu HTTP chúng ta sẽ tiếp cận.

Tìm hiểu nhu cầu

Với cương vị là Data Engineer bạn sẽ phải cần tìm hiểu rõ hơn nhu cầu về việc tracking này, bao gồm các metrics cần hiểu rõ, sự kiện có liên quan tới tài khoản đăng ký trên website hay không, có cần lưu lại thông tin đăng ký hay không để tiện việc đối soát… Nói chung khi có nhu cầu về một chức năng cụ thể, các stakeholders sẽ xây dựng nhiều use-cases cùng một lúc (tranh thủ hehe).

Để bài hướng dẫn ở mức đơn giản, chúng ta sẽ rút gọn nhu cầu sau khi bạn đã tìm hiểu kỹ, nên các user-stories sẽ như sau:

  • Thu thập và đo lường được số lượng đăng ký thành công/thất bại theo device (mobile, desktop), theo browsers (Google Chrome, Firefox…).
  • Thống kê theo thời gian hoàn thành đăng ký (từ lúc ấn đăng ký đến khi nhận màn hình thành công/thất bại); nếu thất bại, thống kê thêm nguyên nhân.
  • Số lượng người đăng ký là có tài khoản trên website.

Sau đó, bạn sẽ cần làm việc với bên Front-end Engineering để họ sẽ gửi log về cho bạn khi sự kiện đăng ký hoàn thành và họ đồng ý sẽ gửi về cho bạn data dưới dạng json với cấu trúc như sau:

JSON
{
  "device": "mobile | desktop",
  "browser": "Chrome | Firefox",
  "result": "success | error",
  "message": "OK | Timeout | SoldOut | UnknownError",
  "duration": 3432, //In milliseconds
  "accountId": 1 | null
}

Lưu ý: thực tế việc phân tách device và browser sẽ nằm ở backend-server. Cụ thể, browser sẽ luôn tự động gửi User-agent về HTTP server để thông báo client là gì. Nhưng như thế bài viết sẽ rất dài, chúng ta sẽ làm đơn giản thôi.

Viết Code

Chúng ta sẽ khởi tạo một gradle project bằng file build.gradle như sau:

JSON
group 'org.vndataproduct'
apply plugin: 'java'
apply plugin: 'maven'
repositories {
    mavenLocal()
    mavenCentral()
}
ext {
    vertxVersion = '4.3.7'
}
dependencies {
    compile group: 'io.vertx', name: 'vertx-core', version: "${vertxVersion}"
    compile group: 'io.vertx', name: 'vertx-web', version: "${vertxVersion}"
}

Sau đó, chúng ta sẽ import vào eclipse hoặc IntelliJ (Create Gradle Project).

Tạo một class MainStarter.java và paste method sau vào class MainStarter:

Java
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;

public class MainStarter {

   public void handleRequest(RoutingContext context) {
      try {
         //This will get body as Buffer then cast it to JsonObject
         JsonObject dataBody = context.body().asJsonObject();
         System.out.println("Data Received: " + dataBody.toString());
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

Tạo thêm method main để có thể chạy được phần code ở trên

Java
   public static void main(String[] args) {
      //Each application should have one vertx instance. We can create multiple threading on one instance later.
      Vertx vertx = Vertx.vertx();
      //Create a MainStarter object from the class, so we can call the method handleRequest from the object.
      MainStarter handlerObj = new MainStarter();
      //Create a router from vertx instance.
      Router router = Router.router(vertx);
      router.post("/accept_tracking")
            .handler(BodyHandler.create()) //Với BodyHandler có sẵn của Vertx này sẽ giúp chúng ta parse Body từ Request mà không cần phải viết lại code.
            .handler(handlerObj::handleRequest);
      //
      HttpServer httpServer = vertx.createHttpServer();
      httpServer.requestHandler(router).listen(8080);
   }

Lưu ý: nếu bạn chưa quen với method referrence, thì dòng số 10 chúng ta có thể viết là router.get(“/accept_tracking”).handler(context -> handlerObj.handleRequest(context));

Chúng ta đã có thể bắt đầu chạy server này bằng IDE của bạn, nếu thành công chúng ta có thể telnet tới port của server: telnet localhost 8080.

Sau đó, chúng ta sẽ chạy thử Postman hoặc dùng command curl sau để gửi dữ liệu về đầu HTTP:

curl --location --request POST 'http://localhost:8080/accept_tracking' \
--header 'Content-Type: application/json' \
--data-raw '{
  "device": "mobile",
  "browser": "Chrome",
  "result": "success",
  "message": "OK",
  "duration": 3432,
  "accountId": null
}'

Khi thành công, trên IDE của bạn sẽ hiển thị kết quả nhận vào như sau:

Data Received: {"device":"mobile","browser":"Chrome","result":"success","message":"OK","duration":3432,"accountId":null}

Nâng cao

Chúc mừng bạn đã hoàn thành bài tập tracking đầu tiên, tuy nhiên công việc của chúng ta vẫn chưa xong, vì bạn sẽ nhận thấy trên Postman hoặc command curl đã bị treo đứng mặc dù ở server đã nhận được kết quả. Đơn giản chúng ta vẫn chưa báo về client biết kết quả của request, thông thường chúng ta sẽ trả về 2xx response code để thông báo với client request này đã thành công. Chúng ta sẽ gắn thêm đoạn code trong handleRequest method như sau:

Java
   public void handleRequest(RoutingContext context) {
      //Get response data from context
      HttpServerResponse response = context.response();
      response.setStatusCode(200).end();
      ...

Có thể bạn sẽ thắc mắc: tại sao lại trả về response code 200 ngay khi method handleRequest được thực thi? Khác với làm API cho application, API tracking dữ liệu này chúng ta chỉ có nhu cầu nhận data mà không cần trả gì về, nên việc “ngậm” connection là không cần thiết.

Ngoài ra nếu số lượng requests của chúng ta đạt 5000 requests/giây, với giả định việc đặt response code 200 ở cuối method sẽ tốn nhiều hơn 1ms so với đặt ở đầu. Thì chúng ta đã phí phạm 5000ms (5s) thời gian chờ ở clients; nhìn có vẻ nó là không nhiều, tuy nhiên chúng ta phải cân nhắc tới việc mỗi khi client gửi request tới server, thì server đều phải mở ra 1 connection mới dưới dạng file (Linux). Như vậy, về lâu dài sẽ khiến đầu HTTP của chúng ta chạy chậm hơn hoặc thậm chí bị bottle-neck khi bị spike traffic, mọi thứ đều sẽ làm việc chậm hơn.

Bạn chạy lại đầu HTTP và gửi request lần nữa, và sẽ thấy là connection đã được trả về 200, Postman hoặc command curl đã không còn bị treo.

Tóm tắt

Chúng ta đã biết thêm về:

  • phân tích request về tracking dữ liệu
  • hiểu nhu cầu từ stakeholders
  • viết đầu HTTP trên Vertx.io
  • hiểu thêm một ít về tối ưu hóa source code

Các bạn có thể trực tiếp lấy git source của bài viết ở đây Github. Ở phần tiếp theo chúng ta sẽ bắt đầu phân tích và xử lý dữ liệu được gửi lên.

Hẹn gặp lại bạn ở bài tiếp theo.

Bài viết liên quan

SQL trong Data Analysis: Procedure và Function – 2 công cụ không thể thiếu

Xin chào các bạn đã quay trở lại chuỗi bài SQL trong Data Analysis...

Tự học Data Analyst: Tổng hợp chuỗi bài SQL 101 trong Data Analysis

Trong bài viết này, chúng ta sẽ tổng hợp các bài viết thành một...

SQL trong Data Analysis: Hiểu rõ và ứng dụng đệ quy (Recursive trong PostgreSQL)

Trong thế giới của cơ sở dữ liệu quan hệ, các truy vấn đệ...

[Phân Tích Dữ Liệu Với Python] Tập 1: Làm Quen Với Pandas

Trong thời đại tiến bộ của khoa học dữ liệu, khả năng phân tích...
spot_img