Redis에서 시스템에서 사용할 수 있는 최대 메모리를 확인하려면, INFO MEMORY 명령어와 함께 maxmemory 설정을 통해 Redis가 할당할 수 있는 최대 메모리 용량을 확인할 수 있습니다.

1. maxmemory 설정 확인

Redis는 메모리 사용에 제한을 둘 수 있는 설정(maxmemory)이 있으며, 이 값을 확인하려면 CONFIG GET 명령어를 사용합니다.

redis-cli CONFIG GET maxmemory

이 명령어는 Redis 서버가 할당할 수 있는 최대 메모리의 값을 반환합니다. 예를 들어, maxmemory가 설정되지 않은 경우 0이 반환되며, 이는 제한 없이 Redis가 사용할 수 있음을 의미합니다.

예시:

redis-cli CONFIG GET maxmemory

출력 예시:

1) "maxmemory" 2) "1073741824"

위 예시에서는 maxmemory가 1073741824 (1GB)로 설정되어 있음을 나타냅니다. 만약 maxmemory가 0이라면 Redis는 메모리에 제한 없이 사용할 수 있습니다.

2. 전체 시스템 메모리 확인

Redis가 할당할 수 있는 최대 메모리는 운영 체제에서 Redis 프로세스에 할당된 메모리와도 관련이 있습니다. INFO MEMORY 명령어로 전체 시스템의 메모리 크기를 확인할 수 있습니다.

redis-cli INFO MEMORY

출력 예시:

# Memory used_memory:12345678 used_memory_human:11.77M used_memory_rss:98765432 used_memory_peak:23456789 mem_fragmentation_ratio:1.78 total_system_memory:8388608000

위 예시에서 **total_system_memory**는 시스템 전체 메모리 용량을 나타내며, 이는 Redis가 실행되고 있는 서버의 전체 RAM 용량입니다.

요약:

  • Redis가 사용할 수 있는 최대 메모리는 CONFIG GET maxmemory 명령어로 확인할 수 있습니다.
  • 전체 시스템 메모리는 INFO MEMORY 명령어에서 total_system_memory 항목으로 확인할 수 있습니다.
  • maxmemory 설정이 0인 경우 Redis는 시스템 전체 메모리의 사용에 제한이 없습니다.



My Case)

C:\Users\xterm>redis-cli CONFIG GET maxmemory
1) "maxmemory"
2) "1572864000"

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:9875616
used_memory_human:9.42M
used_memory_rss:9816928
used_memory_peak:9875616
used_memory_peak_human:9.42M
used_memory_lua:36864
mem_fragmentation_ratio:0.99
mem_allocator:jemalloc-3.6.0
 
C:\Users\xterm>redis-cli CONFIG GET maxmemory
1) "maxmemory"
2) "1572864000"
 

1 메가바이트(MB)는 1,048,576 바이트입니다. 따라서:

1572864000 bytes÷1048576=1500 MB1572864000 \, \text{bytes} \div 1048576 = 1500 \, \text{MB}

따라서, 1572864000 바이트는 **1500 메가바이트(MB)**입니다.

Redis에 할당된 전체 메모리 용량을 확인하려면 INFO MEMORY 명령어를 사용하여 메모리 관련 정보를 확인할 수 있습니다. 이 명령어는 Redis 서버의 메모리 사용 상태를 종합적으로 보여줍니다.

INFO MEMORY 명령어

redis-cli INFO MEMORY

이 명령어를 실행하면 Redis 서버의 메모리 상태에 대한 상세한 정보를 얻을 수 있습니다. 그 중에서 전체 메모리와 관련된 정보는 다음과 같습니다:

주요 항목:

  • used_memory: Redis가 현재 사용 중인 메모리의 총량 (바이트 단위)
  • used_memory_human: 사람이 읽기 쉬운 형식으로 표시된 사용 중인 메모리 (예: 10.5M, 2.3G 등)
  • used_memory_rss: Redis 프로세스가 운영 체제에서 실제로 할당받은 메모리 (Resident Set Size)
  • used_memory_peak: Redis가 사용한 최대 메모리
  • total_system_memory: Redis가 실행되고 있는 시스템의 전체 물리적 메모리 (이 값은 시스템의 RAM 크기를 나타냄)

예시:

 
C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:930832
used_memory_human:909.02K
used_memory_rss:872960
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:0.94
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:1049312
used_memory_human:1.00M
used_memory_rss:1049152
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:1.00
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:2683728
used_memory_human:2.56M
used_memory_rss:2624288
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:0.98
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:3072672
used_memory_human:2.93M
used_memory_rss:3013984
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:0.98
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:3052144
used_memory_human:2.91M
used_memory_rss:3051984
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:1.00
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:3063568
used_memory_human:2.92M
used_memory_rss:3063472
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:1.00
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:3106944
used_memory_human:2.96M
used_memory_rss:3048256
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:0.98
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:3116048
used_memory_human:2.97M
used_memory_rss:3057360
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:0.98
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:3124208
used_memory_human:2.98M
used_memory_rss:3065520
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:0.98
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:3135760
used_memory_human:2.99M
used_memory_rss:3077072
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:0.98
mem_allocator:jemalloc-3.6.0

C:\Users\xterm>redis-cli INFO MEMORY
# Memory
used_memory:4905536
used_memory_human:4.68M
used_memory_rss:4846848
used_memory_peak:7590400
used_memory_peak_human:7.24M
used_memory_lua:36864
mem_fragmentation_ratio:0.99
mem_allocator:jemalloc-3.6.0

주요 항목 설명:

  1. used_memory: Redis가 현재 실제로 사용하고 있는 메모리의 크기입니다.
  2. used_memory_rss: Redis 프로세스가 운영 체제에서 실제로 할당받은 메모리입니다. 이는 used_memory보다 많을 수 있으며, 메모리 단편화로 인해 실제 할당된 메모리 공간이 더 크면 used_memory_rss가 커집니다.
  3. total_system_memory: 시스템의 전체 물리적 메모리 크기입니다. 예를 들어, 8388608000은 약 8GB의 메모리입니다.

used_memory / used_memory_rss 둘은 서로 비례해서 늘기도 하고, 줄기도 한다.

INFO MEMORY 명령어를 사용하면 전체 Redis 메모리 상태를 자세히 알 수 있으며, 특히 시스템 전체 메모리와 Redis에서 사용 중인 메모리를 비교하여 시스템 리소스가 어떻게 사용되고 있는지 파악할 수 있습니다.

Redis에서 각 키에 해당하는 데이터 유형을 확인하고, 해당 유형에 맞는 명령어를 통해 리스트의 크기를 확인할 수 있습니다. Redis는 다양한 데이터 유형(예: 문자열, 리스트, 집합 등)을 지원하므로, 리스트 타입에 대해 구체적으로 크기를 확인하려면 아래 방법을 사용합니다.

C:\Users\xterm>redis-cli LLEN QCS:11270016379155155155155
(integer) 0

C:\Users\xterm>redis-cli LLEN QCS:11270016379155155155155
(integer) 0

C:\Users\xterm>redis-cli LLEN QCS:11270016379155155155155
(integer) 0

C:\Users\xterm>redis-cli LLEN QCS:11270016379155155155155
(integer) 0

C:\Users\xterm>redis-cli LLEN QCS:11270016379155155155155
(integer) 0

C:\Users\xterm>redis-cli LLEN QCS:11270016379155155155155
(integer) 0
 

1. 키의 데이터 유형 확인

먼저, 특정 키가 리스트인지 확인하려면 TYPE 명령어를 사용할 수 있습니다:

redis-cli TYPE <key>

예를 들어, mylist라는 키가 리스트인 경우:

redis-cli TYPE mylist

이 명령어가 list를 반환하면 해당 키는 리스트입니다.

2. 리스트의 크기 확인

