Trang chủ Liên hệ

Microservices : Từ Thiết Kế Đến Triển Khai

CÔNG TY TNHH THIẾT BỊ ĐO LƯỜNG VÀ ĐIỀU KHIỂN 03/04/2023

Chuyển đổi kỹ thuật số là một quá trình không ngừng phát triển. Nếu bạn muốn doanh nghiệp của mình đi nhanh đến thay đổi kỹ thuật số, nó cần phải thích nghi và sẵn sàng cho những cải tiến trong tương lai. Khi bạn phải thực hành và biến nó thành các ứng dụng IoT, sự lựa chọn kiến ​​trúc trở nên rất quan trọng.

Để phát triển phần mềm, thách thức nằm ở việc kết nối một hệ sinh thái rộng lớn của các thiết bị và giao thức truyền thông thay đổi từ thiết bị này sang thiết bị khác sang nền tảng trung tâm, nơi dữ liệu được theo dõi và phân tích. Trong nhiều trường hợp, các kỹ sư phần mềm phải phát triển các bộ điều hợp tùy chỉnh để lấy dữ liệu và gửi nó lên đám mây. Cách tốt nhất để giải quyết thách thức này là chọn kiến ​​trúc về tính khả thi.

Những hạn chế của kiến trúc truyền thống đã thúc đẩy các công ty như Netflix và Spotify phổ biến việc sử dụng một phong cách phát triển phần mềm mới dựa trên microservice, đây là một cách để phá vỡ hạn chế khi phát triển các ứng dụng lớn.

Mỗi Microservice thực hiện một số các chức năng chuyên biệt như quản lý đơn hàng, quản lý khách hàng… Mỗi dịch vụ là một ứng dụng nhỏ có kiến trúc đa diện lõi là business logic kết nối ra các adapter khác nhau. Một số dịch vụ nhỏ lộ đưa ra API cho dịch vụ nhỏ khác hay client gọi tới. Khi vận hành, mỗi dịch vụ nhỏ được chạy trong một máy ảo hoặc Docker container.

Thay vì dùng các cuộc gọi chức năng trong bộ nhớ, mỗi dịch vụ chạy như một tiến trình riêng biệt. Một lợi ích khác là họ sử dụng các cơ chế nhẹ để giao tiếp với nhau. Nhỏ bé, tách rời và đặc thù nhiệm vụ, họ mở đường cho sự nhanh nhẹn và linh hoạt.

Microservices hiện đang nhận được rất nhiều sự chú ý: bài viết, blog, thảo luận trên phương tiện truyền thông xã hội và thuyết trình hội nghị. Họ đang nhanh chóng hướng tới đỉnh cao của kỳ vọng tăng cao trên chu kỳ Gartner Hype. Đồng thời, có những người hoài nghi trong cộng đồng phần mềm bác bỏ microservices như không có gì mới. Naysayers cho rằng ý tưởng chỉ là một hình thái mới của SOA. Tuy nhiên, mặc dù cả sự cường điệu và hoài nghi, Kiến trúc Microservices đều có những lợi ích đáng kể – đặc biệt khi nói đến việc cho phép phát triển nhanh và phân phối các ứng dụng doanh nghiệp phức tạp.

Bài đăng trên blog này là bài viết đầu tiên trong loạt bảy phần về thiết kế, xây dựng và triển khai microservices. Bạn sẽ tìm hiểu về cách tiếp cận và cách nó so sánh với mô hình Kiến trúc nguyên khối truyền thống hơn. Loạt bài này sẽ mô tả các yếu tố khác nhau của một kiến ​​trúc microservices. Bạn sẽ tìm hiểu về các lợi ích và hạn chế của mô hình Kiến trúc Microservices, cho dù nó có ý nghĩa với dự án của bạn hay không và cách áp dụng nó như thế nào.

Trước hết hãy xem xét lý do tại sao bạn nên xem xét sử dụng microservices.

Xây dựng ứng dụng nguyên khối

Hãy tưởng tượng rằng bạn đang bắt đầu xây dựng một ứng dụng taxi mới toanh có thương hiệu để cạnh tranh với Uber và Hailo. Sau một số cuộc họp sơ bộ và các yêu cầu thu thập, bạn sẽ tạo một dự án mới từ đầu hoặc bằng cách sử dụng Rails, Spring Boot, Play hoặc Maven. Ứng dụng mới này sẽ có kiến trúc lục giác (hexagonal architecture) kiểu mô-đun, như trong diagram sau:

