2018년 5월에 일본 자바 유저 그룹(http://www.java-users.jp/ccc2018spring/#/) 세미나가 있었고..
라인 개발자들이 발표한 자료(http://www.java-users.jp/ccc2018spring/#/sessions)가 있었다.






이외 라인에 잼난 slideshare 자료가 있어서 공유한다.




2018.5에 올라온 라인 레디스 자료 















참고 

2015.5 월 발표 자료



Posted by '김용환'

댓글을 달아 주세요

[redis] lua 사용 사례

Redis 2017. 7. 25. 16:28



redis에 lua를 사용할 수 있다. 언제 쓰면 좋을까?



애플리케이션-Redis 구조에서는 특별히 사용할 일이 없어보이지만,


Transmission Time(Latency)을 최대한 늦추고 Network Bandwith를 줄이고 싶을 수 있을 때 사용할 수 있다. 


여러번 또는 수십 번 통신 비용을 쓰기 보다 한 번의 콜로 해결할 수 있다면 통신 비용을 아낄 수 있을 때, 사용하는 것이 좋을 것 같다.





레디스 자료 중에 관련 lua 사례가 있다.


https://github.com/RedisLabs/geo.lua의 코드이다.


여러번 반복적인 커맨드를 사용해서 데이터를 읽어와야 하는 경우에..


geodist key elem1 elem2 

geodist key elem3 elem4

geodist key elem5 elem6



이를 lua 코드로 geopathlen key elem1 elem2 elem3, elem4, elem5, elem6로 한번에 호출할 수 있다.



그리고 리스트의 랜덤 결과 값을 얻어온다고 할 때도 도움이 될 수 있을 것이다.



이외에 WATH/MULTI/DISCARD/EXEC 와 같은 트랙잭션 코드를 lua로 쉽게 해결할 수 있을 것이다.





아래는 RedisLab에서 발표한 Lua 사례 내용이다.



Redis: Lua scripts - a primer and use cases from Redis Labs



Posted by '김용환'

댓글을 달아 주세요


keys 커맨드가 레디스에 큰 영향을 줄 수 있다.


http://knight76.tistory.com/entry/redis-keys-%EB%8C%80%EC%8B%A0-scan



하지만, range 계열(예, lrange)도 잘 써야 한다. 싱글 쓰레드에서는 하나의 커맨드가 느려지만 아예 접속도 못하게 된다.


예를 들어 k 이라는 키에 수백만 개의 값을 저장했다고 하자..


이 때 값의 범위를 얻기 위해 "lrange key start end"를 사용할 수 있다. 값의 범위는 알기 위해서 실수로 아래와 같은 커맨드를 실행하면.. 큰 문제가 발생할 수 있다. 


lrange k 0 -1 

 

start 0, end -1 사실상 처음부터 끝이다. 키의 모든 값이 다 출력될 때까지 thread block되어서 연결이 되지 않았다.


미리 연습해보고 테스트해보면서 위험 요소를 몸으로 아는 수밖에 없다.

Posted by '김용환'

댓글을 달아 주세요


레디스의 z계열 커맨드가 어떻게 동작하는지 까먹어서 공부를 했다.




127.0.0.1:6379> zadd number 1000 samuel

(integer) 1

127.0.0.1:6379> zadd number 2000 matt

(integer) 1

127.0.0.1:6379> zadd number 3000 jonathan

(integer) 1



zscore는 스코어를 본다.


127.0.0.1:6379> zscore number jonathan

"3000"

127.0.0.1:6379> zscore number samuel

"1000"




zrange 커맨드 (-1 매개 변수는  마지막)


127.0.0.1:6379> zrange number 0 100

1) "samuel"

2) "matt"

3) "jonathan"

127.0.0.1:6379> zrange number 0 -1

1) "samuel"

2) "matt"

3) "jonathan"

127.0.0.1:6379> zrange number 0 1

1) "samuel"

2) "matt"


점수도 함께 표현 (withscores)


127.0.0.1:6379> zrange number 0 -1 withscores

