+60

Thiết kế chi tiết hệ thống booking bằng micro service hỗ trợ chịu tải lớn bằng Java và Spring boot, MySQL

Bài toán: Xây dựng một hệ thống quản lý booking vé máy bay tổng hợp cho các đại lý bán vé máy bay và người dùng đầu cuối.

  • Hệ thống sẽ kết nối trực tiếp với các api của các bên cung cấp dịch vụ đặt vé như vietnam airline, Vietjet air ...
  • Hệ thống kết nối trực tiếp với các cổng thanh toán trong nước
  • Hệ thống kết nối tới các phần mềm quản lý khách hàng và tra cứu thông tin
  • Hệ thống cần hỗ trợ lượng lớn người dùng vào một thời điểm và có thể scale khả năng chịu tải cho api.
  • Hệ thống cần handle được tất cả các yêu cầu booking và thanh toán để có thể xử lý tuần tự, Các giao dịch thanh toán trực tuyến cần được ưu tiên xử lý khi có yêu cầu xác thực bằng OTP.
  • Hệ thống cần khoá lại các booking đang được đợi xử lý và có cơ chế mở khoá nếu book không thành công.

Tại sao lại lựa chọn Spring framework Spring Framework là một framework phổ biến trong phát triển ứng dụng Java. Nó cung cấp nhiều module khác nhau để hỗ trợ các chức năng khác nhau trong ứng dụng. Dưới đây là một số module phổ biến của Spring Framework:

  • Spring Core: cung cấp các thành phần cơ bản của framework, bao gồm Dependency Injection (DI) và Inversion of Control (IOC).
  • Spring MVC: cung cấp một kiến trúc để phát triển các ứng dụng web truyền thống và RESTful.
  • Spring Data: cung cấp các tính năng để làm việc với các dịch vụ cơ sở dữ liệu khác nhau, bao gồm JDBC, ORM, NoSQL.
  • Spring Security: cung cấp các tính năng bảo mật, bao gồm xác thực và phân quyền.
  • Spring Integration: cung cấp các tính năng để tích hợp các ứng dụng khác nhau.
  • Spring Batch: cung cấp các tính năng để xử lý các công việc hàng loạt.
  • Spring Cloud: cung cấp các tính năng để phát triển các ứng dụng điện toán đám mây.
  • Spring Boot: cung cấp các tính năng để nhanh chóng và dễ dàng phát triển các ứng dụng Spring.

I. Kiến trúc Microservice?

Microservices là một dạng của kiến trúc hướng dịch vụ, trong đó các ứng dụng được xây dựng như một tập hợp các dịch vụ nhỏ hơn khác nhau chứ không phải toàn bộ một ứng dụng. Thay vì ứng dụng nguyên khối, bạn có một số ứng dụng độc lập có thể tự chạy và có thể được tạo bằng các ngôn ngữ lập trình hoặc mã hóa khác nhau. Các ứng dụng lớn và phức tạp có thể được tạo thành từ các chương trình đơn giản và độc lập được thực thi bởi chính chúng. Các chương trình nhỏ hơn này được nhóm lại với nhau để cung cấp tất cả các chức năng của ứng dụng lớn, nguyên khối. Có nhiều lợi ích khi sử dụng microservices. Thứ nhất, do các ứng dụng nhỏ này không phụ thuộc vào cùng một ngôn ngữ mã hóa, các nhà phát triển (developers) có thể sử dụng ngôn ngữ lập trình mà họ quen thuộc nhất. Điều đó giúp các nhà phát triển đưa ra một chương trình nhanh hơn với chi phí thấp hơn và ít lỗi hơn. Sự nhanh nhẹn và chi phí thấp cũng có thể đến từ việc có thể sử dụng lại những chương trình nhỏ hơn này trên các dự án khác, làm cho nó hiệu quả hơn.

Các công nghệ sẽ sử dụng phát triển hệ thống

  • API gateway: Sử dụng NginX để handle và phân phối các request.
  • Message Queue: Sử dụng message queue để handle lượng lớn các quest booking tại một thời điểm, sử dụng multiple consumer để xử lý các yêu cầu booking.
  • Database: JPA, MySQL, h2 database, MongoDB, Redis.: Sử dụng h2 database để lưu data in memory cho một số data cần sử dụng thường xuyên.
  • Các micro service giao tiếp với nhau qua restful api,
  • Service discovery: Eureka
  • Banking service
  • SMS service
  • Worker: Các job đồng bộ thông tin với các đối tác, xử lý các tính toán tạo report.
  • Caching: lưu các thông tin thường xuyên truy cập như: danh sách chuyến bay, số lượng hành khách, lịch sử xem của khách hàng.

Thiết kế các micro service:

  • Service Account manager: cho phép người dùng đăng ký, đăng nhập, quản lý thông tin tài khoản.
  • Service Payment gateway: Cho phép người dùng thanh toán, hoàn tiền, tra soát giao dịch. kết nối với các trung gian thanh toán.
  • Service Booking: Cho phép người dùng đặt hoặc huỷ chuyến, tìm kiếm các chuyến bay.
  • Service Booking history: Cho phép người dùng xem lịch sử và thông tin vé.
  • Service Notification: gửi các thông tin vé, thông báo thay đổi cho người dùng, email xác thực. email quảng cáo.
  • Service Ads: Quản lý các chương trình khuyến mãi.

Hướng dẫn cài đặt.

Cách trao đổi thông tin giữa các micro service

Các micro service tạo ra các api để truy vấn thông tin cho hầu hết các model mà nó quản lý, đồng thời nó cũng cung cấp các internal api để các module có thể cập nhật các thông tin được phép. Một số các thông tin được phép cập nhật giữa các micro service như: thông tin user, lịch sử booking, Để các service giao tiếp với nhau chúng ta sẽ phụ thuộc vào logic mà module đó sử dụng, về cơ bản các tương tác sẽ thông qua restful api.

Xây dựng hệ thống theo event driven architecture, Với các yêu cầu xử lý booking thì sẽ được tạo thành event booking và gửi qua Kafka

  1. Yêu cầu booking
  2. Yêu cầu thanh toán
  3. Gửi thông báo

Thiết kế cơ sở dữ liệu

Hệ thống sử dụng cơ sở dữ liệu chính là MySQL, Các service sẽ sử dụng các database của riêng mình và được triển khai trên các container khác nhau. Đối với các service cần truy vấn các thông tin tĩnh liên tục chúng ta sử dụng thêm in-memory database Redis

Các database sử dụng MySQL

  • User Database
  • Transaction database
  • Promotion database

Các database sử dụng MongoDB

  • Booking database
  • Notification database
  • Ads Database

Danh sách các Database

User Database

  • Users Table: Lưu trữ thông tin user, password
  • Profiles Table: Lưu trữ thông tin cá nhân của user như: Tên, ngày sinh, số cmt , số cccd, Địa chỉ, Avatar...
  • UserRoles Table: Bảng liên kết User và Roles
  • Roles Table: Lưu trữ thông tin về các role của user
  • Permissions Table: Lưu trữ các quyền truy cập khác nhau trong hệ thống, ví dụ như quyền đọc, quyền ghi, quyền xóa.
  • RolePermissions Table: Liên kết giữa bảng Roles và bảng Permissions để xác định quyền truy cập của vai trò trong hệ thống.
  • LoginAttempts Table: Lưu trữ thông tin về các lần đăng nhập không thành công của người dùng, bao gồm IP, thời gian và số lần thử đăng nhập.
  • PasswordResets Table: Lưu trữ thông tin về các yêu cầu đặt lại mật khẩu của người dùng, bao gồm token, thời gian và trạng thái yêu cầu.

Transaction Database

  • Bảng Transactions: Lưu trữ thông tin về các giao dịch được thực hiện, bao gồm các trường như mã giao dịch, ngày giao dịch, loại giao dịch, giá trị giao dịch và các thông tin khác liên quan.
  • Bảng Categories: Lưu trữ các danh mục cho các giao dịch, bao gồm các trường như mã danh mục, tên danh mục và các thông tin liên quan.
  • Bảng Subcategories: Lưu trữ các danh mục con cho các danh mục, bao gồm các trường như mã danh mục con, tên danh mục con, mã danh mục cha và các thông tin liên quan.
  • Bảng PaymentMethods: Lưu trữ các phương thức thanh toán được sử dụng trong các giao dịch, bao gồm các trường như mã phương thức, tên phương thức và các thông tin liên quan.
  • Bảng TransactionsCategories: Liên kết giữa bảng Transactions và bảng Categories để xác định danh mục của mỗi giao dịch.
  • Bảng TransactionsSubcategories: Liên kết giữa bảng Transactions và bảng Subcategories để xác định danh mục con của mỗi giao dịch.
  • Bảng TransactionsPaymentMethods: Liên kết giữa bảng Transactions và bảng PaymentMethods để xác định phương thức thanh toán của mỗi giao dịch.