리스트의 크기를 확인하려면 LLEN 명령어를 사용합니다. LLEN 명령어는 리스트의 요소 수를 반환합니다:

redis-cli LLEN <key>

예를 들어, mylist라는 키의 리스트 크기를 확인하려면:

redis-cli LLEN mylist

이 명령어는 mylist 키에 저장된 리스트의 길이를 반환합니다.

3. 리스트에 저장된 모든 요소 가져오기 (선택 사항)

리스트의 모든 요소를 확인하고 싶다면, LRANGE 명령어를 사용할 수 있습니다:

redis-cli LRANGE <key> 0 -1

이 명령어는 리스트의 모든 요소를 반환합니다. 예를 들어:

redis-cli LRANGE mylist 0 -1

이 명령어는 mylist 리스트의 모든 항목을 반환합니다.

예시

  1. 리스트 데이터 유형 확인:
  2.  
    redis-cli TYPE mylist # => list
  3. 리스트 길이 확인:
  4.  
    redis-cli LLEN mylist # => 5 (리스트에 5개의 요소가 있을 경우)
  5. 리스트의 모든 요소 가져오기:
  6.  
    redis-cli LRANGE mylist 0 -1 # => 1) "value1" # 2) "value2" # 3) "value3" # 4) "value4" # 5) "value5"

이렇게 하여 Redis에서 특정 키가 리스트일 때, 해당 리스트의 요소 개수를 확인하고, 필요한 경우 모든 요소를 가져올 수 있습니다.

 
 
 

 

 
 

CHAT.GPT에게 질문)
1. windows c# 코드에서 SendMessage 사용할떄에, 파라미터에 담겨진 내용을 1000번 보냈을떄에, 받는부분이 없다면, 어디로 저장되나요? 아니면 없어지나요?

2. PostMessage 를 사용하더라도 PostMessage를 사용한 프로세스가 종료되면, 보낸메세지는 사라지나요? 프로세스가 종료될때에 SendMessage와 PostMessage의 상황을 설명해주세요

3. 송신프로세스 종료에 대해서 설명해주세요

CHAT.GPT 답)

SendMessage 함수는 Windows API에서 메시지를 다른 윈도우에 전달할 때 사용됩니다. 이 함수는 기본적으로 메시지를 지정된 윈도우의 프로시저로 보내며, 수신자가 없다면 메시지는 처리되지 않습니다.

질문에서 말한 **"1000번 보냈을 때 받는 부분이 없다면, 어디로 저장되나요?"**에 대한 답을 설명하겠습니다:

  1. 메시지가 수신되지 않으면: SendMessage 함수는 메시지가 처리될 때까지 호출한 스레드를 블록(block)합니다. 수신자가 없다면 메시지는 처리되지 않지만, 수신자에게 전달될 장소나 큐에 메시지가 저장되는 것은 아닙니다.
  2. 메시지가 손실되는 경우: 만약 SendMessage로 보낸 메시지가 처리되지 않거나, 수신자가 메시지를 받지 못한다면, 해당 메시지는 "사라지게" 됩니다. Windows 메시지 시스템은 수신자 없이 메시지를 저장하는 방식으로 동작하지 않기 때문에, 수신자가 없을 경우 메시지는 내부 큐에 쌓이지 않고, 처리되지 않습니다.
  3. 메시지 큐: Windows 메시지 시스템에서 메시지는 일반적으로 프로세스의 메시지 큐에 저장됩니다. 하지만, SendMessage 함수는 메시지를 동기식으로 처리하며 수신자가 없거나 응답하지 않으면 메시지는 큐에 보관되지 않고 버려지게 됩니다. 이는 PostMessage와 차별되는 점으로, PostMessage는 비동기 방식으로 큐에 메시지를 보내지만, 수신자가 없어도 큐에 남게 되며, 이후에 처리될 수 있습니다.

따라서 1000번의 메시지를 보내고 수신자가 없다면, 이 메시지들은 저장되지 않고 모두 사라지게 됩니다.

대안: 만약 메시지가 수신되지 않거나 처리되지 않았을 때 저장되기를 원한다면, 큐를 관리하거나 다른 방법(예: 파일 저장, 데이터베이스 등)을 통해 메시지를 로그로 기록하는 방법을 고려할 수 있습니다.

 

 

 

PostMessage와 SendMessage는 모두 Windows 메시지 시스템에서 메시지를 다른 윈도우로 보내는 데 사용되지만, 메시지가 처리되는 방식과 관련된 동작에 차이가 있습니다. 이들 두 방식이 각각 프로세스 종료 시 어떤 영향을 받는지에 대해 설명하겠습니다.

1. PostMessage와 메시지 큐

PostMessage는 메시지를 보내는 비동기 방식입니다. 이 방식은 메시지를 호출한 프로세스와 수신 윈도우의 메시지 큐에 넣어주고, 수신 윈도우는 이 메시지를 나중에 처리할 수 있습니다.

  • 메시지 큐: 메시지가 수신 윈도우의 메시지 큐에 저장됩니다.
  • 프로세스 종료 시:
    • 만약 메시지를 보낸 프로세스가 종료되더라도 메시지는 수신 윈도우의 큐에 남아있습니다.
    • 수신 프로세스가 메시지 큐에서 메시지를 처리하지 않으면, 수신 프로세스가 종료되기 전까지 큐에 남을 수 있습니다.
    • 수신 윈도우가 종료되면, 해당 메시지는 큐에서 사라집니다. 즉, 메시지 큐는 수신 윈도우의 존재와 연관이 있기 때문에, 수신 윈도우가 종료되면 그 메시지는 더 이상 유효하지 않습니다.

따라서 PostMessage를 사용하여 메시지를 보낸 후, 메시지를 보내는 프로세스가 종료되면 그 메시지가 수신자에게 도달할 수 있는지는 수신 윈도우의 상태에 따라 달라집니다. 수신 윈도우가 여전히 실행 중이고 메시지 큐가 남아 있다면, 메시지는 처리됩니다. 그러나 수신 윈도우가 종료되면 그 메시지는 더 이상 처리되지 않습니다.

2. SendMessage와 메시지 전달

SendMessage는 메시지를 보내는 동기 방식입니다. 이 방식은 메시지를 호출한 프로세스가 수신 윈도우의 프로시저에서 메시지를 처리할 때까지 대기하는 방식입니다.

  • 메시지 처리: SendMessage는 메시지가 수신 윈도우에 의해 처리될 때까지 호출한 프로세스가 대기합니다.
  • 프로세스 종료 시:
    • 메시지가 전송된 후 수신 프로세스가 종료되면, SendMessage는 메시지를 처리하지 못하게 됩니다.
    • 수신 윈도우가 종료되면 SendMessage는 메시지를 보냈으나 처리할 수 없게 되며, 대기 중인 호출은 중단되고, 메시지는 소실됩니다. 즉, SendMessage는 수신 프로세스가 종료되면 더 이상 그 메시지를 전달할 수 없으며, 메시지 처리가 이루어지지 않습니다.

요약: 프로세스 종료 시 상황

  • PostMessage:
    • 메시지는 수신 윈도우의 메시지 큐에 저장되며, 수신 윈도우가 실행 중이면 메시지를 처리합니다.
    • 수신 윈도우가 종료되면, 큐에 남아있던 메시지는 사라집니다.
    • 메시지를 보낸 프로세스가 종료되면, 해당 메시지가 수신자의 큐에 영향을 미치지 않습니다.
  • SendMessage:
    • 메시지는 수신 프로세스가 메시지를 처리할 때까지 대기합니다.
    • 수신 프로세스가 종료되면, 메시지는 소실되며 처리되지 않습니다.
    • 메시지를 보낸 프로세스가 종료되면 대기 중이던 호출이 중단되고, 메시지는 더 이상 전달되지 않습니다.

