Phát triển phần mềm theo kiến trúc microservice

51589

Bài viết được sự cho phép của tác giả Lê Chí Dũng

Không có một định nghĩa chung nào cho mô hình microservice. Nhưng về cơ bản, microservice là cách thức phát triển phần mềm như một bộ các service có thể triển khai độc lập được [1].

  Fluent Design – Ngôn Ngữ Thiết Kế Mới Của Microsoft
  Cloud-Native Microservices Với TIBCO: Khám phá dịch vụ bằng cách sử dụng Consul

1. Mô hình kiến trúc Microservice

Phát triển phần mềm theo kiến trúc microservice

Sơ đồ trên là một thể hiện của mô hình microservice. Trên thực tế mô hình microservice được áp dụng vào các sản phẩm có rất nhiều biến thể, sẽ rất khó để có một mô hình phù hợp và chính xác cho từng bài toán.

Theo kiến trúc trên, một ứng dụng được chia thành một bộ các microservice, mỗi microservice thực chất là một service có thể được triển khai và chạy độc lập. Chúng tách biệt về mặt mã nguồn, về hoạt động và dữ liệu. Mỗi microservice có nơi chứa dữ liệu của riêng của nó và chỉ có nó có quyền truy cập vào vùng dữ liệu này. Do các microservice là độc lập, chúng không giao tiếp trực tiếp với nhau mà qua một thành phần trung gian được gọi là API gateway. Có thể thấy vai trò của API gateway rất quan trọng trong mô hình microservice. Nó là điểm đến và đi của mọi yêu cầu hay phản hồi.

2. Các đặc trưng của mô hình Microservice

1. Micro-service

Đặc trưng này được thể hiện ngay từ tên của kiến trúc. Nó là microservice chứ không phải là miniservice hay nanoservice. Trên thực tế không tồn tại mô hình kiến trúc cho miniservice hay nanoservice. Từ microservice được sử dụng để giúp người thiết kế có cách tiếp cận đúng đắn. Một ứng dụng lớn cần được chia nhỏ ra thành nhiều thành phần, các thành phần đó cần tách biệt về mặt dữ liệu (database) và phải đủ nhỏ cả về mặt kích cỡ và độ ảnh hưởng của nó trong hệ thống, khi thêm một microservice vào hệ thống cũng nên đảm bảo rằng nó đủ nhỏ để dễ dàng tháo gỡ, xóa bỏ khỏi hệ thống mà không ảnh hưởng nhiều tới các thành phần khác.

2. Tính độc lập

Các microservice hoạt động tách biệt nhau trong hệ thống, do vậy việc build một microservice cũng độc lập với việc build các microservice khác. Thông thường, để tiện cho việc phát triển và duy trì các microservice, người phát triển nên viết các built script khác nhau cho mỗi microservice.

Do tính tách biệt này mà mỗi microservice đều dễ dàng thay thế và mở rộng. Hơn thế nữa, nó còn giúp việc phát triển các microservice linh động hơn, các microservice có thể được phát triển bởi các team khác nhau, dùng các ngôn ngữ khác nhau và tiến độ phát triển dự án cũng nhanh hơn do không có sự phụ thuộc giữa các team, mỗi team có thể chủ động quản lý phần việc riêng của mình.

3. Tính chuyên biệt

Mỗi microservice là một dịch vụ chuyên biệt, có thể hoạt động độc lập, thông thường mỗi microservice đại diện cho một tính năng mà các công ty/ doanh nghiệp muốn cung cấp tới người dùng, do vậy người thiết kế hệ thống microservice cần hiểu rõ về hoạt động kinh doanh của công ty. Các đầu vào đầu ra và chức năng của mỗi microservice cần được định nghĩa rõ ràng.

4. Phòng chống lỗi

Kiến trúc microservice sinh ra là để dành cho các hệ thống từ lớn đến vô cùng lớn. Nó áp dụng phương pháp chia để trị, phương pháp này giúp việc áp dụng các công cụ, kỹ thuật cho việc giám sát,phòng chống lỗi phần mềm, lỗi hệ thống hiệu quả.

Khi một thành phần trong hệ thống bị lỗi, nó có thể được thay thế bằng các thành phần dự phòng một cách dễ dàng, trong quá trình thay thế thành phần bị lỗi, các thành phần khác vẫn hoạt động bình thường, do vậy hoạt động của toàn bộ hệ thống sẽ không hoặc ít bị gián đoạn.

