当前位置:   article > 正文

分布式系统(一)-Redis

分布式系统(一)-Redis

前言

Redis作为当前主流的缓存组件,合理的运用将极大提高系统的性能

说明

redis的优点,简单一个字就是 
基于内存的存储让其吞吐量可以达到读的速度是110000次/s,写的速度是81000次/s(若使用SSD将更快)

持久化
  • RDB
    配置
    save 60000 1 #表示60000s内有1次修改 就进行保存

手动存储
执行命令bgsave

缺点 数据过大时 非常慢

  • AOF
    配置
    1. appendonly yes
    2. appendfilename "appendonly.aof"
    3. #appendfsync always
    4. appendfsync everysec
    5. # appendfsync no
    缺点 aof文件会非常大,但是可以保证数据不丢失

限流

通过lua脚本进行限流操作

  1. local key=KEYS[1]; --key
  2. local second = KEYS[2]; --单位s
  3. local total=KEYS[3]; --并发总数
  4. local count=redis.call("incr",key);
  5. local time=redis.call("ttl",key);
  6. local result=true;
  7. if(time==-1)
  8. then
  9. redis.call("expire",key,second);
  10. end
  11. if(tonumber(count)>tonumber(total))
  12. then
  13. result=false;
  14. end
  15. return tostring(result);

nodejs

  1. var fs = require("fs");
  2. var path = require("path");
  3. var lua = "";//lua脚本
  4. var sha = "";
  5. var redis = require("redis");
  6. var config = require("../../config");
  7. var log = require("../log4");
  8. var client;
  9. var clientoption = { host: config.config.redis.host, port: config.config.redis.port, password: config.config.redis.password, db: 2 };
  10. client = redis.createClient(clientoption);
  11. client.on("ready", function (err) {
  12. // redis.debug_mode = true;
  13. if (err) {
  14. console.log(err);
  15. } else {
  16. // console.log("redis read ok");
  17. log.logger.info("redis read ok");
  18. }
  19. });
  20. client.on("error", function (err) {
  21. // console.log(err, "出异常了");
  22. log.logger.error(err);
  23. })
  24. var privatehandler = {
  25. //整体逻辑
  26. flow: async function (key, second, total) {
  27. if (sha) {
  28. let d = await privatehandler.evalsha(sha, [3, key, second, total]);
  29. if (d == true) {
  30. return true;
  31. } else if (d == false) {
  32. return false;
  33. } else if (d == "NOSCRIPT") {
  34. if (!lua) {
  35. throw "lua文件不存在";
  36. }
  37. }
  38. }
  39. if (!lua) {
  40. lua = fs.readFileSync(path.resolve(__dirname, 'limitflow.lua')).toString();
  41. }
  42. sha = await privatehandler.evalload(lua);
  43. return await privatehandler.flow(key, second, total);
  44. },
  45. //根据hash值执行一个脚本 如果脚本不存在 会返回NOSCRIPT
  46. evalsha: async function (sha, command) {
  47. return new Promise(function (resolve, reject) {
  48. if (!command.constructor === Array) {
  49. reject("请传入参数数组");
  50. return;
  51. }
  52. command.unshift(sha);
  53. command.unshift("evalsha");
  54. // console.log(command);
  55. client.multi([command]).exec(function (err, result) {
  56. // console.log(err,result);
  57. if (err) {
  58. console.log(err);
  59. reject(err);
  60. }
  61. else {
  62. result = result[0];
  63. if (result == "true") {
  64. resolve(true);
  65. } else if (result == "false") {
  66. resolve(false);
  67. } else if (result && result.code == "NOSCRIPT") {//没有读取到数据
  68. resolve("NOSCRIPT");
  69. } else {
  70. reject(result);//其他异常
  71. }
  72. }
  73. });
  74. })
  75. },
  76. evalload: async function (luacontent) {//加载lua脚本 返回一个sha
  77. return new Promise(function (resolve, reject) {
  78. client.multi([["script", "load", luacontent]]).exec(function (err, result) {
  79. if (err) {
  80. console.log(err);
  81. reject(err);
  82. }
  83. else {
  84. result = result[0];
  85. resolve(result);
  86. }
  87. });
  88. })
  89. }
  90. }
  91. //封装给外部调用
  92. var handler = {
  93. /*
  94. key:关键key
  95. second:秒
  96. total:总数
  97. iswait:是否等待 如果不等待 将直接返回结果 如果等待 一直等到true
  98. */
  99. limitflow: async function (key, second, total, iswait) {
  100. if (!key || !second || !total || iswait == null) {
  101. throw "参数错误";
  102. }
  103. if (iswait) {
  104. while (!await privatehandler.flow(key, second, total)) {
  105. //如果不能继续执行 等10ms
  106. await sleep(10);
  107. }
  108. return true;
  109. } else {
  110. return await privatehandler.flow(key, second, total);
  111. }
  112. }
  113. }
  114. var sleep = function (time) {
  115. return new Promise(function (resolve, reject) {
  116. setTimeout(function () {
  117. resolve();
  118. }, time);
  119. })
  120. }
  121. exports.redisflow = handler;

