Link Search Menu Expand Document

Microservices.

Phát triển ứng dụng theo kiến trúc Microservices

Mở đầu Dự án trên GitHub


Giới thiệu Blog

Chào các bác,

Tên tôi là Tuấn Anh, một số bạn bè thường gọi tôi là Andy.

Công việc của tôi là phát triển phần mềm cho một công ty chuyên về Gambling tại Melbourne, Australia. Trong thời gian rảnh rỗi, tôi viết blog cá nhân này để ghi lại kinh nghiệm của mình về Microservice với mục đích:

  • Tổng hợp kiến thức cơ bản về những mô hình và thiết kế
  • Thực hành công nghệ thường dùng trong Microservices

Qua blog này, tôi hi vọng có thể chia sẻ và trao đổi kiến thức của mình cùng các bác. Trong quá trình tham khảo Blog này, nếu thấy những điểm sai sót hoặc chưa rõ ràng, các bác có thể liên lạc qua địa chỉ anhltanz@gmail.com

Thân ái.

Mở đầu

Microservices là mô hình thiết kế phần mềm đang dần trở nên phổ biến hiện nay. Theo mô hình này, ứng dụng được chia nhỏ thành những thành phần dịch vụ nhỏ hơn - services. Mỗi dịch vụ thực hiện một chức năng riêng biệt, hoạt động độc lập và kết nối với nhau một cách hợp lý để đáp ứng các yêu cầu nghiệp vụ của phần mềm.

Do từng dịch vụ có thể phát triển, hoạt động độc lập nên việc xây dựng ứng dụng sẽ có tính linh động và mở rộng cao hơn. Mỗi dịch vụ có thể được đảm nhiệm bởi những nhóm kĩ sư khác nhau, lựa chọn các ngôn ngữ, framework thích hợp để đem lại tính hiệu quả cao nhất.

Với mô hình Microservices, khi muốn phát triển thêm tính năng sản phẩm, chúng ta chỉ cần phát triển dịch vụ mới cho tính năng đó và giao tiếp với những dịch vụ khác thông qua các giao tiếp - API được qui định sẵn. Điều này rất cần thiết khi thị trường thay đổi nhanh chóng bắt buộc sản phẩm phải liên tục được sửa đổi và nâng cấp để đáp ứng yêu cầu mới.

Đặc điểm

Tác giả James Lewis và Martin Fowler mô tả kiến trúc Microservices với những đặc điểm:

  • Tập hợp dịch vụ chạy trên tiến trình riêng biệt
  • Chia dịch vụ theo chức năng doanh nghiệp
  • Tập trung sản phẩm thay vì dự án
  • Sử dụng cơ chế giao tiếp gọn nhẹ
  • Phân chia quản lý
  • Dữ liệu phân tán
  • Tự động hoá cơ sở hạ tầng
  • Quản lý sự cố
  • Chuyển đổi thiết kế

Tập hợp dịch vụ

Khi phát triển một phần mềm, chúng ta có thể phân chia nó thành các thành phần nhỏ hơn - component, với 2 mục đích:

  • Hoạt động độc lập
  • Khả năng thay thế.

Khái niệm này cũng thường gặp trong những mô hình lập trình thủ tục hoặc hướng đối tượng. Với lập trình hướng thủ tục, chúng ta chia ứng dụng thành các thủ tục, thư viện. Với lập trình hướng đối tượng, chúng ta có các đối tượng cùng phương thức tương ứng.

Microservices Ouput So sánh Monilith và Microservices

Mô hình kiến trúc Microservices sử dụng cách tiếp cận tương tự. Hệ thống được chia nhỏ thành nhiều dịch vụ, chạy trên những tiến trình - process độc lập, và có khả năng liên lạc với nhau qua những phương thức gửi nhận thông điệp - message tinh gọn.

So với kiến trúc nguyên khối truyền thống - monolithic, mô hình microservices có ưu điểm quan trọng là khả năng triển khai hay nâng cấp độc lập. Khi áp dụng Monolithic, toàn bộ hệ thống thực thi chung một tiến trình, việc thay đổi một thành phần trong đó sẽ yêu cầu chúng ta cần biên dịch và triển khai lại toàn bộ hệ thống đó, điều này có thể dẫn đến sự mất liền mạch trong hoạt động của hệ thống. Ngược lại, trong mô hình Microservices, chúng ta chỉ cần triển khai lại những service được thay đổi thay vì toàn bộ hệ thống.