1) "samuel"

2) "1000"

3) "matt"

4) "2000"

5) "jonathan"

6) "3000"


zrem은 삭제


127.0.0.1:6379> zrem number "matt"

(integer) 1

127.0.0.1:6379> zrange number 0 -1 withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"



zrangebyscore 스코어로 범위 지정하는 커맨드


127.0.0.1:6379> zrangebyscore number 3000 4000

1) "jonathan"



zounct는 score 범위로 몇개가 있는 계산하는 커맨드 (-inf:음의 무한대, +inf: 양의 무한대)


127.0.0.1:6379> zrange number 0 -1

1) "samuel"

2) "jonathan"

127.0.0.1:6379> zcount number -inf +inf

(integer) 2

127.0.0.1:6379> zcount number 0 4000

(integer) 2

127.0.0.1:6379> zcount number -inf 1000

(integer) 1



zrangebylex는 member로 범위 지정


127.0.0.1:6379> zadd number  5000 jack

(integer) 1

127.0.0.1:6379> zadd number  6000 soa

(integer) 1

127.0.0.1:6379> zrangebylex number - +

1) "samuel"

2) "jonathan"

3) "jack"

4) "soa"


127.0.0.1:6379> zrangebylex number "[soa" +

1) "soa"



zlexcount는 zrangebylex와 같이 개수 구하는 커맨드


127.0.0.1:6379> zlexcount number - +

(integer) 4

127.0.0.1:6379> zlexcount number "[soa" +

(integer) 1



zrangebyscore는 스코어를 기반으로 값을 검색한다.



127.0.0.1:6379> zrangebyscore  number -inf inf

1) "samuel"

2) "jonathan"

3) "jack"

4) "soa"

127.0.0.1:6379> zrangebyscore  number -inf inf  withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"

5) "jack"

6) "5000"

7) "soa"

8) "6000"

127.0.0.1:6379> zrangebyscore  number -inf 5000 withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"

5) "jack"

6) "5000"

127.0.0.1:6379> zrangebyscore  number -inf (4000 withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"

127.0.0.1:6379> zrangebyscore  number 100 3000 withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"




zremrnagebyrank는 스코어 단위로 삭제한다. 


127.0.0.1:6379> zrangebyscore  number -inf inf  withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"

5) "jack"

6) "5000"

7) "soa"

127.0.0.1:6379> zremrangebyrank number 1 1

(integer) 1

127.0.0.1:6379> zrange number 0 -1

1) "samuel"

2) "jack"

3) "soa"



zremrangebylex는 zrangebylex 기반과 비슷하며 삭제한다. 



127.0.0.1:6379> zrangebylex number - +

1) "samuel"

2) "jack"

3) "soa"

127.0.0.1:6379> zrangebylex number "(jack" "(soa"

1) "samuel"

2) "jack"

127.0.0.1:6379> zremrangebylex number "(jack" "(soa"

(integer) 2

127.0.0.1:6379> zrangebylex number - +

1) "soa"



다시 재구성한 한 후.. zremrangebylex 3 -1 는 3개 빼고 다 삭제한다.  zremrangebylex 1 -1 는 1개 빼고 다 삭제한다. 


127.0.0.1:6379> zrevrange number 0 -1 withscores

1) "soa"

2) "6000"

3) "jonathan"

4) "3000"

5) "matt"

6) "2000"

7) "samuel"

8) "1000"

127.0.0.1:6379> zremrangebyrank number 3 -1

(integer) 1

127.0.0.1:6379> zrevrange number 0 -1 withscores

1) "jonathan"

2) "3000"

3) "matt"

4) "2000"

5) "samuel"

6) "1000"

127.0.0.1:6379> zremrangebyrank number 1 -1

(integer) 2

127.0.0.1:6379> zrevrange number 0 -1 withscores

1) "samuel"

2) "1000"



일부러 음수로 저장하면 또 다른 의미로 rank로 최신 데이터만 가져올 수 있다.


127.0.0.1:6379> zadd feed -900 1st