Core ứng dụng là business logic, được thực hiện bởi các mô-đun có các services, domain objects, and events. Xung quanh lõi là các adapters giao tiếp với thế giới bên ngoài. Ví dụ về adapters bao gồm các database access components, messaging components produce và consume các messages, và các web components cung cấp các API hoặc hiển thị giao diện người dùng.

Mặc dù có một kiến ​​trúc mô-đun hợp lý, ứng dụng được đóng gói và triển khai như một khối nguyên khối. Định dạng thực tế phụ thuộc vào ngôn ngữ và khuôn khổ của ứng dụng. Ví dụ, nhiều ứng dụng Java được đóng gói như các tệp tin WAR và được triển khai trên các máy chủ ứng dụng như Tomcat hoặc Jetty. Các ứng dụng Java khác được đóng gói như các executable JARs.

Các ứng dụng được viết theo phong cách này là cực kỳ phổ biến. Chúng đơn giản để phát triển vì IDE của chúng ta và các công cụ khác tập trung vào việc xây dựng một ứng dụng đơn lẻ. Các loại ứng dụng này cũng đơn giản để test. Bạn có thể thực hiện end-to-end testing bằng cách đơn giản khởi chạy ứng dụng và kiểm tra giao diện người dùng bằng Selenium. Các ứng dụng nguyên khối cũng đơn giản để triển khai. Bạn chỉ cần copy ứng dụng đã được build, đóng gói vào máy chủ. Bạn cũng có thể scale ứng dụng bằng cách chạy nhiều bản sao với load balancer. Trong giai đoạn đầu của dự án, nó hoạt động tốt.

Địa ngục Monolithic

Thật không may, cách tiếp cận đơn giản này có một giới hạn rất lớn. Các ứng dụng thành công có thói quen phát triển theo thời gian và cuối cùng trở nên rất lớn. Trong mỗi lần chạy nước rút, nhóm phát triển của bạn thực hiện thêm một vài user stories, điều đó, tất nhiên, có nghĩa là thêm nhiều dòng code. Sau một vài năm, ứng dụng nhỏ, đơn giản của bạn sẽ phát triển thành một khối đá khổng lồ. Để đưa ra một ví dụ cực đoan, gần đây tôi đã nói chuyện với một developer đang viết một công cụ để phân tích sự phụ thuộc giữa hàng nghìn gói JAR trong ứng dụng có hàng triệu dòng code (LOC) của họ.

Một khi ứng dụng của bạn đã trở thành một khối lớn, phức tạp, công việc phát triển phần mềm của bạn có lẽ là trong một thế giới của đau khổ. Bất kỳ nỗ lực phát triển nhanh và giao hàng sẽ không còn dễ dàng. Một vấn đề lớn là ứng dụng quá phức tạp. Nó chỉ đơn giản là quá lớn đối với bất kỳ developer nào để hiểu đầy đủ. Kết quả là, sửa lỗi và triển khai các tính năng mới một cách chính xác sẽ trở nên khó khăn và tốn thời gian. Hơn nữa, điều này tạo ra một vòng xoáy cực độ. Nếu codebase khó hiểu thì các thay đổi sẽ không được thực hiện chính xác. Bạn sẽ kết thúc với một quả bóng bùn khổng lồ (big ball of mud), không thể hiểu nổi, không thể cứu vãn.

Kích thước của ứng dụng cũng sẽ làm chậm sự phát triển. Ứng dụng càng lớn thì thời gian khởi động càng dài. Ví dụ: trong một khảo sát gần đây, một số developer đã báo cáo thời gian khởi động là 12 phút. Tôi cũng đã nghe những giai thoại của các ứng dụng mất tới 40 phút để khởi động. Nếu các developer thường xuyên phải khởi động lại máy chủ ứng dụng, thì một phần lớn trong ngày của họ sẽ chỉ là ngồi chờ đợi xung quanh và năng suất của họ sẽ bị ảnh hưởng.

Một vấn đề khác với một ứng dụng nguyên khối phức tạp là sẽ gây trở ngại cho việc triển khai liên tục (continuous deployment). Ngày nay, với các ứng dụng SaaS, chúng thường được push những thay đổi vào production nhiều lần trong ngày. Điều này cực kỳ khó thực hiện với một khối nguyên khối phức tạp vì bạn phải triển khai lại toàn bộ ứng dụng để cập nhật bất kỳ phần nào của nó. Thời gian khởi động kéo dài mà tôi đã đề cập trước đó cũng không giúp được gì. Ngoài ra, vì tác động của thay đổi thường không được hiểu rõ, có khả năng bạn phải thực hiện manual testing trên diện rộng (regression test). Do đó, việc triển khai liên tục là không thể thực hiện được.