分布式锁

redis的分布式锁主要是通过nx命令来实现,利用redis的原子性操作,若存在相同的键,操作将会失败,同时利用redis的自动过期功能,可防止因意外导致的锁释放异常,不会阻塞后续的锁获取。

各语言实现的分布式锁大同小异,不在赘述,一般java项目使用redission来实现分布式锁

主从、集群、哨兵模式

主从

主从配置非常简单,只要在从库里配置上主库信息即可
slaveof 172.25.254.101 6379

哨兵模式

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

配置

首先配置Redis的主从服务器,修改redis.conf文件如下

  1. # 使得Redis服务器可以跨网络访问
  2. bind 0.0.0.0
  3. # 设置密码
  4. requirepass "123456"
  5. # 指定主服务器,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
  6. slaveof 192.168.11.128 6379
  7. # 主服务器密码,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
  8. masterauth 123456

上述内容主要是配置Redis服务器,从服务器比主服务器多一个slaveof的配置和密码。

配置3个哨兵,每个哨兵的配置都是一样的。在Redis安装目录下有一个sentinel.conf文件,copy一份进行修改

  1. # 禁止保护模式
  2. protected-mode no
  3. # 配置监听的主服务器,这里sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,192.168.11.128代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作。
  4. sentinel monitor mymaster 192.168.11.128 6379 2
  5. # sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码
  6. # sentinel auth-pass <master-name> <password>
  7. sentinel auth-pass mymaster 123456

上述关闭了保护模式,便于测试。

有了上述的修改,我们可以进入Redis的安装目录的src目录,通过下面的命令启动服务器和哨兵

  1. # 启动Redis服务器进程
  2. ./redis-server ../redis.conf
  3. # 启动哨兵进程
  4. ./redis-sentinel ../sentinel.conf

注意启动的顺序。首先是主机(192.168.11.128)的Redis服务进程,然后启动从机的服务进程,最后启动3个哨兵的服务进程。

集群

sentinel模式基本可以满足一般生产的需求,具备高可用性。但是当数据量过大到一台服务器存放不下的情况时,主从模式或sentinel模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中。cluster模式的出现就是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。
cluster可以说是sentinel和主从模式的结合体,通过cluster可以实现主从和master重选功能,所以如果配置两个副本三个分片的话,就需要六个Redis实例。因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量过大时,可以新增机器进行扩容。
集群特点

  1. * 多个redis节点网络互联,数据共享
  2. * 所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用
  3. * 不支持同时处理多个key(如MSET/MGET),因为redis需要把key均匀分布在各个节点上,
  4. 并发量很高的情况下同时创建key-value会降低性能并导致不可预测的行为
  5. * 支持在线增加、删除节点
  6. * 客户端可以连接任何一个主节点进行读写
搭建

开启配置

  1. #配置yes开启redis-cluster
  2. cluster-enabled yes
  3. #配置节点之间超时时间
  4. cluster-node-timeout 15000
  5. #这个配置很重要,cluster开启必须重命名指定cluster-config-file,不能与别的节点相同,否则会启动失败,最好按主机+端口命名
  6. cluster-config-file nodes-6379.conf

创建集群

redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1

若需要修正槽位,使用官方命令

redis-cli --cluster fix

执行命令检查集群

redis-cli --cluster info 127.0.0.1:6382
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/592477
推荐阅读
相关标签
  

闽ICP备14008679号