따라서, **PostMessage**를 사용하면 수신 프로세스가 종료되기 전까지 큐에 남아 있을 수 있으나, **SendMessage**는 수신 프로세스가 종료되면 메시지가 처리되지 않고 소실됩니다.

 
 
 
 
 
 
송신 프로세스가 종료될 때 PostMessage와 SendMessage의 동작 차이에 대해서도 중요한 점들이 있습니다. 각각의 방식이 송신 프로세스 종료와 관련해서 어떻게 동작하는지 설명하겠습니다.

1. SendMessage와 송신 프로세스 종료

SendMessage는 동기식 메시지 전달 방식입니다. 즉, 메시지를 보내고 나서 수신 프로세스가 해당 메시지를 처리할 때까지 송신 프로세스는 대기합니다.

  • 송신 프로세스 종료 시:
    • SendMessage를 호출한 프로세스가 메시지를 보내고, 그 후 메시지가 수신 프로세스에 의해 처리되기를 기다립니다.
    • 송신 프로세스가 종료되면, 송신 프로세스가 대기 중인 상태에서 갑자기 종료되면, 해당 메시지는 더 이상 처리되지 않습니다.
    • 송신 프로세스가 종료되면 메시지를 받기 위해 대기하던 쓰레드가 종료되기 때문에, 그 메시지는 전달되지 않거나 소실됩니다.
    • 송신 프로세스가 종료되면, 해당 프로세스의 메시지 큐가 제거되거나 정리되므로, 메시지 처리도 이루어지지 않게 됩니다.
    • 따라서 송신 프로세스가 종료되면 SendMessage를 통해 보낸 메시지는 사라지게 됩니다.

2. PostMessage와 송신 프로세스 종료

PostMessage는 비동기식 메시지 전달 방식으로, 송신 프로세스가 메시지를 큐에 넣고 난 후, 송신 프로세스는 즉시 반환됩니다. 메시지는 수신 프로세스의 큐에 저장되며, 그 큐에서 메시지를 나중에 수신 프로세스가 처리합니다.

  • 송신 프로세스 종료 시:
    • PostMessage는 메시지를 보내고 나서 송신 프로세스가 종료되어도 상관없습니다.
    • PostMessage는 비동기적으로 메시지를 큐에 넣고, 송신 프로세스는 해당 메시지의 처리와 관계없이 즉시 반환되므로, 송신 프로세스의 종료가 메시지 전송에 영향을 미치지 않습니다.
    • 수신 프로세스가 계속 실행 중이라면, 메시지는 수신 프로세스의 메시지 큐에 저장되고 나중에 처리됩니다.
    • 송신 프로세스가 종료되더라도, 메시지는 수신 프로세스가 처리할 때까지 큐에 남아있습니다.
    • 단, 수신 프로세스가 종료되면 큐에서 메시지가 제거되므로, 메시지는 더 이상 처리되지 않습니다.

요약: 송신 프로세스 종료 시 동작 차이

  • SendMessage:
    • 송신 프로세스가 종료되면, 대기 중인 메시지는 처리되지 않고 사라집니다.
    • 송신 프로세스가 종료되면서 메시지 전송 대기가 중단되기 때문에, 해당 메시지가 수신 프로세스에 전달될 수 없습니다.
  • PostMessage:
    • 송신 프로세스가 종료되더라도, 메시지는 수신 프로세스의 큐에 남아 있을 수 있습니다.
    • 송신 프로세스의 종료는 메시지 큐에 이미 저장된 메시지에 영향을 미치지 않으며, 수신 프로세스가 실행 중이라면 메시지를 계속 처리할 수 있습니다.

결론

  • **SendMessage**는 송신 프로세스의 종료가 메시지 처리에 큰 영향을 미칩니다. 송신 프로세스가 종료되면 메시지가 더 이상 전달되지 않거나 처리되지 않습니다.
  • 반면 **PostMessage**는 송신 프로세스가 종료되어도 메시지 처리에 영향을 주지 않으며, 수신 프로세스가 실행 중이라면 메시지를 처리할 수 있습니다.

 

이 코드는 region[2]로 그룹화하여 첫 번째 <select id="regionGroup"> 에 표시하고,
해당 region[2]에 속하는 region[1]을 두 번째 <select id="regionSub"> 에 표시합니다.
사용자가 region[1]을 선택하면 value가 region[0]이 되고, 이를 sendData(value)로 전달합니다.

    <label for="regionGroup">지역 그룹:</label>
    <select id="regionGroup">
        <option value="">-- 선택하세요 --</option>
    </select>

    <label for="regionSub">세부 지역:</label>
    <select id="regionSub">
        <option value="">-- 선택하세요 --</option>
    </select>

    <script>
        const region = [
            ["1168000000","강남구","서울시"],
            ["1174000000","강동구","서울시"],
            ["1130500000","강북구","서울시"],
            ["1132000000","도봉구","서울시"],
            ["1123000000","동대문구","서울시"],
            ["1159000000","동작구","서울시"],
            ["1147000000","양천구","서울시"],
            ["1156000000","영등포구","서울시"],
            ["1117000000","용산구","서울시"],
            ["1138000000","은평구","서울시"],
            ["1111000000","종로구","서울시"],
            ["1114000000","중구","서울시"],
            ["1126000000","중랑구","서울시"],
            ["4182000000","가평군","경기도"],
            ["4128100000","고양시 덕양구","경기도"],
            ["4128500000","고양시 일산동구","경기도"],
            ["4113300000","성남시 중원구","경기도"],
            ["4163000000","양주시","경기도"],
            ["4122000000","평택시","경기도"],
            ["4165000000","포천시","경기도"],
            ["4145000000","하남시","경기도"],
            ["4159000000","화성시","경기도"],
            ["2871000000","강화군","인천시"],
            ["2824500000","계양구","인천시"],
            ["2641000000","금정구","부산시"],
        ];

        const regionGroupSelect = document.getElementById("regionGroup");
        const regionSubSelect = document.getElementById("regionSub");

        // 그룹화된 지역 그룹 가져오기
        const groups = [...new Set(region.map(item => item[2]))];

        // 그룹화된 데이터 <select>에 추가
        groups.forEach(group => {
            const option = document.createElement("option");
            option.value = group;
            option.textContent = group;
            regionGroupSelect.appendChild(option);
        });

        // 지역 그룹 변경 시 세부 지역 업데이트
        regionGroupSelect.addEventListener("change", () => {
            const selectedGroup = regionGroupSelect.value;
            regionSubSelect.innerHTML = '<option value="">-- 선택하세요 --</option>'; // 초기화

            if (selectedGroup) {
                const filteredRegions = region.filter(item => item[2] === selectedGroup);
                filteredRegions.forEach(item => {
                    const option = document.createElement("option");
                    option.value = item[0];
                    option.textContent = item[1];
                    regionSubSelect.appendChild(option);
                });
            }
        });

        // 세부 지역 선택 시 SendData 호출
        regionSubSelect.addEventListener("change", () => {
            const selectedValue = regionSubSelect.value;
            if (selectedValue) {
                SendData(selectedValue);
            }
        });

        // SendData 함수 정의
        function SendData(value) {
            alert(`Selected value: ${value}`);
            console.log("SendData called with value:", value);
        }
    </script>

아파트명클릭시에 네이버부동산으로 LINK)

 

index.html
0.03MB

문법적 내용과 동작 원리

다음 코드는 C#에서 비동기 작업을 별도의 스레드에서 실행하는 데 사용됩니다:

_ = Task.Run(async () => await RUN());

구성 요소 분석

  1. Task.Run
    • Task.Run은 비동기 작업을 스레드 풀에서 실행하기 위해 사용됩니다.
    • 일반적으로 CPU 바운드 작업을 스레드 풀로 오프로드하여 UI 스레드를 차단하지 않게 하기 위해 사용됩니다.
    • 반환값은 Task입니다.
  2. 람다 표현식 (async () => await RUN())
    • Task.Run의 매개변수로 비동기 람다 함수가 전달되었습니다.
    • async로 선언된 람다 함수는 비동기 작업을 실행할 수 있으며, await를 통해 다른 비동기 작업(RUN)의 완료를 기다립니다.
  3. await RUN()
    • RUN 메서드는 비동기 메서드입니다. 해당 작업이 완료될 때까지 기다리지만, 비동기 흐름을 유지하므로 호출한 스레드를 차단하지 않습니다.
    • 이 코드에서는 RUN 메서드가 완료될 때까지 비동기적으로 처리됩니다.
  4. _ =
    • 반환된 Task 객체를 변수에 할당하지 않고 무시합니다.
    • 실행 중인 작업을 추적하거나 결과를 사용할 필요가 없을 때 주로 사용됩니다.
    • _는 읽지 않는 변수를 나타내는 C# 표준 컨벤션입니다.

동작 원리

  1. Task.Run의 호출:
    • Task.Run은 스레드 풀의 대기열에 람다 함수(async () => await RUN())를 작업으로 추가합니다.
    • 이 작업은 백그라운드 스레드에서 실행됩니다.
  2. 람다 함수 실행:
    • 스레드 풀에서 작업이 실행되면서 람다 함수가 호출됩니다.
    • 람다 함수는 비동기적으로 RUN()을 호출하고, RUN()이 완료될 때까지 비동기적으로 기다립니다.
  3. 비동기 흐름 유지:
    • await 키워드는 비동기 작업(RUN)의 완료를 기다리되, 호출 스레드를 차단하지 않습니다.
    • RUN()이 완료되면 await 뒤의 코드를 계속 실행합니다.
  4. 결과 처리 없음:
    • Task.Run의 반환값(Task)을 무시했기 때문에 작업 결과를 추적하거나 예외를 처리하지 않습니다.

주의사항

  1. 예외 처리:
    • 무시된 Task에서 발생한 예외는 호출자가 확인하지 않기 때문에 런타임 경고가 발생하거나 추적되지 않을 수 있습니다.
    • 안정성을 위해 예외 처리를 추가하거나 반환된 Task를 추적하는 것이 좋습니다.
  2. 예시:
    _ = Task.Run(async () => {
    try { await RUN(); }
    catch (Exception ex)
    { // 예외 로깅 또는 처리 Console.WriteLine($"Error: {ex.Message}");
    }
    });

  3. UI 컨트롤 접근:
    • 백그라운드 스레드에서 UI 컨트롤을 직접 조작하면 문제가 발생합니다. Invoke 또는 SynchronizationContext를 통해 UI 스레드에서 실행해야 합니다.
  4. 성능 고려:
    • Task.Run은 스레드 풀의 작업자 스레드를 사용하므로, 남용하면 시스템 리소스에 영향을 줄 수 있습니다. IO 바운드 작업이나 짧은 작업의 경우 다른 비동기 메서드로 해결하는 것이 더 효율적입니다.

한 줄 요약

이 코드는 비동기 작업을 백그라운드 스레드에서 실행하여 UI 스레드를 차단하지 않도록 설계된 패턴입니다. Task.Run과 비동기 람다를 결합하여 비동기 작업을 효율적으로 실행합니다.



필자의 사용예)

protected override bool ProcessCmdKey(ref Message message, Keys keyData)
{
    const int WM_KEYDOWN = 0x0100;

     switch (message.Msg)
     {
          case WM_KEYDOWN:
          if (keyData.ToString() == "Space")
          {
          // 비동기 메서드를 실행하되, 기다리지 않고 처리
          _ = Task.Run(async () => await RUN());

          return true; // 키가 처리되었음을 나타냄
          }
          break;
          default:
          break;
     }
     return base.ProcessCmdKey(ref message, keyData);
}



Winsows Form에 DataGridView를 이용한 네이버 부동산 크롤링 데이타 표시(서울시.마포구)

>dotnet new winforms -o NaverWinFrm401
>dotnet add package ServiceStack
>dotnet add package Newtonsoft.Json
>dotnet add package System.Data.SQLite
>dotnet add package ServiceStack.Redis

특징)
1. INFO table에서 데이타를 읽어와서 표시하는것을 원칙
2. INFO table의 데이타는 프로그램시작시에, INFOTMP에 크롤링데이타 저장후에 INFO로 복사

EstateMainFrm.cs
0.02MB

 

네이버부동산.매매상한.하한 (Web Crawling) 아키텍쳐

1. Web Crawling Server By C# code
2. Web Crawling Data Interface By Redis Engine
3. Node Server

웹페이지 Estate.Redis.Server(네이버부동산.크롤링 엔진) <-> REDIS
Estate.Node.Server <-> REDIS

 

**웹페이지 크롤링(Web Crawling)**은 인터넷 상의 웹페이지들을 자동으로 탐색하고 데이터를 수집하는 작업을 의미합니다. 이 작업은 주로 웹 크롤러(Web Crawler) 또는 **스파이더(Spider)**라고 불리는 프로그램이나 스크립트를 사용해 수행됩니다.