Các ứng dụng nguyên khối cũng có thể khó mở rộng khi các mô-đun khác nhau có các yêu cầu xung đột về tài nguyên. Ví dụ, một mô-đun có thể triển khai logic xử lý hình ảnh chuyên sâu của CPU và lý tưởng nhất sẽ được triển khai trong các instance của Amazon EC2 Compute Optimized. Một mô-đun khác có thể là một cơ sở dữ liệu trong bộ nhớ và phù hợp nhất với các instance EC2 Memory-optimized. Tuy nhiên, vì các mô-đun này được triển khai cùng nhau, bạn phải thỏa hiệp về lựa chọn phần cứng.

Một vấn đề khác với các ứng dụng nguyên khối là độ tin cậy. Bởi vì tất cả các mô-đun đang chạy trong cùng một process, một lỗi trong bất kỳ mô-đun nào, chẳng hạn như rò rỉ bộ nhớ, có khả năng có thể làm down toàn bộ process. Hơn nữa, vì tất cả các trường hợp của ứng dụng đều giống hệt nhau, lỗi đó sẽ ảnh hưởng đến tính khả dụng của toàn bộ ứng dụng.

Cuối cùng nhưng không kém phần quan trọng, các ứng dụng nguyên khối làm cho việc áp dụng các frameworks and languages mới trở nên vô cùng khó khăn. Ví dụ, hãy tưởng tượng rằng bạn có 2 triệu dòng mã được viết bằng cách sử dụng framework XYZ. Nó sẽ cực kỳ tốn kém (cả về thời gian và chi phí) để viết lại toàn bộ ứng dụng để sử dụng framework ABC mới hơn, ngay cả khi framework đó tốt hơn đáng kể. Kết quả là, có một rào cản lớn đối với việc áp dụng các công nghệ mới. Bạn đang mắc kẹt với bất kỳ lựa chọn công nghệ nào bạn đã thực hiện khi bắt đầu dự án.

Tóm lại: bạn có một ứng dụng đã phát triển thành một khối đá khổng lồ mà rất ít, nếu có, các developer hiểu được. Nó được viết bằng cách sử dụng công nghệ lạc hậu, không hiệu quả làm cho việc thuê developer tài năng trở nên khó khăn. Ứng dụng này khó mở rộng và không đáng tin cậy. Kết quả là việc phát triển nhanh và phân phối các ứng dụng là không thể.

Vậy bạn có thể làm gì với nó?

Microservices – Giải quyết sự phức tạp

Nhiều tổ chức, như Amazon, eBay và Netflix , đã giải quyết vấn đề này bằng cách áp dụng những gì bây giờ được gọi là Microservices Architecture pattern. Thay vì xây dựng một ứng dụng đơn khối, ý tưởng là chia ứng dụng của bạn thành một tập hợp các services kết nối nhỏ hơn.

Một service thường thực hiện một tập hợp các tính năng hoặc chức năng riêng biệt, chẳng hạn như quản lý đơn hàng, quản lý khách hàng, v.v … Mỗi microservice là một ứng dụng nhỏ có cấu trúc lục giác (hexagonal architecture) riêng bao gồm logic nghiệp vụ cùng với các adapters khác nhau. Một số microservice sẽ cung cấp API được sử dụng bởi các microservices khác hoặc bởi các ứng dụng của khách hàng. Một số Microservices khác có thể triển khai giao diện người dùng web. Khi chạy, mỗi instance thường là một cloud VM or a Docker container.

Ví dụ, hệ thống được decomposite thành các microservices được mô tả trước đó được thể hiện trong diagram sau:

Mỗi khu vực chức năng của ứng dụng hiện được thực hiện bởi microservice riêng của nó. Hơn nữa, các ứng dụng web được chia thành một tập hợp các ứng dụng web đơn giản hơn (ví dụ như một cho hành khách và một cho các trình điều khiển trong ví dụ taxi của chúng ta). Điều này giúp dễ dàng triển khai trải nghiệm khác biệt cho người dùng cụ thể, thiết bị hoặc case studychuyên biệt.

