GIỚI THIỆU VỀ SOA

Ba kiến trúc phân tán phổ biến nhất hiện này là CORBA, DCOM và EJB. Các kiến trúc này là sự mở rộng của các hệ thống hướng đối tượng bằng cách cho phép phân tán các đối tượng trên mạng. Đối tượng đó có thể có không gian địa chỉ bên ngoài ứng dụng, hoăc ở một máy khác với máy chứa ứng dụng trong khi vẫn được tham chiếu sử dụng như một phần của ứng dụng. Tuy nhiên các kiến trúc đó gặp phải rất nhiều khó khăn trong thiết kế và triển khai. Vì vậy có 1 kiến trúc có thể giải quyết được những khó khăn và đã được triển khai trong thực tế, đó là “kiến trúc hướng dịch vụ” Service oriented Architecture (SOA)

Kiến trúc hướng dịch vụ là gì ? Kiến trúc hướng dịch vụ (Service-oriented architecture) là một hướng tiếp cận với việc thiết kế và tích hợp các phần mềm, chức năng, hệ thống theo dạng module, trong đó mỗi module đóng vai trò là một “dịch vụ có tính loose coupling”, và có khả năng truy cập thông qua môi trường mạng. Hiểu một cách đơn giản thì một hệ thống SOA là một tập hợp các dịch vụ được chuẩn hoá trên mạng trao đổi với nhau trong ngữ cảnh một tiến trình nghiệp vụ. Trong SOA có ba đối tượng chính, minh họa trong hình sau:

Nhà cung cấp (service provider) dịch vụ cần cung cấp thông tin về dịch vụ của mình cho một dịch vụ lưu trữ thông tin dịch vụ (service registry). Người sử dụng (service consumer) thông qua service registry để tìm kiếm thông tin mô tả về dịch vụ cần tìm và sau đó là xây dựng kênh giao tiếp với phía nhà cung cấp. SOA cung cấp giải pháp để giải quyết các vấn đề tồn tại của các hệ thống hiện nay như: phức tạp, không linh hoạt và không ổn định. Một hệ thống triển khai theo mô hình SOA có khả năng dễ mở rộng, liên kết tốt. Đây chính là cơ sở và nền tảng cho việc tích hợp, tái sử dụng lại những tài nguyên hiện có.

CASE STUDY VỀ SOA:

Trong bài viết này, tôi sẽ thực hiện thiết kế một mô hình SOA với bài toán:  Đặt hàng trên trang thương mại điện tử sử dụng SOA

Mô tả về bài toán

Bài toán yêu cầu xây dựng chức năng đặt hàng trên trang thương mại điện tử với các chức năng: đặt hàng, thanh toán.

  • Chức năng đặt hàng:
  • Cho phép người dùng tìm kiếm sản phẩm, xem sản phẩm, sau đó thêm sản phẩm vào giỏ hàng
  • Sau khi đã có giỏ hàng, người dùng sẽ tiến hành đặt hàng. Trong quá trình đặt hàng người dùng phải nhập thông tin giao hàng.
  • Sau khi đặt hàng, người dùng phải tiến hành thanh toán để hoàn tất đơn hàng
  • Chức năng thanh toán:
  • Người dùng tiến hành nhập thông tin khách hàng sau đó nhập thông tin thẻ để tiến hành thanh toán đơn hàng, kết thúc quá trình đặt hàng

PHÂN RÃ SERVICE

Để tiến hành thiết kế hệ thống SOA, đầu tiên cần phân rã các chức năng thành các service tương ứng. Đối với bài toán này, em sẽ thực hiện phân rã thành các service sau:

  • Product service: Dịch vụ của sản phẩm
  • Shopping cart service: Dịch vụ về giỏ hàng
  • Order service: Dịch vụ về phần đặt hàng
  • Payment service: Dịch vụ chịu trách nhiệm về thanh toán
  • Customer service: Dịch vụ về khách hàng

THIẾT KẾ CÁC SERVICE

Với mỗi dịch vụ ta coi đây là 1 bài toán nhỏ, các dịch vụ cần phải được thiết kế và hoạt động như những thực thể độc lập mà không lệ thuộc vào một dịch vụ khác. Dịch vụ phải có tính bền vững cao, nghĩa là nó sẽ không bị sụp đổ khi có sự cố.

Các dịch vụ nên cung cấp thành phần giao tiếp của nó (interface) ra bên ngoài, và hỗ trợ chia sẻ các cấu trúc thông tin, các ràng buộc dữ liệu thông qua các lược đồ dữ liệu (schema) chuẩn (độc lập ngôn ngữ, độc lập hệ nền.). Như thế hệ thống của ta sẽ có tính liên kết và khả năng dễ mở rộng.

Mỗi service sẽ bao gồm các lớp:

