Xin chào! Chúng ta lại quay trở lại với loạt bài về trải nghiệm của mình trong việc xây dựng hệ thống microservices tại EGANY, trong số trước – Microsevices #2 – Triển khai microservices “tinh gọn” để bắt đầu dễ dàng hơn đã có đề cập tới một số yếu tố cần để xây dựng microservices "tinh gon", hôm nay mình sẽ chia sẻ với mọi người về vấn đề trong bài viết trên có đề cập đó là service communication. Bài viết này sẽ nói về quá trình mình bắt đầu triển khai, thay đổi khi tìm hiểu về cái dân tình xôn xao là gRPC (ở đây mình dùng gRPC danh cho nodejs) và những vấn đề phát sinh. Vậy những vấn đề này là gì? Giải quyết như thế nào? Hãy cùng tìm hiểu trong bài viết hôm này nhé.
Khởi đầu
Sự khởi đầu luôn là một thử thách, sau khi tìm hiểu một số tài liệu về vấn đề giao tiếp giữa các service như hình dưới đây
Rồi hình này
Tiếp nè
Trời mình là thằng sinh viên mới ra trường mà? Cái gì thế này? Mình nên lựa chọn cài gì đây? Mình nên theo "ông" nào đây? Thôi kiếm gì xem tiếp coi có gì nữa không.
Rồi! Có vẻ như dùng REST API được nhiều nơi đề cập tới, hơn nữa trong trường mình cũng được học và làm quen còn mấy cái kia nhìn nó mông lung quá, triển khai thì giờ chưa có kinh nghiệm, sản phẩm thì cần hoàn thành sớm nhất có thể, giờ mà lao đầu vào mấy cái cao siêu thì chắc mùa thu sang năm mới xong. Vì thế mình quyết định chọn REST API để triển khai trước, rồi sau này nâng cấp sau. Mình bắt đầu triển khai những services đầu tiên và mọi thứ khá ổn trên môi trường production.
Sự xuất hiện của gRPC
Trong thời gian này mình có tìm hiểu thêm về chủ đề này và rồi một công nghệ đang được chia sẻ và thảo luận đó là gRPC, tại thời điểm mình tìm hiểu thường những so sánh về tốc độ giữa gRPC với REST API (HTTP) sau khi xem xét và cân nhắc một số đặc điểm sau:
- Tốc độ gRPC là nhanh hơn
- Xuất phát từ công nghệ của Google
- Được nhiều người đánh giá tích cực
- Phù hợp với giao tiếp nội bộ giữa các service
- Và là dân công nghệ thì mình cũng muốn ứng dụng những công nghệ tốt cho những sản phẩm của công ty và học thêm cái mới.
Mình bắt đầu thử nghiệm và thực hiện demo với thư viện grpc dành cho nodejs, xong xui mình trình bày với sếp và anh sếp rất hoan nghênh, nhưng vẫn nhắc nhở về những vấn đề có thể gặp phải và khuyên là nên cân nhắc kĩ trước khi triển khai.
Dạ, để em xem xét nhưng mà cái này em cũng test rồi, nó chạy ngon như demo anh thấy đấy
À, lúc này mình cũng có làm việc với một bạn dev không cao nhưng mà thân thiện và vui vẻ ^^. Sau khi trình bày với sếp xong, mình có trao đổi thêm với bạn này, 2 người thống nhất và lao vào thay thế toàn bộ REST API thành gRPC (Chỗ này in đậm nè, cuối bài sẽ hiểu), dùng thư viện grpc dành cho nodejs.
Kết quả
Sau hơn 1 tuần triển khai thì mọi thứ đã xong, khá nhanh phải không một phần là vì mình có tự triển khai một package (npm) để hỗ trợ về việc khai báo các file proto
đó là @zerocore/grpc-helper. Nếu các bạn có tìm hiểu hoặc đã dùng thì đối với ứng dụng có nhiều model hay entity phức tạp thì việc viết file proto
khá là mất thời gian và "rường rà" mình nghĩ là vậy, do đó thư viện trên giúp tối giản hay đơn giản việc khai báo, cụ thể bạn tìm hiểu thêm nhé.
Giờ quay lại chủ đề chính, sau khi triển khai trên môi trường production, mọi thứ hoạt động khá ổn.
Gác chân lên bàn, ăn miếng bánh, uống miếng trà tận hưởng cuộc sống.
Vấn đề phát sinh
Bổng một ngày, một thành viên trong đội hỗ trợ khách hàng phản hồi rằng khách hàng không đăng nhập được.
Quầy, quầy chuyện gì đây?
Đời không như là mơ, sau một thời gian hoạt động, đâu đó khoảng gần 1 tháng thì một số vấn đề bắt đầu phát sinh, sau khi nhận được phản hồi và bắt đầu điều tra thì phát hiện hệ thống gặp lỗi 14 UNAVAILABLE: read ECONNRESET
do thư viện grpc ghi nhận.
Chuyện gì vậy? Có thể nó chỉ xảy ra cục bộ trong 1 services mà thôi.
Chưa dứt suy nghĩ, một đồng đội lên tiếng, anh ơi sao mấy cái service khác nó cũng báo lỗi ý vậy nè rồi 1,2,5,7… tất cả các service đều gặp lỗi tương tự. Trong tình thế cấp bách cần khôi phục hệ thống càng nhanh càng tốt, quyết định được đưa ra ngay lập tức bằng một phương pháp mà ai cũng biết là phương pháp gì với hiểu quả đạt 96,69%… Các bạn đoán đúng rồi đấy, đó là tắt máy khởi động lại, cũng may là hệ thống được triển khai với docker swarm do đó việc khởi động/restart khá đơn giản và nhanh chóng, càng dễ dàng hơn khi sử dụng portainer. Và kết quả đúng như mong đợi mọi thứ đã hoạt động trở lại bình thường.
Sau đấy mình vẫn tiếp tục theo dõi hệ thống, lỗi trên vẫn xảy ra, sau tìm hiểu thêm, đã đưa ra một số giả thiết cho nguyên nhân gây ra lỗi được liệt kê bên dưới, nhưng trước hết cần lướt qua một số thông tin khác về bối cảnh lúc này.
Tình trạng
- Lúc này hệ thống đang được triển khai trên Digital Ocean
- Do kinh phí hạn hẹp nên sử dụng tài nguyên khá tiết kiệm
- Digital Ocean lâu lâu cũng hay có thông báo về lỗi network
- Thư viện grpc danh cho nodejs cũng còn khá mới mặc dù công nghệ gRPC do Google giới thiệu cũng khá lâu rồi.
Giả thiết
- Vì thiếu tài nguyên, ở đây có thể là do thiếu RAM nên ảnh hưởng tới network dẫn tới lỗi trên.
- Cũng có thể do lỗi của nhà cung cấp về network.
- Lỗi thư viện.
- Lỗi code của developer.
Trong thời gian này mình cũng tự cải thiện tình hình bằng cách tự triển khai code xử lý retry. Rồi một ngày đẹp trời khi kiểm tra thư viện grpc trên thì nhận được thông tin như các bạn thấy ở hình phía dưới
Trong phiên bản mới có các tính năng đáng chú ý như các bạn có thể thấy ở hình dưới
Mình cũng đã chuyển đổi sang thư viện mới nhưng tình hình vẫn không được cải thiện. Một tháng sau mình quyết định quay trở lại phương thức giao tiếp cũ (REST API), cổ điển nhưng lại ổn định.
Quay lại với REST API
Vì trước kia, khi chuyển sang dùng thư viện grpc dành cho nodejs mình không có dự phòng việc sẽ quay lại REST API, vì vậy việc chuyển đổi này khá mất thời gian, một phần vì các tính năng cũ đang chạy, tính năng mới được phát triển sắp được ra mắt. Nhưng với sự nỗ lực thì cũng đã hoàn thành việc chuyển đổi. Với REST API hệ thống hoạt động ổn định cho tới thời điểm hiện tại.
Tuy nhiên với REST API mà dùng giao tiếp nội bộ thì việc định nghĩa các endpoint, rồi viết tài liệu, mỗi khi có thay đổi thì cập nhật ở các service liên quan khá là tốn thời gian và công sức, vì vậy mình luôn tìm kiếm thêm các cách thức khác tốt hơn. À, ở đây mình không đề cập tới thiết kế theo hướng sự kiện như pub/sub nha ^^, cái này có thể mình sẽ nói ở các bài tiếp theo. Rồi một ngày sếp gọi tên, ê tới chỉ cho cái này hay lắm nè.
Moleculer – Transporter
Moleculer Service, anh sếp mặc dù bận nhiều việc nhưng vẫn hay tìm được mấy thứ khá là mới và hay họ – nể thật sự, về framework trên ai có hứng thú thì click vào link ở đầu đoạn này để xem nhé. Rồi quay lại câu chuyện, tại thời điểm này moleculer vẫn chưa có bản phát hành chính thức và khi xem qua thì cũng chưa hỗ trợ tốt Typescript, mà Typescript là mục tiêu hướng tới tiếp theo của mình, nên lúc đấy cũng chưa có quyết định dùng.
Quay lại thời điểm hiện tại, cuối năm công việc cũng bận nhưng chủ yếu là hoàn thành những gì chưa làm xong chứ cũng không phát triển mới nữa, vì vậy mình mới có thời gian nghĩ về việc cải thiện hệ thống, nên có xem lại moleculer và thấy nó đã hỗ trợ Typescript cũng khá ổn và hơn nữa moleculer có triển khai transporter khá linh hoạt, xem hình dưới để dễ hình dung.
Ngoài ra còn có các cơ chế liên quan tới event
Những ưu điểm dễ nhận thấy gồm
- Được hỗ trợ sẵn/built-in.
- Dễ tích hợp với nhiều transporter khác nhau như redis, kafka, rabbitmq…
- Các node sẽ tự động register khi chạy.
- Giao tiếp giữa các service khá đơn giản khi code.
- Và các ưu điểm khác.
Hiện tại mình đang refactor các dự án cũ sang moleculer giúp hệ thống hoạt động được tốt hơn, phát triển trong những năm tiếp theo được dễ dàng hơn, nhanh hơn hy vọng mọi việc đúng như kỳ vọng ^^.
Một số bài học rút ra
Qua những gì đã trải qua, mình rút ra được một số bài học sau:
- Những gì mới, được nhiều đánh giá tốt thì chưa chắc là phù hợp với mình.
- Thay đổi gì đấy đang chạy thì hay nên thay đổi từ từ và chạy thử trong thời gian đủ để đánh giá. Vì có thể chạy trong 1, 2 tháng đầu không sao nhưng nó lại có vấn đề ở tháng thứ 3 ^^. À mà có nhớ chỗ in đậm ở trên không nhỉ? ^^.
- Hãy có kế hoạch cho trường hợp rollback.
- Những cái cũ hay lâu đời thường sẽ ổn định.
- Nếu những gì ở hiện tại gây khó hay cảm thấy chưa hài lòng thì đừng ngần ngại tìm kiếm cái mới.
Bài viết tới đây là hết rồi, cảm ơn các bạn đã theo dõi, chúc một ngày tốt lành và hẹn gặp lại trong các bài tiếp theo. Ngoài ra bạn có thể đọc thêm các bài viết trong loạt bài về trải nghiệm phát triển hệ thống microservices tại EGANY ở dưới đây:
- Microsevices #1 – Sự lựa chọn
- Microsevices #2 – Triển khai microservices “tinh gọn” để bắt đầu dễ dàng hơn
Tham khảo
Nội dung, hình ảnh sử dụng trong bài việt được tham khảo và lấy từ các nguồn dưới đây:
- https://docs.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/media/communication-in-microservice-architecture/sync-vs-async-patterns-across-microservices.png
- https://docs.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/communication-in-microservice-architecture
- https://microservices.io/i/Microservice_Architecture.png
- https://microservices.io/patterns/microservices.html
- https://microservices.io/i/PatternsRelatedToMicroservices.jpg
- https://microservices.io/patterns/microservices.html
- https://www.nginx.com/wp-content/uploads/2016/04/Richardson-microservices-part1-2_microservices-architecture.png
- https://www.nginx.com/blog/introduction-to-microservices/
- https://grpc.io/
- https://www.npmjs.com/package/grpc
- https://www.npmjs.com/package/@zerocore/grpc-helper
- https://docs.docker.com/engine/swarm/
- https://www.portainer.io/
- https://www.digitalocean.com/
- https://www.npmjs.com/package/@grpc/grpc-js
- https://moleculer.services/
- https://moleculer.services/docs/0.14/assets/networking.svg
- https://moleculer.services/docs/0.14/networking.html#Transporters
- https://moleculer.services/docs/0.14/assets/balanced-events.gif
- https://moleculer.services/docs/0.14/assets/broadcast-events.gif