Booking Database

  • Bảng Bookings: Lưu trữ thông tin về các đặt phòng của khách hàng, bao gồm các trường như mã đặt phòng, ngày đặt phòng, ngày nhận phòng, ngày trả phòng, số lượng khách, số lượng phòng và các thông tin liên quan.
  • Bảng Rooms: Lưu trữ thông tin về các phòng khách sạn, bao gồm các trường như mã phòng, loại phòng, giá phòng, số lượng giường, số lượng khách tối đa, tình trạng phòng và các thông tin liên quan.
  • Bảng Flights: Lưu trữ thông tin về các chuyến bay, bao gồm các trường như mã chuyến bay, ngày khởi hành, giờ khởi hành, điểm khởi hành, điểm đến, số chỗ trống, giá vé và các thông tin liên quan.
  • Bảng Customers: Lưu trữ thông tin về các khách hàng, bao gồm các trường như tên khách hàng, địa chỉ email, số điện thoại, địa chỉ và các thông tin liên quan.
  • Bảng BookingRooms: Liên kết giữa bảng Bookings và bảng Rooms để xác định phòng khách hàng đã đặt trong mỗi đặt phòng.
  • Bảng BookingFlights: Liên kết giữa bảng Bookings và bảng Flights để xác định chuyến bay liên quan đến mỗi đặt chỗ.
  • Bảng BookingCustomers: Liên kết giữa bảng Bookings và bảng Customers để xác định khách hàng liên quan đến mỗi đặt phòng.
  • Bảng BookingPayments: Liên kết giữa bảng Bookings và bảng Payments để xác định thanh toán liên quan đến mỗi đặt phòng.
  • Bảng Venders: Thông tin các nhà cung cấp dịch vụ
  • Bảng Currency: Lưu thông tin currency

Notification Database

  • Bảng Messages: Lưu trữ thông tin về các tin nhắn, bao gồm các trường như tiêu đề, nội dung, ngày gửi, người gửi, người nhận và các thông tin liên quan.
  • Bảng MessageStatus: Lưu trữ trạng thái của các tin nhắn, bao gồm các trường như trạng thái đã gửi, đang chờ, đã nhận và các thông tin liên quan.
  • Bảng Templates: Lưu trữ các mẫu thông báo để sử dụng lại, bao gồm các trường như tiêu đề, nội dung, và các thông tin liên quan.
  • Bảng MessageRecipients: Liên kết giữa bảng Messages và bảng Users để xác định người nhận của mỗi tin nhắn.
  • Bảng MessageTemplates: Liên kết giữa bảng Messages và bảng Templates để xác định mẫu thông báo được sử dụng cho mỗi tin nhắn.

Promotion Database

  • Bảng Promotions: Lưu trữ thông tin về các chương trình khuyến mãi, bao gồm các trường như tên khuyến mãi, mô tả, ngày bắt đầu, ngày kết thúc và các thông tin liên quan.
  • Bảng PromotedRooms: Liên kết giữa bảng Promotions và bảng Rooms để xác định loại phòng được áp dụng khuyến mãi.
  • Bảng PromotedFlights: Liên kết giữa bảng Promotions và bảng Rooms để xác định loại phòng được áp dụng khuyến mãi.
  • Bảng DiscountTypes: Lưu trữ thông tin về các loại giảm giá, bao gồm các trường như tên, mô tả và các thông tin liên quan.
  • Bảng PromotionsDiscountTypes: Liên kết giữa bảng Promotions và bảng DiscountTypes để xác định loại giảm giá được áp dụng cho từng chương trình khuyến mãi.
  • Bảng PromotionCodes: Lưu trữ thông tin về các mã khuyến mãi, bao gồm các trường như mã code, mô tả và các thông tin liên quan.
  • Bảng PromotionsPromotionCodes: Liên kết giữa bảng Promotions và bảng PromotionCodes để xác định mã khuyến mãi được áp dụng cho từng chương trình khuyến mãi.

Ads Database

  • Bảng Quảng cáo: Bảng này chứa thông tin về các chiến dịch quảng cáo, bao gồm tên chiến dịch, ngày bắt đầu và kết thúc, mục tiêu, ngân sách, đối tượng khách hàng, v.v.
  • Bảng Quảng cáo Hiển thị: Bảng này chứa thông tin về các quảng cáo hiển thị, bao gồm tiêu đề, hình ảnh, văn bản quảng cáo, liên kết đến trang web, địa chỉ URL, v.v.
  • Bảng Khách hàng mục tiêu: Bảng này chứa thông tin về đối tượng khách hàng mục tiêu của chiến dịch quảng cáo, bao gồm độ tuổi, giới tính, vị trí địa lý, sở thích, hành vi, v.v.
  • Bảng Phương tiện truyền thông: Bảng này chứa thông tin về các phương tiện truyền thông sử dụng trong chiến dịch quảng cáo, bao gồm các kênh quảng cáo trực tuyến, truyền hình, tạp chí, v.v.
  • Bảng Kết quả quảng cáo: Bảng này chứa thông tin về kết quả của chiến dịch quảng cáo, bao gồm số lượt hiển thị, số lượt nhấp chuột, tỷ lệ chuyển đổi, chi phí cho mỗi lượt nhấp chuột, v.v.

