[:en]Sending simultaneous requests using Python[:ko]Python에서 여러 Request 동시에 보내기[:]

[:en]Sending simultaneous requests using Python[:ko]Python에서 여러 Request 동시에 보내기[:]

[:en]

Python is a perfect programming language unless you have to deal with asynchronous action.

Many programmers using Javascript complain about ‘callback hell’. Still, people become grateful when they have to deal with asynchronous action (simultaneous, parallel, concurrent, etc.).

Recently I needed to send several requests at the same time, instead of waiting for the previous request to finish. Moreover, all responses should be collected as one variable.

1. Ordinary request

It took 11.285 seconds to call 10 requests

If we call 10 requests sequentially, it took 11.285 seconds.

The more request, the longer time 🙁

Let’s see how we can solve this!

2. Concurrent GET request

First two lines are the packages that we are using.

Line 4 shows the function that we will use to request.

Line 7 is a list of 10 URLs that we want to request simultaneously.

Line 9-10 is the core part of this script. “ThreadPoolExecutor” is a pool of threads that can run asynchronously. Therefore you can specify the number of workers who can work at the same time.

However, a higher number of workers don’t always guarantee better performance. (See this answer)

In the line 10, people may get confused about arguments ‘get_url’ and ‘list_of_urls’. This statement indicates of mapping between function and each element of the list.

For example,

get_url(url1)
get_url(url2)
...
get_url(url10)

Now we run the code.

10 requests took 1.310 seconds

In theory, the total time spent is decided by the longest request.

If 10 threads can be completely independent (one thread per one CPU core and no context switching), there will be no difference between one request and 10 requests.

3. Concurrent POST request

However, you may wonder how we can pass more than one argument to the function.

In this case, you can set the element as a tuple like below code.

As you can see from line 11, each element (tuple) consists of URL and dict_data.

In the line 4, args[0] indicates the first element of the tuple (URL), and args[1] indicates the second element of the tuple (form_data).

Final test!

10 post requests took 1.435 seconds.

Happy coding!

[:ko]

비동기식 동작을 대응하기 전까지는, 파이썬만큼 완벽한 프로그래밍 언어도 없습니다.

자바스크립트를 사용하는 많은 개발자 분들이, 콜백 지옥에 대해 불평하면서도, 막상 비동기 호출을 해야 하는 경우에는 금방 자바스크립트에 감사하게 됩니다.

최근에 여러 요청을 한 번에 보내야 하는 작업이 필요했습니다. 또한 그 요청들의 응답을 한 변수에 모아야 했습니다.

1. 일반 요청

10개의 요청을 보내는데 11.285초가 걸렸습니다.

10개의 요청을 순차적으로 보내면, 총 11.285초가 걸립니다.

더 많은 요청을 보내면, 비례해서 더 긴 시간이 걸릴 것입니다.

어떻게 문제를 해결했는지 보시죠!

2. 동시 GET request

첫 번째, 두 번째 줄은 필요한 파이썬 패키지를 임포트 한 곳이고,

네 번째 줄은 request를 요청할 함수입니다.

7번째 줄은 우리가 동시에 요청을 보낼 10개의 URL을 list에 담아 두었습니다.

9-10 번째 줄이 가장 중요한 부분인데, “ThreadPoolExecutor”란 비동기식으로 사용할 수 있는 스레드들의 묶음이라고 생각하시면 됩니다. 그래서 코드에서 보시는 바와 같이, 동시에 동작할 수 있는 worker의 숫자를 지정할 수 있습니다. 

하지만, worker를 많이 넣는 다고 해서 꼭 더 좋은 성능이 보장되는 것은 아닙니다. (이 답변 참조)

10번째 줄에서, 인자로 넣은 get_url과 list_of_urls 때문에 보통 많은 분들이 헷갈려하시는데, 간단하게 말해서 각각의 URL을 함수에 매핑한다고 보시면 됩니다.

다음과 같이.

get_url(url1)
get_url(url2)
...
get_url(url10)

이제 코드를 실행해보겠습니다.

10개의 요청이 1.3초가 걸렸습니다.

이론적으로는, 가장 길게 걸린 요청 시간이 전체 요청 시간과 같습니다.

만약 10개의 스레드가 완전히 독립적으로 동작을 한다면, (각 스레드가 각 CPU 코어에 걸려서 동작을 하는 이상적인..) 1개의 요청과 10개의 요청의 시간 차이는 없을 것입니다.

3. 동시 POST request

그런데, get_url 함수에 어떻게 두 개 이상의 인자를 넣을 수 있을지 헷갈리실 수도 있습니다.

이때는 리스트를 구성하는 각각의 원소를 아래 코드처럼 tuple로 구성하시면 됩니다. 

11번째 줄에서 보시는 바와 같이, 각각의 원소는 URL과 form_data라는 dict변수로 구성되어 있습니다.

4번째 줄에서 args [0]은 튜플의 첫 번째 원소인 url을 의미하고, args [1]는 튜플의 두 번째 원소인 form_data를 의미합니다.

마지막 테스트

10개의 post요청이 1.435초가 걸렸습니다.

Happy coding!

[:]

댓글 남기기

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.

%d 블로거가 이것을 좋아합니다: