zoukankan      html  css  js  c++  java
  • 部署Redis Cluster 6.0 集群并开启密码认证 和 Redis-cluster-proxy负载

    部署Redis Cluster集群并开启密码认证

    如果只想简单的搭建Redis Cluster,不需要设置密码和公网访问,可以参考官方文档。

    节点介绍

    Cluster模式推荐最少有6个节点,本次实验搭建了6个节点,使用的端口为7000-7005。Cluster模式是数据分开存放在不同的节点上,如果有6个节点,通常设置3个主节点,每个主节点有一个从节点作为备份。正常情况下读写操作的是主节点,从节点会同步主节点的更变。当某个主节点挂了之后,其对应的从节点会变成主节点。如果一段时间后之前挂掉的主节点恢复了,它将变成从节点。如果某个主节点挂了之后,其对应的从节点也挂了,集群将不可访问。即每个主节点相互独立,从节点作为主节点的备份。

    下载和编译

    下载地址:https://redis.io/download
    下载、解压并编译:

    wget http://download.redis.io/releases/redis-6.0.5.tar.gz
    tar -zxvf redis-6.0.5.tar.gz
    cd cd redis-6.0.5
    make
    

    坑:6.x版本的Redis需要新版的gcc才能编译,centOS 7 自带的gcc版本为4.8.5,无法完成编译,需要升级版本。

    解决方案:

    yum install centos-release-scl
    yum install devtoolset-9-gcc*
    
    # 执行如下仅对当前bash生效
    scl enable devtoolset-9 bash
    
    # 以上操作仅对当bash生效,若想永久生效,按以下操作:
    echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
    source /etc/profile
    

    gcc升级版本之后,需要重新make编译,编译完成后src目录下会生成几个可执行文件redis-benchmark redis-cli redis-sentinel redis-server等。

    集群部署

    新建6个目录,并将redis.conf和src目录中的redis-server复制到每个目录,目录名称可随意,这里采用redis运行时的端口号作为目录的名称。

    cd /data/soft/redis/
    
    mkdir -p /data/soft/redis/conf
    cp redis.conf /data/soft/redis/conf/7001_redis.conf
    cp redis.conf /data/soft/redis/conf/7002_redis.conf
    cp redis.conf /data/soft/redis/conf/7003_redis.conf
    cp redis.conf /data/soft/redis/conf/7004_redis.conf
    cp redis.conf /data/soft/redis/conf/7005_redis.conf
    cp redis.conf /data/soft/redis/conf/7006_redis.conf
    
    cp src/redis-server /data/bin/redis-server
    cp src/redis-cli    /data/bin/redis-cli
    
    

    修改配置

    修改conf目录中的*.conf,需要修改配置项如下:

    # Redis configuration file.
    # ./redis-server /path/to/redis.conf
    
    ################################## MODULES #####################################
    # include /path/to/local.conf
    # loadmodule /path/to/my_module.so
    
    ################################## NETWORK #####################################
    # By default, if no "bind" configuration directive is specified, Redis listens
    # for connections from all the network interfaces available on the server.
    bind 192.168.0.239
    protected-mode no
    port 7005
    tcp-backlog 2000
    # unixsocket /tmp/redis.sock
    # unixsocketperm 700
    timeout 0
    tcp-keepalive 300
    
    ################################# GENERAL #####################################
    daemonize yes
    supervised no
    pidfile /log/redis/redis-7005.pid
    # debug, verbose, notice, warning
    loglevel notice
    logfile /log/redis/redis-7005.log
    # syslog-enabled no
    # syslog-ident redis
    # syslog-facility local0
    databases 16
    always-show-logo yes
    
    ################################ SNAPSHOTTING  ################################
    # save <seconds> <changes>
    save 600 1
    save 300 10
    save 250 50
    save 100 1000
    save 80 5000
    save 60 10000
    
    stop-writes-on-bgsave-error yes
    rdbcompression yes
    rdbchecksum yes
    dbfilename dump-7005.rdb
    dir /log/redis/dump/
    
    ################################# REPLICATION #################################
    # slaveof 192.168.53.5 7005
    masterauth 123456
    # slave-serve-stale-data yes
    # slave-read-only yes
    # repl-diskless-sync no
    # repl-ping-slave-period 10
    # repl-timeout 60
    # repl-disable-tcp-nodelay no
    # repl-backlog-size 5mb
    # repl-backlog-ttl 3000
    # slave-priority 100
    # min-slaves-to-write 1
    # min-slaves-max-lag 10
    # slave-announce-ip 5.5.5.5
    # slave-announce-port 1234
    
    ################################## SECURITY ###################################
    requirepass 123456
    # rename-command CONFIG XCONFIG
    
    ################################### CLIENTS ####################################
    maxclients 10000
    
    ############################## MEMORY MANAGEMENT ################################
    maxmemory 2048mb
    # volatile-lru -> Evict using approximated LRU among the keys with an expire set.
    # allkeys-lru -> Evict any key using approximated LRU.
    # volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
    # allkeys-lfu -> Evict any key using approximated LFU.
    # volatile-random -> Remove a random key among the ones with an expire set.
    # allkeys-random -> Remove a random key, any key.
    # volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
    # noeviction -> Don't evict anything, just return an error on write operations.
    maxmemory-policy allkeys-lru
    maxmemory-samples 5
    
    ############################# LAZY FREEING ####################################
    # release memory in a non-blocking. DEL, UNLINK and ASYNC
    # option of FLUSHALL and FLUSHDB are user-controlled.
    lazyfree-lazy-eviction no
    lazyfree-lazy-expire no
    lazyfree-lazy-server-del no
    slave-lazy-flush no
    
    ############################## APPEND ONLY MODE ###############################
    appendonly yes
    appendfilename appendonly-7005.aof
    # always|everysec|no
    appendfsync everysec
    no-appendfsync-on-rewrite no
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 3mb
    aof-load-truncated yes
    aof-use-rdb-preamble no
    
    ################################ LUA SCRIPTING  ###############################
    lua-time-limit 5000
    
    ################################ REDIS CLUSTER  ###############################
    cluster-enabled yes
    cluster-config-file /log/redis/nodes-7005.conf
    cluster-node-timeout 15000
    cluster-slave-validity-factor 10
    cluster-migration-barrier 1
    cluster-require-full-coverage yes
    
    ################################## SLOW LOG ###################################
    slowlog-log-slower-than 5000
    slowlog-max-len 516
    
    ################################ LATENCY MONITOR ##############################
    latency-monitor-threshold 0
    
    ############################# EVENT NOTIFICATION ##############################
    notify-keyspace-events ""
    
    ############################### ADVANCED CONFIG ###############################
    hash-max-ziplist-entries 512
    hash-max-ziplist-value 64
    # -5: max size: 64 Kb  <-- not recommended for normal workloads
    # -4: max size: 32 Kb  <-- not recommended
    # -3: max size: 16 Kb  <-- probably not recommended
    # -2: max size: 8 Kb   <-- good
    # -1: max size: 4 Kb   <-- good
    list-max-ziplist-size -2
    # 0|1|2|3
    list-compress-depth 0
    set-max-intset-entries 512
    zset-max-ziplist-entries 128
    zset-max-ziplist-value 64
    hll-sparse-max-bytes 3000
    activerehashing yes
    # normal -> normal clients including MONITOR clients
    # slave  -> slave clients
    # pubsub -> clients subscribed to at least one pubsub channel or pattern
    # client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
    client-output-buffer-limit normal 0 0 0
    client-output-buffer-limit slave 256mb 64mb 60
    client-output-buffer-limit pubsub 32mb 8mb 60
    # client-query-buffer-limit 1gb
    # proto-max-bulk-len 512mb
    hz 10
    aof-rewrite-incremental-fsync yes
    # lfu-log-factor 10
    # lfu-decay-time 1
    
    ########################### ACTIVE DEFRAGMENTATION #######################
    # Enabled active defragmentation
    # activedefrag yes
    
    # Minimum amount of fragmentation waste to start active defrag
    # active-defrag-ignore-bytes 100mb
    
    # Minimum percentage of fragmentation to start active defrag
    # active-defrag-threshold-lower 10
    
    # Maximum percentage of fragmentation at which we use maximum effort
    # active-defrag-threshold-upper 100
    
    # Minimal effort for defrag in CPU percentage
    # active-defrag-cycle-min 25
    
    # Maximal effort for defrag in CPU percentage
    # active-defrag-cycle-max 75
    
    

    上面的配置文件已经列出,当前为7005的端口,其他端口的配置文件都类似,改下端口既可。

    创建集群

    启动每个节点:

    /data/soft/redis/src/redis-check-aof --fix /log/redis/dump/appendonly-7001.aof
    
    /data/soft/redis/src/redis-check-aof --fix /log/redis/dump/appendonly-7002.aof
    
    /data/soft/redis/src/redis-check-aof --fix /log/redis/dump/appendonly-7003.aof
    
    /data/soft/redis/src/redis-check-aof --fix /log/redis/dump/appendonly-7004.aof
    
    /data/soft/redis/src/redis-check-aof --fix /log/redis/dump/appendonly-7005.aof
    
    /data/soft/redis/src/redis-check-aof --fix /log/redis/dump/appendonly-7006.aof
    
    /data/bin/redis-server /data/soft/redis/conf/redis-7001.conf >/dev/null 2>&1 &
    
    /data/bin/redis-server /data/soft/redis/conf/redis-7002.conf >/dev/null 2>&1 &
    
    /data/bin/redis-server /data/soft/redis/conf/redis-7003.conf >/dev/null 2>&1 &
    
    /data/bin/redis-server /data/soft/redis/conf/redis-7004.conf >/dev/null 2>&1 &
    
    /data/bin/redis-server /data/soft/redis/conf/redis-7005.conf >/dev/null 2>&1 &
    
    /data/bin/redis-server /data/soft/redis/conf/redis-7006.conf >/dev/null 2>&1 &
    

    启动集群:

    /data/bin/redis-cli --cluster create ip:7000 ip:7001 ip:7002 ip:7003 ip:7004 ip:7005 --cluster-replicas 1 -a 123456
    
    Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
    >>> Performing hash slots allocation on 6 nodes...
    Master[0] -> Slots 0 - 5460
    Master[1] -> Slots 5461 - 10922
    Master[2] -> Slots 10923 - 16383
    Adding replica 192.168.0.239:7006 to 192.168.0.239:7001
    Adding replica 192.168.0.239:7003 to 192.168.0.239:7004
    Adding replica 192.168.0.239:7005 to 192.168.0.239:7002
    M: 4c8fd6640bac7463a517e10fda942568177f1175 192.168.0.239:7001
       slots:[0-5460] (5461 slots) master
    M: 8a924d45c5094bca66c736661f267de3ff348134 192.168.0.239:7002
       slots:[10923-16383] (5461 slots) master
    S: 9e6a1995558a142002579a1bf2659b0fff6a986b 192.168.0.239:7003
       replicates b8985dcd19735b50a9b13ba8ef2449504b1da755
    M: b8985dcd19735b50a9b13ba8ef2449504b1da755 192.168.0.239:7004
       slots:[5461-10922] (5462 slots) master
    S: baf6fb8fa06e58e7ca541d4f9c76ee63255e797f 192.168.0.239:7005
       replicates 8a924d45c5094bca66c736661f267de3ff348134
    S: 10993fd7b1cdf5d0293373bec821bdb7763426ed 192.168.0.239:7006
       replicates 4c8fd6640bac7463a517e10fda942568177f1175
    Can I set the above configuration? (type 'yes' to accept): yes
    >>> Nodes configuration updated
    >>> Assign a different config epoch to each node
    >>> Sending CLUSTER MEET messages to join the cluster
    Waiting for the cluster to join
    ...
    >>> Performing Cluster Check (using node 192.168.0.239:7001)
    M: 4c8fd6640bac7463a517e10fda942568177f1175 192.168.0.239:7001
       slots:[0-5460] (5461 slots) master
       1 additional replica(s)
    S: 9e6a1995558a142002579a1bf2659b0fff6a986b 192.168.0.239:7003
       slots: (0 slots) slave
       replicates b8985dcd19735b50a9b13ba8ef2449504b1da755
    S: 10993fd7b1cdf5d0293373bec821bdb7763426ed 192.168.0.239:7006
       slots: (0 slots) slave
       replicates 4c8fd6640bac7463a517e10fda942568177f1175
    M: 8a924d45c5094bca66c736661f267de3ff348134 192.168.0.239:7002
       slots:[10923-16383] (5461 slots) master
       1 additional replica(s)
    S: baf6fb8fa06e58e7ca541d4f9c76ee63255e797f 192.168.0.239:7005
       slots: (0 slots) slave
       replicates 8a924d45c5094bca66c736661f267de3ff348134
    M: b8985dcd19735b50a9b13ba8ef2449504b1da755 192.168.0.239:7004
       slots:[5461-10922] (5462 slots) master
       1 additional replica(s)
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots...
    >>> Check slots coverage...
    [OK] All 16384 slots covered.
    
    
    

    这里的IP可以是公网IP也可以是内网IP,-a后面的参数是设置的访问密码,–cluster-replicas后面的参数是每个主节点对应的从节点的数量。
    这时会自动分配主从节点,如果确认分配无误,输入yes即可创建cluster。

    坑:在cluster模式中,每个节点除了要使用设置的访问端口以外,还需要使用访问端口加10000的端口号进行数据传输,所以在防火墙开放端口时也需要打开对应的端口,否则会出现wait…的提示。
    在本例中,需要开放的端口号为7001-7006和17001-17006。

    测试集群:

    可以使用redis-cli来连接集群,-c代表使用cluster模式。

    /data/soft/redis-cli -c -h ip -p 7000 -a 密码
    

    查看集群信息:

    192.168.0.239:7001> cluster info 
    cluster_state:ok
    cluster_slots_assigned:16384
    cluster_slots_ok:16384
    cluster_slots_pfail:0
    cluster_slots_fail:0
    cluster_known_nodes:6
    cluster_size:3
    cluster_current_epoch:6
    cluster_my_epoch:1
    cluster_stats_messages_ping_sent:107
    cluster_stats_messages_pong_sent:111
    cluster_stats_messages_sent:218
    cluster_stats_messages_ping_received:106
    cluster_stats_messages_pong_received:107
    cluster_stats_messages_meet_received:5
    cluster_stats_messages_received:218
    
    

    测试节点读写信息:

    192.168.0.239:7001> set foo bar
    -> Redirected to slot [12182] located at 192.168.0.239:7002
    OK
    192.168.0.239:7002> get foo
    "bar"
    
    

    设置公网访问

    现在通过公网访问会出现unknown cluster ip:7000的报错信息,这是因为在nodes.conf文件中某些节点的IP是内网IP。解决方法为如下:

    • 停止每个节点的进程。
    • 编辑每个节点的nodes.conf文件,将其中的内网IP换成公网IP。
    • 重新启动每个节点。

    注意:设置公网访问Redis集群属于危险行为,一般生产环境进制使用公网redis集群

    配置Redis-cluster-proxy

    以下信息来自于官方的说明:

    redis-cluster-proxy是Redis集群的代理。Redis能够在基于自动故障转移和分片的集群模式下运行。
    这种特殊模式(指Redis集群模式)需要使用特殊的客户端来理解集群协议:通过代理,集群被抽象了出来,可以实现像单实例一样实现redis集群的访问。

    Redis集群代理是多线程的,默认情况下,它目前使用多路复用通信模型,这样每个线程都有自己的集群连接,所有属于线程本身的客户端都可以共享该连接。

    无论如何,在某些特殊情况下(多事务或阻塞命令),多路复用被禁用,客户端将拥有自己的集群连接。
    通过这种方式,只发送简单命令(比如get和set)的客户端将不需要一组到Redis集群的私有连接。

    注:依赖 gcc 4.9 以上版本,所以需要升级gcc版本,前面已经写过了。

    下载proxy工具

    https://github.com/RedisLabs/redis-cluster-proxy/releases
    
    wget https://github.com/RedisLabs/redis-cluster-proxy/releases/redis-cluster-proxy-1.0-beta2.tar.gz
    
    tar -zxvf redis-cluster-proxy-1.0-beta2.tar.gz
    
    mv redis-cluster-proxy-1.0-beta2 /data/soft/redis-cluster-proxy
    
    cd /data/soft/redis-cluster-proxy/ &&  make install
    

    修改配置文件

    修改/data/soft/redis-cluster-proxy目录下proxy.conf文件,该配置文件和redis配置文件相似,具体参数还未整理,后续更新文档

    重点修改以下配置:

    • 配置监听的集群节点信息

    • 可修改代理端口,默认7777

    • 可修改线程数,因6.0后是多线程

    • 如果后端redis集群配置了密码,需要在auth参数增加后端redis密码

    配置文件如下:

    # Redis Cluster Proxy configuration file example.
    #
    # Note that in order to read the configuration file, the proxy must be
    # started with the file path passed to the -c option:
    #
    # ./redis-cluster-proxy -c /path/to/proxy.conf
    
    ################################## INCLUDES ###################################
    
    # Include one or more other config files here.  Include files can include
    # other files.
    #
    # If instead you are interested in using includes to override configuration
    # options, it is better to use include as the last line.
    #
    # include /path/to/local.conf
    # include /path/to/other.conf
    
    ######################## CLUSTER ENTRY POINT ADDRESS ##########################
    
    # Indicate the entry point address in the same way it can be indicated in the
    # redis-cluster-proxy command line arguments.
    # Note that it can be overridden by the command line argument itself.
    # You can also specify multiple entry-points, by adding more lines, ie:
    # cluster 127.0.0.1:7000
    # cluster 127.0.0.1:7001
    # You can also use the "entry-point" alias instead of cluster, ie:
    # entry-point 127.0.0.1:7000
    #
    cluster 192.168.0.239:7001 
    cluster 192.168.0.239:7002 
    cluster 192.168.0.239:7003 
    cluster 192.168.0.239:7004 
    cluster 192.168.0.239:7005 
    cluster 192.168.0.239:7006
    
    ################################### MAIN ######################################
    
    # Set the port used by Redis Cluster Proxy to listen to incoming connections
    # from clients (default 7777)
    port 7000
    
    # If you want you can bind a single interface, if the bind option is not
    # specified all the interfaces will listen for incoming connections.
    # You can also bind on multiple interfaces by declaring bind on multiple lines
    #
    # bind 127.0.0.1
    
    # Specify the path for the Unix socket that will be used to listen for
    # incoming connections. There is no default, so Redis Cluster Proxy won't
    # listen on a Unix socket when not specified.
    #
    # unixsocket /path/to/proxy.socket
    
    # Set the Unix socket file permissions (default 0)
    #
    # unixsocketperm 760
    
    # Set the number of threads.
    threads 8
    
    # Set the TCP keep-alive value on the Redis Cluster Proxy's socket
    #
    # tcpkeepalive 300
    
    # Set the TCP backlog on the Redis Cluster Proxy's socket
    #
    # tcp-backlog 511
    
    
    # Size of the connections pool used to provide ready-to-use sockets to
    # private connections. The number (size) indicates the number of starting
    # connections in the pool.
    # Use 0 to disable connections pool at all.
    # Every thread will have its pool of ready-to-use connections.
    # When the proxy starts, every thread will populate a pool containing
    # connections to all the nodes of the cluster.
    # Whenever a client needs a private connection, it can take a connection
    # from the pool, if available. This will speed-up the client transition from
    # the thread's shared connection to its own private connection, since the
    # connection from the thread's pool should be already connected and
    # ready-to-use. Otherwise, clients with priovate connections must re-connect
    # the the nodes of the cluster (this re-connection will act in a 'lazy' way).
    #
    # connections-pool-size 10
    
    # Minimum number of connections in the the pool. Below this value, the
    # thread will start re-spawning connections at the defined rate until
    # the pool will be full again.
    #
    # connections-pool-min-size 10
    
    # Interval in milliseconds used to re-spawn connections in the pool.
    # Whenever the number of connections in the pool drops below the minimum
    # (see 'connections-pool-min-size' above), the thread will start
    # re-spawing connections in the pool, until the pool will be full again.
    # New connections will be added at this specified interval.
    #
    # connections-pool-spawn-every 50
    
    # Number of connections to re-spawn in the pool at every cycle that will
    # happen with an interval defined by 'connections-pool-spawn-every' (see above).
    #
    # connections-pool-spawn-rate 50
    
    # Run Redis Cluster Proxy as a daemon.
    daemonize no
    
    # If a pid file is specified, the proxy writes it where specified at startup
    # and removes it at exit.
    #
    # When the proxy runs non daemonized, no pid file is created if none is
    # specified in the configuration. When the proxy is daemonized, the pid file
    # is used even if not specified, defaulting to
    # "/var/run/redis-cluster-proxy.pid".
    #
    # Creating a pid file is best effort: if the proxy is not able to create it
    # nothing bad happens, the server will start and run normally.
    #
    pidfile /log/redis/redis-cluster-proxy.pid
    
    # Specify the log file name. Also the empty string can be used to force
    # Redis Cluster Porxy to log on the standard output. Note that if you use
    # standard output for logging but daemonize, logs will be sent to /dev/null
    #
    logfile "/log/redis/redis-cluster-proxy.log"
    
    # Enable cross-slot queries that can use multiple keys belonging to different
    # slots or even different nodes.
    # WARN: these queries will break the the atomicity deisgn of many Redis
    # commands.
    # NOTE: cross-slots queries are not supported by all the commands, even if
    # this feature is enabled
    #
    # enable-cross-slot no
    
    # Maximum number of clients allowed
    #
    # max-clients 10000
    
    # Authentication password used to authenticate on the cluster in case its nodes
    # are password-protected. The password will be used both for fetching cluster's
    # configuration and to automatically authenticate proxy's internal connections
    # to the cluster itself (both multiplexing shared connections and clients'
    # private connections. So, clients connected to the proxy won't need to issue
    # the Redis AUTH command in order to be authenticated.
    #
    auth nfJNhCDwv0hOlyFdbT9XvlkdZq 
    
    # Authentication username (supported by Redis >= 6.0)
    #
    # auth-user myuser
    
    ################################# LOGGING #####################################
    
    # Log level: can be debug, info, success, warning o error.
    log-level error
    
    # Dump queries received from clients in the log (log-level debug required)
    #
    # dump-queries no
    
    # Dump buffer in the log (log-level debug required)
    #
    # dump-buffer no
    
    # Dump requests' queues (requests to send to cluster, request pending, ...)
    # in the log (log-level debug required)
    #
    # dump-queues no
    
    
    

    启动redis-cluster-proxy

    第一次先当前控制台启动,可以看下错误输出

    /data/bin/redis-cluster-proxy -c /usr/local/redis-cluster-proxy/proxy.conf
    

    第二次用后台启动

    /data/bin/redis-cluster-proxy --daemonize -c /usr/local/redis-cluster-proxy/proxy.conf
    

    启动成功后,可以通过ps -ef 查看是否启动

    ps -ef|grep redis
    

    然后通过redis客户端连接7000端口(proxy代理端口)

    /data/bin/redis-cli -h 127.0.0.1 -p 7000 -a 123456
    127.0.0.1:7000> set foo bar1 
    OK
    127.0.0.1:7000> get foo
    "bar1"
    
    

    redis-cluster-proxy是完美的解决方案?

    因为刚推出不久,生产环境基本上不会有太多实际的应用,里面肯定有不少坑,但不妨害对其有更多的期待。
    初次尝试可以感受的到,redis-cluster-proxy是一个非常轻量级,清爽简单的proxy代理层,它解决了一些redis cluster存在的一些实际问题,对应于程序来说也带来了一些方便性。

    如果没有源码开发能力,相比其他第三方proxy中间件,必须要承认官方可靠性和权威性。

    那么,redis-cluster-proxy是一个完美的解决方案么,留下两个问题

    1. 如何解决redis-cluster-proxy单点故障?

    2. proxy节点的如何面对网络流量风暴?

  • 相关阅读:
    Educational Codeforces Round 10 C. Foe Pairs 水题
    Educational Codeforces Round 10 B. z-sort 构造
    CDOJ 1048 Bob's vector 三分
    Educational Codeforces Round 10 A. Gabriel and Caterpillar 模拟
    第14届电子科大初赛民间盗版部分题目题解
    HDU 5654 xiaoxin and his watermelon candy 离线树状数组 区间不同数的个数
    HDU 5653 Bomber Man wants to bomb an Array. dp
    HDU 5652 India and China Origins 二分+并查集
    HDU 5651 xiaoxin juju needs help 数学
    HDU 5650 so easy 数学
  • 原文地址:https://www.cnblogs.com/passzhang/p/13233485.html
Copyright © 2011-2022 走看看