paint-brush
Kiểm tra tải tăng áp: Combo YandexTank + ghz để kiểm tra mã nhanh như chớp!từ tác giả@mrdrseq
503 lượt đọc
503 lượt đọc

Kiểm tra tải tăng áp: Combo YandexTank + ghz để kiểm tra mã nhanh như chớp!

từ tác giả Ilia Ivankin14m2023/11/23
Read on Terminal Reader

dài quá đọc không nổi

Tóm lại, khi bạn cần đánh giá nhanh về khả năng dịch vụ của mình xử lý hơn 100 yêu cầu mỗi giây hoặc xác định các điểm yếu tiềm ẩn, bạn không cần phải bắt đầu các quy trình phức tạp liên quan đến các nhóm, tìm kiếm sự hỗ trợ từ AQA hoặc dựa vào nhóm cơ sở hạ tầng. Thông thường, các nhà phát triển có máy tính xách tay và máy tính đủ khả năng có thể thực hiện một thử nghiệm tải nhỏ. Vì vậy, hãy tiếp tục và thử—hãy tiết kiệm thời gian cho bản thân!
featured image - Kiểm tra tải tăng áp: Combo YandexTank + ghz để kiểm tra mã nhanh như chớp!
Ilia Ivankin HackerNoon profile picture

Chào bạn!


Đôi khi, nảy sinh nhu cầu kiểm tra tải nhanh, cho dù đó là trong môi trường cục bộ hay trên nền tảng thử nghiệm. Thông thường, những nhiệm vụ như vậy được giải quyết bằng cách sử dụng các công cụ chuyên dụng đòi hỏi sự hiểu biết kỹ lưỡng trước đó. Tuy nhiên, trong các doanh nghiệp và công ty khởi nghiệp, nơi việc xác nhận giả thuyết kịp thời và thời gian tiếp thị nhanh chóng là điều tối quan trọng, việc làm quen với công cụ quá mức sẽ trở thành một điều xa xỉ.


Bài viết này nhằm mục đích nêu bật các giải pháp lấy nhà phát triển làm trung tâm giúp loại bỏ sự cần thiết phải tham gia sâu sắc, cho phép thử nghiệm thô sơ mà không cần đi sâu vào các trang tài liệu.


chạy cục bộ

Bạn nên cài đặt::

  1. Docker - tất cả các dịch vụ và công cụ đều được yêu cầu cho nó.


  2. Java 19+ - dành cho dịch vụ kotlin. Ngoài ra, bạn có thể thử sử dụng phiên bản Java 8; nó sẽ hoạt động nhưng bạn phải thay đổi cài đặt Gradle.


  3. Golang - một trong những dịch vụ là dịch vụ golang =)


  4. Python 3+ - dành cho xe tăng Yandex.

Yêu cầu công nghệ

Trước khi bắt đầu cuộc hành trình của chúng tôi, bạn nên tạo một số dịch vụ có thể dùng làm ví dụ minh họa cho mục đích thử nghiệm.


Ngăn xếp: Kotlin + webflux. r2dbc + postgres.


Dịch vụ của chúng tôi có:

– nhận tất cả cổ phiếu (giới hạn 10) NHẬN /api/v1/cổ phiếu
– lấy cổ phiếu theo tên GET__ /api/v1/stock ?name=appl
– lưu kho POST /
api/v1/cổ phiếu


Đây hẳn là một dịch vụ dễ dàng vì chúng tôi phải tập trung vào việc kiểm tra tải =)

Kotlin và Dịch vụ HTTP

Hãy bắt đầu bằng cách tạo một dịch vụ nhỏ với một số logic cơ bản bên trong. Chúng tôi sẽ chuẩn bị một mô hình cho mục đích này:


 @Table("stocks") data class Stock( @field:Id val id: Long?, val name: String, val price: BigDecimal, val description: String )


Bộ định tuyến đơn giản:

 @Configuration @EnableConfigurationProperties(ServerProperties::class) class StockRouter( private val properties: ServerProperties, private val stockHandler: StockHandler ) { @Bean fun router() = coRouter { with(properties) { main.nest { contentType(APPLICATION_JSON).nest { POST(save, stockHandler::save) } GET(find, stockHandler::find) GET(findAll, stockHandler::findAll) } } } }