Thiết kế Model: để thiết kế model chúng ta sẽ sử dụng JPA và Caching bằng redis để tối ưu hiệu quả. Chúng ta sử dụng JPA vì nó có một số ưu điểm sau:

  • Tính đa nền tảng: JPA được thiết kế để làm việc trên nhiều cơ sở dữ liệu khác nhau, cho phép chuyển đổi giữa các cơ sở dữ liệu một cách dễ dàng.
  • Tính tương thích cao: JPA tương thích với các framework và công nghệ khác trong hệ sinh thái Java, cho phép sử dụng cùng với các framework như Spring hay Hibernate.
  • Quản lý mối quan hệ giữa các đối tượng: JPA cung cấp khả năng quản lý mối quan hệ giữa các đối tượng trong cơ sở dữ liệu, giúp giảm thiểu việc phải thực hiện các thao tác liên quan đến mối quan hệ tay trong tay.
  • Tối ưu hóa hiệu suất: JPA cung cấp nhiều cơ chế tối ưu hóa hiệu suất để tăng tốc độ truy cập cơ sở dữ liệu.
  • Tiết kiệm thời gian: Sử dụng JPA cho phép người phát triển tập trung vào logic ứng dụng hơn là việc phải quản lý và thao tác với cơ sở dữ liệu một cách trực tiếp.
  • Kiểm soát tốt: JPA giúp kiểm soát tốt về an toàn, bảo mật và tính nhất quán dữ liệu trong ứng dụng.
  • Giảm độ phức tạp: Sử dụng JPA giúp giảm độ phức tạp của ứng dụng, do đó giảm thiểu lỗi và nâng cao tính ổn định của ứng dụng.

Thiết kế caching bằng Redis: Để hỗ trợ các trường hợp dữ liệu thường xuyên được truy vấn và xử lý, chúng ta sử dụng Redis để lưu các dữ liệu:

  • Thông tin phiên giao dịch, thông tin cá nhân bao gồm thông tin đăng nhập, cài đặt ngôn ngữ, lịch sử xem, giỏ hàng...
  • Lịch sử booking của khách hàng
  • Lịch sử thanh toán của khách hàng
  • Danh sách yêu thích của khách hàng.
  • Danh sách khách sạn, phòng, chuyến bay
  • Danh sách các đơn vị cung cấp dịch vụ.
  • Danh sách promotion
  • Danh sách thông báo hệ thống.
  • Danh sách các quảng cáo đang hoạt động

Thiết kế kiến trúc hệ thống

  • Context diagram

  • Component diagram

  • Class diagram

Các dependency chính:

  • org.springframework.kafka
  • dropwizard: dropwizard-core, dropwizard-jdbi3
  • org.jdbi
  • mysql, org.mongodb, redis.clients
  • com.fasterxml.jackson.core
  • com.google.cloud, com.firebase

Spring security

Spring Security là một framework của Spring được sử dụng để bảo vệ ứng dụng trước các cuộc tấn công mạng bằng cách cung cấp các tính năng bảo mật như xác thực, phân quyền, và bảo vệ các tài nguyên của ứng dụng. Lợi ích lớn nhất của Spring Security là giúp bạn tích hợp tính năng xác thực và phân quyền một cách dễ dàng vào ứng dụng của mình.

Ngoài ra Spring Security còn giúp:

  • Chống lại CSRF attack
  • Bảo vệ Session Fixation
  • Mã hóa mật khẩu.
  • Cache control
  • X-XSS-Protection

Cách sử dụng Spring Security trong hệ thống ngân hàng bao gồm các bước sau:

Cấu hình Spring Security: Để bắt đầu sử dụng Spring Security trong ứng dụng Spring, ta cần thêm dependency của Spring Security vào file pom.xml và cấu hình Spring Security trong file cấu hình của ứng dụng (ví dụ như file application.properties hoặc application.yml). Xác thực người dùng: Sử dụng Spring Security, ta có thể xác thực người dùng thông qua các phương thức như đăng nhập bằng tài khoản và mật khẩu, đăng nhập bằng mã xác thực OTP, đăng nhập bằng chứng chỉ số, vân vân. Các phương thức này đều được hỗ trợ bởi Spring Security và ta chỉ cần cấu hình và sử dụng chúng trong ứng dụng.

