utils/drainpipe.py
class DrainPipe:
def __init__(self, data, gu_name):
self.IDN = data.get("IDN")
self.GUBN = data.get("GUBN")
self.GUBN_NAM = gu_name
self.MEA_YMD = data.get("MEA_YMD")
self.MEA_WAL = data.get("MEA_WAL")
self.SIG_STA = data.get("SIG_STA")
self.REMARK = data.get("REMARK")
def __str__(self):
return (
f"IDN: {self.IDN}, GUBN: {self.GUBN}, GUBN_NAM: {self.GUBN_NAM}, "
f"MEA_YMD: {self.MEA_YMD}, MEA_WAL: {self.MEA_WAL}, "
f"SIG_STA: {self.SIG_STA}, REMARK: {self.REMARK},"
)
# utils/drainpipecontroller.py
class DrainPipeController(SeoulOpenApi):
GUBN_CODE = {
"종로구": "01",
"중구": "02",
"용산구": "03",
"성동구": "04",
"광진구": "05",
...
}
...
def get_result(self, datas, set_len):
"""
latest 결과값 추출
"""
result = []
for data in datas[: -(set_len + 1) : -1]:
result.append(DrainPipe(data, self.gu_name))
return result
...
# utils/rainfall.py
class RainFall:
def __init__(self, data):
self.RAINGAUGE_CODE = data.get("RAINGAUGE_CODE")
self.RAINGAUGE_NAME = data.get("RAINGAUGE_NAME")
self.GU_CODE = data.get("GU_CODE")
self.GU_NAME = data.get("GU_NAME")
self.RAINFALL10 = data.get("RAINFALL10")
self.RECEIVE_TIME = data.get("RECEIVE_TIME")
def __str__(self):
return (
f"RAINGAUGE_CODE: {self.RAINGAUGE_CODE}, RAINGAUGE_NAME: {self.RAINGAUGE_NAME}, GU_CODE: {self.GU_CODE}, "
f"GU_NAME: {self.GU_NAME}, RAINFALL10: {self.RAINFALL10}, RECEIVE_TIME: {self.RECEIVE_TIME}"
)
# utils/rainfallcontroller.py
class RainFallController(SeoulOpenApi):
def __init__(self, gu_name):
super(RainFallController, self).__init__()
self.function_name = "ListRainfallService/"
self.gu_name = Util().get_gu_name(gu_name)
...
def get_result(self, datas, set_len):
# 최신 데이터 리스트
result = list(map(lambda x: RainFall(x), datas[:set_len]))
return result
...
OpenAPI를 요청할 때 필요한 기본 틀의 클래스를 생성하여 요청할 api의 클래스에서 상속받아 사용하게끔 만들어 주었다.
SeoulOpenApi 클래스를 상속받아 전반적인 작업을 수행하는 클래스이다. 요청 시간, url 생성, 데이터 추출 등의 기능을 관리한다.
SeoulOpenApi 클래스를 상속받아 전반적인 작업을 수행하는 클래스이다. url 생성, 데이터 추출 등의 기능을 관리한다.
강우량은 하루 데이터를 구하기 위해 간단한 계산으로 금방 구현이 가능하였다. 하지만 하수관 데이터는 시간별로 데이터를 조회해야 하므로 많은 url 생성과 요청이 필요하다.
이러한 순서로 url 리스트 생성(조회하는
- 00시 ~ url -> 리스트에 추가
- total_count와 end와 비교 => 처음 얻으면 다른 시간 데에도 같은 카운트...
- end가 크면 다음 시간, end가 작다면
3-1. start += 1000, end += 1000
3-2. url 리스트에 추가, 다시 3번으로- 다음 시간으로
구마다 결과 개수가 달라 주의)ProcessPoolExecutor
멀티프로세싱은 프로세스를 여러 개 두어 병렬로 처리하는 방법이다.# Test * max_work = 3 >>> 6.709592342376709 * max_work = 4 >>> 6.519164562225342 * max_work = 5 >>> 6.145772695541382 * max_work = default >>> 5.745122671127319 * max_work = 10 >>> 9.75408148765564 ... with ThreadPoolExecutor(max_work=) as executor: result = sum(executor.map(drain.test_method, url_list), []) ... ProcessPoolExecutor 최고 속도 max_work=default로 했을 때 9.8초
ThreadPoolExecutor
스레드 기반의 병렬 처리를 지원하는 모듈이다.# Test * max_work = 3 >>> 3.136035919189453 * max_work = 4 >>> 2.3571252822875977 * max_work = 5 >>> 2.1027050018310547 * max_work = 6 >>> 1.7558386325836182 * max_work = default >>> 1.4996910095214844 * max_work = 10 >>> 1.2695400714874268 * max_work = 20 >>> 1.1200051307678223 * max_work = 30 >>> 1.0350220203399658 * max_work = 40 >>> 1.0153048038482666 * max_work = 50 >>> 0.9869108200073242 * max_work = 60 >>> 1.06866455078125 ... with ThreadPoolExecutor(50) as executor: result = sum(executor.map(drain.test_method, url_list), []) ... ThreadPoolExecutor 최고 속도 max_work=50로 했을 때 0.99초
약 8배 정도의 시간 차이가 있어 ThreadPoolExecutor를 사용하여 데이터 조회 시간을 단축할 수 있었다. 그러나 모든 데이터를 하나의 응답하다 보니 응답시간은 최악이 되었다. pagenation이 필요한 상황이 되었다. 다음엔 pagenation을 같이 구현해야겠다.