và xử lý:

 @Service class StockHandlerImpl( private val stockService: StockService ) : StockHandler { private val logger = KotlinLogging.logger {} private companion object { const val DEFAULT_SIZE = 10 const val NAME_PARAM = "name" } override suspend fun findAll(req: ServerRequest): ServerResponse { logger.debug { "Processing find all request: $req" } val stocks = stockService.getAll(DEFAULT_SIZE) return ServerResponse.ok() .contentType(MediaType.APPLICATION_JSON) .body(stocks, StockDto::class.java) .awaitSingle() } override suspend fun find(req: ServerRequest): ServerResponse { logger.debug { "Processing find all request: $req" } val name = req.queryParam(NAME_PARAM) return if (name.isEmpty) { ServerResponse.badRequest().buildAndAwait() } else { val stocks = stockService.find(name.get()) ServerResponse.ok() .contentType(MediaType.APPLICATION_JSON) .body(stocks, StockDto::class.java) .awaitSingle() } } override suspend fun save(req: ServerRequest): ServerResponse { logger.debug { "Processing save request: $req" } val stockDto = req.awaitBodyOrNull(StockDto::class) return stockDto?.let { dto -> stockService.save(dto) ServerResponse .ok() .contentType(MediaType.APPLICATION_JSON) .bodyValue(dto) .awaitSingle() } ?: ServerResponse.badRequest().buildAndAwait() } }


Mã đầy đủ ở đây: GitHub