Mỗi dịch vụ phụ trợ cho thấy các REST API và hầu hết các dịch vụ đều consume các API do các dịch vụ khác cung cấp. Ví dụ: Driver Management sử dụng Notification server để cho tài xế đang rảnh biết về chuyến đi tiềm năng. Các dịch vụ UI gọi các dịch vụ khác để hiển thị các site . Các dịch vụ cũng có thể sử dụng giao tiếp không đồng bộ, dựa trên thông điệp (message-based communication). Giao tiếp giữa các dịch vụ sẽ được trình bày chi tiết hơn trong phần sau của loạt bài này.

Một số API REST cũng được tiếp xúc với các ứng dụng dành cho thiết bị di động dành cho tài xế và hành khách. Tuy nhiên, các ứng dụng không có quyền truy cập trực tiếp vào các dịch vụ phụ trợ. Thay vào đó, giao tiếp được trung gian bởi một thành phần trung gian được gọi là API Gateway. API Gateway chịu trách nhiệm về các nhiệm vụ như cân bằng tải, bộ nhớ đệm, kiểm soát truy cập, đo lường API và monitoring. Các bài viết sau trong loạt bài này sẽ trình bày về API Gateway.

Mẫu Kiến trúc Microservices tương ứng với tỷ lệ trục Y của Scale Cube, là mô hình 3D có khả năng mở rộng từ cuốn sách tuyệt vời có tên The Art of Scalability. Hai trục tỷ lệ khác là chia tỷ lệ trục X, bao gồm chạy nhiều bản sao giống hệt nhau của ứng dụng phía sau bộ cân bằng tải và chia tỷ lệ trục Z (hoặc phân vùng dữ liệu), trong đó thuộc tính của yêu cầu (ví dụ, khóa chính của một hàng hoặc danh tính của một khách hàng) được sử dụng để định tuyến yêu cầu tới một máy chủ cụ thể.

Các ứng dụng thường sử dụng ba loại chia tỷ lệ với nhau. Trục Y chia tỷ lệ decomposes các ứng dụng thành microservices như được hiển thị ở trên trong hình đầu tiên trong phần này. Khi chạy, quy mô trục X sẽ chạy nhiều instances của từng dịch vụ phía sau bộ cân bằng tải cho thông lượng và tính khả dụng. Một số ứng dụng cũng có thể sử dụng chia tỷ lệ trục Z để phân vùng dịch vụ. Diagram sau đây cho thấy Trip Management service có thể được triển khai với Docker chạy trên Amazon EC2 như thế nào.

Khi chạy, Trip Management service bao gồm nhiều service instances. Mỗi service instance là một Docker container. Để có tính sẵn sàng cao, các containers đang chạy trên nhiều Cloud VMs. Ở phía trước của các service instances là một bộ cân bằng tải phân phối các yêu cầu trên các cá thể. Trình cân bằng tải cũng có thể xử lý các mối quan tâm khác như caching, access control, API metering, and monitoring.

Mô hình Microservices tác động đáng kể đến mối quan hệ giữa ứng dụng và cơ sở dữ liệu. Thay vì chia sẻ một lược đồ cơ sở dữ liệu duy nhất với các dịch vụ khác, mỗi dịch vụ có lược đồ cơ sở dữ liệu riêng của nó. Một mặt, cách tiếp cận này là mâu thuẫn với ý tưởng của một mô hình dữ liệu toàn doanh nghiệp. Ngoài ra, nó thường dẫn đến trùng lặp một số dữ liệu. Tuy nhiên, có một lược đồ cơ sở dữ liệu cho mỗi dịch vụ là điều cần thiết nếu bạn muốn hưởng lợi từ microservices, bởi vì nó đảm bảo các khớp nối lỏng lẻo (loose coupling). Diagram sau đây cho thấy database architecture cho ứng dụng ví dụ.

Mỗi dịch vụ đều có cơ sở dữ liệu riêng. Hơn nữa, một dịch vụ có thể sử dụng một loại cơ sở dữ liệu phù hợp nhất với nhu cầu của nó, cái gọi là kiến ​​trúc bền bỉ đa điểm (polyglot persistence architecture). Ví dụ: Driver Management cần tìm tài xết gần với hành khách tiềm năng, phải sử dụng cơ sở dữ liệu hỗ trợ truy vấn địa lý hiệu quả.