Phân quyền: Sau khi xác thực người dùng, ta có thể sử dụng Spring Security để phân quyền cho người dùng dựa trên vai trò của họ. Việc phân quyền này giúp đảm bảo rằng các tài nguyên của ứng dụng chỉ được truy cập bởi người dùng có đủ quyền hạn. Bảo vệ tài nguyên: Sử dụng Spring Security, ta có thể bảo vệ các tài nguyên của ứng dụng bằng cách chỉ định các quy tắc truy cập cho từng tài nguyên. Việc bảo vệ này giúp đảm bảo rằng các tài nguyên của ứng dụng chỉ được truy cập bởi người dùng có đủ quyền hạn. Ví dụ HttpSecurity là đối tượng chính của Spring Security, cho phép chúng ta cấu hình mọi thứ cần bảo mật, và nó được xây dựng dưới design pattern giống với Builder Pattern, nên mọi cài đặt có thể viết liên tục thông qua toán tử . , những gì chúng ta muốn cho phép, chúng ta sẽ xài method .permit(), còn những gì cấm hoặc yêu cầu xác thực sẽ dùng .authenticated()

Mặc định Spring Security sẽ chặn hết tất cả các request, tự động generate login form và sử dụng http basic cho phần authentication. Như mình đã nói với các bạn trong bài viết về Cấu hình Spring Security sử dụng WebSecurityConfigurerAdapter và AbstractSecurityWebApplicationInitializer, chúng ta có thể override class WebSecurityConfigurerAdapter để thay đổi cấu hình này.

Internal Workflow: Cách Spring Security hoạt động? Dưới đây là workflow cách mà Spring Security mặc định (User Credentials) hoạt động: image.pnghttps://i0.wp.com/s3.ap-southeast-1.amazonaws.com/techover.storage/wp-content/uploads/2023/01/02215334/Biểu-dồ-không-có-tiêu-dề.drawio-4.png?resize=764%2C358&ssl=1

Ngoài ra bạn có thể tìm hiểu về mô hình phân quyền ACL và RBAC tại bài viết này: https://viblo.asia/p/mo-hinh-phan-quyen-acl-trong-spring-security-maGK7G1eKj2 Và hướng dẫn implementation ở đây: https://huongdanjava.com/vi/tong-quan-ve-quy-trinh-xu-ly-request-trong-spring-security.html image.pnghttps://images.viblo.asia/b720822f-aa43-45cc-9460-bb63f766e78c.PNG

API gateway

Service discovery Eureka

chúng ta sử dụng service discovery bằng dependency Eureka để đăng ký các dịch vụ mà hệ thống cung cấp như: api user info, booking info .... Chúng ta sử dụng Eureka vì

Cài đặt Eureka: Các thư viện: Eureka Discovery, Feign, Zuul, Rest Repositories, Web, Hystrix, và Lombok

Bước 1: Thêm dependency Eureka Server vào file pom.xml:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  <version>2.2.6.RELEASE</version>
</dependency>

Bước 2: Thêm annotation @EnableEurekaServer vào class chính của ứng dụng để kích hoạt Eureka Server:

@SpringBootApplication
@EnableEurekaServer
public class ServiceDiscoveryApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceDiscoveryApplication.class, args);
    }
}

Bước 3: Cấu hình Eureka Server bằng cách tạo một file application.properties hoặc application.yml với nội dung sau:

server.port=8761

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

Trong đó, server.port là cổng mà Eureka Server lắng nghe, eureka.client.register-with-eureka và eureka.client.fetch-registry được đặt giá trị là false để đánh dấu Eureka Server là một server và không đăng ký với bất kỳ service registry nào khác.

Bước 4: Khởi động Eureka Server bằng cách chạy class chính của ứng dụng.

Sau khi triển khai xong Eureka Server, các service khác có thể đăng ký với Eureka Server bằng cách thêm dependency spring-cloud-starter-netflix-eureka-client vào file pom.xml và cấu hình trong file application.properties hoặc application.yml như sau:

bash
eureka.client.service-url.defaultZone=http://localhost:8761/eureka

Trong đó, defaultZone là URL của Eureka Server. Sau khi đăng ký, các service có thể được tìm kiếm thông qua Eureka Server bằng cách sử dụng tên của service, được định nghĩa trong file cấu hình của service.

Cấu hình cho các client service các bạn có thể xem chi tiết ở video hướng dẫn sau: https://www.youtube.com/watch?v=ee9hwZltd2M

Thiết kế load balancer

Bạn có thể sử dụng load balancer trong service để chia tải cho các instance của service trong hệ thống, ví dụ nếu service lấy thông tin chuyến bay thường có rất nhiều truy cập, chúng ta sẽ tạo multiple instance cho service đó Bạn có thể xem hướng dẫn tạo load balancer service ở video hướng dẫn sau: https://www.youtube.com/watch?v=0LJBQX1l7HY