Tạo một tập tin docker:

 FROM openjdk:20-jdk-slim VOLUME /tmp COPY build/libs/*.jar app.jar ENTRYPOINT ["java", "-Dspring.profiles.active=stg", "-jar", "/app.jar"]


Sau đó, tạo hình ảnh docker và điều chỉnh nó 🤤

 docker build -t ere/stock-service . docker run -p 8085:8085 ere/stock-service


Nhưng hiện tại, tốt hơn hết bạn nên giữ nguyên ý tưởng chạy mọi thứ thông qua bộ chứa Docker và di chuyển dịch vụ của chúng tôi sang thiết lập Docker Compose.

 version: '3.1' services: db: image: postgres container_name: postgres-stocks ports: - "5432:5432" environment: POSTGRES_PASSWORD: postgres adminer: image: adminer ports: - "8080:8080" stock-service: image: ere/stock-service container_name: stock-service ports: - "8085:8085" depends_on: - db 



Được rồi, bài kiểm tra ở đâu?

Tiến về phía trước: làm thế nào chúng ta có thể tiến hành thử nghiệm? Cụ thể, làm cách nào chúng tôi có thể bắt đầu thử nghiệm tải khiêm tốn cho dịch vụ được phát triển gần đây của mình? Điều bắt buộc là giải pháp thử nghiệm phải dễ cài đặt và thân thiện với người dùng.


Do hạn chế về thời gian của chúng tôi, việc đào sâu vào các tài liệu và bài viết phong phú không phải là một lựa chọn khả thi. May mắn thay, có một giải pháp thay thế khả thi — tham gia Yandex Tank. Xe tăng là một công cụ mạnh mẽ để thử nghiệm và có sự tích hợp quan trọng với JMeter , nhưng trong bài viết, chúng ta sẽ sử dụng nó như một công cụ đơn giản.


Tài liệu: https://yandextank.readthedocs.org/en/latest/


Hãy bắt đầu bằng cách tạo một thư mục cho các bài kiểm tra của chúng tôi. Khi chúng tôi đã đặt các cấu hình và các tệp cần thiết khác—may mắn thay, chỉ một vài trong số chúng—chúng tôi sẽ hoàn tất.

Đối với dịch vụ của chúng tôi, chúng tôi cần thử nghiệm các phương thức “get-all” và “save”. Cấu hình đầu tiên cho phương thức tìm kiếm.

 phantom: address: localhost port: "8085" load_profile: load_type: rps schedule: line(100, 250, 30s) writelog: all ssl: false connection_test: true uris: - /api/v1/stocks overload: enabled: false telegraf: enabled: false autostop: autostop: - time(1s,10s) # if request average > 1s - http(5xx,100%,1s) # if 500 errors > 1s - http(4xx,25%,10s) # if 400 > 25% - net(xx,25,10) # if amount of non-zero net-codes in every second of last 10s period is more than 25


Các cài đặt chính cho cấu hình:

  • Địa chỉ và cổng: Giống như ứng dụng của chúng tôi.


  • Tải hồ sơ kiểm tra (load_profile): Chúng tôi sẽ sử dụng loại 'xếp hàng', dao động từ 100 yêu cầu mỗi giây đến 250 với giới hạn 30 giây.


  • URI: Danh sách các URL cần kiểm tra.


  • Mẫu tự động dừng: Không cần phải kiểm tra căng thẳng nếu dịch vụ của chúng tôi đã ngừng hoạt động! =)


Sao chép và dán tập lệnh bash (tank sh):

 docker run \ -v $(pwd):/var/loadtest \ --net="host" \ -it yandex/yandex-tank


Và chạy!


Kết quả là chúng ta sẽ thấy gì? Yandex Tank sẽ ghi lại mọi thứ mà nó cho là xứng đáng trong quá trình thử nghiệm. Chúng ta có thể quan sát các số liệu như phân vị thứ 99 và số yêu cầu mỗi giây (rps).


phần cuối? Thực ra?


Vì vậy, bây giờ chúng ta có bị mắc kẹt với thiết bị đầu cuối không? Tôi muốn có GUI! Đừng lo lắng, Yandex Tank cũng có giải pháp cho điều đó. Chúng tôi có thể sử dụng một trong các plugin quá tải. Đây là một ví dụ về cách thêm nó:

 overload: enabled: true package: yandextank.plugins.DataUploader job_name: "save docs" token_file: "env/token.txt"


Chúng ta nên thêm mã thông báo của mình; chỉ cần vào đây và logic bằng GitHub: https://overload.yandex.net

mã số


Được rồi, việc xử lý yêu cầu GET rất đơn giản, nhưng còn POST thì sao? Chúng ta cấu trúc yêu cầu như thế nào? Vấn đề là, bạn không thể ném yêu cầu vào thùng chứa; bạn cần phải tạo ra các mẫu cho nó! Những mẫu này là gì? Thật đơn giản — bạn cần viết một tập lệnh nhỏ, bạn có thể lấy lại tập lệnh này từ tài liệu và chỉnh sửa một chút cho phù hợp với nhu cầu của chúng ta.


Và chúng ta nên thêm phần thân và tiêu đề của riêng mình:

 #!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys import json # http request with entity body template req_template_w_entity_body = ( "%s %s HTTP/1.1\r\n" "%s\r\n" "Content-Length: %d\r\n" "\r\n" "%s\r\n" ) # phantom ammo template ammo_template = ( "%d %s\n" "%s" ) method = "POST" case = "" headers = "Host: test.com\r\n" + \ "User-Agent: tank\r\n" + \ "Accept: */*\r\n" + \ "Connection: Close\r\n" def make_ammo(method, url, headers, case, body): """ makes phantom ammo """ req = req_template_w_entity_body % (method, url, headers, len(body), body) return ammo_template % (len(req), case, req) def generate_json(): body = { "name": "content", "price": 1, "description": "description" } url = "/api/v1/stock" h = headers + "Content-type: application/json" s1 = json.dumps(body) ammo = make_ammo(method, url, h, case, s1) sys.stdout.write(ammo) f2 = open("ammo/ammo-json.txt", "w") f2.write(ammo) if __name__ == "__main__": generate_json()


Kết quả:

 212 POST /api/v1/stock HTTP/1.1 Host: test.com User-Agent: tank Accept: */* Connection: Close Content-type: application/json Content-Length: 61 {"name": "content", "price": 1, "description": "description"}


Đó là nó! Chỉ cần chạy tập lệnh và chúng ta sẽ có ammo-json.txt. Chỉ cần đặt thông số mới để định cấu hình và xóa URL:

 phantom: address: localhost:9001 ammo_type: phantom ammofile: ammo-json.txt


Và chạy nó một lần nữa!

Đã đến lúc kiểm tra GRPC!

Chưa?


Sau khi đã làm quen với việc tải các phương thức HTTP, việc xem xét kịch bản cho GRPC là điều tự nhiên. Liệu chúng ta có đủ may mắn để có được một công cụ dễ tiếp cận như nhau dành cho GRPC, giống như sự đơn giản của một chiếc xe tăng không? Câu trả lời là khẳng định. Cho phép tôi giới thiệu với bạn về 'ghz'. Hãy thử nhìn mà xem:


Nhưng trước khi làm điều đó, chúng ta nên tạo một dịch vụ nhỏ với Go và GRPC như một dịch vụ thử nghiệm tốt.


Chuẩn bị một tệp proto nhỏ:

 syntax = "proto3"; option go_package = "stock-grpc-service/stocks"; package stocks; service StocksService { rpc Save(SaveRequest) returns (SaveResponse) {} rpc Find(FindRequest) returns (FindResponse) {} } message SaveRequest { Stock stock = 1; } message SaveResponse { string code = 1; } message Stock { string name = 1; float price = 2; string description = 3; } message FindRequest { enum Type { INVALID = 0; BY_NAME = 1; } message ByName { string name = 1; } Type type = 1; oneof body { ByName by_name = 2; } } message FindResponse { Stock stock = 1; }


Và tạo ra nó! (ngoài ra, chúng ta nên cài đặt protoc )

 protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative stocks.proto


Ket qua cua chung toi:
tập tin grpc ở đây!

Thời gian mã hóa!

Các bước tiếp theo: Tạo dịch vụ nhanh nhất có thể.

dễ dàng cho chúng tôi!


  1. Tạo dto (thực thể chứng khoán cho lớp db)

     package models // Stock – base dto type Stock struct { ID *int64 `json:"Id"` Price float32 `json:"Price"` Name string `json:"Name"` Description string `json:"Description"` }


  2. Máy chủ triển khai

     // Server is used to implement stocks.UnimplementedStocksServiceServer. type Server struct { pb.UnimplementedStocksServiceServer stockUC stock.UseCase } // NewStockGRPCService stock gRPC service constructor func NewStockGRPCService(emailUC stock.UseCase) *Server { return &Server{stockUC: emailUC} } func (e *Server) Save(ctx context.Context, request *stocks.SaveRequest) (*stocks.SaveResponse, error) { model := request.Stock stockDto := &models.Stock{ ID: nil, Price: model.Price, Name: model.Name, Description: model.Description, } err := e.stockUC.Create(ctx, stockDto) if err != nil { return nil, err } return &stocks.SaveResponse{Code: "ok"}, nil } func (e *Server) Find(ctx context.Context, request *stocks.FindRequest) (*stocks.FindResponse, error) { code := request.GetByName().GetName() model, err := e.stockUC.GetByID(ctx, code) if err != nil { return nil, err } response := &stocks.FindResponse{Stock: &stocks.Stock{ Name: model.Name, Price: model.Price, Description: model.Description, }} return response, nil }


Mã đầy đủ ở đây: nhấp vào, xin vui lòng!

Kiểm tra nó!

  1. Cài đặt GHz bằng brew (như thường lệ): link


  2. Hãy kiểm tra một ví dụ đơn giản: link


Bây giờ chúng ta nên thay đổi một chút:

  1. di chuyển đến thư mục chứa các tập tin proto.


  2. thêm phương thức: stock.StocksService.Save .


  3. thêm nội dung đơn giản: '{“stock”: { “name”:”APPL”, “price”: “1.3”, “description”: “apple stock”} }'.


  4. 10 kết nối sẽ được chia sẻ giữa 20 công nhân goroutine. Mỗi cặp 2 goroutines sẽ chia sẻ một kết nối duy nhất.


  5. đặt cổng dịch vụ


và kết quả:

 cd .. && cd stock-grpc-service/proto ghz --insecure \ --proto ./stocks.proto \ --call stocks.StocksService.Save \ -d '{"stock": { "name":"APPL", "price": "1.3", "description": "apple stocks"} }' \ -n 2000 \ -c 20 \ --connections=10 \ 0.0.0.0:5007


Chạy nó đi!

 Summary: Count: 2000 Total: 995.93 ms Slowest: 30.27 ms Fastest: 3.11 ms Average: 9.19 ms Requests/sec: 2008.16 Response time histogram: 3.111 [1] | 5.827 [229] |∎∎∎∎∎∎∎∎∎∎∎ 8.542 [840] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 11.258 [548] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎ 13.973 [190] |∎∎∎∎∎∎∎∎∎ 16.689 [93] |∎∎∎∎ 19.405 [33] |∎∎ 22.120 [29] |∎ 24.836 [26] |∎ 27.551 [6] | 30.267 [5] | Latency distribution: 10 % in 5.68 ms 25 % in 6.67 ms 50 % in 8.27 ms 75 % in 10.49 ms 90 % in 13.88 ms 95 % in 16.64 ms 99 % in 24.54 ms Status code distribution: [OK] 2000 responses


Và cái gì, nhìn chằm chằm vào mọi thứ trong thiết bị đầu cuối một lần nữa? Không, với ghz, bạn cũng có thể tạo báo cáo, nhưng không giống như Yandex, nó sẽ được tạo cục bộ và có thể mở trong trình duyệt. Chỉ cần đặt nó:

 ghz --insecure -O html -o reports_find.html \ ...

-O + html → định dạng đầu ra

-o tên tệp


kết quả dưới dạng trang web :D


Phần kết luận

:D


Tóm lại, khi bạn cần đánh giá nhanh về khả năng dịch vụ của mình xử lý hơn 100 yêu cầu mỗi giây hoặc xác định các điểm yếu tiềm ẩn, bạn không cần phải bắt đầu các quy trình phức tạp liên quan đến các nhóm, tìm kiếm sự hỗ trợ từ AQA hoặc dựa vào nhóm cơ sở hạ tầng.


Thông thường, các nhà phát triển có máy tính xách tay và máy tính đủ khả năng có thể thực hiện một thử nghiệm tải nhỏ. Vì vậy, hãy tiếp tục và thử—hãy tiết kiệm thời gian cho bản thân!


Tôi tin rằng bạn thấy bài viết ngắn gọn này có ích.

Tài liệu có giá trị tôi khuyên bạn nên đọc:

Xe tăng Yandex: liên kết tài liệu

Yandex Tank GitHub: liên kết GitHub

Cài đặt xe tăng Yandex: liên kết

trang chính thức của ghz: liên kết

cài đặt ghz: liên kết
cấu hình ghz: liên kết


Mạnh mẽ lên!

Cảm ơn một lần nữa và chúc may mắn! 🕵🏻