3. Các ưu và nhược điểm của Microservice

Phát triển phần mềm theo kiến trúc microservice

 1. Kiến trúc ứng dụng nguyên khối (Monolithic application)

Các ứng dụng phần mềm được phát triển theo kiến trúc nguyên khối rất phổ biến.

Với kiến trúc nguyên khối toàn bộ ứng dụng là một khối lớn, trong khối lớn ấy có chia thành các mô đun nhỏ, mỗi mô đun thực hiện một nhiệm vụ riêng và các mô đun thường gọi nhau qua function call. Việc phát triển và triển khai ứng dụng với kiến trúc này khá đơn giản khi mà các IDE hỗ trợ rất tốt việc kiểm tra và chạy ứng dụng với chỉ một cú click chuột hay một phím tắt. Kiến trúc này cũng đặc biệt phù hợp với các công ty outsource, họ có thể tạo ra một mẫu ứng dụng(template) và khi nhận được dự án với khách hàng mới họ chỉ việc bê mẫu này ra và chỉnh sửa, thêm thắt một vài phần khác biệt mà khách hàng yêu cầu.

Kiến trúc nguyên khối có thể mở rộng bằng cách thêm một load balancer (thiết bị cân bằng tải) và triển khai thêm các khối ứng dụng nữa nếu cần.

Tuy nhiên khi ứng dụng trở lên ngày càng lớn thì mô hình kiến trúc này bộc lộ càng nhiều điểm yếu.

Trong ứng dụng nguyên khối, sự chặt chẽ là vừa là ưu điểm vừa là nhược điểm. Với một khối ứng dụng lớn, người ta phải định nghĩa ra các tiêu chuẩn và các mẫu thiết kế, các mẫu đầu vào, đầu ra để đảm bảo cho code có chất lượng cao và nhất quán. Sự chặt chẽ và nhất quán này đôi khi là rào cản cho một số lập trình viên khác, họ không quen hoặc họ thích dùng các chuẩn viết code khác hơn, những lập trình viên mới cũng phải mất một thời gian dài ban đầu để làm quen với điều này.

Khi ứng dụng nguyên khối ngày một lớn thì quá trình maintain (duy trì) và sửa lỗi ứng dụng cũng trở thành ác mộng vì thời gian build và khởi động lại ứng dụng rất lớn. Đôi khi chỉ sửa một dòng code mà phải khởi động lại cả một chương trình to mất tới cả nửa tiếng.

Khi một ứng dụng thành công, ngày càng nhiều người biết tới và sử dụng, việc mở rộng ứng dụng để đáp ứng được nhiều người dùng là điều cần làm.

Giả sử bạn xây dựng một trang mạng xã hội theo kiến trúc nguyên khối có các thành phần chính là upload ảnh để chia sẻ, gợi ý kết bạn, hiển thị quảng cáo và hộp tin nhắn. Lượng người dùng trên trang của bạn tăng lên rất nhiều lần nhưng lưu lượng chủ yếu lại đổ vào phần hộp tin nhắn. Lúc này để mở rộng năng lực của website bạn phải triển khai thêm toàn bộ khối ứng dụng trên các server khác bao gồm tất cả các thành phần: upload ảnh, gợi ý kết bạn, hiển thị quảng cáo và hộp tin nhắn. Rõ ràng việc này gây lãng phí tài nguyên vì các thành phần upload ảnh, gợi ý kết bạn, hiển thị quảng cáo cũng được triển khai và chiếm dụng tài nguyên dù rằng việc mở rộng chúng là điều không cần thiết. Hơn nữa việc nhiều thành phần khác nhau thực hiện các business logic khác nhau được triển khai trên cùng server có thể cũng gây ảnh hưởng và đụng độ lẫn nhau.

Như vậy khi ứng dụng lớn dần lên, đến một lúc nào đó, kiến trúc nguyên khối sẽ không còn thực sự phù hợp nữa. Sự gia đời của kiến trúc Microservice là để khắc phục điều này.

2. Kiến trúc Microservice