Tối ưu hoá hệ thống

Có nhiều cách để tối ưu hiệu suất của ứng dụng Spring Boot. Dưới đây là một số cách cơ bản để tối ưu hiệu suất:

  • Sử dụng bộ nhớ đệm (Caching): Sử dụng bộ nhớ đệm để lưu trữ dữ liệu đã truy vấn từ cơ sở dữ liệu hoặc kết quả tính toán. Có thể sử dụng các thư viện như Caffeine hoặc Ehcache để quản lý bộ nhớ đệm.
  • Tối ưu hoá cấu hình: Sử dụng các cấu hình tối ưu hóa hiệu suất như tăng kích thước của thread pool, tăng giới hạn kết nối tối đa, giảm thời gian chờ kết nối.
  • Sử dụng kết nối đến cơ sở dữ liệu theo cách đúng cách: Điều này bao gồm việc sử dụng các câu truy vấn hiệu quả và cấu hình hợp lý cho kết nối đến cơ sở dữ liệu. Nên tránh việc gọi nhiều câu truy vấn liên tiếp hoặc không cần thiết.
  • Sử dụng HTTP/2: Sử dụng giao thức HTTP/2 để tối ưu hiệu suất của ứng dụng. HTTP/2 có khả năng tải trang nhanh hơn, giảm độ trễ và tăng tốc độ tải trang.
  • Tối ưu hóa mã nguồn: Nên tối ưu hóa mã nguồn bằng cách sử dụng các thư viện phù hợp, sử dụng các thuật toán hiệu quả, sử dụng các cấu trúc dữ liệu phù hợp và tối ưu hóa việc sử dụng bộ nhớ.
  • Sử dụng các công cụ kiểm tra hiệu suất: Sử dụng các công cụ như JMeter hoặc Gatling để kiểm tra hiệu suất của ứng dụng. Điều này giúp xác định các vấn đề về hiệu suất và giúp các nhà phát triển tìm giải pháp phù hợp.
  • Sử dụng cache CDN: Sử dụng cache CDN để tối ưu hóa tốc độ tải trang. Cache CDN giúp giảm thời gian truyền tải và tăng tốc độ tải trang.

Thiết kế kiến trúc Micro service

Để các micro service có thể dùng chung các entity chúng ta có thể tạo ra 1 project core để define các entity, connect tới database, Redis, Các exception, Mapping, Validation, DAO objecty

Thiết kế micro service Account Manager

Các use-case chính của service này là:

  • Đăng ký tài khoản
  • Đăng nhập
  • Quản lý thông tin tài khoản, cập nhật thông tin.
  • Truy vấn thông tin tài khoản
  • Cấp quyền cho tài khoản.

Thiết kế micro service payment gateway:

Đối với các object để lưu các thông tin ít thay đổi chúng ta sẽ sử dụng design pattern singleton để lưu thông tin và cập nhật thông tin trong nó khi cần thiết, hạn chế việc truy cập vào database dể update.

Giao tiếp đối với các API của banking SOAP là gì? SOAP là từ viết tắt của cụm Simple Object Access Protocol. Đây đang là giao thức nhắn tin và cho phép những chương trình chạy trực tiếp trên nhiều hệ điều hành khác nhau (Linux và Windows,…) giao tiếp được cùng với nhau qua Ngôn ngữ XML và Giao thức HTTP.

Chúng ta sẽ chia các phương thức thanh toán đối với các bên ra thành nhiều class quản lý khác nhau, ví dụ như: BaoKimAPI, BankAPI, VNPayAPI

  • Mỗi đối tác sẽ có những config và phương thức khác nhau, Mỗi kết nối sẽ được định nghĩa qua SOAP hoạc Restful api của từng đơn vị
  • Mỗi khách hàng sẽ lựa chọn đơn vị thanh toán khác nhau, khi đó cần lưu lại chi tiết thông tin từng giao dịch để phục vụ đối soát.
  • Trong trường hợp hệ thống bị lỗi khi xử lý cần có hình thức gửi thông báo hợp lý

Thiết kế micro service Booking history

Do hệ thống hỗ trợ lượng rất lớn các yêu cầu truy vấn lịch sử và truy vấn thông tin nên chúng ta cần tách riêng 1 service booking history nhằm xử lý các yêu cầu truy vấn lịch sử cũng như quản lý nó. Lịch sử của mỗi user sau khi được truy vấn từ cơ sở dữ liệu chính (MySQL) sẽ được lưu lại trong Redis Lịch sử thanh toán của user sẽ được tra soát và đồng bộ với các đối tác cung cấp dịch vụ thanh toán để đảm bảo tính nhất quán của dữ liệu

