Concurrency kết hợp với RESTful API trong Golang

Sharing is caring!

Sau vài ngày “chìm đắm” trong mớ hỗn độn của ngôn ngữ Go để tìm hiểu về cái gọi là concurrency với 2 khái niệm mới đó là GoRoutine và Channel thì tui thấy rằng bạn Go này hay quá. Chưa bao giờ thấy việc lập trình concurreny nó dễ dàng như vậy. Sau khi “học” xong thì cảm thấy Go nó có một chút gì đó mạnh mẽ của C/C++, uyển chuyển của PHP/Python. Cú pháp thì gọn gàng (có thời gian, tui sẽ viết bài về cách học ngôn ngữ Go này).

Đó giờ tui làm backend chỉ dùng PHP là chính bao gồm viết website cũng như làm RESTful api. Cái hạn chế của PHP đó là PHP là scripting, có timeout khi run script. Cho nên khi build API hoặc là gọi API từ một service khác sẽ gặp một số rủi ro về việc blocking request trong trường hợp tạo nhiều request để gọi API cùng lúc. Có thể tại thời điểm này, PHP cũng đã có những giải pháp để giải quyết vấn đề trên. Nhưng hôm nay, tui muốn sử dụng Go để làm giải pháp cho vấn đề non-blocking request.

1. Goroutine và channel

Bài toán đặt ra là như thế này. Tui có một hệ thống cung cấp Rest API, khi gọi đến endpoint này, hệ thống sẽ phải collect dữ liệu từ nhiều nguồn khác từ các service khác thông qua Rest API luôn, sau đó tổng hợp dữ liệu này lại và trả kết quả về cho người dùng. Giả sử tui phải gọi API từ 15 service khác nhau, nếu bình thường, tui sẽ phải tạo 15 cái request và thực hiện tuần tự 15 cái request này. Nếu trung bình, 1 request xử lý xong trong vòng từ 0.5s – 1s thì 15 request tui mất khoản 7.5s – 15s (do vấn đề blocking request, xử lý xong request này mới đến request kế tiếp), điều này thật sự không thể chấp nhận đối với việc gọi API.

Nếu ta áp dụng Concurrency (nó hơi khác với Parallel nha) với 15 request này, có nghĩa là cho 15 request này được xử lý đồng thời thì tui chỉ mất từ 0.5s – 1s cho tất cả các request, vậy thì performance tăng lên rất nhiều.

Ví dụ cụ thể đi ha. Tui đã đăng ký 3 cái api để lấy nhiệt độ tại thời điểm request của một thành phố bất kỳ đó là:

(Chỉ tìm được 3 cái này nó free thôi, coi như là nhiều hơn 1 là được rồi, để minh họa bài toán trên cũng ok nha.)

Tạo 3 routine để xử lý đồng thời 3 cái request đến 3 cái api trên, dùng channel để hứng dữ liệu sau khi các routine này xử lý phần lấy giá trị nhiệt độ từ kết qua json trả về. Tiếp theo, tính trung bình nhiệt độ có được và trả kết quả trung bình này cho Rest API, sau đó Rest API này sẽ trả kết quả json về cho người dùng. Xem cái hình sau nhé:

2. Code chương trình bằng Go:

2.1. Tạo Interface:

Do ta có 3 service cần gọi, mỗi service này thì trả dữ liệu JSON về có cấu trúc khác nhau, nên ta cần tạo một interface chung có một hàm là GetTemperature, sau đó tạo 3 cái provider tương ứng với 3 service rồi implement cái interface này.

weatherapi/weather_interface.go

2.1. Tạo các provider:

Mỗi provider cụ thể sẽ có  fields: APIKey và URL:

weatherapi/open_weather_map.go

weatherapi/api_xu.go

weatherapi/weather_bit.go

2.3. Tạo hàm temperature() để collect và tính nhiệt độ trung bình:

Tạo một danh sách chứa các Provider:

Tạo kiểu dữ liệu TemperatureData để lưu dữ liệu là nhiệt độ (độ C, độ K, độ F) để trả về cho người dùng khi người dùng gọi Rest API

Hàm temperature, trong hàm này ta sẽ dùng goroutine và channel

2.4. Khởi tạo các provider:

Tạo các provider với APIKey và URL tương ứng, sau đó thêm chúng vào ProviderList:

2.5. Tạo Rest API với Gorilla Mux:

Tổng hợp lại trong file main.go:

3. Run chương trình:

Đứng ngay tại thư mục chứa file main.go, run lệnh:

$ go run main.go

Dùng PostMan để test Rest API:

http://localhost:9000/api/temperature/saigon

http://localhost:9000/api/temperature/tokyo

http://localhost:9000/api/temperature/paris

Kết quả hiển thị bên server:

Tui để source code tham khảo bài này tại git nhé: https://github.com/lmdat/weather-services

Cách viết của tui trong bài này thật sự chưa hoàn hảo đâu, sẽ có một vài bug xuất hiện đó, nhưng về cơ bản, hi vọng bài viết này sẽ giúp các bạn có hứng thú với ngôn ngữ Go hơn cũng như cách sử dụng goroutinechannel trong lập trình concurrency.

Sharing is caring!

Monitor việc gọi API real-time với Go và Redis

Vincent Le

Tui là Lê Minh Đạt, tên tiếng anh là Vincent(do thích nhân vật Vincent Valentine, ai từng là fan của trò Final Fantasy VII thì sẽ biết nhân vật này, hehe). Đang tập tành làm một blogger viết về mảng lập trình, mong muốn được chia sẻ những gì đã học được, tích góp được trong 10 năm đi làm thợ code.

shares