Nhìn bề ngoài, mô hình Microservices tương tự như SOA. Với cả hai cách tiếp cận, kiến ​​trúc bao gồm một tập hợp các dịch vụ. Tuy nhiên, có thể coi Microservices như là SOA nhưng không có sự thương mại hóa và dấu hiệu nhận biết về các đặc tả dịch vụ web (WS-*) và Service Service Bus (ESB). Các ứng dụng dựa trên microservice có các giao thức đơn giản, nhẹ hơn như REST, chứ không phải WS-*. Microservices cũng rất tránh sử dụng ESB và thay vào đó thực hiện chức năng giống như ESB trong bản thân microservices. Mô hình Microservices cũng loại bỏ các phần khác của SOA, chẳng hạn như khái niệm lược đồ chuẩn (canonical schema).

Quản lý dữ liệu phi tập trung

Trong kiến trúc dạng khối, ứng dụng lưu trữ dữ liệu trong một cơ sở dữ liệu duy nhất và tập trung để thực hiện các chức năng khác nhau/khả năng của ứng dụng.

ứng dụng Monolithic sử dụng một cơ sở dữ liệu tập trung để thực hiện tất cả các tính năng của nó.

Trong kiến trúc vi dịch vụ các chức năng được phân tán trên nhiều vi dịch vụ và nếu chúng ta sử dụng cơ sở dữ liệu tập trung tương tự, sau đó các vi dịch vụ sẽ không còn độc lập với nhau (ví dụ, nếu giản đồ cơ sở dữ liệu đã thay đổi từ một microservice nhất định, sẽ phá vỡ một số dịch vụ khác). Do đó mỗi microservice phải có cơ sở dữ liệu riêng của mình.

Hình 8: Microservices có cơ sở dữ liệu riêng và họ không thể truy cập trực tiếp vào cơ sở dữ liệu của các vi dịch vụ khác.

Dưới đây là các khía cạnh chính của việc triển khai de-tập trung quản lý dữ liệu trong kiến trúc vi dịch vụ.

Việc quản lý dữ liệu phi tập trung cung cấp cho bạn các vi dịch vụ hoàn toàn tách và tự do lựa chọn các kỹ thuật quản lý dữ liệu khác nhau (SQL hay nosql vv, Hệ thống quản lý cơ sở dữ liệu cho mỗi dịch vụ). Tuy nhiên, các trường hợp sử dụng giao dịch phức tạp liên quan đến nhiều vi dịch vụ, hành vi giao dịch đã được thực hiện bằng cách sử dụng các API cung cấp từ mỗi Service và logic nằm ở cấp khách hàng hoặc trung gian (GW).

Kiến trúc microservices theo hướng quản trị phân cấp. Khi nói ‘ quản trị ‘ có nghĩa là thiết lập và thực thi cách con người và các giải pháp làm việc cùng nhau để đạt được các mục tiêu tổ chức. Trong bối cảnh của SOA, quản trị SOA hướng dẫn sự phát triển của các dịch vụ tái sử dụng, thiết lập như thế nào dịch vụ sẽ được thiết kế và phát triển và làm thế nào các dịch vụ sẽ thay đổi theo thời gian. Nó thiết lập các thỏa thuận giữa các nhà cung cấp dịch vụ và người tiêu dùng của các dịch vụ đó, nói cho người tiêu dùng những gì họ có thể mong đợi và các nhà cung cấp những gì họ đang có nghĩa vụ cung cấp. Trong SOA quản trị có hai loại quản trị được sử dụng phổ biến:

Vì vậy, những gì hiện quản trị trong bối cảnh Microservices, thực sự có nghĩa là? Trong kiến trúc vi dịch vụ, các vi dịch vụ được xây dựng như các dịch vụ hoàn toàn độc lập và tách ra với nhiều công nghệ và nền tảng khác nhau.

Vì vậy, không cần phải xác định một tiêu chuẩn chung cho các dịch vụ thiết kế và phát triển. Vì vậy, chúng ta có thể tóm tắt các khả năng quản trị phân cấp của Microservices như sau.

Dịch vụ đăng ký và khám phá dịch vụ

Trong vi dịch vụ kiến trúc số lượng vi dịch vụ mà bạn cần phải quản lý với là khá cao. Và cũng có vị trí của họ thay đổi tự động do sự phát triển nhanh chóng và linh hoạt/triển khai bản chất của microservices. Do đó, bạn cần tìm vị trí của một microservice trong thời gian chạy. Giải pháp cho vấn đề này là sử dụng một dịch vụ đăng ký.

Dịch vụ đăng ký