Ưu điểm Nhược điểm
– Cho phép lập trình viên linh động hơn trong việc lựa chọn ngôn ngữ, công cụ và nền tảng để phát triển và triển khai các microservice (tuy nhiên trong một hệ thống, việc lựa chọn các ngôn ngữ khác nhau để phát triển các microservice không được khuyến khích)
– Một microservice có thể được phát triển bởi một team nhỏ. Do vậy việc quản lý sẽ dễ dàng hơn.
– Dễ dàng thực hiện tự động tích hợp và tự động triển khai (CI-CD) bằng cách sử dụng một số công cụ như Jenkins, Hudson …
– Mỗi microservice có kích thước nhỏ, giúp cho các lập trình viên dễ tiếp cận, đọc hiểu source code. Do vậy các thành viên mới tham gia team sẽ hòa nhập và đóng góp cho team nhanh hơn.
– Các microservice khởi động nhanh giúp quá trình phát triển, kiểm thử cũng nhanh hơn.
– Dễ dàng mở rộng và tích hợp với các dịch vụ của bên thứ ba.
– Cô lập lỗi tốt hơn, khi một microservice bị lỗi và ngừng hoạt động thì các microservice khác vẫn có thể hoạt động bình thường. Với mô hình nguyên khối, một lỗi nhỏ có thể làm cả hệ thống ngừng hoạt động.
– Khi cần thay đổi một thành phần, thì chỉ cần sửa đổi, cập nhật và triển khai lại thành phần đó chứ không cần triển khai lại toàn bộ hệ thống.
– Nhược điểm của kiến trúc microservice đến từ bản chất của hệ thống phân tán.
– Việc triển khai hệ thống microservice phức tạp hơn nhiều so với việc triển khai hệ thống nguyên khối.
– Các lập trình viên phải tốn nhiều công sức hơn để thực hiện phần giao tiếp giữa các microservice, với kiến trúc nguyên khối có khi họ chỉ cần gọi hàm để thực hiện việc này.
– Các microservice thường (nên) được triển khai bên trong docker container và giao tiếp với nhau qua REST API. Việc này làm hiệu năng của toàn bộ chương trình ứng dụng giảm xuống đáng kể do giới hạn tốc độ truyền tải của các giao thức và tốc độ mạng. Hơn nữa việc giao tiếp giữa các microservice có thể bị lỗi khi các kết nối bị lỗi.
– Cần tính toán kích cỡ của một microservice. Nếu một microservice quá lớn, bản thân nó trở thành một ứng dụng theo kiến trúc nguyên khối. Nếu một microservice quá nhỏ thì độ phức tạp của hệ thống tăng lên rất nhiều, làm cho hệ thống trở lên khó hiểu, lúc này việc quản lý giám sát và triển khai hệ thống sẽ khó khăn hơn.
– Khi ứng dụng ngày càng lớn lên, số lượng microservice ngày càng nhiều, các lập trình viên thường có xu hướng sử dụng sự hỗ trợ từ các công cụ mã nguồn mở, hoặc của bên thứ 3, việc sử dụng, tích hợp các công cụ này làm cho hệ thống khó kiểm soát và có thể bị dính các mã độc làm cho hệ thống kém an toàn.

4. Thiết kế phần mềm theo kiến trúc Microservice

Tham khảo: What Is Microservices – Introduction To Microservice Architecture

1. Mỗi microservice nên có một database riêng biệt

Việc này đảm bảo cho microservice có tính đóng gói cao. Tuy vậy việc phân tách nơi chứa dữ liệu cho mỗi microservice không hề đơn giản, nó là phần khó nhất trong việc thiết kế ứng dụng phần mềm theo kiến trúc microservice. Do đó, rất nhiều người chọn lựa thiết kế sử dụng chung database cho nhiều microservice.

Phát triển phần mềm theo kiến trúc microservice

Cách này tồn tại một hạn chế là khi database schema cần thay đổi cho microservice1 thì nó sẽ làm ảnh hưởng và gây lỗi cho các microservice khác sử dụng chung database này. Để sửa lỗi, ta cần sửa đổi, cập nhật toàn bộ microservice còn lại để chúng có thể hoạt động với schema mới.

Có một cách tiếp cận khác, giúp tránh được hạn chế trên.

Phát triển phần mềm theo kiến trúc microservice