웹 크롤링의 주요 과정

  1. 시작 URL 설정: 크롤링할 웹사이트의 URL(예: https://example.com)을 시작점으로 설정합니다.
  2. HTML 문서 다운로드: 웹페이지의 HTML 소스 코드를 HTTP 요청을 통해 다운로드합니다.
  3. 데이터 파싱: HTML 문서에서 필요한 데이터(텍스트, 이미지, 링크 등)를 분석 및 추출합니다.
  4. 새로운 링크 수집: 페이지 내의 하이퍼링크를 추출하여, 크롤링할 새로운 페이지를 추가로 찾아냅니다.
  5. 반복: 설정된 범위나 조건에 따라 위 과정을 반복합니다.

사용 목적

  • 검색 엔진: 구글이나 네이버 같은 검색 엔진은 크롤러를 사용해 웹페이지를 수집하고 색인화하여 검색 결과를 제공합니다.
  • 데이터 수집: 특정 주제나 산업 관련 데이터를 대량으로 수집하여 분석에 활용합니다.
  • 가격 비교: 전자상거래 사이트에서 상품의 가격 정보를 비교하기 위해 사용됩니다.
  • 콘텐츠 추출: 뉴스, 블로그, 리뷰 사이트 등에서 정보를 수집하여 재가공합니다.

웹 크롤링 도구 및 라이브러리

  • Python: BeautifulSoup, Scrapy, Selenium
  • Java: Jsoup, Apache Nutch
  • JavaScript: Puppeteer, Cheerio

주의사항

  1. 저작권 및 이용약관: 크롤링 대상 사이트의 데이터를 수집하고 사용할 때는 저작권과 이용약관을 준수해야 합니다.
  2. robots.txt: 대부분의 웹사이트는 robots.txt 파일을 통해 크롤러가 접근 가능한 경로와 불가능한 경로를 명시합니다.
  3. 서버 부하: 크롤러의 과도한 요청은 서버에 부담을 줄 수 있으므로 요청 간격 조정과 적절한 속도 제어가 필요합니다.

웹 크롤링은 데이터를 자동화하여 효율적으로 수집할 수 있는 강력한 도구이지만, 윤리적이고 합법적인 방식으로 활용하는 것이 중요합니다.

네이버부동산.크롤링 매매상한,하한,전세상한,하한(경기도)

 

index.html
0.02MB

"4182000000","가평군"
"4128100000","고양시 덕양구"
"4128500000","고양시 일산동구"
"4128700000","고양시 일산서구"
"4129000000","과천시"
"4121000000","광명시"
"4161000000","광주시"
"4131000000","구리시"
"4141000000","군포시"
"4157000000","김포시"
"4136000000","남양주시"
"4125000000","동두천시"
"4119400000","부천시 소사구"
"4119600000","부천시 오정구"
"4119200000","부천시 원미구"
"4113500000","성남시 분당구"
"4113100000","성남시 수정구"
"4113300000","성남시 중원구"
"4111300000","수원시 권선구"
"4111700000","수원시 영통구"
"4111100000","수원시 장안구"
"4111500000","수원시 팔달구"
"4139000000","시흥시"
"4127300000","안산시 단원구"
"4127100000","안산시 상록구"
"4155000000","안성시"
"4117300000","안양시 동안구"
"4117100000","안양시 만안구"
"4163000000","양주시"
"4183000000","양평군"
"4167000000","여주시"
"4180000000","연천군"
"4137000000","오산시"
"4146300000","용인시 기흥구"
"4146500000","용인시 수지구"
"4146100000","용인시 처인구"
"4143000000","의왕시"
"4115000000","의정부시"
"4150000000","이천시"
"4148000000","파주시"
"4122000000","평택시"
"4165000000","포천시"
"4145000000","하남시"
"4159000000","화성시"

Main 메서드와 STAThread 속성

C#에서 비동기 메서드를 동기적으로 호출하는 구조를 포함하고 있습니다. 주로 Task.Run과 await 키워드를 사용하여 비동기 메서드를 처리하는 방식입니다. 이 코드를 더 잘 이해하기 위해 각 부분을 세분화해서 설명드리겠습니다.

1. Main 메서드와 STAThread 속성

  • Main 메서드는 C# 애플리케이션의 진입점(entry point) 입니다.
  • STAThread 속성은 Windows Forms 또는 WPF 애플리케이션에서 메인 스레드가 단일 스레드 아파트(Single-Threaded Apartment, STA) 모드로 실행되도록 지정합니다. 이는 주로 COM 상호 운용성(Interoperability)과 관련이 있습니다. Windows Forms 및 WPF에서 UI 스레드는 STA 모드로 실행되어야 합니다.
// FetchComplexDetailsClass:주로 크롤링작업이 이루어지는 프로그램작업일떄에 쓰이는 호출방법
// FetchComplexDetailsClass:주로 크롤링작업이 이루어지는 프로그램작업일떄에 쓰이는 호출방법
 
[STAThread]
static void Main()
{
     // 비동기 메서드를 호출하는 부분
     Task.Run(async () =>
     {
          FetchComplexDetailsClass frm = new FetchComplexDetailsClass();
          await frm.RUN();
    }).GetAwaiter().GetResult(); // 비동기 작업이 완료될 때까지 대기
}
 

2. Task.Run(async () => { ... })

  • Task.Run은 주어진 델리게이트 또는 메서드를 비동기적으로 실행하기 위해 새로운 태스크를 생성합니다.
  • async () => { ... }는 비동기 메서드를 정의하는 람다 표현식입니다. 이 람다 안에서 비동기 작업을 수행할 수 있습니다.
  • 이 부분은 백그라운드 스레드에서 비동기 작업을 수행하게 합니다. 이 경우 frm.RUN() 메서드를 비동기적으로 호출하려고 합니다.

3. await frm.RUN()

  • await는 비동기 메서드를 기다리게 하며, 메서드가 완료될 때까지 나머지 코드를 실행하지 않습니다.
  • frm.RUN()은 FetchComplexDetailsClass 객체의 RUN이라는 비동기 메서드를 호출합니다.
  • 이 메서드가 완료될 때까지 현재 작업(스레드)이 대기 상태에 들어갑니다. RUN 메서드 내부에서 비동기 작업(예: 파일 I/O, 네트워크 호출 등)을 처리하고 그 결과를 반환할 때까지 기다리게 됩니다.

4. .GetAwaiter().GetResult()

  • .GetAwaiter().GetResult()는 비동기 작업이 완료될 때까지 기다리면서, 비동기 결과를 동기적으로 처리하는 방법입니다.
  • GetAwaiter()는 Task에서 비동기 작업의 상태를 가져옵니다.
  • GetResult()는 비동기 작업이 완료될 때까지 기다리고, 그 결과를 동기적으로 반환합니다. 이 부분에서 예외가 발생하면 AggregateException을 발생시킬 수 있습니다.

5. 전체 흐름 설명

  1. Main 메서드 실행: 프로그램이 시작되면 Main 메서드가 호출됩니다. 여기서는 STAThread 속성 덕분에 단일 스레드 아파트에서 실행됩니다.
  2. Task.Run 실행: Task.Run은 새로운 비동기 작업을 시작합니다. 이 작업은 백그라운드 스레드에서 실행됩니다. async 람다 표현식 안에서 비동기 작업을 호출하고 있습니다.
  3. await frm.RUN(): frm.RUN() 비동기 메서드를 호출하면서, 이 메서드가 완료될 때까지 기다립니다. 이 메서드 내부에서 실제 비동기 작업이 수행됩니다(예를 들어, 네트워크 요청 또는 데이터 처리).
  4. GetAwaiter().GetResult(): await가 내부적으로 비동기 작업을 기다리지만, .GetAwaiter().GetResult()를 통해 해당 작업이 완료될 때까지 현재 스레드를 동기적으로 차단합니다. 즉, 이 코드가 없으면 비동기 메서드의 작업이 완료되기 전에 Main이 종료되었을 수 있기 때문에, 프로그램이 예기치 않게 종료되지 않도록 합니다.
  5. 비동기 메서드 완료 후 종료: frm.RUN() 메서드가 완료되면, Task.Run 내의 비동기 작업이 종료되고, Main 메서드가 종료됩니다. 이제 프로그램이 정상적으로 종료됩니다.

동기와 비동기 결합

  • 비동기 작업은 일반적으로 UI 스레드를 차단하지 않으며, 다른 작업을 처리하는 동안 동시에 실행됩니다.
  • 하지만 GetAwaiter().GetResult()는 비동기 작업을 동기적으로 기다리게 하므로, Main 스레드는 비동기 작업이 완료될 때까지 대기하고, 작업이 완료된 후에 종료됩니다.

6. 왜 이런 방식으로 사용하나요?

  • 비동기 작업을 UI 스레드에서 처리할 때 동기적으로 메인 스레드가 대기하도록 강제하는 경우가 있습니다. 이를 통해 UI를 잠시 멈추게 하지 않고 비동기 작업을 안전하게 처리할 수 있습니다.
  • Task.Run을 사용하면 비동기 작업이 백그라운드에서 실행되므로 UI 스레드의 블로킹을 피할 수 있습니다. 다만, .GetAwaiter().GetResult()를 사용하여 그 결과를 동기적으로 기다리는 방식은 메인 스레드가 작업 완료를 기다리게 만들기 때문에 주로 Main 메서드에서 비동기 작업을 호출할 때 사용됩니다.

결론

  • Task.Run을 통해 비동기 작업을 백그라운드에서 시작하고, await를 사용해 비동기 메서드를 기다립니다.
  • GetAwaiter().GetResult()를 사용하여 비동기 작업이 완료될 때까지 메인 스레드를 동기적으로 대기하게 만듭니다.
  • 이 방식은 주로 콘솔 애플리케이션에서 비동기 작업을 동기적으로 처리하고 싶을 때 사용됩니다.

 

네이버부동산.크롤링 매매상한,하한,전세상한,하한(전체지역)

{"1100000000","서울시"},
{"4100000000","경기도"},
{"2800000000","인천시"},
{"2600000000","부산시"},
{"3000000000","대전시"},
{"2700000000","대구시"},
{"3100000000","울산시"},
{"3600000000","세종시"},
{"2900000000","광주시"},
{"5100000000","강원도"},
{"4300000000","충청북도"},
{"4400000000","충청남도"},
{"4700000000","경상북도"},
{"4800000000","경상남도"},
{"5200000000","전북도"},
{"4600000000","전라남도"},
{"5000000000","제주도"},

https://new.land.naver.com/api/regions/list?cortarNo=5011011100
https://new.land.naver.com/api/regions/list?cortarNo=5011013300
[2024-12-29 11:28:10]
Table 'ESTATENAVERALLINFO' has been successfully deleted.
[2024-12-29 11:28:10]
62483 rows have been transferred.
[2024-12-29 11:28:10]


Recursive 형태로 접근, 전체 APT를 수집해서 SQLite 테이블에 저장

1. 서버에서 크롤링및 저장
2. 조회시에 REDIS를 통한 데이타제공

 

 

index.html
0.01MB
index_by_dong.html
0.02MB

 

쓰레드 프로그램에서 여러 사람이 동시에 사용 중에 데드락(교착 상태, Deadlock)이 발생하는 경우는 다음과 같은 상황에서 주로 나타납니다.

1. 상호 배제 (Mutual Exclusion)

  • 공유 자원이 동시에 여러 쓰레드에 의해 사용되지 않도록 하기 위해 락(Lock)을 사용하는 경우.
  • 특정 자원이 한 번에 하나의 쓰레드만 사용할 수 있도록 제한되기 때문에 다른 쓰레드가 자원을 대기하게 됩니다.

2. 점유 및 대기 (Hold and Wait)

  • 한 쓰레드가 이미 락을 점유한 상태에서 다른 자원을 요청하지만, 그 자원이 다른 쓰레드에 의해 점유되고 있는 상황.
  • 이미 점유한 락을 해제하지 않고 대기하면서 교착 상태가 발생합니다.

3. 비선점 (No Preemption)

  • 점유한 자원을 강제로 회수하지 못하는 경우.
  • 즉, 쓰레드가 스스로 자원을 반환할 때까지 기다려야 하며, 대기 상태가 해결되지 않을 가능성이 있습니다.

4. 순환 대기 (Circular Wait)

  • 두 개 이상의 쓰레드가 서로 자원을 대기하면서 순환 구조가 형성되는 경우.
  • 예:
    • 쓰레드 A는 자원 1을 점유하고 자원 2를 요청.
    • 쓰레드 B는 자원 2를 점유하고 자원 1을 요청.

데드락 발생 예시

class DeadlockExample {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    public void method1() {
        synchronized (lock1) {
            System.out.println("Thread 1: Holding lock1...");
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock2...");
            synchronized (lock2) {
                System.out.println("Thread 1: Acquired lock2!");
            }
        }
    }

    public void method2() {
        synchronized (lock2) {
            System.out.println("Thread 2: Holding lock2...");
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock1...");
            synchronized (lock1) {
                System.out.println("Thread 2: Acquired lock1!");
            }
        }
    }
}

public class DeadlockTest {
    public static void main(String[] args) {
        DeadlockExample example = new DeadlockExample();

        Thread t1 = new Thread(example::method1);
        Thread t2 = new Thread(example::method2);

        t1.start();
        t2.start();
    }
}

설명

  • Thread 1은 lock1을 점유하고 lock2를 요청.
  • 동시에, Thread 2는 lock2를 점유하고 lock1을 요청.
  • 두 쓰레드가 서로를 기다리며 순환 대기 상태가 발생하여 데드락이 생깁니다.

데드락 방지 방법

  1. 자원 획득 순서 지정
    • 모든 쓰레드가 자원을 요청하는 순서를 동일하게 유지합니다.
  2. 타임아웃 설정
    • 자원 요청 시 일정 시간 대기 후 실패로 처리하여 데드락을 방지합니다.
  3. 데드락 감지 및 회복
    • 데드락 감지 알고리즘을 통해 순환 대기를 탐지하고 적절히 해소합니다.
  4. 자원 회수
    • 자원 점유가 너무 길어지면 강제로 회수하는 방법을 고려합니다.
  5. 락 분해 (Lock Granularity 감소)
    • 큰 락을 여러 작은 락으로 분리하여 동시성을 높이고 데드락 가능성을 줄입니다.

error CS0246: 'ServiceStack' 형식 또는 네임스페이스 이름을 찾을 수 없습니다. using 지시문 또는 어셈블리 참조가 있는지 확인하세요.
error CS0246: 'Newtonsoft' 형식 또는 네임스페이스 이름을 찾을 수 없습니다. using 지시문 또는 어셈블리 참조가 있는지 확인하세요.
error CS0234: 'System.Data' 네임스페이스에 'SQLite' 형식 또는 네임스페이스 이름이 없습니다. 어셈블리 참조가 있는지 확인하세요.
error CS0246: 'SQLiteConnection' 형식 또는 네임스페이스 이름을 찾을 수 없습니다. using 지시문 또는 어셈블리 참조가 있는지 확인하세요.
error CS0246: 'RedisClient' 형식 또는 네임스페이스 이름을 찾을 수 없습니다. using 지시문 또는 어셈블리 참조가 있는지 확인하세요.

제시하신 에러 메시지를 보면, 코드에서 사용하고 있는 네임스페이스나 타입들이 찾을 수 없다는 오류입니다. 이는 관련된 NuGet 패키지가 프로젝트에 설치되지 않았거나, 제대로 참조되지 않았기 때문에 발생한 문제로 보입니다.

각 에러 메시지에 대해 필요한 NuGet 패키지를 아래와 같이 정리할 수 있습니다:

1. 'ServiceStack' 형식 또는 네임스페이스 이름을 찾을 수 없습니다.

이 에러는 ServiceStack 라이브러리를 찾을 수 없다는 오류입니다. 이 라이브러리를 사용하려면 ServiceStack NuGet 패키지를 설치해야 합니다.

설치 명령:

 
dotnet add package ServiceStack

2. 'Newtonsoft' 형식 또는 네임스페이스 이름을 찾을 수 없습니다.

이 에러는 Newtonsoft.Json 라이브러리를 찾을 수 없다는 오류입니다. 이 라이브러리를 사용하려면 Newtonsoft.Json 패키지를 설치해야 합니다.

설치 명령:

dotnet add package Newtonsoft.Json

3. 'System.Data.SQLite' 형식 또는 네임스페이스 이름을 찾을 수 없습니다.

이 에러는 SQLite 관련 네임스페이스를 찾을 수 없다는 오류입니다. SQLite를 사용하려면 System.Data.SQLite 패키지를 설치해야 합니다.

설치 명령:

dotnet add package System.Data.SQLite

4. 'SQLiteConnection' 형식 또는 네임스페이스 이름을 찾을 수 없습니다.

위와 동일한 이유로, System.Data.SQLite 패키지가 필요합니다.

설치 명령:

dotnet add package System.Data.SQLite

5. 'RedisClient' 형식 또는 네임스페이스 이름을 찾을 수 없습니다.

이 에러는 Redis 관련 클래스인 RedisClient를 찾을 수 없다는 오류입니다. Redis를 사용하려면 ServiceStack.Redis 패키지를 설치해야 합니다.

설치 명령:

dotnet add package ServiceStack.Redis

요약

위의 네임스페이스와 클래스를 사용하기 위한 NuGet 패키지들은 다음과 같습니다:

  1. ServiceStack - dotnet add package ServiceStack
  2. Newtonsoft.Json - dotnet add package Newtonsoft.Json
  3. System.Data.SQLite - dotnet add package System.Data.SQLite
  4. ServiceStack.Redis - dotnet add package ServiceStack.Redis

각각의 패키지를 설치한 후, 다시 빌드를 시도해보세요.

 

안양만안구,동안구 모든 아파트 리스트대한 네이버부동산 매매호가, 상한,하한 크롤링에 의한 리스트정리

 

index.html
0.01MB



"4117310300": "평촌동",
"4117310200": "관양동",
"4117310100": "비산동",
"4117310400": "호계동",
"4117110300": "박달동",
"4117110200": "석수동",
"4117110100": "안양동"
"4117300000": "안양시 동안구"
"4117100000": "안양시 만안구"

 

Windows 환경에서 사용할 수 있는 부하 테스트 도구와 명령어를 아래에 정리하였습니다. 각각의 특징과 설치 방법도 함께 제공합니다.


1. Apache JMeter

특징:

  • 가장 널리 사용되는 오픈소스 부하 테스트 도구.
  • HTTP, HTTPS, WebSocket 등 다양한 프로토콜을 지원.
  • GUI 모드와 CLI(Command Line Interface) 모드 제공.

설치 및 사용:

  1. JMeter 공식 사이트에서 다운로드 후 설치.
  2. bin 디렉토리에서 jmeter.bat 실행하여 GUI 실행.
  3. 부하 테스트 스크립트를 생성 후, CLI 모드로 실행: 
    • -n: 비GUI 모드 실행.
    • -t: 테스트 계획 파일(.jmx).
    • -l: 결과 로그 파일.
    • -o: HTML 보고서 생성 디렉토리.
  4.  
    jmeter -n -t 테스트파일.jmx -l 결과파일.jtl -e -o 보고서_디렉토리

2. Artillery

특징:

  • Node.js 기반의 경량화된 부하 테스트 도구.
  • HTTP, WebSocket, Socket.io 등을 지원.
  • YAML로 시나리오 작성 가능.

설치 및 사용:

  1. Node.js 설치 후, Artillery 설치:
    npm install -g artillery
  2. 기본적인 부하 테스트 실행:
     
    artillery quick --count 50 --num 10 http://localhost:3000/api
    • 50명의 사용자가 10회 요청.
  3. 고급 테스트 시나리오 작성:
    • test.yml 작성 예:
      yaml
      config: target: 'http://localhost:3000' phases: - duration: 60 arrivalRate: 10 scenarios: - flow: - get: url: "/api"
    • 실행:
       
      artillery run test.yml

3. k6

특징:

  • 고성능 부하 테스트 도구로, JavaScript로 스크립트 작성.
  • CLI 중심으로 작동하며, 간단한 설치와 사용법.
  • 실시간 결과 제공.

설치 및 사용:

  1. k6 다운로드 페이지에서 Windows용 설치 파일 다운로드.
  2. 간단한 스크립트 작성 (script.js):
    javascript
    import http from 'k6/http'; import { check, sleep } from 'k6'; export let options = { vus: 10, // 가상 사용자 수 duration: '30s', // 테스트 지속 시간 }; export default function () { let res = http.get('http://localhost:3000/api'); check(res, { 'status is 200': (r) => r.status === 200 }); sleep(1); }
  3. 테스트 실행:
     
    k6 run script.js

4. Locust

특징:

  • Python 기반 부하 테스트 도구.
  • 웹 인터페이스로 실시간 결과 확인 가능.
  • 테스트 코드 작성 가능(Python).

설치 및 사용:

  1. Python 설치 후 Locust 설치:
     
    pip install locust
  2. 테스트 스크립트 작성 (locustfile.py):
    python
    from locust import HttpUser, task, between class MyUser(HttpUser): wait_time = between(1, 2) @task def test_api(self): self.client.get("/api")
  3. Locust 실행:
     
    locust -f locustfile.py --host=http://localhost:3000
  4. 브라우저에서 http://localhost:8089 접속 후 테스트 실행.

5. Wrk

특징:

  • 단순하고 빠른 CLI 기반 부하 테스트 도구.
  • HTTP 프로토콜만 지원.
  • 고성능 테스트 가능.

설치 및 사용:

  1. Wrk 공식 사이트에서 Windows용 빌드 다운로드.
  2. 부하 테스트 실행:
    cmd
    코드 복사
    wrk -t4 -c100 -d30s http://localhost:3000/api
    • -t4: 쓰레드 수.
    • -c100: 동시 연결 수.
    • -d30s: 테스트 지속 시간(30초).

6. Postman

특징:

  • GUI 기반의 HTTP 요청 테스트 도구.
  • Postman Collection Runner를 이용해 여러 요청을 시뮬레이션 가능.

사용법:

  1. Postman 설치 및 요청 작성.
  2. Collection Runner를 통해 반복 요청 수행:
    • 요청 반복 횟수와 데이터를 설정하여 부하 테스트.

7. Gatling

특징:

  • Scala 기반의 부하 테스트 도구.
  • 고성능 테스트를 위해 최적화.

설치 및 사용:

  1. Gatling 다운로드 페이지에서 설치.
  2. 기본 테스트 실행:
     
    gatling.sh
  3. 사용자 정의 테스트 작성(Scala 필요).

추천 조합:

  • 간단한 부하 테스트: Artillery, k6
  • 고급 부하 테스트: JMeter, Gatling
  • Python 친화적: Locust
  • 빠른 CLI 테스트: Wrk

실시간 Push 데이타 적용(Index.HTML)

<div id="hiddenstockcode" hidden-data-stock-code=""></div>
// 데이터 입력하기
const stockcodeelement = document.getElementById("hiddenstockcode");
stockcodeelement.setAttribute("hidden-data-stock-code", dataReal.stockCode);
// HTML 요소 가져오기, HTML에 저장된 데이터 읽기
const stockcodeelement = document.getElementById("hiddenstockcode");
const storedstockcode = stockcodeelement.getAttribute("hidden-data-stock-code");
if(dataObject.stockCode === storedstockcode) {}
else {}

실시간 Push 데이타 적용)
화면에 보이는 종목코드에 해당되는지를 판단해서, 맞으면 업데이트한다.

index.html
0.05MB

 

Redis의 MONITOR 명령어는 Redis 서버에서 실행되는 모든 명령어를 실시간으로 보여줍니다. 
제공된 로그를 분석해 보겠습니다.

1734668509.318594 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668509.425162 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668519.504719 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668519.612482 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668529.678985 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668529.784129 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668539.847388 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668539.953286 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668550.015080 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668550.122723 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668560.195185 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668560.300094 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668570.331758 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668570.439404 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668580.507469 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668580.612993 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668590.665583 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668590.771936 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668600.852657 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668600.959458 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668610.892517 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668610.997436 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668620.962275 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668621.069678 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668631.063810 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668631.169203 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668641.161092 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668641.266712 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"
1734668651.240528 [0 127.0.0.1:15996] "BRPOP" "CLI" "10"
1734668651.346548 [0 127.0.0.1:15997] "BRPOP" "CLK" "10"

형식 분석
각 라인의 구조는 다음과 같습니다:

[타임스탬프] [데이터베이스 번호 클라이언트 정보] "명령어" "키" "옵션"

타임스탬프
로그의 첫 번째 값(예: 1734668438.713544)은 Unix Epoch 시간입니다. 초 단위와 밀리초 단위로 명령이 실행된 시점을 나타냅니다.

데이터베이스 번호 및 클라이언트 정보
[0 127.0.0.1:15996]에서 [0]은 Redis 데이터베이스 번호(기본값은 0)이고, 127.0.0.1:15996은 명령을 실행한 클라이언트의 IP와 포트입니다.

명령어와 인자
"BRPOP" "CLI" "10"는 Redis 명령어와 그 인자입니다.

BRPOP: 블로킹 팝 명령으로, 리스트의 마지막 요소를 제거하고 반환합니다. 리스트가 비어 있으면 지정된 시간 동안 대기합니다.
"CLI"와 "CLK": 각각 대기하려는 리스트의 키 이름입니다.
"10": 대기 시간(10초)을 의미합니다.

로그의 의미
동작
클라이언트 두 개(127.0.0.1:15996과 127.0.0.1:15997)가 BRPOP 명령어를 주기적으로 호출하고 있습니다.
각각 CLI와 CLK라는 키를 대상으로 10초 동안 대기합니다.
결과 추정
로그에서 주기적으로 BRPOP 호출이 반복되고 있습니다. 이는 리스트에서 요소를 가져오려는 시도지만, 리스트에 항목이 없기 때문에 BRPOP의 대기 시간이 끝난 뒤 명령이 다시 호출되는 것으로 보입니다.

주요 내용
주기적인 호출: 두 클라이언트가 각각 10초 간격으로 BRPOP을 호출하며, 대기 시간이 끝난 뒤 다시 명령을 재시도하고 있습니다.
리스트 상태: CLI와 CLK 키에 매핑된 리스트들이 비어 있는 상태일 가능성이 큽니다.
실제로 리스트에 데이터가 있었다면 BRPOP은 즉시 데이터를 반환하고, 명령이 재호출되는 주기가 더 불규칙적일 것입니다.

개선 또는 검토할 점
리스트 상태 확인
CLI와 CLK 리스트가 의도한 대로 데이터가 푸시되고 있는지 확인하세요. 리스트에 항목이 없다면 데이터 생산자(프로듀서)의 작업 상태를 점검해야 합니다.

논블로킹 대안 검토
반복적으로 BRPOP을 호출하는 대신, 데이터 생산자와 소비자 간의 데이터 흐름이 원활하게 이루어지도록 설계해야 합니다. 
예를 들어 Pub/Sub 모델을 사용하는 방안을 고려할 수 있습니다.

성능 영향
빈 리스트에 대해 계속 BRPOP을 호출하면 Redis 서버에 불필요한 부하가 생길 수 있습니다. 
호출 주기와 데이터 흐름을 최적화해야 합니다.

using System;
using System.Timers;
using System.IO;

class Program
{
    public static void Main()
    {
CHKNM nm = new CHKNM();
        nm.RUN();
    }
}
class CHKNM
{
    string manualDT = "20241201180000";
    DateTime now;

    public void RUN()
    {
        int days = 0;
        now = DateTime.ParseExact(manualDT, "yyyyMMddHHmmss", System.Globalization.CultureInfo.InvariantCulture);

while(true)
{
    DateTime manual = now.AddDays(days);

    //0: Sunday, 1: Monday, ..., 6: Saturday
    int dayNumber = (int)manual.DayOfWeek;

    if(dayNumber == 5)
    {
        if(manual.ToString("dd") == "13")
{
    Console.WriteLine(manual.ToString("yyyy-MM-dd HH:mm:ss") + "/" + dayNumber.ToString());
}
    }
    days++;

    if(Convert.ToInt32(manual.ToString("yyyy")) > 2030) break;
}
    }
}

증권데이타(정보분배데이타) 수신.처리를 위한 언어

1. c++(Ace Framework사용)
1.1

2. c언어
2.1

3. c#언어
3.1

>체결데이타 HHMM으로 그룹화 HTML(.feat 서버처리)
서버내에서, 전체 SELECT되어진 데이타에 대해서 그룹 처리한다.

- DB에서 Group By하기에는, 시간이 많이 소요된다.
- 전체 데이타를 가져와서, 코드상에서 Group By를 실행한다.

List<object[]> dataList = datasqlite.SelectDataFromSQLiteToStr(PublicNM.findPossibleCodeTableNM(trText), "WHERE STOCKCODE = " + "'" + codeText + "'", APMSQLite.GetInstance.groupAConn);
                List<ExturePlusKseTradeText> indexList = datasqlite.ConvertToExturePlusKseTrade(dataList);

List<ExturePlusKseTradeText> groupedindexList = indexList
    .GroupBy(data => data.transactionProcessingTime.Substring(0, 4)) // HHMM 기준으로 그룹화
    .Select(group => {
var firstItem = group.First();  // 그룹 내 첫 번째 항목을 선택
var lastItem = group.Last();    // 그룹 내 마지막 항목을 선택

// 그룹 내 tradePrice 값들로부터 highPrice, lowPrice, openPrice 계산
var tradePrices = group
    .Select(item => int.TryParse(item.tradePrice, out int tradePrice) ? tradePrice : 0)
    .ToList();

var highPriceInGroup = tradePrices.Max(); // 그룹 내에서 최고 가격
var lowPriceInGroup = tradePrices.Min();  // 그룹 내에서 최저 가격
var openPriceInGroup = tradePrices.FirstOrDefault().ToString();  // 그룹 내 첫 번째 항목의 tradePrice

return new ExturePlusKseTradeText
{
    // 1분 단위로 그룹화된 시간 (HHMM)
    transactionProcessingTime = group.Key,

    // 거래량 합산 후 문자열로 변환
    tradeVolume = group.Sum(item => int.TryParse(item.tradeVolume, out int tradeVol) ? tradeVol : 0).ToString(),

    // 마지막 데이터에서 tradePrice, upDown, gap 등을 선택
    tradePrice = lastItem.tradePrice ?? "0",  // null인 경우 "0"을 기본값으로 설정
    upDown = lastItem.upDown ?? "0",  // null인 경우 "0"을 기본값으로 설정
    gap = lastItem.gap ?? "0",  // null인 경우 "0"을 기본값으로 설정

    // 그룹 내 첫 번째부터 마지막까지의 최고/최저 가격을 설정
    highPrice = highPriceInGroup.ToString(),
    lowPrice = lowPriceInGroup.ToString(),

    // 그룹 내 첫 번째 항목에서 openPrice를 가져오기
    openPrice = openPriceInGroup,  // 첫 번째 항목에서 tradePrice 가져오기

    // 나머지 필드들도 마지막 데이터에서 가져오기
    accmTradeVolume = lastItem.accmTradeVolume ?? "0",  // null인 경우 "0"을 기본값으로 설정
    accmTradeAmount = lastItem.accmTradeAmount ?? "0", // null인 경우 "0"을 기본값으로 설정

    // 나머지 항목들도 마지막 데이터에서 가져오기
    dataClass = lastItem.dataClass ?? "0",  // null인 경우 "0"을 기본값으로 설정
    infoMarketClass = lastItem.infoMarketClass ?? "0",  // null인 경우 "0"을 기본값으로 설정
    distributeStockIndex = lastItem.distributeStockIndex ?? "0",  // null인 경우 "0"을 기본값으로 설정
    boardID = lastItem.boardID ?? "0",  // null인 경우 "0"을 기본값으로 설정
    sessionID = lastItem.sessionID ?? "0",  // null인 경우 "0"을 기본값으로 설정
    stockCode = lastItem.stockCode ?? "0",  // null인 경우 "0"을 기본값으로 설정
    stockSeq = lastItem.stockSeq ?? "0",  // null인 경우 "0"을 기본값으로 설정

    // 나머지 필드들도 "NULL" 또는 "0" 처리
    lastAskBidClassCode = lastItem.lastAskBidClassCode ?? "0", 
    LPHoldingVolume = lastItem.LPHoldingVolume ?? "0",
    askOneQuote = lastItem.askOneQuote ?? "0",
    bidOneQuote = lastItem.bidOneQuote ?? "0",
    endText = lastItem.endText ?? "0"  // "NULL"을 "0"으로 처리
};
    })
    .ToList();

                for (int kk = 0; kk < groupedindexList.Count; kk++)
                {
                    mmmList.Add(JsonConvert.SerializeObject(groupedindexList[kk]));
                }
                return mmmList;

Redis.Tick.Server에 TCP 정보분배.데이타 강제 입력하는 부분.스크린샷

Windows 에서의 debug는 SendMessage를 통해서 로그를 출력한다.
Windows 에서는 파일로 로그출력하는 방식보다, 훨씬 효과적이다. 리눅스에서는 파일로 디버깅 로그를 출력하도록 한다.

app.js
0.01MB
index.html
0.05MB

+ Recent posts