Dịch vụ đăng ký giữ các phiên bản vi dịch vụ và vị trí của họ. Phiên bản microservice được đăng ký với các dịch vụ đăng ký trên khởi động và deregi. Người tiêu dùng có thể tìm thấy các vi dịch vụ có sẵn và vị trí của họ thông qua đăng ký dịch vụ.

Khám phá dịch vụ

Để tìm các vi dịch vụ có sẵn và vị trí của họ, chúng ta cần phải có một cơ chế khám phá dịch vụ. Có hai loại cơ chế khám phá dịch vụ, khám phá phía máy khách và khám phá phía máy chủ. Chúng ta hãy xem xét kỹ hơn các cơ chế khám phá dịch vụ.

Khám phá phía máy khách

Trong cách tiếp cận này, khách hàng hoặc API-GW có được vị trí của một phiên bản dịch vụ bằng cách truy vấn một đăng ký dịch vụ.

Hình 9 — khám phá phía máy khách

Ở đây khách hàng/API-GW đã thực hiện các dịch vụ khám phá logic bằng gọi các thành phần dịch vụ-đăng ký.

Khám phá phía máy chủ

Với cách tiếp cận này, khách hàng/API-GW gửi yêu cầu đến một thành phần (chẳng hạn như cân bằng tải) chạy trên một vị trí nổi tiếng. Thành phần đó gọi Dịch vụ đăng ký và xác định vị trí tuyệt đối của microservice.

Hình 10 — khám phá phía máy chủ

Triển khai

Khi nói đến kiến trúc vi dịch vụ, việc triển khai các vi dịch vụ đóng một vai trò quan trọng và có các yêu cầu then chốt sau đây.

Docker (một công cụ nguồn mở cho phép các nhà phát triển và quản trị viên hệ thống triển khai các vùng chứa ứng dụng tự đủ trong môi trường Linux) cung cấp một cách tuyệt vời để triển khai các vi dịch vụ giải quyết các yêu cầu trên. Các bước quan trọng tham gia như sau.

Kubernetes đang mở rộng khả năng của docker bằng cách cho phép quản lý một cụm chứa Linux như một hệ thống duy nhất, quản lý và chạy các vùng chứa docker trên nhiều máy chủ, cung cấp đồng vị trí của container, khám phá dịch vụ và kiểm soát nhân bản. Như bạn có thể thấy, hầu hết các tính năng này là rất cần thiết trong bối cảnh vi dịch vụ của chúng tôi. Do đó sử dụng kubernetes (trên đầu trang của docker) để triển khai vi dịch vụ đã trở thành một cách tiếp cận cực kỳ mạnh mẽ, đặc biệt cho các triển khai vi dịch vụ quy mô lớn.

Hình 11: xây dựng và triển khai các vi dịch vụ như container.

Trong hình 11, nó cho thấy một tổng quan về việc triển khai các vi dịch vụ của các ứng dụng bán lẻ. Mỗi phiên bản microservice được triển khai như một container và có hai container mỗi máy chủ. Bạn có thể thay đổi tùy ý không có vùng chứa mà bạn chạy trên một máy chủ nhất định.

Lợi ích của Microservices

Mô hình Kiến trúc Microservices có một số lợi ích quan trọng. Đầu tiên, nó giải quyết vấn đề phức tạp. Decompose một ứng dụng nguyên khối khổng lồ thành một tập hợp các dịch vụ. Mặc dù tổng số chức năng không thay đổi, ứng dụng đã được chia thành các phần hoặc dịch vụ có thể quản lý.

Mỗi dịch vụ có một ranh giới được xác định rõ ràng dưới dạng một API RPC hoặc message‑driven API. Mô hình Microservices thực thi một mức mô đun mà trong thực tế là cực kỳ khó khăn để đạt được với monolithic code base. Do đó, các dịch vụ riêng lẻ được sẽ phát triển nhanh hơn, dễ hiểu và dễ bảo trì hơn nhiều.

Thứ hai, kiến ​​trúc này cho phép mỗi service được phát triển độc lập bởi một nhóm tập trung vào service đó. Các developer được tự do lựa chọn bất kỳ công nghệ nào có ý nghĩa, miễn là dịch vụ này tôn trọng các API contract. Tất nhiên, hầu hết các tổ chức sẽ muốn tránh tình trạng hỗn loạn và giới hạn các tùy chọn công nghệ.