Nguồn ảnh: https://dzone.com/articles/microservices-verticals-and-business-process-manag

Mỗi service sẽ được tách riêng về database, có lớp bussiness logic riêng và giao tiếp với các service hoặc ứng dụng khác thông qua REST API. Ngoài ra các service cũng có thể giao tiếp được với nhau thông qua các sự kiện (events). Từ thiết kế này, ta sẽ tiến hành đi xây dựng từng service với các thành phần bên trong tương ứng.

Trong các event, ta sẽ phân ra làm 2 loại:

Consumer: Các service gồm các consumer tiến hành lắng nghe các sự kiện từ service khác

Producer: Các service gồm các producer sẽ gửi các sự kiện đến các service đang lắng nghe

Ngoài ra mỗi event ta còn có thêm ‘topic’ để đặt tên cho các events.

Product Service

Service này sẽ xử lý các nghiệp vụ liên quan đến sản phẩm.

Trong interface Product sẽ có các phương thức sau:

+ searchProduct(Filter: filter): Phương thức dùng để tìm kiếm sản phẩm, tham số truyền vào là 1 bộ filter có kiểu dữ liệu gồm: query, condition1, condition2. Phương thức này trả về mảng các sản phẩm

+ getProduct(id:string): Phương thức này sẽ trả về thông tin của sản phẩm, tham số truyền vào là id sản phẩm.

+ updateProductInventory(Product): Dùng để cập nhật thông tin kho của sản phẩm khi có yêu cầu cập nhật sản phẩm, trả về trạng thái (true – thành công, false – thất bại)

+ checkInventory(Product, quantity:number): Dùng để kiểm tra thông tin về kho hàng của sản phẩm, trả về true – còn hàng, false – hết hàng.

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

Dựa vào yêu cầu thiết kế, cần xây dựng cơ sở dữ liệu cho product service như sau:

Thiết kế API:

Shopping cart service

Trong interface ShoppingCart sẽ có các phương thức sau:

+ addProduct(Product): Thêm sản phẩm vào giỏ hàng, tham số truyền vào là 1 đối tượng Product, hàm trả về true nếu thêm thành công, trả về false nếu thêm không thành công

+ removeProduct(id: string): Hàm này sẽ xóa sản phẩm trong giỏ hàng, tham số truyền vào là id của sản phẩm trong giỏ hàng, trả về true nếu xóa thành công false nếu xóa thất bại.

+ changeQuantity(Product, quantity): Hàm này dùng để thay đổi số lượng sản phẩm có trong giỏ hàng, trả về true nếu thêm thành công, false nếu thất bại

+ createShoppingCartIfNotExist(Customer): Dùng để tạo giỏ hàng cho khách hàng nếu giỏ hàng chưa được tạo, trả về đối tượng ShoppingCart

+ calculateTotalPrice(): Tính toán giá trị của giỏ hàng. Trả về kiểu dữ liệu là float – giá trị giỏ hàng.

Thiết kế CSDL:

Cơ sở dữ liệu cho service giỏ hàng:

Thiết kế API:

Order service

Trong interface Order có các phương thức sau:

+ placeOrder(ShoppingCart: cart): Phương thức dùng để tạo order, tham số truyền vào là đối tượng shopping cart, nghĩa là chúng ta sẽ tạo đơn hàng từ giỏ hàng.

+ getOrder(id: string): Trả về chi tiết đơn hàng, tham số truyền vào là id của đơn hàng

+ setStatus(id, status, paymentRef): Thay đổi trạng thái đơn hàng, tham số truyền vào là id đơn hàng, trạng thái đơn hàng (status) và thông tin về thanh toán (nếu có) của đơn hàng (paymentRef)

+ getShipping(Customer): Trả về thông tin về giao hàng của đơn hàng

+ calculateTotalPrice(): Tính giá trị đơn hàng

Thiết kế CSDL

Thiết kế API webservice

Payment service

Các phương thức trong interface:
+ createPayment(Order: order): Tạo thanh toán từ đơn hàng, khi cần thanh toán cho đơn hàng, tham số truyền vào là đối tượng đơn hàng

+ getCustomerPayments(customer_id): Trả về danh sách các giao dịch thanh toán của khách hàng

+ addCreditCard(CreditCard): Thêm thẻ ngân hàng của khách hàng

+ getCards(customer_id): Trả về danh sách các thẻ ngân hàng của khách hàng, tham số truyền vào là id của khách hàng

+ getPaymentDetail(payment_ref_id): Trả về chi tiết của thanh toán.

Thiết kế CSDL

Thiết kế API

THIẾT KẾ HỆ THỐNG

Sau khi có các thiết kế của các service, chúng ta sẽ tiến hành tích hợp các service thành hệ thống duy nhất để đảm bảo các service hoạt động đúng chức năng thiết kế và đảm bảo được yêu cầu của hệ thống. Nhắc lại 1 chút về mô hình kết nối của các service, mỗi service sẽ là 1 dịch vụ riêng biệt, hoạt động trên nền tảng phần cứng, database khác nhau:

