Microservices

Microservices: Từ Thiết Kế Đến Triển Khai – Phần 7: Tái cấu trúc ứng dụng nguyên khối thành Microservices

Đây là bài viết thứ bảy và cũng là bài cuối cùng trong loạt bài về Microservices: Từ Thiết Kế Đến Triển Khai. Trong bài đầu tiên, mô hình kiến trúc Microservices đã được giới thiệu và thảo luận về những ưu điểm và hạn chế trong việc ứng dụng và triển khai. Bài thứ hai mô tả về cách thức mà các client tương tác với các microservice thông qua một phương tiện trung gian gọi là API GatewayBài thứ ba tìm hiểu về cách mà các microservices trong cùng một hệ thống giao tiếp với nhau thông qua IPC (Inter-process Communication). Bài thứ tư chúng ta đã tìm hiểu qua về truy tìm service (Service Discovery). Bài thứ năm đã xem xét các vấn đề về quản lý dữ liệu phân tán trong kiến ​​trúc microservices. Bài thứ sáu đã xem xét các chiến lược để triển khai (deploy) các dịch vụ microservices. Trong bài viết này, hãy cùng tìm hiểu các chiến lược để chuyển đổi một ứng dụng nguyên khối thành microservices.

Trước khi bắt đầu, nếu các bạn chưa đọc các phần trước thì có thể đọc lại ở đây: https://edwardthienhoang.wordpress.com/microservices-tu-thiet-ke-den-trien-khai-phan-mo-dau/