(integer) 1

127.0.0.1:6379> zadd feed -950 2nd

(integer) 1

127.0.0.1:6379> zadd feed -955 3rd

(integer) 1

127.0.0.1:6379> zrevrange feed 0 -1 withscores

1) "1st"

2) "-900"

3) "2nd"

4) "-950"

5) "3rd"

6) "-955"

127.0.0.1:6379> zremrangebyrank feed 2 -1

(integer) 1

127.0.0.1:6379> zrevrange feed 0 -1 withscores

1) "2nd"

2) "-950"

3) "3rd"

4) "-955"





zrank는 오름차순 순서를 보여준다.


127.0.0.1:6379> zrange number 0 -1 withscores

1) "samuel"

2) "1000"

3) "matt"

4) "2000"

5) "jonathan"

6) "3000"

7) "soa"

8) "6000"

127.0.0.1:6379> zrank number "jonathan"

(integer) 2

127.0.0.1:6379> zrank number "soa"

(integer) 3


점수 높은것부터 보려면 zrevrank를 호출한다.


127.0.0.1:6379> zrevrange number 0 -1 withscores

1) "soa"

2) "6000"

3) "jonathan"

4) "3000"

5) "matt"

6) "2000"

7) "samuel"

8) "1000"

127.0.0.1:6379> zrevrank number "soa"

(integer) 0

127.0.0.1:6379> zrevrank number "samuel"

(integer) 3


Posted by '김용환'

댓글을 달아 주세요



최근 redis(3.2 이상 아니면 3.0 이상)부터 redis 프로세스의 설정 파일이 보이지 않는다. 



redis 2.x에서는 실행한 후 ps 커맨드로 확인하면 다음처럼 볼 수 있는데.. (설정 파일 기반)


/usr/local/redis/src/redis-server /etc/redis/6379.conf



redis 3.x(3.0 아니면 3.2부터는 정확히는 기억 안남)동일한 커맨드를 써도 이제는 설정의 내용을 기반으로 보여준다.

(처음에는 내가 잘못 실행한 줄 알고 깜짝 놀랐음.)



/usr/local/redis/src/redis-server *:6379


/usr/local/redis/src/redis-server 0.0.0.0:6379





Posted by '김용환'

댓글을 달아 주세요






redis 3.2를 설치한 후, 접근이 막혀있다. 보안을 높였다. 


$ telnet 1.2.3.4 6379


Escape character is '^]'.

-DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.

Connection closed by foreign host.




HA 기능을 사용한다면, HA 애플리케이션에서 정상적으로 동작하는지 접근할 수 있도록 localhost에서 레디스 redis-cli 실행해서 config set protected-mode no를 실행해 외부 접근을 가능하게 한다. 


$ redis-cli

> CONFIG SET protected-mode no


외부에서 접근가능해져 HA 애플리케이션이 접근할 수 있다.


$ telnet 1.2.3.4 6379

Escape character is '^]'.

OK



이제는 정상적으로 접근된다. 



처음부터 protected-mode가 동작되지 않도록 하려면, redis.conf 에 다음을 추가한다.


bind 0.0.0.0


모든 IP에서 들어올 수 있도록 설정한다는 의미를 가진다. 



참조 : https://redis.io/topics/security

Posted by '김용환'

댓글을 달아 주세요







자주 사용하는 redis cluster 명령어들 


클러스터 접속하는 방법이다. 그냥 redis-cli -p 7000 커맨드로 redis cluster에 접근할 수 없다.

slave 없이 master로 모인 정보이다.


$ ./src/redis-cli  -c -p 7000

> cluster nodes

928190fd... 1.1.1.1:7000 master - 0 1466475134595 2 connected 3277-6553

bb90967f... 1.1.1.2:7000 master - 0 1466475134595 4 connected 9830-13106

d2c52014... 1.1.1.3:7000 master - 0 1466475133995 1 connected 0-3276

7a31e871... 1.1.1.4:7000 master - 0 1466475134995 3 connected 6554-9829