Tuy nhiên, quyền tự do này có nghĩa là các developer không còn bị bắt buộc phải sử dụng các công nghệ có thể đã lỗi thời tồn tại khi bắt đầu một dự án mới. Khi viết một dịch vụ mới, họ có tùy chọn sử dụng công nghệ hiện tại. Hơn nữa, vì các dịch vụ tương đối nhỏ nên nó trở nên khả thi để viết lại một dịch vụ cũ sử dụng công nghệ hiện tại.

Thứ ba, mô hình Microservices cho phép mỗi microservice được triển khai độc lập. Các developer không bao giờ cần phải phối hợp triển khai các thay đổi cục bộ với dịch vụ của họ. Những loại thay đổi này có thể được triển khai ngay khi chúng được kiểm tra. Ví dụ, nhóm UI có thể thực hiện A/B testing và lặp lại nhanh chóng trên các thay đổi giao diện người dùng. Mô hình Microservices giúp triển khai liên tục (continuous deployment) trở nên khả thi.

Cuối cùng, mô hình Microservices cho phép mỗi service được mở rộng (scale) một cách độc lập. Bạn có thể triển khai số lượng instance của từng dịch vụ đáp ứng nhu cầu và khả năng sẵn có của nó. Hơn nữa, bạn có thể sử dụng phần cứng phù hợp nhất với yêu cầu tài nguyên của một dịch vụ. Ví dụ, bạn có thể triển khai một dịch vụ xử lý ảnh chuyên sâu của CPU trên các instance EC2 Compute Optimized, và triển khai dịch vụ cơ sở dữ liệu bộ nhớ trong trên các instance EC2 Memory-optimized.

Những hạn chế của Microservices

Như Fred Brooks đã viết gần 30 năm trước, không có viên đạn bạc (there are no silver bullets). Giống như mọi công nghệ khác, kiến ​​trúc Microservices cũng có nhược điểm. Một nhược điểm là tên của chính nó. Thuật ngữ microservice đặt trọng tâm quá mức vào kích thước dịch vụ. Trên thực tế, có một số developer, những người ủng hộ việc xây dựng các dịch vụ chỉ có 10–100 LOC. Tuy nhiên, mục tiêu của microservices là decompose đầy đủ ứng dụng để tạo điều kiện phát triển và triển khai ứng dụng nhanh nhẹn.

Một nhược điểm lớn khác của microservices là sự phức tạp phát sinh từ thực tế là một ứng dụng microservices là một hệ thống phân tán. Các developer cần phải lựa chọn và thực hiện một cơ chế giao tiếp giữa các process dựa trên messaging hoặc RPC. Hơn nữa, họ cũng phải viết mã để xử lý việc thất bại giữa chừng (partial failure) vì điểm đến của request có thể chậm hoặc không khả dụng. Rõ ràng là nó phức tạp hơn nhiều so với trong một ứng dụng nguyên khối nơi các mô-đun gọi nhau thông qua các cuộc gọi phương thức / thủ tục mức ngôn ngữ.

Một thách thức khác với microservices là kiến ​​trúc cơ sở dữ liệu phân vùng (partitioned database architecture). Các business transactions cập nhật nhiều business entities là điều khá phổ biến. Các loại transaction này thường được thực hiện khá dễ dàng trong một ứng dụng nguyên khối vì chỉ có một cơ sở dữ liệu duy nhất. Tuy nhiên, trong một ứng dụng dựa trên microservices, bạn cần cập nhật nhiều cơ sở dữ liệu thuộc sở hữu của các dịch vụ khác nhau.

Sử dụng các distributed transactions thường không phải là một lựa chọn, và không chỉ vì định lý CAP. Đơn giản là sự toàn vẹn về cơ sở dữ liệu không được hỗ trợ bởi các dạng cơ sở dữ liệu như NoSQL (có khả năng mở rộng cao) và các messaging brokers. Chúng ta cuối cùng phải sử dụng một phương pháp tiếp cận dựa trên sự nhất quán cuối cùng (eventual consistency), điều này cũng gây không tí khó khăn cho các developer.

Test một ứng dụng microservices cũng phức tạp hơn nhiều. Ví dụ, với một framework hiện đại như Spring Boot, rất đơn giản để viết một lớp thử nghiệm khởi động một ứng dụng web nguyên khối và kiểm tra API REST của nó. Ngược lại, một lớp thử nghiệm tương tự cho một dịch vụ sẽ cần phải khởi chạy dịch vụ đó và bất kỳ dịch vụ nào mà nó phụ thuộc (hoặc ít nhất là cấu hình các stubs cho các dịch vụ đó). Một lần nữa là không đánh giá thấp sự phức tạp của việc này.