Sơ đồ trên, microservice2 không truy cập trực tiếp vào database, thay vào đó nó truy cập database thông qua microservice1. Do đó việc thay đổi schema của database sẽ không ảnh hưởng tới microservice2. Tuy nhiên với cách tiếp cận này thì microservice2 phụ thuộc vào microservice1, khi microservice1 có lỗi thì microservice2 cũng bị lỗi theo.

Việc chọn cách tiếp cận nào phụ thuộc rất nhiều vào tình hình thực tế của dự án. Cần cân nhắc thiệt hơn của mỗi phương án để đưa ra lựa chọn cuối cùng.

2. Giữ source code của microservice ở mức hợp lý

Như đã đề cập ở phần ưu và nhược điểm của microservice. Kích thước source code của một microservice không nên quá nhỏ hoặc quá lớn. Tuy nhiên cái khó ở đây là không có một con số định lượng cho kích thước của một microservice, nên thông thường việc quyết định kích thước của một microservice là do kinh nghiệm, cảm tính.

3. Tạo build script cho mỗi microservice

Build script có thể là một file bash hoặc Dockerfile để đóng gói microservice vào bên trong một docker image.

4. Triển khai mỗi microservice bên trong một app (docker container)

Việc triển khai mỗi microservice trong một docker container đem lại rất nhiều lợi ích cho việc triển khai và mở rộng ứng dụng cũng như việc phân chia tài nguyên phần cứng cho mỗi microservice. Hiện nay có rất nhiều công cụ hỗ trợ cho việc liên tục tích hợp, liên tục triển khai hệ thống microservice. Các công cụ này giúp tăng hiệu quả làm việc cho các lập trình viên, giảm thời gian phôi phối sản phẩm phần mềm, và các công cụ này đòi hỏi mỗi microservice được đóng gói trong một docker image và triển khai trên app.

5. Stateless server

Khi một yêu cầu được gửi đến server thì một phiên làm việc (session) được mở ra, kèm theo đó là các thông tin của phiên. Stateless server là server không lưu thông tin của phiên. Mà thông tin về phiên được lưu ở một nơi khác, như caching server chẳng hạn.

Việc này rất quan trọng, bởi vì mỗi microservice thường được đóng gói thành một docker image. Khi muốn cập nhật một microservice, ta cập nhật docker image của nó, và khi chạy docker image mới (xóa bỏ container cũ và tạo container mới dựa trên image mới) thì toàn bộ thông tin của các phiên hoạt động trên container cũ sẽ bị mất, thông tin phiên thường bao gồm thông tin mà client gửi tới server, mất thông tin này là một lỗi vô cùng nghiêm trọng. Nếu container là stateless thì nó không lưu thông tin của các phiên nên không có gì để mất cả.

Kết luận

Kiến trúc microservice không nhỏ như cái tên của nó. Mà nó là một kiến trúc dành cho các ứng dụng lớn. Nếu ứng dụng của bạn chưa đủ lớn và cũng không có ý định mở rộng trong tương lai thì hướng phát triển theo kiến trúc nguyên khối vẫn là một lựa chọn hợp lý.

Kiến trúc microservice giải quyết được rất nhiều vấn đề của kiến trúc nguyên khối, nó tỏ ra vượt trội kiến trúc nguyên khối ở nhiều khía cạnh. Tuy nhiên kiến trúc này cũng có không ít những hạn chế.

Nguồn tham Khảo

[1] https://smartbear.com/learn/api-design/what-are-microservices/

[2] https://www.edureka.co/blog/what-is-microservices/

[3] https://searchmicroservices.techtarget.com/definition/business-capability

[4] https://smartbear.com/learn/api-design/what-are-microservices/

[5] The What, Why, and How of a Microservices Architecture

[6] https://blog.newrelic.com/technology/microservices-what-they-are-why-to-use-them/

[7] https://whatis.techtarget.com/definition/business-logic

[8] https://www.quora.com/Should-each-microservice-have-its-own-database

[9] http://blog.christianposta.com/microservices/the-hardest-part-about-microservices-data/

[10] Best Practices for Building a Microservice Architecture

[11] https://blog.runscope.com/posts/5-reasons-not-to-use-microservices

Bài viết gốc được đăng tải tại lcdung.top

Có thể bạn quan tâm:

Xem thêm Việc làm Developer hấp dẫn trên TopDev