(Các bạn cũng có thể đọc bài viết gốc tiếng Anh tại đây: https://www.nginx.com/blog/refactoring-a-monolith-into-microservices/)

Có khả năng là bạn đang làm việc trên một ứng dụng nguyên khối phức tạp kèm với việc phát triển và triển khai ứng dụng chậm và khó khăn. Việc chuyển đổi thành Microservice dường như là bất khả thi. Dù vậy, trong phần này, chúng ta hãy xem xét những chiến lược mà bạn có thể sử dụng để thoát khỏi địa ngục nguyên khối, cách làm thế nào để từng bước tái cấu trúc một ứng dụng nguyên khối thành một tập hợp các microservices.

Tổng quan về việc tái cấu trúc thành Microservices

Quá trình chuyển đổi một ứng dụng nguyên khối thành microservices là một hình thức hiện đại hóa ứng dụng. Đó là điều mà các developer đã làm trong nhiều thập kỷ. Kết quả là, có một số ý tưởng mà chúng ta có thể học hỏi khi tái cấu trúc một ứng dụng thành microservices.

Điều đầu tiên và chắc chắn là các bạn không nên viết lại toàn bộ ứng dụng theo lối “Big Bang”. Nghĩa là khi bạn tập trung tất cả các nỗ lực vào việc xây dựng một ứng dụng dựa trên microservices mới từ đầu. Mặc dù có vẻ hấp dẫn nhưng nó rất nguy hiểm và có khả năng sẽ kết thúc trong thất bại. Như Martin Fowler đã nói, “khi bạn làm theo kiểu Big Bang thì khả năng bạn nhận được là một Vụ nổ Big Bang!”

Thay vì viết lại toàn bộ ứng dụng từ đầu, bạn nên từng bước tái cấu trúc ứng dụng nguyên khối của mình. Dần xây dựng các ứng dụng mới theo hướng microservice, và chạy nó cùng với ứng dụng nguyên khối. Theo thời gian, số lượng chức năng được thực hiện bởi ứng dụng nguyên khối co lại cho đến khi nó biến mất hoàn toàn hoặc trở thành một microservice khác.

Một số chiến lược trong việc tái cấu trúc

Chiến lược 1 – Đừng làm nó phình to ra nữa

Định luật Holes nói rằng bất cứ khi nào bạn đang ở trong một cái hố thì bạn nên ngừng đào nó tiếp. Đây là lời khuyên tuyệt vời khi ứng dụng nguyên khối của bạn trở nên không thể quản lý được. Nói cách khác, bạn nên ngừng làm cho khối đá lớn hơn. Điều này có nghĩa là khi đang thực hiện chức năng mới, bạn không nên thêm nhiều mã nguồn vào khối. Thay vào đó hãy viết thành các microservice. Sơ đồ sau đây cho thấy kiến ​​trúc hệ thống sau khi áp dụng phương pháp này.

1

Song kèm với ứng dụng nguyên khối hiện tại là các microservice mới được viết mới, cùng với đó là 2 thành phần khác.

Đầu tiên là request router, xử lý các request (HTTP) tới. Nó tương tự như API Gateway được mô tả trong bài viết số 2 trước đó. Router gửi các yêu cầu tương ứng với chức năng mới đến service mới. Nó định tuyến các legacy request tới ứng dụng nguyên khối.

Các thành phần khác là Glue code dùng để tích hợp service mới với ứng dụng nguyên khối. Một dịch vụ hiếm khi tồn tại trong sự cô lập và thường cần truy cập dữ liệu thuộc sở hữu của ứng dụng nguyên khối. Glue code nằm trong ứng dụng khối hoặc trong service mới hoặc cả hai, chịu trách nhiệm tích hợp dữ liệu. Service sử dụng Glue code để đọc và ghi dữ liệu thuộc sở hữu của ứng dụng khối.

Có ba chiến lược mà một service mới có thể sử dụng để truy cập dữ liệu của ứng dụng nguyên khối:

  1. Gọi một remote API được cung cấp bởi ứng dụng nguyên khối
  2. Truy cập trực tiếp cơ sở dữ liệu của ứng dụng nguyên khối
  3. Duy trì bản sao dữ liệu của riêng nó, được đồng bộ hóa với cơ sở dữ liệu của ứng dụng nguyên khối

Glue code đôi khi được gọi là lớp anti-corruption layer. Glue code ngăn cản các service mới phải quan tâm trực tiếp đến các khái niệm domain model trong ứng dụng nguyên khối cũ. Glue code sẽ làm nhiệm vụ chuyển đổi giữa các domain model khác nhau. Thuật ngữ anti-corruption layer đầu tiên xuất hiện trong cuốn sách nổi tiếng Domain Driven Design của Eric Evans. Phát triển một anti-corruption layer có thể là một công việc không dễ dàng. Nhưng nó là điều cần thiết nếu muốn phát triển theo hướng dần thoái khỏi khỏi địa ngục nguyên khối.

Thực hiện chức năng mới như một lightweight service có một vài lợi ích. Nó ngăn cản khối trở nên không thể quản lý được nữa. Dịch vụ có thể được phát triển, triển khai và thu nhỏ độc lập với khối. Bạn trải nghiệm những lợi ích của kiến ​​trúc microservices cho mỗi dịch vụ mới mà bạn tạo ra.

Tuy nhiên, cách tiếp cận này chưa bay vào trực tiếp mổ xẻ khối thành những phần nhỏ hơn. Để khắc phục những vấn đề đó bạn cần phải phá vỡ khối đá. Hãy xem xét các chiến lược để làm điều đó.

Chiến lược 2 – Phân tách Frontend và Backend

Một chiến lược thu nhỏ ứng dụng nguyên khối là tách presentation layer khỏi business logic và data access layers. Một ứng dụng doanh nghiệp điển hình bao gồm ít nhất ba loại component khác nhau:

  • Presentation layer – Các thành phần xử lý các yêu cầu HTTP và triển khai API (REST) ​​hoặc giao diện người dùng web dựa trên HTML. Trong một ứng dụng có giao diện người dùng, Presentation layer thường cũng có một phần lớn mã nguồn logic trong đó.
  • Business logic layer – Các core component của ứng dụng thực hiện các business rules.
  • Data-access layer – Các thành phần truy cập các thành phần cơ sở hạ tầng như cơ sở dữ liệu và message brokers.

Thường có sự tách biệt rõ ràng giữa presentation logic một bên và business và data-access logic một bên. Business tier có API tổng hợp bao gồm một hoặc nhiều interface dạng facades đóng gói các thành phần business logic. API này là một đường biên tự nhiên mà bạn có thể chia khối nguyên khối thành hai ứng dụng nhỏ hơn. Một ứng dụng có chứa tầng trình bày (presentation layer). Ứng dụng kia có chứa logic nghiệp vụ (business logic) và truy cập dữ liệu (data access). Sau khi chia tách, presentation layer sẽ thực hiện các cuộc gọi remote đến ứng dụng logic nghiệp vụ. Sơ đồ sau đây cho thấy kiến ​​trúc trước và sau khi tái cấu trúc.

2

Tách một khối bằng cách này có hai lợi ích chính. Nó cho phép bạn phát triển, triển khai và mở rộng hai ứng dụng độc lập với nhau. Đặc biệt, nó cho phép các developer phát triển presentation layer nhanh chóng trên giao diện người dùng và dễ dàng thực hiện A/B testing. Một lợi ích khác của phương pháp này là nó cung cấp các remote API có thể được gọi bởi các microservice mà bạn đang phát triển.

Tuy nhiên, chiến lược này chỉ là một phần của giải pháp. Bạn cần phải sử dụng chiến lược thứ ba để hoàn toàn chia nhỏ khối thành các service.

Chiến lược 3 – Chia nhỏ khối thành các service

Chiến lược tái cấu trúc thứ ba là chuyển các mô-đun hiện có trong khối đơn thành các microservice độc lập. Mỗi khi bạn trích xuất một mô-đun và biến nó thành một dịch vụ, khối sẽ co lại. Bạn sẽ tiến hành chuyển đổi đủ mô-đun cho đến khi nó biến mất hoàn toàn hoặc nó trở nên đủ nhỏ để trở thành một dịch vụ khác.

Ưu tiên các mô-đun cần chuyển đổi thành dịch vụ

Một ứng dụng nguyên khối phức tạp lớn bao gồm hàng chục hoặc hàng trăm mô-đun, tất cả đều là các ứng cử viên trong việc chuyển đổi. Tìm ra mô-đun để chuyển đổi đầu tiên thường là thử thách, nhưng rất quan trọng. Một cách tiếp cận tốt là bắt đầu với một vài mô-đun dễ dàng trích xuất. Điều này sẽ cung cấp cho bạn kinh nghiệm với microservices nói chung và quá trình chuyển đổi nói riêng. Sau đó, bạn nên trích xuất các mô-đun mang lại lợi ích lớn nhất.

Chuyển đổi một mô-đun thành một dịch vụ thường tốn thời gian. Bạn cần xếp hạng các mô-đun theo lợi ích sẽ nhận được. Sẽ tốt hơn nếu trích xuất các mô-đun thay đổi thường xuyên trước. Một khi bạn đã chuyển đổi một mô-đun thành một dịch vụ, bạn có thể phát triển và triển khai nó một cách độc lập với nguyên khối, và tất nhiên, từ đây có thể đẩy nhanh tốc độ phát triển của các mô-đun đã được trích xuất thành công.

Hoặc là trích xuất các mô-đun có yêu cầu tài nguyên khác biệt đáng kể so với các phần còn lại của khối. Ví dụ để biến một mô-đun có sử dụng in-memory database thành một dịch vụ, và sau đó có thể được triển khai trên máy chủ có nhiều memory. Tương tự như vậy, trích xuất các mô-đun thực hiện các thuật toán tính toán tốn kém, sau đó dịch vụ này có thể được triển khai trên các máy chủ có nhiều CPU. Bằng cách chuyển các mô-đun với các yêu cầu tài nguyên cụ thể thành các dịch vụ, bạn có thể làm cho ứng dụng dễ dàng hơn trong việc mở rộng quy mô.

Khi tìm ra các mô-đun để trích xuất, hãy tìm ra các coarse-grained boundaries (hay còn gọi là seams – các đường nối). Chúng làm cho việc chuyển các mô-đun thành dịch vụ trở nên dễ dàng và ít chi phí hơn. Một ví dụ về ranh giới như vậy là một mô-đun chỉ giao tiếp với phần còn lại của ứng dụng thông qua các synchronous messages. Nó có thể tương đối tốn ít chi phí và dễ dàng biến mô-đun đó thành một dịch vụ nhỏ.

Cách trích xuất một mô-đun

Bước đầu tiên để trích xuất một mô-đun là xác định coarse-grained interface giữa mô-đun và khối. Nó hầu như là một API hai chiều, vì khối sẽ cần dữ liệu thuộc sở hữu của dịch vụ và ngược lại. Sẽ có lúc bạn cần phải thực hiện các thay đổi lớn về mã nguồn để phá vỡ các phụ thuộc này. Sơ đồ sau đây cho thấy việc tái cấu trúc.

Khi bạn triển khai coarse-grained interface, bạn sẽ biến mô-đun thành một dịch vụ độc lập. Để thực hiện điều đó, bạn phải viết mã để cho phép nguyên khối và dịch vụ giao tiếp thông qua một API sử dụng IPC. Sơ đồ sau đây cho thấy kiến ​​trúc trước, trong và sau khi tái cấu trúc.

3

Trong ví dụ này, Mô-đun Z là mô-đun ứng cử viên cần trích xuất. Các thành phần của nó được sử dụng bởi Module X và nó sử dụng Module Y. Bước tái cấu trúc đầu tiên là định nghĩa một cặp các coarse-grained APIs. Interface đầu tiên được sử dụng bởi Module X để gọi Module Z. Interface thứ hai được sử dụng bởi Module Z để gọi Module Y.

Bước tái cấu trúc thứ hai biến mô-đun thành một dịch vụ độc lập. Các interface trong và ngoài được thực hiện bằng cơ chế IPC. Có thể sẽ cần phải xây dựng dịch vụ bằng cách kết hợp Module Z với Microservice Chassis framework để xử lý các vấn đề như service discovery.

Khi đã trích xuất một mô-đun thành công thành các dịch vụ sẽ cho phép nó phát triển, triển khai và thu nhỏ độc lập với khối đơn và với bất kỳ dịch vụ nào khác. Thậm chí có thể viết lại dịch vụ từ đầu; trong trường hợp này, mã nguồn dùng để tích hợp dịch vụ với khối đơn trở thành anti-corruption layer giúp chuyển đổi giữa hai domain model. Theo thời gian, khối đá sẽ co lại và sẽ có một số lượng microservices sẽ ngày càng tăng lên.

Tóm lược

Quá trình di chuyển một ứng dụng hiện có sang microservices là một hình thức hiện đại hóa ứng dụng. Bạn không nên chuyển sang microservices bằng cách viết lại ứng dụng của bạn từ đầu. Thay vào đó, nên từng bước tái cấu trúc ứng dụng của mình thành một tập hợp các microservice. Có ba chiến lược có thể sử dụng: triển khai chức năng mới dưới dạng dịch vụ nhỏ; phân tách các thành phần trình bày từ các thành phần kinh doanh và truy cập dữ liệu; và chuyển đổi các mô-đun hiện có trong ứng dụng nguyên khối thành các dịch vụ. Theo thời gian, số lượng các microservice sẽ phát triển, kèm theo đó là khả năng phát triển ứng dụng trong nhóm phát triển sẽ nhanh, linh hoạt và dễ dàng hơn.

OK, như vậy là chúng ta đã trải qua 7 bài viết trong series về Microservices: Từ Thiết Kế Đến Triển Khai. Trong các phần sau, chúng ta sẽ bắt tay vào tìm hiểu cách viết ứng dụng dạng Microservices thực tế bằng Spring Boot.

Đây là loạt bài viết về Microservices mình đã đọc, đúc kết và sưu tầm được trong quá trình tìm hiểu về nó. Hi vọng nó cũng giúp ích được cho bạn trong quá trình design và deploy các ứng dụng microservices.

Tổng hợp và dịch by edwardthienhoang

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.