ffc4b321... 1.1.1.5:7000 myself,master - 0 0 5 connected 13107-16383





만약 slave가 있다면, slave 뒤에 master 서버값이 들어가 있는 것을 볼 수 있다.  


> cluster nodes

6b38bb... 1.1.1.1:7379 master - 0 0 25 connected 0-5460                      

7bb78c... 127.0.0.1:7380 master - 0 1449730618304 2 connected 5461-10922       

164888... 127.0.0.1:7381 master - 0 1449730618304 3 connected 10923-16383      

b8b5ee... 127.0.0.1:7382 slave 6b38bb... 0 1449730618304 25 connected  




"키"가 어느 키슬롯에 저장될 지 보고 싶다면, 키 값을 살펴본다.

(keys  커맨드를 사용하려면 클러스터의 key slot를 찾고, 해당 key의 값을 찾는다.)

> cluster keyslot 20000226

(integer) 475

> cluster keyslot a

(integer) 15495




하지만, keys * 커맨드로 전체 키를 찾을 수 없도록 해놯고 SCAN을 쓸 수 있도록 권장하고 있다.


https://github.com/antirez/redis/issues/1962


You can't. You would have to ask each node seperately. But please avoid KEYS * whenever possible. If you really must iterate the whole keyspace of an instance use SCAN.


Btw, this issue tracker should be used for bugs or improvements to the Redis server. For questions like your's please ask on the mailing list or in the irc channel.






key *는 클러스터 중 특정 서버에 있을 때만 볼 수 있다.


> set a a

-> Redirected to slot [15495] located at 172.17.64.45:7000

OK


> set b b

-> Redirected to slot [3300] located at 172.17.64.42:7000

OK


> keys *

1) "b"


> del a

-> Redirected to slot [15495] located at 172.17.64.45:7000

(integer) 1


> del b

-> Redirected to slot [3300] located at 172.17.64.42:7000

(integer) 1





redis cluster : scan에 대한 내부 동작 원리를 보고 싶다면 다음 정보를 확인한다.


http://www.paluch.biz/blog/162-iterate-over-all-keys-in-a-redis-cluster.html






기존 info 명령어처럼 cluster 정보를 확인할 수 있다.


> cluster info

cluster_state:ok

cluster_slots_assigned:16384

cluster_slots_ok:16384

cluster_slots_pfail:0

cluster_slots_fail:0

cluster_known_nodes:5

cluster_size:5

cluster_current_epoch:5

cluster_my_epoch:1

cluster_stats_messages_sent:6824123

cluster_stats_messages_received:6824123







Posted by '김용환'

댓글을 달아 주세요

[redis] slave-read-only

Redis 2016. 3. 9. 11:25


redis 3.0.2에서 테스트한 정보이며, 