Bên cạnh đó, việc phát triển các service độc lập cũng giúp chúng ta tăng cường tính đóng gói và giảm thiểu sự phụ thuộc giữa các thành phần bên trong hệ thống. Việc các service chỉ có thể tương tác với nhau qua những API / Event giúp chúng có khả năng độc lập, không ảnh hưởng đến logic bên trong của service khác.

Phân chia theo chức năng doanh nghiệp

Khi phát triển các ứng dụng lớn, doanh nghiệp thường tổ chức bộ phận IT tương ứng với các kĩ năng công nghệ khác nhau:

  • Team UI có nhiệm vụ xây dựng giao diện ứng dụng UI/UX
  • Team Back-end phát triển logic chính của ứng dụng
  • Team Data quản lý, khai thông tin trong cơ sở dữ liệu.

Với mô hình tổ chức như vậy, một thay đổi nhỏ có thể sẽ ảnh hưởng đến nhiều bộ phận làm việc khác nhau, cùng những qui trình làm việc phức tạp tốn kém thời gian và công sức.

Conway Law

Định luật Conway

Để khắc phục nhược điểm này, mô hình phát triển ứng dụng theo kiến trúc Microservices đề xuất một phương pháp tiếp cận khác: phân chia dịch vụ theo chức năng doanh nghiệp. Việc phát triển mỗi dịch vụ sẽ đi theo một phạm vi sâu rộng hơn, bao gồm giao diện người dùng, logic doanh nghiệp, lưu trữ dữ liệu cùng các liên kết có thể đến dịch vụ khác. Với cách phân chia này, khi phát triển một dịch vụ, các thành viên trong nhóm phải có khả năng làm việc theo chiều dọc - crosss-functional: từ việc xây dựng giao diện ứng dụng đến thiết kế mô hình cơ sở dữ liệu, quản lý sản phẩm.

Việc phân chia phát triển thep chức năng doanh nghiệp giúp nâng cao tính tự chủ - autonomous và tính sở hữu - ownership của nhóm kĩ sư đối với sản phẩm mà họ làm ra. Những nhóm làm việc cần có trách nhiệm với việc xây dựng cũng như vận hành các sản phẩm tương ứng với dịch vụ mà họ thực hiện.

Functional staff

Cross-functional staffs

Tập trung vào sản phẩm thay vì dự án

Thông thường việc phát triển sản phẩm được phân chia thành nhiều dự án. Nhóm kĩ sư được thành lập để thực hiện dự án với các yêu cầu được đưa ra ban đầu. Khi kết thúc dự án và hoàn thành quá trình bàn giao, nhóm kĩ sư đó sẽ giải thể để chuyển sang làm những sản phẩm hay chức năng mới.

Mô hình Microservices hạn chế cách làm trên bằng cách nâng cao tính trách nhiệm và tính sở hữu của những kĩ sư đối với sản phẩm được họ tạo ra. Phạm vi công việc được mở rộng từ việc phát triển những tính năng đến việc quản trị, nâng cấp những tính năng đó. Phương pháp làm việc này được thể hiện trong một khẩu hiệu nổi tiếng của tập đoàn Amazon với ý nghĩa: “You built it, you run it”.

Phân chia quản lý

Mô hình quản lý tập trung thường dẫn đến việc chúng ta cố gắng xây dựng tiêu chuẩn cho một công nghệ nhất định. Tuy vậy, kinh nghiệm thực tế đã chỉ ra cách tiếp cận này cũng đem lại điểm hạn chế. Không thể có một giải pháp tối ưu cho mọi vấn đề, nói cách khác chúng ta cần có những phương pháp cụ thể để giải quyết những vấn đề khác nhau một cách tối ưu (i.e, Polyglot Programming).

Điều này cũng khuyến khích các kĩ sư thay vì tập trung vào những tài liệu hướng dẫn tiêu chuẩn, họ nên giành công sức tạo ra những công cụ phần mềm giải quyết những vấn đề họ thường gặp phải và chia sẻ nó một cách rộng rãi. Nền tảng mã nguồn mở trên GitHub là một phương tiện hỗ trợ giúp họ thực hiện điều này.

Dữ liệu phân tán