Nguồn ảnh: Microservices vs SOA – O’Reilly

Các service sẽ giao tiếp với nhau thông qua các API web service và Message Queue:

Web service

Mặc dù ngày nay CORBA vẫn còn được sử dụng, chúng ta nhận thấy việc giảm các communication qua network là điều cần thiết để cải thiện performance cho hệ thống, chúng ta cần một kênh giao tiếp đáng tin cậy, và chúng ta cần một đặc tả giao tiếp đơn giản.

Chúng ta cần một kênh giao tiếp đáng tin cậy, do đó:

  • HTTP qua cổng 80 là đường truyền mặc định;
  • Một ngôn ngữ giao tiếp đơn giản được sử dụng (như XML hay JSON);
  • Chúng ta cần giảm các remote call, do đó:
  • Chúng ta khai báo các remote call một cách tường minh để biết chính xác khi nào nên thực hiện remote cal;
  • Chúng ta có coarse-grained remote calls, tức là thay vì gọi các remote objects, chúng tôi gọi remote service;

Chúng ta cần một đặc tả giao tiếp đơn giản, vì vậy:

- REST (REpresentational State Transfer) được định nghĩa vào năm 2000 bởi Roy Fielding trong luận văn tiến sĩ về “Architectural Styles and the Design of Network-based Software Architectures”. REST là một kiểu cấu trúc (architectural style) cung cấp API thông qua internet để xử lý các hoạt động CRUD trên dữ liệu. REST tập trung vào việc truy cập các tài nguyên được đặt tên thông qua một giao diện duy nhất.

Thông qua Web services, SOA thực hiện chuyển đổi mô hình từ remote call trên object (CORBA), sang truyền thông điệp giữa các service.

Các Web Service thường cung cấp các dữ liệu thô mà nó khó hiểu đối với đa số người dùng thông thường, chúng thường được trả về dưới dạng XML hoặc JSON. Trong trường hợp này em sử dụng JSON.

Tuy nhiên, chúng ta cần phải hiểu rằng dưới cái ô SOA, một Web services không chỉ là một API đơn giản chỉ đơn giản là cung cấp các tính năng CRUD vào cơ sở dữ liệu thông qua HTTP. Nó có thể hữu ích trong một số trường hợp, tuy nhiên đòi hỏi người dùng phải hiểu được mô hình cơ bản và tuân theo các business logic để đảm bảo tính toàn vẹn của dữ liệu được bảo vệ. SOA ngụ ý rằng Web services được thiết kế như là các bounded contexts cho từng business sub-domains, trừu tượng hóa và bao đóng các conceptual service mà chúng cung cấp.

Message Queue:

Message queues cung cấp một giao thức giao tiếp không đồng bộ, có nghĩa là người gửi và người nhận tin nhắn không cần phải tương tác với message queue cùng một lúc. Message được đặt vào queue được lưu trữ cho đến khi người nhận nhận lại chúng, cung cấp khả năng cải thiện việc mở rộng (scalability) và phân tách (decoupling) giữa các ứng dụng vì chúng không cần biết chính xác vị trí của các ứng dụng khác, số lượng và thậm chí không cần phải biết các ứng dụng đó là gì.

Mô hình Message Queue cũng giống như publisher/subcriber pattern, và là một phần của một message-oriented middleware system. Hầu hết các messaging systems đều hỗ trợ cả publisher/subscriber and message queue models trong API của họ, ví dụ: Java Message Service (JMS).

Một Message Queue sử dụng phần broker trung gian thư (ví dụ: RabbitMQ, Beanstalkd, Kafka,…) làm cơ sở hạ tầng để thực hiện giao tiếp giữa các ứng dụng:

Request/Reply

Client sẽ gửi một tin nhắn đến Message Queue, bao gồm cả nội dung “hội thoại”. Message được gửi đến một node cụ thể, và node đó sẽ làm nhiệm vụ reply bằng một message khác, tới sender ban đầu. Nó rất hữu ích cho các business processes trung bình và dài hạn (sagas).

Publish/Subscribe

- List-Based

Nó duy trì danh sách các topic sẽ được publish và các subscribers đã đăng ký lắng nghe các topic đó. Khi nhận được message cho một topic, nó sẽ thông báo tất cả các subscribers đã đăng ký với topic đó.

- Broadcast-Based

Khi nó nhận được message, nó Broadcast cho tất cả các node đang nghe trên queue. Mỗi node lắng nghe chỉ chịu trách nhiệm lọc và xử lý các message mà nó quan tâm.

XÂY DỰNG BIỂU ĐỒ LUỒNG HOẠT ĐỘNG GIỮA CÁC SERVICE