Một thách thức lớn khác với mô hình Microservices là thực hiện các thay đổi trải rộng trên nhiều dịch vụ. Ví dụ, hãy tưởng tượng rằng bạn đang thực hiện câu chuyện yêu cầu thay đổi đối với dịch vụ A, B và C, trong đó A phụ thuộc vào B và B phụ thuộc vào C. Trong ứng dụng nguyên khối, bạn có thể thay đổi mô-đun tương ứng, tích hợp các thay đổi, và triển khai chúng trong một lần. Ngược lại, trong Microservices, bạn cần phải lập kế hoạch và điều phối cẩn thận việc triển khai các thay đổi cho từng dịch vụ. Ví dụ, bạn sẽ cần cập nhật dịch vụ C, tiếp theo là dịch vụ B, và cuối cùng là dịch vụ A. May thay, hầu hết các thay đổi thường chỉ tác động đến một dịch vụ và các thay đổi dịch vụ đa yêu cầu phối hợp tương đối hiếm.

Triển khai một ứng dụng dựa trên microservices cũng phức tạp hơn nhiều. Một ứng dụng nguyên khối được triển khai đơn giản trên một tập hợp các máy chủ giống hệt nhau phía sau bộ cân bằng tải truyền thống. Mỗi cá thể ứng dụng được cấu hình với các vị trí (host and ports) của các dịch vụ cơ sở hạ tầng như cơ sở dữ liệu và message broker. Ngược lại, một ứng dụng microservice thường bao gồm một số lượng lớn các dịch vụ. Ví dụ, Hailo có 160 dịch vụ khác nhau và Netflix có hơn 600 dịch vụ. Mỗi dịch vụ sẽ có nhiều phiên bản running. Có nhiều bộ phận cần được cấu hình, triển khai, thu nhỏ và giám sát. Ngoài ra, bạn cũng sẽ cần triển khai một cơ chế khám phá dịch vụ (service discovery) (được thảo luận trong bài sau) cho phép một dịch vụ khám phá các vị trí (host and ports) của bất kỳ dịch vụ nào khác mà nó cần giao tiếp. Các cách tiếp cận thủ công truyền thống dựa trên ticket-based và thủ công không thể mở rộng đến mức độ phức tạp này. Do đó, việc triển khai thành công ứng dụng microservices yêu cầu các developer kiểm soát các phương thức triển khai và mức độ tự động hóa cao hơn.

Một cách tiếp cận để tự động hóa là sử dụng một PaaS như Cloud Foundry . PaaS cung cấp cho các developer một cách dễ dàng để triển khai và quản lý microservices của họ. Nó cách ly chúng khỏi những lo ngại như mua sắm và cấu hình tài nguyên IT . Đồng thời, các chuyên gia hệ thống và network cấu hình PaaS có thể đảm bảo tuân thủ các best practices và với các chính sách của công ty. Một cách khác để tự động hóa việc triển khai microservices là phát triển giải pháp PaaS cho riêng bạn. Một điểm khởi đầu điển hình là sử dụng một giải pháp phân cụm (clustering solution), chẳng hạn như Kubernetes, kết hợp với một công nghệ như Docker. Trong phần sau của loạt bài này, chúng ta sẽ xem xét cách phân phối ứng dụng dựa trên phần mềm phương pháp tiếp cận như NGINX, dễ dàng xử lý caching, access control, API metering, and monitoring ở cấp độ dịch vụ, có thể giúp giải quyết vấn đề này.

Kết luận

Xây dựng các ứng dụng phức tạp vốn đã khó khăn. Một kiến ​​trúc Monolithic chỉ có ý nghĩa đối với các ứng dụng đơn giản, nhẹ. Bạn sẽ kết thúc trong một thế giới đau đớn nếu bạn sử dụng nó cho các ứng dụng phức tạp. Mô hình kiến ​​trúc Microservices là sự lựa chọn tốt hơn cho các ứng dụng phức tạp, bất chấp những hạn chế và thách thức thực hiện.

Trong các bài đăng trên blog sau, tôi sẽ đi sâu vào chi tiết về các khía cạnh khác nhau của mô hình Kiến trúc Microservices và thảo luận các chủ đề như khám phá dịch vụ, tùy chọn triển khai dịch vụ và chiến lược để tái cấu trúc ứng dụng nguyên khối thành dịch vụ.

Bài viết liên quan