Sự phân tán trong quản lý dữ liệu được thể hiện những mức độ khác nhau.

  • Thiết kế mô hình dữ liệu: Khi thiết kế mô hình dữ liệu phức tạp, người ta thường sử dụng phương pháp thiết kế theo hướng nghiệp vụ, Design-Domain Driven, để phân tách và xây dựng mối quan hệ giữa những đối tượng dữ liệu. Phương pháp thiết kế dữ liệu này có thể áp dụng đồng thời cho cách xây dựng ứng dụng theo kiến trúc Microservices hoặc Monolithic. Tuy nhiên, vói Microservices, chúng ta sẽ xây dựng được một sự tương đồng giữa việc phân chia các service và phân chia mô hình dữ liệu. Điều này là logic khi các dịch vụ cũng được phân chia theo các chức năng, nghiệp vụ sản phẩm.

  • Quản lý cơ sở dữ liệu: Thay vì tập trung lưu trữ mọi thông tin trong một cơ sở dữ liệu đơn nhất, kiến trúc Microservices cho phép mỗi dịch vụ có thể quản lý một cơ sở dữ liệu tương ứng. Những cơ sở dữ liệu này có thể chạy trên cùng một hệ thống quản trị cơ sở dữ liệu, hoặc phân tán trên những hệ quản trị cơ sở dữ liệu khác nhau - Polygot Persistence.

Decentralised Data

Dữ liệu phân tán

Việc phân chia trong quản lý dữ liệu cũng tạo nên yêu cầu về sự đồng bộ giữa những đối tượng dữ liệu trong quá trình cập nhật. Giải pháp phổ biến cho yêu cầu này là sử dụng các giao dịch - transaction để đảm bảo tính đồng bộ và toàn vẹn thông tin giữa các cơ sở dữ liệu. Tuy nhiên, giải pháp này cũng sẽ tạo nên sự phụ thuộc giữa những dịch vụ trong hệ thống, đó là điều chúng ta đang cố gắng giảm thiểu. Để khắc phục điều này, chúng ta có thể chấp nhận tính bất đồng bộ “tạm thời”, và triển khai các chức năng cho phép dữ liệu trở nên đồng nhất sau một “thời gian trễ” nhất định - eventual consistency

Định nghĩa

Design-Domain Driven (DDD) - thiết kế hệ thống phản ánh chính xác qui trình và qui tắc của một loại hình nghiệp vụ. Nói theo cách đơn giản hơn, cấu trúc và logic của phần mềm (các lớp, thuộc tính, phương thức etc..) cần phải thống nhất và thể hiện đúng mô hình nghiệp vụ - domain model. Tham khảo DDD Microservices

Tự động hoá cơ sở hạ tầng

Sự phát triển của nền tảng điện toán đám mây như Amazon Web Service đã tạo ra nhiều công nghệ cho phép chúng ta xây dựng một qui trình tự động hoá cho việc biên dịch, triển khai và quản lý hệ thống phần mềm.

Hệ thống phần mềm dựa trên kiến trúc Microservices thường sử dụng rất nhiều kĩ thuật cho phép tích hợp (Continuous Integration) và triển khai (Continuous Delivery) những dịch vụ một cách tự động hoá nhất có thể. Qui trình này thường bao gồm các bước:

  • Biên dịch chương trình
  • Triển khai trên môi trường tích hợp
  • Triển khai trên môi trường kiểm thử chức năng
  • Triển khai trên môi trường kiểm thử hiệu năng
  • Triển khai trên môi trường sản phẩm
  • Giám sát, quản lý, báo cáo chất lượng chức năng sản phẩm

Quản lý sự cố

Khi hệ thống gồm nhiều dịch vụ chạy riêng biệt, một yêu cầu quan trọng với việc thiết kế là khả năng quản lý, khắc phục những sự cố có thể xảy ra trên các dịch vụ đó. Các dịch vụ thường được xây dựng như một chuỗi cung ứng các chức năng, do vậy việc gián đoạn một dịch vụ có thể dẫn tới sự gián đoạn những chức năng mà nó chịu trách nhiệm.