레디스 슬레이브 서버 공식 페이지(http://redis.io/topics/admin)에서 반드시 명시하고 있는 내용인데.. 테스트해본 것이다.


  • Allow writes to the slave using CONFIG SET slave-read-only no






레디스 슬레이브 서버에서 info 명령어를 내려면, 슬레이브의 상태는 slave_read_only이다.

> info

slave_read_only:1


슬레이브 서버에 redis-cli로 접속해서 값을 저장하려면 안된다고 에러 문구가 뜬다.


> set key value

(error) READONLY You can't write against a read only slave.




그러나, slave_read_only 값으로 이 상태를 바꿀 수 있다.


> config set slave-read-only no

OK

> set slave true

OK



마스터에 해당 값을 요청을 해보면, (nil)로 나오고, 슬레이브에 요청하면 이전에 저장한 slave의 값을 볼 수 있다. 


~$ redis-cli -p 마스터포트 get slave

(nil)

~$ redis-cli -p 슬레이브포트 GET slave

"true"



마스터와 슬레이브간의 일관성을 중요시 여기기 때문에 slave_read_only의 기본 값이 yes인 것인데, 

따라서, slave_read_only 값을 굳이 바꿀 필요가 없다. 




참고

상황에 따라서... 레디스 슬레이브이지만, master로 변경하는 과정에서 사용할 수 있다.

그 때는 CONFIG SET  slave_read_only no로 변경해서 쓰기를 받아들이고, master를 중단하는 방법을 사용할 수 있다.
이를 위해서 해당 설정이 존재한다.



Posted by '김용환'

댓글을 달아 주세요




redis 소스를 살펴보면, 리눅스일 때는 메모리 관련 체크 설정이 들어가 있다.


https://github.com/antirez/redis/blob/3.2/src/server.c



int main(int argc, char **argv) {

...


    #ifdef __linux__


       linuxMemoryWarnings();

  #endif

...


}




linuxMemoryWarnings() 함수는 두 가지를 확인하고 이슈가 있을 것 같으면 warning을 출력한다.

1 번째는 memory overcommit 를 체크한다. (/proc/sys/vm/overcommit_memory)

2 번째는 Transparent Huge Table 정보를 확인한다.(vm.overcommit_memory)




https://github.com/antirez/redis/blob/3.2/src/server.c


#ifdef __linux__

int linuxOvercommitMemoryValue(void) {

    FILE *fp = fopen("/proc/sys/vm/overcommit_memory","r");

    char buf[64];


    if (!fp) return -1;

    if (fgets(buf,64,fp) == NULL) {

        fclose(fp);

        return -1;

    }

    fclose(fp);


    return atoi(buf);

}


void linuxMemoryWarnings(void) {

    if (linuxOvercommitMemoryValue() == 0) {

        serverLog(LL_WARNING,"WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.");

    }

    if (THPIsEnabled()) {

        serverLog(LL_WARNING,"WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.");

    }

}

#endif /* __linux__ */




https://github.com/antirez/redis/blob/3.2/src/latency.c




#ifdef __linux__

/* Returns 1 if Transparent Huge Pages support is enabled in the kernel.

 * Otherwise (or if we are unable to check) 0 is returned. */

int THPIsEnabled(void) {

    char buf[1024];


    FILE *fp = fopen("/sys/kernel/mm/transparent_hugepage/enabled","r");

    if (!fp) return 0;

    if (fgets(buf,sizeof(buf),fp) == NULL) {

        fclose(fp);

        return 0;

    }

    fclose(fp);

    return (strstr(buf,"[never]") == NULL) ? 1 : 0;

}

#endif





* 메모리 오버커밋에 대한 설명이다.

메모리 오버커밋(memory overcommit)은 물리 메모리 공간 이상을 쓸 수 있는 방법으로, 가상 메모리(virtual memory)를 함께 사용하여 더 많은 메모리를 할당할 수 있는 기법을 의미한다. 파일에 존재하는 디스크의 공간을 스왑 메모리로 설정하면, 사용할 수 있는 메모리 공간이 커진다


백그라운드 저장을 빨리 하기 위해 메모리 오버커밋(overcommit) 설정을 1로 명세한다. /etc/sysctl.conf 파일에 다음을 추가한다.

vm.overcommit_memory=1


/etc/sysctl.conf를 저장한 후에는 리눅스 서버를 재시작한다.

 

또한, 레디스 설정에는 레디스가 사용할 수 있는 메모리의 용량(바이트)를 제한하는 maxmemory라는 지시자가 있다.




* THP  (Transparent Huge Pages)에 대한 설명이다.


CentOS 운영체제는 메모리의 관리를 페이지(page) 단위로 한다. 페이지는 작은 단위(4KB)이지만, 애플리케이션이 대용량 메모리를 필요로 하면 운영체제가 특별한 설정 없이 페이지의 크기를 아주 크게 할당(2MB 또는 1GB)하려 애플리케이션에서 쓸 수 있게 한다. 이를 THP 기능이라 한다




THP리눅스 커널 기능을 쓰지 않으려면, 다음과 같이 설정한다. 


sudo echo never > /sys/kernel/mm/transparent_hugepage/enabled






Posted by '김용환'

댓글을 달아 주세요


BRPOP 커맨드와 BLPOP 커맨드는 키의 목록과 타임아웃을 받는다. 

BRPOPLPUSH 커맨드는 원본 키, 대상 키, 타임아웃을 받는다. 


세 커맨드의 타임아웃 매개변수를 선택할 수 있고, 해당 매개변수의 기본 값은 0이다.

타임아웃이 0이란 말은, 레디스는 명세한 원본 키 목록에서 엘리먼트를 발견할 때까지 커맨드 호출이 블럭된다는 것을 의미한다. 

List에서 최소 한 개의 엘리먼트를 발견할 때까지 또는 타임아웃을 초과할 때까지 레디스 클라이언트가 블럭되기 때문에 해당 커맨드를 blocking 커맨드라 불린다.


*BRPOP : RPOP 커맨드의 블럭킹 버전으로서, 매개변수로 주어진 키 리스트의 키를 순서대로 확인하면서, 비어있지 않은 리스트를 먼저 찾은 후 해당 리스트의 가장 마지막에 추가된 엘리먼트를 pop한다.

*BLPOP : LPOP 커맨드의 블럭킹 버전이다. 매개변수로 주어진 키 리스트의 키를 순서대로 확인하면서, 비어있지 않은 리스트를 먼저 찾은 후 해당 리스트의 가장 먼저 추가된 엘리먼트를 pop한다.

*BRPOPLPUSH : 원본 키의 마지막에 추가된 엘리먼트를 꺼내 대상 키의 첫 번째에 추가한다.



1. BRPOP 데모


A 터미널에서 list2에 a,b,c를 저장 (미리 BRPOP을 해도 된다.)


127.0.0.1:6379> RPUSH list2 a b c

(integer) 3



B 터미널에서 list1과 list2의 값을 얻기 위해 BRPOP 커맨드를 호출한다. 마지막 엘리먼트를 pop한 것을 확인할 수 있다.


127.0.0.1:6379> BRPOP list1 list2 0

1) "list2"

