Microservices và Phản-khuôn-mẫu
Thiên Chúa tạo ra… ánh sáng và bóng đêm, thiên đường và địa ngục… Người tạo ra vạn vật theo cặp đối lập, tương xứng và cực kỳ cân bằng… Khoa học cũng khẳng định tương tự, rằng vụ nổ Big Bang tạo ra vạn vật trong vũ trụ theo những cặp đối lập. Bao gồm vật chất và phản vật chất (1)
― Dan Brown – Chương 19, Thiên thần và Ác quỷ.
Thuật ngữ Microservices trong vài năm gần đây nổi lên như một phương pháp thiết kế hệ thống mới. Nó dựa trên nguyên tắc Chia-để-trị bằng việc phân tách hệ thống lớn thành tập hợp các service độc lập, riêng lẻ nhỏ hơn dựa trên các Bounded Context (Khung ngữ cảnh/nghiệp vụ) khác nhau. Nhiều người cho rằng đây là là một biến thể tiếp theo của mô hình SOA. Tuy nhiên không hẳn như vậy, bản thân kiến trúc này cũng chứa đựng rất nhiều các khuôn mẫu và tư tưởng mới. Nhưng do thiếu một định nghĩa nhất quán và các ràng buộc trong việc implement, như một lẽ tự nhiên, những phản-khuôn-mẫu (anti-pattern) và cạm bẫy (pitfalls) luôn hiện hữu, khiến chúng ta dễ dàng sa vào chúng. Vì vậy bắt đầu từ những thứ cần tránh không phải là ý kiến tồi, và sau đây là một vài điều nên xem xét…
The path to paradise begins in hell (2) ― Dante Alighieri, Thần Khúc
- Áp dụng Microservices vào hệ thống một cách vội vàng
Có rất nhiều tổ chức trong đó có các tên tuổi như Amazon, Facebook, Twitter, eBay, Netflix… đã áp dụng Microservices. Điều đó rất dễ khiến chúng ta nghĩ rằng Microservices là một viên đạn bạc (silver bullet) có thể giải quyết tất cả những vấn đề trong hệ thống của mình. Nhưng không có bữa ăn trưa nào là miễn phí, bản thân kiến trúc này cũng đặt cho bạn vào những thử thách khiến bạn phải cân nhắc khi sử dụng. Việc mọi người đề cao quá mức Microservices cùng tương lai của nó sẽ rất dễ bạn rơi vào cám dỗ. Hãy tỉnh táo để suy xét nó có thực sự cần thiết với hệ thống của bạn hay không.
Ma Quỷ lại đem Ngài ³ lên trên núi rất cao, chỉ cho Ngài các nước trong thế gian, cùng sự vinh quang của chúng, và nói với Ngài:
– Ta sẽ ban cho ngươi hết thảy mọi sự này, nếu ngươi sấp mình xuống mà thờ phụng ta.
Đức Chúa Jesus liền phán với nó rằng:
– Hỡi Sa-tan, ngươi hãy lui ra! Vì có lời chép rằng: Ngươi phải thờ phượng Chúa là Đức Chúa Trời ngươi và chỉ phụng sự Ngài (4)– Trích Ba lần Đức Chúa Jesu chịu cám dỗ của Satan
Nhìn chung, Microservices phù hợp nhất với hệ thống lớn và phức tạp. Nếu bạn đang làm cho một startup, hoặc dự án mới bước vào giai đoạn khởi dựng, hãy nghĩ đến kiến trúc Monolithic (kiến trúc 1 khối) đầu tiên, cố gắng giữ chúng hoạt động dựa trên mô-đun định nghĩa bởi từng Bounded Context. Sau đó chia tách thành Microservices khi hệ thống đã đủ lớn để buộc phải thay đổi. Sự tồn vong của công ty hay dự án mới là điều ưu tiên của bạn. Nên nhớ rằng: “To be or not to be that is the question”(5) mới là cái bạn cần tìm lời giải trong thời điểm này.
- Không triển khai hoặc thiếu đi các thành phần DevOps(6)
Với các hệ thống lớn trong đó có Microservices, những thành phần DevOps là tối quan trọng và sống còn. Nếu thiếu đi kế hoạch triển khai các thành phần này, hệ thống Microservices có lẽ không thích hợp dành cho bạn. Trong Microservices, hàng chục (thậm chí hàng trăm, hàng ngàn) service giao tiếp qua lại nhau khiến việc quản lý theo phương pháp thông thường sẽ không còn thích hợp. Chỉ cần một node gặp vấn đề cũng có thể khiến cả hệ thống sụp đổ. Chúng ta chỉ có thể quản lý chúng tự động và tập trung bằng những công cụ như:
- Config Management: Consul, ConfigMap…
- Proxy & Load Balancing: Ribbon, Zuul…
- Circuit Breaker: Hytrix, Akka…
- Centralized Logging: LogStash, Kibana…
- Deployment: Bamboo, Jenkin, Docker, Chef…
- Testing: Hoverfly, Ambassador, Diffy…
Có thể nói, khi triển khai bạn sẽ cần dùng đến những OpenSource trên hoặc tương tự. Chúng sẽ giúp công việc của bạn nhẹ nhàng và ít lỗi hơn, hệ thống vận hành cũng trơn tru đúng như dự định.
- Đề cao quá mức tính sử dụng lại
Microservice được biết đến là kiến trúc “Không-chia-sẻ” (share nothing) giữa các thành phần và chúng phải tự quản lý data của riêng mình. Vậy nên nếu quá đề cao việc sử dụng lại code sẽ vi phạm mục đích “chia-để-trị” của Microservices và dần rà service của bạn sẽ trở nên rối rắm và khó maintain. Tuy nhiên, việc lặp lại 1 đoạn code ở quá nhiều chỗ khác nhau cũng không phải là ý tưởng hay. Giải pháp ở đây chúng ta nên thiết kế những thành phần dùng chung dưới dạng module và đóng gói lại (Jar file, .NET assembly, code dependencies… VD: string_util.jar, sercurity.jar) đồng thời đánh version cho chúng để tăng tính độc lập cho từng service.
- Phân chia version cho API khi thiếu chiến lược quản lý hiệu quả
Việc phân chia version cho API không xấu, thậm chí rất tốt cho việc đảm bảo tính đúng đắn cũng như linh hoạt của services trong nhiều trường hợp. Nhưng nếu không có chiến lược quản lý việc này 1 cách hiệu quả, service của bạn sẽ có khả năng hỗn loạn. Việc maintain trở nên khó khăn do code phải rẽ nhánh quá nhiều. Vì vậy, hãy chắc rằng bạn có kế hoạch source-control hiệu quả khi tiến hành chia API theo version. Khi đó cách này mới thực sự phát huy hiệu quả của nó.
- Không chuẩn bị cho việc xảy ra lỗi
Khi nỗi buồn tới, chúng không chỉ đi một mình mà còn là cả đạo quân (7)
– W.Shakespeare, Hamlet
Khi làm việc với Microservices, bạn cần mặc định rằng xảy ra lỗi là điều Đương-Nhiên và khi đó phải có phương án cho những trường hợp này. Bởi vì Microservices là một hệ thống phân tán, các services liên kết với nhau qua nhiều phương thức giao tiếp từ xa. Việc này phụ thuộc rất nhiều không chỉ phần mềm mà còn là phần cứng và hạ tầng mạng. Không có gì để đảm bảo những kết nối này luôn thông suốt, cũng như các phản hồi diễn ra đúng như tuần tự. Vì thế chúng ta cần có cơ chế tự động phát hiện lỗi và ứng phó một cách hợp lý (Circuit Breaker pattern, Services Discovery + Health Check Endpoint…). Việc lường trước biến cố giúp chúng ta có thể khoanh vùng nhằm giảm thiệt hại và tiêu tốn tài nguyên của hệ thống, trong Microservices đây được gọi là Bulkhead pattern (thiết kế dạng vách ngăn).
- Request đến từ Client và các Services gọi trực tiếp đến nhau
Đây có lẽ cũng là sai lầm mà khi mới apply Microservices người ta thường mắc phải. Việc này khiến các Client/Consumer Services phải nắm giữ quá nhiều thông tin của Producer Services khiến quá trình scale out (mở rộng) bị ảnh hưởng.
Để khác phục điều này, Services Discovery và 1 API Gateway đóng vai trò lớp Facade thường sẽ được sử dụng.
- Sử dụng REST như một cách giao tiếp giữa các services duy nhất
REST là phương thức rất phổ biến dựa trên http, nhưng REST không là tất cả. Trong Microservices còn có những cách khác để các services giao tiếp với nhau. Sự tương tác của REST dựa trên cơ chế Synchronous, không phù hợp với những cách giao tiếp Broadcast(như Notification, Publish/Subcribe) đơn giản chỉ cần bắn-quên (fire-and-forget) hoặc dạng Transaction Request. Vậy nên tuỳ từng kịch bản giao tiếp mà chúng ta cần chọn phương thức phù hợp.
- Các thành phần phình to thiếu kiểm soát hoặc chia nhỏ quá mức cần thiết.
Mọi thứ đều nên đơn giản nhất có thể. Nhưng không nên đơn giản hơn bản chất của nó. (8)
― Albert Einstein
Việc thiết kế phạm vi(scope) của 1 services có lẽ là một trong những khó khăn và thách thức nhất đối với người thiết kế theo mô hình Microservices. Chữ “Micro” khiến cho nhiều người dễ dàng lầm tưởng phải chia nhỏ module của mình đến mức nhỏ nhất có thể. Thậm chí mới đây tôi còn được nghe về “luật” mỗi service phải dưới 100 dòng code, tôi cho rằng cách hiểu này không phản ánh đúng bản chất việc phân chia scope. Ở chiều hướng khác, để tránh việc gọi tới các services khác nhau, người ta cố gắng đưa quá nhiều nghiệp vụ vào trong một service. Những cách xác định scope như vừa rồi (quá to hoặc quá nhỏ) khiến việc maintain khó khăn hơn bao giờ hết. Thêm vào đó là giảm hàng loạt các đặc tính của hệ thống như performance, robustness, reliability, change control, testability và deployment.
Vì vậy để xác định chính xác Scope của từng service, ta nên tập trung phân tách chúng thành từng Bounded Context thực hiện 1 nghiệp vụ đơn lẻ. Chúng có tính tự chủ cao, nghĩa là hoàn thành trọn vẹn nghiệp vụ của mình mà ít phụ thuộc với các service khác nhất có thể. Khái niệm “Low Coupling & High Cohesion” trong thiết kế OOP có thể mở rộng trong trường hợp này.
Kết luận:
Bài viết này không có tham vọng chỉ ra tất cả những khó khăn hay thiếu sót khi cài đặt Microservices. Bởi đối với bất cứ mục được kể trên đây đều là những chủ đề mà ta cần nhiều thời gian hơn nữa để đi sâu chi tiết về chúng. Thay vào đó, tôi chỉ muốn đưa ra cái nhìn tổng quan nhất về những hướng đi không phù hợp phổ biến cần tránh. Nếu bạn nghiêm túc về việc triển khai Microservices thì trên đây có thể là những gợi ý nhỏ để bạn có thể làm tốt hơn.
Bên trong tất cả chúng ta ai cũng có phần sáng và tối. Quan trọng là chúng ta chọn phần nào. Đó mới thực sự là con người chúng ta (9)
– J.K. Rowling, Harry Potter và mệnh lệnh Phượng Hoàng
Tham khảo thêm:
- https://www.nginx.com/blog/building-microservices-inter-process-communication/
- https://theswissbay.ch/pdf/_to_sort/O’Reilly/microservices-antipatterns-and-pitfalls.pdf
Chú thích:
- God created… light and dark, heaven and hell. He created everything in opposites. Symmetry. Perfect balance. Science claims the same thing as religion, that the Big Bang created everything in the universe with an opposite. Including matter itself, anti-matter
- Tạm dịch: “Con đường chạm tới thiên đường bắt đầu từ địa ngục”. Hình minh hoạ là cuộc chiến trên thiên đường của hoạ sĩ Gustave Doré.
- Chỉ Chúa Jesus.
- Ma-thi-ơ 4:8-10.
- Hamlet, W.Shakespeare.
- “DevOps (kết hợp của cụm từ tiếng Anh “software DEVelopment” và “information technology OPerationS”) là một thuật ngữ để chỉ một tập hợp các hành động trong đó nhấn mạnh sự hợp tác và trao đổi thông tin của các lập trình viên và chuyên viên tin học khi cùng làm việc để tự động hóa quá trình chuyển giao sản phẩm phần mềm và thay đổi kiến trúc hệ thống. Điều này nhằm thiết lập một nền văn hóa và môi trường nơi mà việc build (biên dịch phần mềm), kiểm tra, và phát hành phần mềm có thể xảy ra nhanh chóng, thường xuyên, và đáng tin cậy hơn.” – Wikipedia
- When sorrows come, they come not single spies, but in battalions.
- Everything should be made as simple as possible, but not simpler.
- “We’ve all got both light and dark inside us. What matters is the part we choose to action. That’s who we really are.”, Sirius Black.