Để nhanh chóng khắc phục khi sự cố xảy ra, điều quan trọng là chúng ta cần một hệ thống giám sát và báo cáo sự cố theo cách sớm nhất có thể, cùng với khả năng tự động khắc phục sự cố (ví dụ, chuyển sang sự dụng hệ thống dự phòng, hay chuyển đổi phiên bản bị lỗi sang một phiên bản ổn định khác). Để làm được điều này, hệ thống giám sát cần được triển khai ở nhiều khía cạnh, từ việc giám sát truy cập dữ liệu, đến thời gian phản hồi yêu cầu đến phía người dùng, tự động gửi tin nhắn, email đến người chịu trách nhiệm khi phát hiện bất kì cảnh báo bất thường nào.

Chuyển đổi thiết kế

Cùng với sự thay đổi nhanh chóng từ thị trường, những chức năng của một hệ thống phần mềm cũng thường xuyên phải thay đổi và cải tiến. Việc thiết kế phần mềm phải giúp cho những người kĩ sư có thể thực hiện những thay đổi đó một cách nhanh chóng và hiệu quả.

Sự phân chia một hệ thống phần mềm thành các thành phần nhỏ hơn phải dựa trên những nguyên tắc nhất định. Một trong những nguyên tắc đó là đảm bảo cho các thành phần có khả năng thay thế và nâng cấp. Điều đó có nghĩa rằng ở những thời điểm nhất định, chúng ta có thể viết lại hay loại bỏ một thành phần nào đó mà không ảnh hưởng đến những thành phần khác trong hệ thống. Trên thực tế, có rất nhiều trường hợp chúng ta xây dựng những dịch vụ chỉ hoạt động trong một thời gian ngắn cho những mục đích cụ thể và loại bỏ chúng khi không còn sử dụng. Một ví dụ đưa ra là hệ thống báo điện tử Guardian. Thành phần trung tâm của hệ thống này vốn được xây dựng theo mô hình đơn khối truyền thống, dựa trên đó, những dịch vụ được xây dựng, sử dụng những API cung cấp bởi thành phần này. Những dịch vụ này có thể xây dựng để cung cấp thông tin về một sự kiện thể thao, văn hoá trong thời gian ngắn, và được loại bỏ sau khi sự kiện kết thúc.

Kết luận

Cùng những ưu điểm trong thiết kế, mô hình Microservices đang được áp dụng rộng rãi ở nhiều công ty lớn trên thế giới như Amazon, Netflix, The Guardian, etc. Tuy nhiên, những hệ thống phần mềm cho dù được thiết kế tốt, hay được phát triển bởi những kĩ sư giỏi đều cần nhiều năm để đánh giá xem chúng có thực sự thành công hay không. Cũng vì vậy, chúng ta cần thêm thời gian để có một sự so sánh đúng đắn giữa mô hình Microservices và mô hình truyền thống Monolithic.

Bên cạnh đó, cho dù sự dụng mô hình thiết kế nào, chúng ta đều gặp những khó khăn khi phân chia hệ thống thành những thành phần nhỏ hơn hay xác định danh giới những những thành phần đó. Khi những thành phần này được thể hiện dưới những dịch vụ, việc cải tiến logic bên trong chúng - refactoring thường gặp nhiều khó khăn hơn so với việc sử dụng thư viện. Chúng ta không dễ dàng chuyển đổi logic code từ một dịch vụ này sang một dịch vụ khác, hay bất kì thay đổi về mặt giao tiếp của một dịch vụ cũng sẽ ảnh hưởng đến những dịch vụ liên quan. Ngoài ra, nếu những dịch vụ không được phân chia rõ ràng, chúng sẽ tạo ra những liên kết phức tạp và khó kiểm soát.

Một vấn đề phụ thuộc khác là “tay nghề” của những kĩ sư trong nhóm. Những kĩ thuật mới luôn dễ dàng được áp dụng với những kĩ sư có khả năng tốt. Tuy nhiên, kĩ thuật đó có thể không được áp dụng hiệu quả với những kĩ sư có tay nghề thấp hơn. Có nhiều trường hợp hệ thống theo kiến trúc Monolithic bị xây dựng thất bại bởi những kĩ sư như vậy, và chúng ta cũng cần thêm thời gian xác nhận kết quả có thể xảy ra khi họ áp dụng kiến trúc Microservices :)

Trước khi đi vào chi tiết những công nghệ và thực hành trong ứng dụng Microservices, chúng ta có một danh sách công cụ phần mềm sẽ sử dụng blog này: Cài đặt và thiết lập.


Tài liệu tham khảo


Copyright © 2019-2022 Tuan Anh Le.