2) "c"

127.0.0.1:6379> BRPOP list1 list2 0

1) "list2"

2) "b"

127.0.0.1:6379> BRPOP list1 list2 0

1) "list2"

2) "a"

127.0.0.1:6379> BRPOP list1 list2 0

(블럭됨)



2. BLPOP 데모


A 터미널에서 RPUSH 커맨드로 list2 키를 추가한다.


127.0.0.1:6379> RPUSH list2 a b c

(integer) 3



B 터미널에서 BLPOP 커맨드를 호출한다. 리스트의 앞 엘리먼트부터 pop한 것을 확인할 수 있다.



127.0.0.1:6379> BLPOP list1 list2 0

1) "list2"

2) "a"

127.0.0.1:6379> BLPOP list1 list2 0

1) "list2"

2) "b"

127.0.0.1:6379> BLPOP list1 list2 0

1) "list2"

2) "c"

127.0.0.1:6379> BLPOP list1 list2 0

(블럭됨)




3. BRPOPLPUSH 데모



A 터미널에서 list1 키를 추가한다.


127.0.0.1:6379> RPUSH list1 a b c

(integer) 3



B 터미널에서 아래와 같이 실행한다. BRPOPLPUSH는  list1에 저장된 끝 엘리먼트부터 꺼내서 list1에 저장한다.


127.0.0.1:6379> BRPOPLPUSH list1 list2 0

"c"

127.0.0.1:6379> BRPOPLPUSH list1 list2 0

"b"

127.0.0.1:6379> BRPOPLPUSH list1 list2 0

"a"

127.0.0.1:6379> BRPOPLPUSH list1 list2 0

(블럭됨)


A 터미널에서 list2를 확인한다.



127.0.0.1:6379> LLEN list2

(integer) 3


127.0.0.1:6379> LRANGE list2 0 -1

1) "a"

2) "b"

3) "c"






참고 

http://redis.io/commands/BRPOP


http://redis.io/commands/blpop


http://redis.io/commands/brpoplpush

Posted by '김용환'

댓글을 달아 주세요