Thiết kế micro service promotion:

Service promotion cho phép quản lý tạo các chương trìng promotion cho các dịch vụ đang chạy, các chương trình sẽ có thời gian và chi tiết khác nhau. Hệ thống yêu cầu các chương trình promotion cần hoạt động nhanh, ổn định, sẵn sàng để phục vụ: Load nhanh và cập nhật đúng lúc Từ yêu cầu trên chúng ta sẽ cài đặt các api để lấy promtion có sử dụng Redis, khi api được gọi nó sẽ kiểm tra thông tin trong Redis trước, nếu có sẽ trả về luôn và yêu cầu update redis nếu cần Các dữ liệu promotion như ảnh, video được lưu tại CDN tốc độ cao để giảm thiểu độ trễ như user yêu cầu. Sử dụng kỹ thuật AOP để tạo advice và các pointcut vào các yêu cầu gửi thông báo bị lỗi hoặc các function xác minh thông tin của promotion

Thiết kế micro service notification:

Hệ thống có nhiều yêu cầu gửi notification và gửi qua nhiều kênh khác nhau như: Email, SMS, Push notification trên mobile. Một thông báo có thể được yêu cầu gửi qua đa kênh. Thiết kế nhiều consumer để nhận các message yêu cầu gửi thông báo:

  • Email Consumer
  • SMS Consumer
  • Push notification consumer

Các consumer sau khi nhận được message sẽ tiến hành parse và tạo message với template phù hợp để gửi đi. Đối với các thông báo quan trọng, consumer cần log lại kết quả Đối với một số kênh có yêu cầu rate limit cho việc sử dụng api, chúng ta cần có thời gian nghỉ hợp lý

Các thông báo chính của hệ thống:

  • Xác nhận đơn đặt phòng: sau khi khách hàng đã hoàn tất đặt phòng, hệ thống cần gửi thông báo xác nhận đơn đặt phòng đến email hoặc số điện thoại của khách hàng để xác nhận thông tin đặt phòng.
  • Thông báo thanh toán: khi khách hàng đã hoàn tất đặt phòng, hệ thống cần gửi thông báo thanh toán đến email hoặc số điện thoại của khách hàng, bao gồm thông tin về số tiền cần thanh toán và hạn chót thanh toán.
  • Nhắc nhở thanh toán: nếu khách hàng chưa thanh toán đúng thời hạn, hệ thống cần gửi thông báo nhắc nhở thanh toán đến email hoặc số điện thoại của khách hàng.
  • Xác nhận thanh toán: sau khi khách hàng đã thanh toán đầy đủ, hệ thống cần gửi thông báo xác nhận thanh toán đến email hoặc số điện thoại của khách hàng để xác nhận việc thanh toán đã được thực hiện thành công.
  • Thông báo đến khách hàng về thay đổi hoặc hủy đặt phòng: nếu có bất kỳ thay đổi hoặc hủy đặt phòng nào, hệ thống cần gửi thông báo đến email hoặc số điện thoại của khách hàng để thông báo về tình trạng mới nhất của đơn đặt phòng.
  • Lời chào đón: khi khách hàng đến nhận phòng, hệ thống cần gửi lời chào đón đến email hoặc số điện thoại của khách hàng để chào đón và thông báo về các dịch vụ và tiện ích có sẵn tại khách sạn.
  • Thông báo đến khách hàng về check-out: trước thời điểm check-out, hệ thống cần gửi thông báo đến email hoặc số điện thoại của khách hàng để nhắc nhở về việc thanh toán các chi phí phát sinh nếu có và chuẩn bị cho quá trình check-out.

Hướng dẫn cài đặt các kỹ thuật khác

Cài đặt Kafka để gửi và nhận message

khi nhận được các yêu cầu booking hay thanh toán, chúng ta sẽ tạo ra các message yêu cầu xử lý đẩy vào kafka. Các consumer sẽ nhận các message theo topic mà nó đăng ký để lấy ra xử lý. Khi hệ thống nhận được quá nhiều request có thể dẫn tới gửi message qua kafka bị lỗi, chúng ta cần addCallback để bắt trường hợp lỗi để lưu lại hoặc gửi lại. có thể config timeout và retry cho kafka Một topic có quá nhiều message cần được partition và tạo replication

Ví dụ tạo producer và consumer message Configuring Topics Previously, we ran command-line tools to create topics in Kafka:

$ bin/kafka-topics.sh --create \
  --zookeeper localhost:2181 \
  --replication-factor 1 --partitions 1 \
  --topic mytopic