Sau khi đã có thiết kế hệ thống để đưa ra cách thức giao tiếp giữa các service, chúng ta sẽ tiến hành xây dựng biểu đồ hoạt động của hệ thống để cho biết sự kết hợp và móc nối lẫn nhau giữa các service để thực hiện một yêu cầu từ người dùng:

Biểu đồ luồng hoạt động xem và tìm kiếm sản phẩm

Biểu đồ luồng hoạt động thêm sản phẩm vào giỏ hàng

Biểu đồ luồng hoạt động mua hàng và thanh toán

Cài đặt hệ thống

Các thư viện và phần mềm cần dùng để xây dựng hệ thống hướng dịch vụ:

Database: MongoDb, MySQL (chính): Ở đây em sử dụng 1 trong 2 loại database cho từng service khác nhau, mỗi service là 1 database khác nhau

  • Caching: Redis: Dùng để cache trong hệ thống
  • Broker Pub/Sub: Apache kafka: Dùng làm phương thức để giao tiếp bất đồng bộ giữa các service với nhau.
  • API webservice proxy: Nginx: Dùng làm proxy giữa các API và làm API gateway cho toàn bộ hệ thống.
  • Backend: NodeJS, Python: Đa dạng hóa ngôn ngữ lập trình phía backend bằng các ngôn ngữ khác nhau để thấy được sự độc lập về công nghệ giữa các service

Tổ chức file và cấu trúc thư mục:

Trong mỗi service tôi sẽ chia ra thành các layer để tổ chức code dễ hơn bao gồm:

- Transport: Tầng giao vận để giao tiếp với các service khác thông qua mạng qua API web serive hoặc message queue

- Handler: Dùng để thực hiện các xử lý liên quan đến nghiệp vụ và là phần xử lý chính xủa service.

- Repository: Là tầng thực hiện các thao tác với cơ sở dữ liệu, chuyên xử lý về mặt dữ liệu như thêm/sửa/xóa vào database và các xử lý phức tạp hơn liên quan đến dữ liệu. Đây là tầng quan trọng để kết nối handler với storage

- Storage: là tầng xử lý liên quan đến hệ cơ sở dữ liệu, bao gồm cách thức thực hiện các thao tác dữ liệu và quy định dữ liệu ra/vào.

Ví dụ chi tiết:

Xây dựng công cụ để theo dõi hoạt động của các service

Khác với hệ thống tập trung, hệ thống phân tán là sự phân tán của các dịch vụ. Vì vậy để đảm bảo được các service hoạt động ta cần các công cụ để theo dõi và đánh giá sự hoạt động đó. Em đã sử dụng netdata để theo dõi thông tin về server và các phần mềm hoạt động trên đó:

Xây dựng logging cho hệ thống

Fluentd là một ứng dụng miễn phí và open-source giúp có thể lấy được log ở web server và xử lý log data đó. Dữ liệu log lấy đươc rất có thể từ nginx, apache hoặc đơn giản là từ các gói tin tcp hay các message của http.

Demo

Giao diện trang tìm kiếm sản phẩm

Sau khi người dùng điền từ khóa vào ô tìm kiếm rồi bấm enter, hệ thống sẽ trả về danh sách sản phẩm và hiển thị ra giao diện

 

Trang chi tiết sản phẩm

Người dùng bấm vào sản phẩm sẽ hiển thị ra trang sản phẩm, trang sản phẩm này hoạt động chủ yếu lấy dữ liệu và thao tác trên service Product

Sau khi bấm vào thêm vào giỏ hàng: Giỏ hàng sẽ được xử lý ở service ShoppingCart

Trang giỏ hàng

Giao diện này lấy dữ liệu tử service Shopping cart, bao gồm danh sách các sản phẩm, số lượng và tổng giá trị trong giỏ hàng.

Người dùng bấm vào thanh toán sẽ hiện ra trang:

Trang đặt hàng

Giao diện đặt hàng sẽ gọi từ Payment Service, các thông tin về đơn hàng được tạo và trả về tử service này.

Sau khi bấm vào đặt hàng sẽ yêu cầu người dùng nhập thông tin thẻ thanh toán

Giao diện nhập thông tin thẻ

Sau khi người dùng bấm đồng ý, nếu thông tin thẻ chính xác đơn hàng sẽ được thanh toán tự động. Nếu thông tin bị sai thì sẽ báo lỗi.

Tổng kết

Như vậy mình đã giới thiệu sơ qua 1 số thành phần của microservice và thiết kế cơ bản của hệ thống. SOA là 1 hệ thống phức tạp và phải xử lý rất nhiều vấn đề nên mình không thể đề cập được hết trong 1 bài viết. Hi vọng qua bài viết mọi người có cái nhìn tổng quát về SOA.