• docker安装单机版redis、集群版redis


    一、redis单机版安装

    1、创建文件夹指定配置文件路径

    mkdir -p /usr/local/app/redis/data
    touch /usr/local/app/redis/redis.conf 
    
    • 1
    • 2

    2、编辑redis.conf文件

    • 以下只列出部分配置。详细的配置可以查看官方reds版本对应的redis.conf
    # Redis configuration file example.
    #
    # Note that in order to read the configuration file, Redis must be
    # started with the file path as first argument:
    #
    # ./redis-server /path/to/redis.conf
    
    # Note on units: when memory size is needed, it is possible to specify
    # it in the usual form of 1k 5GB 4M and so forth:
    #
    # 1k => 1000 bytes
    # 1kb => 1024 bytes
    # 1m => 1000000 bytes
    # 1mb => 1024*1024 bytes
    # 1g => 1000000000 bytes
    # 1gb => 1024*1024*1024 bytes
    #
    # units are case insensitive so 1GB 1Gb 1gB are all the same.
    
    ################################## INCLUDES ###################################
    # include /path/to/local.conf
    # include /path/to/other.conf
    # include /path/to/fragments/*.conf
    #
    
    ################################## MODULES #####################################
    
    # Load modules at startup. If the server is not able to load modules
    # it will abort. It is possible to use multiple loadmodule directives.
    #
    # loadmodule /path/to/my_module.so
    # loadmodule /path/to/other_module.so
    
    ################################## NETWORK #####################################
    
    # bind 192.168.1.100 10.0.0.1     # listens on two specific IPv4 addresses
    # bind 127.0.0.1 ::1              # listens on loopback IPv4 and IPv6
    # bind * -::*                     # like the default, all available interfaces
    #
    
    #
    # bind-source-addr 10.0.0.1
    
    
    # Accept connections on the specified port, default is 6379 (IANA #815344).
    # If port 0 is specified Redis will not listen on a TCP socket.
    port 6379
    
    # TCP listen() backlog.
    #
    # In high requests-per-second environments you need a high backlog in order
    # to avoid slow clients connection issues. Note that the Linux kernel
    # will silently truncate it to the value of /proc/sys/net/core/somaxconn so
    # make sure to raise both the value of somaxconn and tcp_max_syn_backlog
    # in order to get the desired effect.
    tcp-backlog 511
    
    # Unix socket.
    #
    # Specify the path for the Unix socket that will be used to listen for
    # incoming connections. There is no default, so Redis will not listen
    # on a unix socket when not specified.
    #
    # unixsocket /run/redis.sock
    # unixsocketperm 700
    
    # Close the connection after a client is idle for N seconds (0 to disable)
    timeout 0
    
    tcp-keepalive 300
    
    # Apply OS-specific mechanism to mark the listening socket with the specified
    # ID, to support advanced routing and filtering capabilities.
    #
    # On Linux, the ID represents a connection mark.
    # On FreeBSD, the ID represents a socket cookie ID.
    # On OpenBSD, the ID represents a route table ID.
    #
    # The default value is 0, which implies no marking is required.
    # socket-mark-id 0
    
    ################################# TLS/SSL #####################################
    
    # By default, TLS/SSL is disabled. To enable it, the "tls-port" configuration
    # directive can be used to define TLS-listening ports. To enable TLS on the
    # default port, use:
    #
    # port 0
    # tls-port 6379
    
    
    # By default, a Redis replica does not attempt to establish a TLS connection
    # with its master.
    #
    # Use the following directive to enable TLS on replication links.
    #
    # tls-replication yes
    
    # By default, the Redis Cluster bus uses a plain TCP connection. To enable
    # TLS for the bus protocol, use the following directive:
    #
    # tls-cluster yes
    
    # By default, only TLSv1.2 and TLSv1.3 are enabled and it is highly recommended
    # that older formally deprecated versions are kept disabled to reduce the attack surface.
    # You can explicitly specify TLS versions to support.
    # Allowed values are case insensitive and include "TLSv1", "TLSv1.1", "TLSv1.2",
    # "TLSv1.3" (OpenSSL >= 1.1.1) or any combination.
    # To enable only TLSv1.2 and TLSv1.3, use:
    #
    # tls-protocols "TLSv1.2 TLSv1.3"
    
    # Configure allowed ciphers.  See the ciphers(1ssl) manpage for more information
    # about the syntax of this string.
    #
    # Note: this configuration applies only to <= TLSv1.2.
    #
    # tls-ciphers DEFAULT:!MEDIUM
    #
    # tls-session-cache-timeout 60
    
    ################################# GENERAL #####################################
    
    # By default Redis does not run as a daemon. Use 'yes' if you need it.
    # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
    # When Redis is supervised by upstart or systemd, this parameter has no impact.
    daemonize no
    
    replica-serve-stale-data yes
    
    # You can configure a replica instance to accept writes or not. Writing against
    # a replica instance may be useful to store some ephemeral data (because data
    # written on a replica will be easily deleted after resync with the master) but
    # may also cause problems if clients are writing to it because of a
    # misconfiguration.
    #
    # Since Redis 2.6 by default replicas are read-only.
    #
    # Note: read only replicas are not designed to be exposed to untrusted clients
    # on the internet. It's just a protection layer against misuse of the instance.
    # Still a read only replica exports by default all the administrative commands
    # such as CONFIG, DEBUG, and so forth. To a limited extent you can improve
    # security of read only replicas using 'rename-command' to shadow all the
    # administrative / dangerous commands.
    replica-read-only yes
    
    # Replication SYNC strategy: disk or socket.
    #
    # New replicas and reconnecting replicas that are not able to continue the
    # replication process just receiving differences, need to do what is called a
    # "full synchronization". An RDB file is transmitted from the master to the
    # replicas.
    #
    ################################## SECURITY ###################################
    
    # Warning: since Redis is pretty fast, an outside user can try up to
    # 1 million passwords per second against a modern box. This means that you
    # should use very strong passwords, otherwise they will be very easy to break.
    # Note that because the password is really a shared secret between the client
    # and the server, and should not be memorized by any human, the password
    # can be easily a long string from /dev/urandom or whatever, so by using a
    # long and unguessable password no brute force attack will be possible.
    
    # Redis ACL users are defined in the following format:
    #
    #   user <username> ... acl rules ...
    #
    # For example:
    #
    #   user worker +@list +@connection ~jobs:* on >ffa9203c493aa99
    #
    # The special username "default" is used for new connections. If this user
    # has the "nopass" rule, then new connections will be immediately authenticated
    # as the "default" user without the need of any password provided via the
    # AUTH command. Otherwise if the "default" user is not flagged with "nopass"
    # the connections will start in not authenticated state, and will require
    # AUTH (or the HELLO command AUTH option) in order to be authenticated and
    # start to work.
    #
    # ACL LOG
    #
    # The ACL Log tracks failed commands and authentication events associated
    # with ACLs. The ACL Log is useful to troubleshoot failed commands blocked
    # by ACLs. The ACL Log is stored in memory. You can reclaim memory with
    # ACL LOG RESET. Define the maximum entry length of the ACL Log below.
    acllog-max-len 128
    
    # Command renaming (DEPRECATED).
    #
    # ------------------------------------------------------------------------
    # WARNING: avoid using this option if possible. Instead use ACLs to remove
    # commands from the default user, and put them only in some admin user you
    # create for administrative purposes.
    # ------------------------------------------------------------------------
    #
    # It is possible to change the name of dangerous commands in a shared
    # environment. For instance the CONFIG command may be renamed into something
    # hard to guess so that it will still be available for internal-use tools
    # but not available for general clients.
    #
    # Example:
    #
    ################################### CLIENTS ####################################
    
    # Set the max number of connected clients at the same time. By default
    # this limit is set to 10000 clients, however if the Redis server is not
    # able to configure the process file limit to allow for the specified limit
    # the max number of allowed clients is set to the current file limit
    # minus 32 (as Redis reserves a few file descriptors for internal uses).
    #
    # Once the limit is reached Redis will close all the new connections sending
    # an error 'max number of clients reached'.
    #
    # IMPORTANT: When Redis Cluster is used, the max number of connections is also
    # shared with the cluster bus: every node in the cluster will use two
    # connections, one incoming and another outgoing. It is important to size the
    # limit accordingly in case of very large clusters.
    #
    # maxclients 10000
    
    ############################## MEMORY MANAGEMENT ################################
    
    ############################# LAZY FREEING ####################################
    
    lazyfree-lazy-eviction no
    lazyfree-lazy-expire no
    lazyfree-lazy-server-del no
    replica-lazy-flush no
    lazyfree-lazy-user-del no
    lazyfree-lazy-user-flush no
    
    ################################ THREADED I/O #################################
    
    ############################ KERNEL OOM CONTROL ##############################
    
    oom-score-adj no
    
    disable-thp yes
    
    ############################## APPEND ONLY MODE ###############################
    
    appendonly no
    
    appendfilename "appendonly.aof"
    
    appenddirname "appendonlydir"
    
    # The fsync() call tells the Operating System to actually write data on disk
    # instead of waiting for more data in the output buffer. Some OS will really flush
    # data on disk, some other OS will just try to do it ASAP.
    #
    # appendfsync always
    appendfsync everysec
    # appendfsync no
    
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    
    aof-load-truncated yes
    
    aof-timestamp-enabled no
    
    ################################ SHUTDOWN #####################################
    
    
    ################ NON-DETERMINISTIC LONG BLOCKING COMMANDS #####################
    
    # Maximum time in milliseconds for EVAL scripts, functions and in some cases
    # modules' commands before Redis can start processing or rejecting other clients.
    #
    ################################ REDIS CLUSTER  ###############################
    #
    # Default is 'yes' (allow automatic migrations).
    #
    # cluster-allow-replica-migration yes
    
    
    
    
    # In order to setup your cluster make sure to read the documentation
    # available at https://redis.io web site.
    
    ########################## CLUSTER DOCKER/NAT support  ########################
    
    # * cluster-announce-ip
    # * cluster-announce-port
    # * cluster-announce-tls-port
    # * cluster-announce-bus-port
    
    #
    # cluster-announce-ip 10.1.1.5
    # cluster-announce-tls-port 6379
    # cluster-announce-port 0
    # cluster-announce-bus-port 6380
    
    ################################## SLOW LOG ###################################
    
    slowlog-log-slower-than 10000
    
    # There is no limit to this length. Just be aware that it will consume memory.
    # You can reclaim memory used by the slow log with SLOWLOG RESET.
    slowlog-max-len 128
    
    ################################ LATENCY MONITOR ##############################
    
    #
    # By default latency monitoring is disabled since it is mostly not needed
    # if you don't have latency issues, and collecting data has a performance
    # impact, that while very small, can be measured under big load. Latency
    # monitoring can easily be enabled at runtime using the command
    # "CONFIG SET latency-monitor-threshold <milliseconds>" if needed.
    latency-monitor-threshold 0
    
    ################################ LATENCY TRACKING ##############################
    
    ############################# EVENT NOTIFICATION ##############################
    
    # Redis can notify Pub/Sub clients about events happening in the key space.
    # This feature is documented at https://redis.io/topics/notifications
    #
    # For instance if keyspace events notification is enabled, and a client
    # performs a DEL operation on key "foo" stored in the Database 0, two
    # messages will be published via Pub/Sub:
    #
    #  Example: to enable list and generic events, from the point of view of the
    #           event name, use:
    #
    #  notify-keyspace-events Elg
    #
    #  Example 2: to get the stream of the expired keys subscribing to channel
    #             name __keyevent@0__:expired use:
    #
    #  notify-keyspace-events Ex
    #
    #  By default all notifications are disabled because most users don't need
    #  this feature and the feature has some overhead. Note that if you don't
    #  specify at least one of K or E, no events will be delivered.
    notify-keyspace-events ""
    
    ############################### ADVANCED CONFIG ###############################
    
    list-max-listpack-size -2
    
    # Lists may also be compressed.
    # Compress depth is the number of quicklist ziplist nodes from *each* side of
    # the list to *exclude* from compression.  The head and tail of the list
    # are always uncompressed for fast push/pop operations.  Settings are:
    # 0: disable all list compression
    # 1: depth 1 means "don't start compressing until after 1 node into the list,
    #    going from either the head or tail"
    #    So: [head]->node->node->...->node->[tail]
    #    [head], [tail] will always be uncompressed; inner nodes will compress.
    # 2: [head]->[next]->node->node->...->node->[prev]->[tail]
    #    2 here means: don't compress head or head->next or tail->prev or tail,
    #    but compress all nodes between them.
    # 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]
    # etc.
    list-compress-depth 0
    
    set-max-intset-entries 512
    
    # Similarly to hashes and lists, sorted sets are also specially encoded in
    # order to save a lot of space. This encoding is only used when the length and
    # elements of a sorted set are below the following limits:
    zset-max-listpack-entries 128
    zset-max-listpack-value 64
    
    hll-sparse-max-bytes 3000
    
    # Streams macro node max size / items. The stream data structure is a radix
    # tree of big nodes that encode multiple items inside. Using this configuration
    # it is possible to configure how big a single node can be in bytes, and the
    # maximum number of items it may contain before switching to a new node when
    # appending new stream entries. If any of the following settings are set to
    # zero, the limit is ignored, so for instance it is possible to set just a
    # max entries limit by setting max-bytes to 0 and max-entries to the desired
    # value.
    stream-node-max-bytes 4096
    stream-node-max-entries 100
    
    activerehashing yes
    
    # Both the hard or the soft limit can be disabled by setting them to zero.
    client-output-buffer-limit normal 0 0 0
    client-output-buffer-limit replica 256mb 64mb 60
    client-output-buffer-limit pubsub 32mb 8mb 60
    
    # Redis calls an internal function to perform many background tasks, like
    # closing connections of clients in timeout, purging expired keys that are
    # never requested, and so forth.
    
    hz 10
    
    dynamic-hz yes
    
    aof-rewrite-incremental-fsync yes
    
    rdb-save-incremental-fsync yes
    
    # lfu-log-factor 10
    # lfu-decay-time 1
    
    ########################### ACTIVE DEFRAGMENTATION #######################
    #
    # What is active defragmentation?
    # -------------------------------
    
    # activedefrag no
    
    
    # Jemalloc background thread for purging will be enabled by default
    jemalloc-bg-thread yes
    #
    # ignore-warnings ARM64-COW-BUG
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414

    3、拉取redis镜像

    docker pull redis
    
    • 1

    4、启动redis容器

    docker run -d -p 6379:6379 \
    -v /usr/local/docker/redis/redis.conf:/etc/redis/redis.conf  \
    -v /usr/local/docker/redis/data:/data \
    --privileged=true \
    --name redis redis redis-server /etc/redis/redis.conf
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、集群redis的搭建

    命令说明
    docker run创建并运行docker容器实例
    –name redis-node-6 redis容器名字
    –net host使用宿主机的ip和端口,默认
    –privileged=true获取宿主机root用户权限
    -v /usr/local/docker/redis/share/redis-node-6:/data容器卷,宿主机地址:容器内部地址
    redis:xxxredis镜像和版本号
    –cluster-enabled yes开启redis集群
    –appendonly yes开启持久化
    –port 6386redis端口号

    1、启动容器(三主三从)

    docker run -d --name redis-node-1 --net host --privileged=true -v /usr/local/docker/redis/share/redis-node-1:/data redis --cluster-enabled yes --appendonly yes --port 6381
    
    docker run -d --name redis-node-2 --net host --privileged=true -v /usr/local/docker/redis/share/redis-node-2:/data redis --cluster-enabled yes --appendonly yes --port 6382
    
    docker run -d --name redis-node-3 --net host --privileged=true -v /usr/local/docker/redis/share/redis-node-3:/data redis --cluster-enabled yes --appendonly yes --port 6383
    
    docker run -d --name redis-node-4 --net host --privileged=true -v /usr/local/docker/redis/share/redis-node-4:/data redis --cluster-enabled yes --appendonly yes --port 6384
    
    docker run -d --name redis-node-5 --net host --privileged=true -v /usr/local/docker/redis/share/redis-node-5:/data redis --cluster-enabled yes --appendonly yes --port 6385
    
    docker run -d --name redis-node-6 --net host --privileged=true -v /usr/local/docker/redis/share/redis-node-6:/data redis --cluster-enabled yes --appendonly yes --port 6386
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2、构建主从关系

    # 进入redis-node-1构建
    docker exec -it redis-node-1 bash
    # 注意,进入docker容器后才能执行一下命令,且注意自己的真实IP地址
    redis-cli --cluster create 192.168.11.133:6381 192.168.11.133:6382 192.168.11.133:6383 192.168.11.133:6384 192.168.11.133:6385 192.168.11.133:6386 --cluster-replicas 1
    # 切记里面设置配置的时候是yes不是y
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 以6381为切入点,查看集群状态
    #进入界面redis内部查看信息
    redis-cli -p 6381
    cluster info
    cluster nodes
    
    • 1
    • 2
    • 3
    • 4
    127.0.0.1:6381> cluster nodes
    3c6a4b9d6bd658adc057833a9917b153c12843af 192.168.11.133:6384@16384 slave 81adde9dae53d8c0bf2464f5835f4ba2364f0d30 0 1655264316000 2 connected
    81adde9dae53d8c0bf2464f5835f4ba2364f0d30 192.168.11.133:6382@16382 master - 0 1655264318198 2 connected 5461-10922
    78c999fc70290fcbb1a8e73ed8b3bb9bc5222036 192.168.11.133:6383@16383 master - 0 1655264317187 3 connected 10923-16383
    b0f9fa3ea5b038b796c10c0e30ec109a94711a71 192.168.11.133:6385@16385 slave 78c999fc70290fcbb1a8e73ed8b3bb9bc5222036 0 1655264315171 3 connected
    7b08ed9f3866a631d3f48c5c1e024651df0c1520 192.168.11.133:6386@16386 slave 9edd9ee67e62e26deae7fcfb2d385c4d66ff2719 0 1655264317000 1 connected
    9edd9ee67e62e26deae7fcfb2d385c4d66ff2719 192.168.11.133:6381@16381 myself,master - 0 1655264316000 1 connected 0-5460
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    请看以上的6382、6383、6381都是master。然后6384、6385、6386都是slave,是内部随机分配的一主一从。

    看服务尾号id:  
    2719的master下面绑定的是1520的从服务  
    2036的master下面绑定的是1a71的从服务  
    0d30的master下面绑定的是43af的从服务
    
    • 1
    • 2
    • 3
    • 4

    3、整个操作图示:

    在这里插入图片描述
    在这里插入图片描述

    • 至此,redis集群搭建完毕
  • 相关阅读:
    【JVM】如何定位、解决内存泄漏和溢出
    jquery链接导入和基本选择器的使用
    Go使用qrcode包解析微信和支付宝二维码,生成一个链接(前端拿到链接即可解析成对应的支付二维码)
    python多线程是如何工作
    东软睿驰总裁兼CTO杜强受邀出席 CICV 2024智能网联汽车技术首脑(CTO)闭门峰会
    java计算机毕业设计汽车销售系统源程序+mysql+系统+lw文档+远程调试
    配置参考一
    在WinForms应用程序中创建一个定时任务以监听鼠标左键点击事件可以通过以下步骤实现
    【ARM CoreLink 系列 4 -- NIC-400 控制器详细介绍】
    ssm及springboot整合shiro
  • 原文地址:https://blog.csdn.net/www1056481167/article/details/125580167