But with the introduction of AdminClient in Kafka, we can now create topics programmatically. We need to add the KafkaAdmin Spring bean, which will automatically add topics for all beans of type NewTopic:

@Configuration
public class KafkaTopicConfig {
    
    @Value(value = "${spring.kafka.bootstrap-servers}")
    private String bootstrapAddress;

    @Bean
    public KafkaAdmin kafkaAdmin() {
        Map<String, Object> configs = new HashMap<>();
        configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
        return new KafkaAdmin(configs);
    }
    
    @Bean
    public NewTopic topic1() {
         return new NewTopic("baeldung", 1, (short) 1);
    }
}

Producer Configuration

@Configuration
public class KafkaProducerConfig {

    @Bean
    public ProducerFactory<String, String> producerFactory() {
        Map<String, Object> configProps = new HashMap<>();
        configProps.put(
          ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, 
          bootstrapAddress);
        configProps.put(
          ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, 
          StringSerializer.class);
        configProps.put(
          ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, 
          StringSerializer.class);
        return new DefaultKafkaProducerFactory<>(configProps);
    }

    @Bean
    public KafkaTemplate<String, String> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }
}

We can send messages using the KafkaTemplate class:

@Autowired
private KafkaTemplate<String, String> kafkaTemplate;

public void sendMessage(String msg) {
    kafkaTemplate.send(topicName, msg);
}

The send API returns a CompletableFuture object. If we want to block the sending thread and get the result about the sent message, we can call the get API of the CompletableFuture object. The thread will wait for the result, but it will slow down the producer. Kafka is a fast-stream processing platform. Therefore, it's better to handle the results asynchronously so that the subsequent messages do not wait for the result of the previous message. We can do this through a callback:

public void sendMessage(String message) {
     CompletableFuture<SendResult<String, String>> future = kafkaTemplate.send(topicName, message);
     future.whenComplete((result, ex) -> {
         if (ex == null) {
             System.out.println("Sent message=[" + message + 
                 "] with offset=[" + result.getRecordMetadata().offset() + "]");
         } else {
             System.out.println("Unable to send message=[" + 
                 message + "] due to : " + ex.getMessage());
         }
     });
}

Consuming Messages

@KafkaListener(topics = "topicName", groupId = "foo")
public void listenGroupFoo(String message) {
    System.out.println("Received Message in group foo: " + message);
}

Tạo interface restfull api bằng Swagger

Thêm depndency Maven

Như đã đề cập ở trên, chúng ta sẽ sử dụng Springfox để triển khai Swagger. Phiên bản mới nhất có thê rtimf thấy ở đây Để thêm nó vào Maven project của chúng ta. ta cần thêm depndency vào file pom.xml:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>3.0.0</version>
</dependency>

Spring boot depndency

Đối với project loại Spring Boot ta chỉ cần thêm duy nhất depndency springfox-boot-starter là đủ:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

Chúng ta có thể thêm các starter khác nếu cần.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
</dependency>

Swagger UI Swagger UI là một giải pháp giúp sự tuơng tác của user với tài liệu của Swagger-generated API một cách dễ dàng hơn. Kích hoạt Springfox's Swagger UI Để sử dụng Swagger UI, ta cần thêm phần dependency Maven:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

Bây giờ ta có thể test bằng cách truy cập URL sau: http://localhost:8080/your-app-root/swagger-ui/

Làm mới dữ liệu trên cache

  • Với các dữ liệu cơ bản, hệ thống sẽ tự động làm mới dữ liệu với dữ liệu mới nhất trên database chính (MySQL) sau một khoảng thời gian
  • Với các dữ liệu theo từng dịch vụ, người dùng như lịch sử giao dịch, lịch sử booking, lịch sử xem ... chúng ta sẽ update mỗi khi có update mới của dữ liệu
  • Với các dữ liệu cần thông tin realtime như thông tin phòng, thông tin chuyến bay cần xây dựng các API để các bên cung cấp dịch vụ cập nhật hoặc làm mới chủ động

Kiểm thử phần mềm

Chúng ta sẽ tập trung viết các unit test cho các logic xử lý chính của hệ thống như: Account manager, Booking service, Payment service. Ví dụ:

Để tìm hiểu thêm về các annotation trong jUnit bạn có thể xem thêm ở đây: https://viblo.asia/p/junit-5-annotations-Eb85opxWK2G

Tài liệu

Discover what we do: dwarves.foundation Meet our team: discord.gg/dwarvesv Join the squad: careers.d.foundation Follow our journey Fanpage: facebook.com/dwarvesf LinkedIn: linkedin.com/company/dwarvesf


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.