Giao tiếp hiệu quả giữa các Microservice
Lựa Chọn Giữa http/1.1 Và gRPC. TL; DR: Use gRPC Làm thế nào mà việc giao tiếp giữa các microservice lại gây ảnh hưởng đến hiệu suất và khả năng mở rộng của ứng dụng. Giao tiếp giữa các dịch vụ có thể đồng bộ hoặc không. Đối với bài viết này, chúng tôi sẽ tập trung vào sự đồng bộ. ...
Lựa Chọn Giữa http/1.1 Và gRPC. TL; DR: Use gRPC
Làm thế nào mà việc giao tiếp giữa các microservice lại gây ảnh hưởng đến hiệu suất và khả năng mở rộng của ứng dụng. Giao tiếp giữa các dịch vụ có thể đồng bộ hoặc không. Đối với bài viết này, chúng tôi sẽ tập trung vào sự đồng bộ. Theo ý kiến cá nhân của chúng tôi thì có 2 giao thức phổ biến:
- HTTP/1.1: giao thức http mặc định
- gRPC: cuộc gọi thủ tục từ xa với hiệu suất cao (RPC framework). gRPC.io hoạt động dựa vào tài liệu toàn diện của mình.
Hãy thử xem một số code mẫu và chạy thử một số thử nghiệm hiệu suất để xem thử các lựa của chúng tôi
Có thể bạn quan tâm:
Chế Độ Non-cluster
Cùng bắt đầu với một mô hình nhỏ và cố gắng nhận một cuộc trò chuyện microservice với một bên khác.
Sơ đồ cấu trúc ứng dụng đơn giản
Ứng dụng của chúng tôi bao gồm các thành phần:
- Load Testing tool: jMeter
- Service A: Đưa ra yêu cầu cho service B và trả về phản hồi đó.
- Service B: Trả lời bằng static JSON sau 10ms cho tất cả các API
- VMs: Cả hai VM đều là máy Amazon EC2 t2.xlarge
HTTP / 1.1
Đây là yêu cầu mặc định mà chúng tôi thực hiện khi sử dụng bất kỳ thư viện HTTP client nào như axios, superagent.
Hãy tạo ServiceB để triển khai API của chúng tôi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
server.route({ method: 'GET', path: '/', handler: async (request, h) => { const response = await new Promise((resolve) => { setTimeout(() => { resolve({ id: 1, name: 'Abhinav Dhasmana', enjoys_coding: true, }); }, 10); }); return h.response(response); }, }); |
Service B triển khai HTTP / 1.1
Tiếp theo, chúng tôi tạo serviceA gọi serviceB bằng HTTP / 1.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
server.route({ method: 'GET', path: '/', handler: async (request, h) => { try { const response = await Axios({ url: 'http://localhost:8001/', method: 'GET', }); return h.response(response.data); } catch (err) { throw Boom.clientTimeout(err); } }, }); |
Service A triển khai HTTP/1.1
Với hai service đang chạy này chúng tôi có thể kích hoạt jMeter để thử nghiệm hiệu năng. Chúng tôi sẽ sử dụng 50 người dùng với 2000 yêu cầu mỗi người. Chúng ta có thể thấy trong ảnh chụp màn hình bên dưới trung bình của chúng là 37ms.
Kết quả hoạt động của HTTP / 1.1 ở chế độ Non-cluster
gRPC
gRPC sử dụng protocol buffers theo mặc định. Ngoài serviceA và serviceB, chúng tôi còn cần thêm một file proto sẽ xác định cuộc gọi từ xa của chúng tôi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
syntax = "proto3"; service SampleDataService { rpc GetSampleData (Empty) returns (SampleData) {} } message SampleData { int32 id = 1; string name = 2; bool enjoys_coding = 3; } message Empty {} |
File proto cho tiếp xúc với RPC
Hãy triển khai lại serviceB, lần này hãy sử dụng gRPC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
const grpc = require('grpc'); const proto = grpc.load('serviceB.proto'); const server = new grpc.Server(); const GetSampleData = (call, callback) => { setTimeout(() => { callback(null, { id: 1, name: 'Abhinav Dhasmana', enjoys_coding: true, }); }, 10); }; server.addService(proto.SampleDataService.service, { GetSampleData, }); const port = process.env.PORT; console.log('port', port); server.bind(`0.0.0.0:${port}`, grpc.ServerCredentials.createInsecure()); server.start(); console.log('grpc server is running'); |
Service B khi triển khai gRPC
Vài điều cần lưu ý:
- Chúng tôi tạo máy chủ grpc ở line 4
- Chúng tôi thêm một service vào máy chủ của mình ở line 16
- Chúng tôi liên kết port và cedentials ở line 23
serviceA được triển khai bằng cách sử dụng gRPC bên dưới
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
onst protoPath = `${__dirname}/../serviceB/serviceB.proto`; const proto = grpc.load(protoPath); const client = new proto.SampleDataService('localhost:8001', grpc.credentials.createInsecure()); const getDataViagRPC = () => new Promise((resolve, reject) => { client.GetSampleData({}, (err, response) => { if (!response.err) { resolve(response); } else { reject(err); } }); }); server.route({ method: 'GET', path: '/', handler: async (request, h) => { const allResults = await getDataViagRPC(); return h.response(allResults); }, }); |
Service A khi triển khai gRPC
Một số ghi chú:
- Chúng tôi tạo client grpc tại line 4
- Chúng tôi tựực hiện một cuộc gọi từ xa tại line 6
Hãy chạy thử nghiệm công cụ jMeter một lần nữa.
Kết quả hoạt động của gRPC ở chế độ Non-cluster
Như chúng ta có thể thấy từ dữ liệu này, gRPC nhanh hơn 27% so với yêu cầu HTTP / 1.1 thông thường.
Cấu Trúc Ứng Dụng Trong Chế Độ Cluster
Cấu trúc ứng dụng
Nếu bạn đang chạy bất cứ thứ gì trong quá trình sản xuất, rất có thể bạn đang chạy nhiều phiên bản của bất kỳ dịch vụ cụ thể nào. Trong trường hợp này, chúng ta đang thực hiện các thay đổi sau:
- Chạy ba phiên bản dịch vụ của chúng tôi trên các cổng khác nhau
- Chúng tôi đang sử dụng NGINX tạo sự cân bằng tải.
HTTP/1.1
Không có thay đổi được yêu cầu từ phía dịch vụ. Tất cả chúng ta cần làm đó là cấu hình nginx để cân bằng lưu lượng tải với serviceB. Chúng tôi có thể sửa đổi /etc/nginx/sites-available/default giống như bên dưới
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
upstream httpservers { server ip_address:8001; server ip_address:8002; server ip_address:8003; } server { listen 80; location / { proxy_pass http://httpservers; } } |
nginx với HTTP/1.1
Bây giờ chúng ta có thể bắt đầu với 3 phiên bản dịch vụ của chúng tôi và chạy thử nghiệm hiệu suất của chúng.
Kết quả hoạt động của HTTP / 1.1 ở chế độ Cluster
gRPC
Hỗ trợ cho gRPC gần đây đã được thêm vào nginx phiên bản 1..13.10. Vì thế chúng ta cần sử dụng bản mới nhất vì sudo apt-get install nginx mặc định sẽ không thể hoạt động.
Chúng tôi không sử dụng node trong chế độ cluster vì gRPC không được hỗ trợ.
Thực hiện các bước dưới đây để có được nginx mới nhất.
1 2 3 4 5 6 |
sudo apt-get install -y software-properties-common sudo add-apt-repository ppa:nginx/stable sudo apt-get update sudo apt-get install nginx |
Nhận ngay phiên bản nginx mới nhất
Chúng tôi cũng sẽ cần các chứng chỉ ssl. Chúng tôi có thể tạo các chứng chỉ tự ký bằng openSSL
1 2 3 4 |
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -keyout localhost-privatekey.pem -out localhost-certificate.pem |
Lệnh tạo chứng chỉ tự ký
Tiếp theo, chúng ta cần thay đổi tệp /etc/nginx/sites-available/default để bắt đầu sử dụng gRPC.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
upstream grpcnodes { server ip_address:8001; server ip_address:8002; server ip_address:8003; } server { listen 1443 http2; ssl_certificate /home/ubuntu/interserviceCommunication/http2/certificates/localhost-certificate.pem; ssl_certificate_key /home/ubuntu/interserviceCommunication/http2/certificates/localhost-privatekey.pem; location / { grpc_pass grpcnodes; ## try_files $uri $uri/ =404; // Don't forget to comment this else you will get 404 as a response } |
Chính là nó. Hãy chạy thử nghiệm hiệu suất một lần nữa
Kết quả hoạt động của gRPC ở chế độ non cluster
Như chúng ta có thể thấy gRPC nhanh hơn 31% so với HTTP / 1.1
Kết Luận
gRPC thì nhanh. Trong các bản thử nghiệm ở trên, chúng ta có thể thấy nó nhanh hơn 31% so với HTTP / 1.1 chỉ bằng cách thay đổi cách mà chúng tôi trao đổi.
Những mã nguồn này có thể tìm thấy trên GitHub
Topdev via ITNEXT