后端杂七杂八系列篇二

这篇具有很好参考价值的文章主要介绍了后端杂七杂八系列篇二。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

① Redis–消息队列

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

① 发布-订阅模式

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

② 发布-订阅常见的模型

① 一个发布者多个订阅者模型
后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud
② 多个发布者一个订阅者模型

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

③ 多个发布者多个订阅者模型
后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

③ 发布-订阅redis命令


后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

④ 发布-订阅模式redis实战


一个发布者多个订阅者模型

# 先订阅到一个频道
127.0.0.1:6379> SUBSCRIBE FM888
1) "subscribe"
2) "FM888"
3) (integer) 1
 
# 发布者发布消息
127.0.0.1:6379> PUBLISH FM888 test1
(integer) 1
127.0.0.1:6379> PUBLISH FM888 test2
(integer) 2 (代表订阅个数)
 
# 订阅者们接收消息
127.0.0.1:6379(subscribed mode)> SUBSCRIBE FM888
1) "subscribe"
2) "FM888"
3) (integer) 1
1) "message"
2) "FM888"
3) "test1"
1) "message"
2) "FM888"
3) "test2"

多个发布者个一个订阅者模型

# 订阅者订阅频道
127.0.0.1:6379(subscribed mode)> SUBSCRIBE FM888
 
# 发布者1
127.0.0.1:6379> PUBLISH FM888 test2
(integer) 1
 
# 发布者2
127.0.0.1:6379> PUBLISH FM888 test3
(integer) 1
 
# 订阅者
127.0.0.1:6379(subscribed mode)> SUBSCRIBE FM888
1) "subscribe"
2) "FM888"
3) (integer) 1
1) "message"
2) "FM888"
3) "test2"
1) "message"
2) "FM888"
3) "test3"

多个发布者个多个订阅者模型

# 订阅者们订阅频道
127.0.0.1:6379> PSUBSCRIBE *
 
# 发布者1
127.0.0.1:6379> PUBLISH FM3 test10
(integer) 1
 
# 发布者2
127.0.0.1:6379> PUBLISH FM888 test10
(integer) 1
 
# 发布者3
127.0.0.1:6379> PUBLISH FM888 test1
(integer) 1
 
# 发布者4
127.0.0.1:6379> PUBLISH FM12 test12
(integer) 1
 
# 订阅者们收到的消息
127.0.0.1:6379> PSUBSCRIBE *
1) "psubscribe"
2) "*"
3) (integer) 1
1) "pmessage"
2) "*"
3) "FM3"
4) "test10"
1) "pmessage"
2) "*"
3) "FM888"
4) "test10"
1) "pmessage"
2) "*"
3) "FM888"
4) "test1"
1) "pmessage"
2) "*"
3) "FM12"
4) "test12"

② Redis+Lua

① 什么是Lua?

Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放。Redis 2.6 版本通过内嵌支持 Lua 环境。也就是说一般的运用,是不需要单独安装Lua。


使用 Lua 脚本最大的好处是 Redis 会将整个脚本作为一个整体执行,不会被其他请求打断,可以保持原子性且减少了网络开销。

② Lua常用的命令

常用的命令不多,就下面这几个

  1. EVAL
  2. EVALSHA
  3. SCRIPT LOAD
  4. SCRIPT EXISTS
  5. SCRIPT FLUSH
  6. SCRIPT KILL

① eval命令

eval script numkeys key [key ...] arg [arg ...]

script 参数: 是一段 Lua 脚本程序,它可以是一段程序也可以是一个文件。
numkeys参数: 指定后续参数有几个key,即:key [key …]中key的个数。如没有key,则为0
key [key …]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key)。在Lua脚本中通过KEYS[1], KEYS[2]获取。
arg [arg …]: 附加参数,通过ARGV[1],ARGV[2]获取

// 例1:numkeys=1,keys数组只有1个元素key1,arg数组无元素
127.0.0.1:6379> EVAL "return KEYS[1]" 1 key1
"key1"
// 例2:numkeys=0,keys数组无元素,arg数组元素中有1个元素value1
127.0.0.1:6379> EVAL "return ARGV[1]" 0 value1
"value1"
// 例3:numkeys=2,keys数组有两个元素key1和key2,arg数组元素中有两个元素first和second 
127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 
1) "key1"
2) "key2"
3) "first"
4) "second"

Lua 脚本中执行 Redis 命令

redis.call(command, key [key …] argv [argv…])

command:Redis 中的命令,如 set、get 等。
key:操作 Redis 中的 key 值,相当于我们调用方法时的形参。
param:代表参数,相当于我们调用方法时的实参。

② SCRIPT LOAD命令 和 EVALSHA命令

SCRIPT LOAD命令格式:SCRIPT LOAD script

EVALSHA命令格式:EVALSHA sha1 numkeys key [key …] arg [arg …]

SCRIPT LOAD 将脚本 script 添加到Redis服务器的脚本缓存中,并不立即执行这个脚本,而是会立即对输入的脚本进行求值。并返回给定脚本的 SHA1 校验和。如果给定的脚本已经在缓存里面了,那么不执行任何操作。

在脚本被加入到缓存之后,在任何客户端通过EVALSHA命令,可以使用脚本的 SHA1 校验和来调用这个脚本。脚本可以在缓存中保留无限长的时间,直到执行SCRIPT FLUSH为止。

简单来说,就是执行SCRIPT LOAD命令会存到缓存,执行EVALSHA会校验缓存中是否有,有就执行缓存的。

## SCRIPT LOAD加载脚本,并得到sha1值
127.0.0.1:6379> SCRIPT LOAD "redis.call('SET', KEYS[1], ARGV[1]);redis.call('EXPIRE', KEYS[1], ARGV[2]); return 1;"
"6aeea4b3e96171ef835a78178fceadf1a5dbe345"

## EVALSHA使用sha1值,并拼装和EVAL类似的numkeys和key数组、arg数组,调用脚本。
127.0.0.1:6379> EVALSHA 6aeea4b3e96171ef835a78178fceadf1a5dbe345 1 userAge 10 60
(integer) 1
127.0.0.1:6379> get userAge
"10"

② SCRIPT EXISTS 和 SCRIPT FLUSH

SCRIPT EXISTS sha1 [sha1 …]
判断脚本是否在缓存中。

SCRIPT FLUSH sha
清除Redis服务端所有 Lua 脚本缓存

③ SCRIPT KILL

SCRIPT FLUSH
杀死当前正在运行的 Lua 脚本

③ Lua 写法的Demo

Demo 1

// Redis_CompareAndSet.lua 文件
local key = KEYS[1]
local val = redis.call("GET", key);

if val == ARGV[1]
then
        redis.call('SET', KEYS[1], ARGV[2])
        return 1
else
        return 0
end
// 执行命令
如:redis-cli -a 123456 --eval ./Redis_CompareAndSet.lua userName , zhangsan lisi 
## Redis客户端执行
127.0.0.1:6379> set userName zhangsan 
OK
127.0.0.1:6379> get userName
"zhangsan"

Demo 2

例如:同一IP在10秒内最多访问三次

// Redis_LimitIpVisit.lua
local visitNum = redis.call('incr', KEYS[1])

if visitNum == 1 then
        redis.call('expire', KEYS[1], ARGV[1])
end

if visitNum > tonumber(ARGV[2]) then
        return 0
end

return 1;
## LimitIP:127.0.0.1为key, 10 3表示:同一IP10秒内最多访问三次
## 前三次返回1,代表未被限制;第四、五次返回0,代表127.0.0.1这个ip已被拦截
[root@vm01 learn_lua]# redis-cli -a 123456 --eval Redis_LimitIpVisit.lua LimitIP:127.0.0.1 , 10 3
 (integer) 1
[root@vm01 learn_lua]# redis-cli -a 123456 --eval Redis_LimitIpVisit.lua LimitIP:127.0.0.1 , 10 3
 (integer) 1
[root@vm01 learn_lua]# redis-cli -a 123456 --eval Redis_LimitIpVisit.lua LimitIP:127.0.0.1 , 10 3
 (integer) 1
[root@vm01 learn_lua]# redis-cli -a 123456 --eval Redis_LimitIpVisit.lua LimitIP:127.0.0.1 , 10 3
 (integer) 0
[root@vm01 learn_lua]# redis-cli -a 123456 --eval Redis_LimitIpVisit.lua LimitIP:127.0.0.1 , 10 3
 (integer) 0

Demo 3

Springboot 继承 Redis 使用 Lua

# Redis数据库地址 
spring.redis.host=127.0.0.1 
# Redis端口 
spring.redis.port=6379 
# Redis密码(如果没有密码不用填写) 
spring.redis.password= 

// RedisCRUD.lua脚本

-- set 
if KEYS[1] and ARGV[1] then 
redis.call('SET', KEYS[1], ARGV[1]) 
return 1 
end 
-- get 
if KEYS[1] and not ARGV[1] then 
return redis.call('GET', KEYS[1]) 
end 
-- delete 
if KEYS[1] and not ARGV[1] then 
redis.call('DEL', KEYS[1]) 
return 1 
end 
-- exists 
if KEYS[1] and not ARGV[1] then 
    if redis.call('EXISTS', KEYS[1]) == 1 then 
    return true 
    else 
    return false 
    end 
end 
-- hset 
if KEYS[1] and ARGV[1] and ARGV[2] and ARGV[3] then 
redis.call('HSET', KEYS[1], ARGV[1], ARGV[2]) 
redis.call('EXPIRE', KEYS[1], ARGV[3]) 
return 1 
end 
-- hget 
if KEYS[1] and ARGV[1] and not ARGV[2] then 
return redis.call('HGET', KEYS[1], ARGV[1]) 
end 
-- hdelete 
if KEYS[1] and ARGV[1] and not ARGV[2] then 
redis.call('HDEL', KEYS[1], ARGV[1]) 
return 1 
end 
-- hexists 
if KEYS[1] and ARGV[1] and not ARGV[2] then 
    if redis.call('HEXISTS', KEYS[1], ARGV[1]) == 1 then 
    return true 
    else 
    return false 
    end 
end

// 在RedisTemplate的Bean中添加

 @Bean 
 public RedisScript<Long> redisScript() {
     RedisScript<Long> redisScript = new DefaultRedisScript<>(); 
     redisScript.setLocation(new ClassPathResource("lua/RedisCRUD.lua"));
     redisScript.setResultType(Long.class); 
     return redisScript; 
 } 

// 实现RedisService

@Service 
public class RedisServiceImpl implements RedisService { 
    @Autowired 
    private RedisTemplate<String, Object> redisTemplate; 
    @Autowired 
    private RedisScript<Long> redisScript;
    
    public void set(String key, Object value) { 
        redisTemplate.opsForValue().set(key, value); 
    } 
    public Object get(String key) { 
        return redisTemplate.opsForValue().get(key); 
    } 
    public void delete(String key) { 
        redisTemplate.delete(key); 
    } 
    public Boolean exists(String key) { 
        return redisTemplate.hasKey(key); 
    } 
    public Long hset(String key, String field, Object value) { 
        return redisTemplate.opsForHash().put(key, field, value); 
    } 
    public Object hget(String key, String field) { 
        return redisTemplate.opsForHash().get(key, field); 
    } 
    public void hdelete(String key, String... fields) { 
        redisTemplate.opsForHash().delete(key, fields); 
    } 
    public Boolean hexists(String key, String field) {
       return redisTemplate.opsForHash().hasKey(key, field); 
    } 
    public Long eval(String script, List<String> keys, List<Object> args) { 
        return redisTemplate.execute(RedisScript.of(script), keys, args.toArray()); 
    } 
    public Long eval(List<String> keys, List<Object> args) { 
        return redisTemplate.execute(redisScript, keys, args.toArray()); 
    } 
 } 

//测试RedisService

@RunWith(SpringRunner.class) 
@SpringBootTest 
public class RedisServiceImplTest { 
    @Autowired 
    private RedisService redisService; 
    @Test 
    public void test() {
        //第一种方式:执行string的lua
        redisService.eval("redis.call('SET', KEYS[1], ARGV[1])",Collections.singletonList(hashKey), Collections.singletonList(hashValue));
        //第二种方式:执行lua脚本
        String key ="key";
        String value ="value";
        redisService.eval(Collections.singletonList(hashKey), Collections.singletonList(hashValue));
    }

③ 常见的限流算法

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

① 固定窗口算法

又称计数器算法:在指定周期内累加访问次数,当访问次数达到设定的阈值时,触发限流策略,当进入下一个时间周期时进行访问次数的清零。如图所示,我们要求3秒内的请求不要超过150次:

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

但是,貌似看似很“完美”的流量统计方式其实存在一个非常严重的临界问题,即:如果第2到3秒内产生了150次请求,而第3到4秒内产生了150次请求,那么其实在第2秒到第4秒这两秒内,就已经发生了300次请求了,远远大于我们要求的3秒内的请求不要超过150次这个限制。

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

② 滑动窗口算法

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

③ 令牌桶限流算法(控制令牌生成速度,取的速度不控制)

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

④ 漏桶限流算法(控制水滴流出速度,不控制水滴产生速度)

后端杂七杂八系列篇二,后端杂七杂八系列,java,spring boot,redis,spring,spring cloud

④ Spring Retry

重试框架,就是当我们方法失败后的重试机制文章来源地址https://www.toymoban.com/news/detail-800137.html

@Service
@Slf4j
public class SpringRetryAnnotationService {
    @Autowired
    CommonService commonService;

    /**
     * 如果失败,定义重试3次,重试间隔为3s,指定恢复名称,指定监听器
     */
    @Retryable(value = RuntimeException.class, maxAttempts = 3, backoff = @Backoff(value = 3000L), recover = "testRecover", listeners = {"myRetryListener"})
    public void test() {
        commonService.test("注解式");
    }

    @Recover
    public void testRecover(RuntimeException runtimeException) {
        commonService.recover("注解式");
    }
}

到了这里,关于后端杂七杂八系列篇二的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • rpc入门笔记 0x02 protobuf的杂七杂八

    安装grpcio和grpcio-tools库 生成proto的python文件 python -m grpc_tools.protoc :使用grpc_tools包中的protoc命令进行代码生成。 --python_out=. :指定生成的Python代码的存放位置为当前目录。 --grpc_python_out=. :指定生成的gRPC代码的存放位置为当前目录。 -I. :指定搜索.proto文件的路径为当前目录

    2024年02月08日
    浏览(46)
  • 博客系统后端(项目系列2)

    目录 前言 : 1.准备工作 1.1创建项目 1.2引入依赖 1.3创建必要的目录 2.数据库设计 2.1博客数据 2.2用户数据 3.封装数据库 3.1封装数据库的连接操作 3.2创建两个表对应的实体类 3.3封装一些必要的增删改查操作 4.前后端交互逻辑的实现 4.1博客列表页 4.1.1约定前后端交互接口 4.1

    2024年02月10日
    浏览(25)
  • 建站系列(六)--- 后端开发语言

    建站系列(一)— 网站基本常识 建站系列(二)— 域名、IP地址、URL、端口详解 建站系列(三)— 网络协议 建站系列(四)— Web服务器之Apache、Nginx 建站系列(五)— 前端开发语言之HTML、CSS、JavaScript 建站系列(六)— 后端开发语言 建站系列(七)— 常用前后端框架

    2024年02月09日
    浏览(32)
  • 【docker】之基础篇二

    在生产环境中使用docker,需要对数据进行持久化,或者多个容器之间进行数据共享。 容器中管理数据的两种方式: 数据卷:容器内的数据直接映射到本地主机环境 数据卷容器:使用特定的容器维护数据卷 数据卷是一个可供容器使用的特殊目录,它将主机操作系统目录直接映

    2024年01月21日
    浏览(25)
  • Linux(基础篇二)

    5.1 Linux中的进程和服务 计算机中,一个正在执行的程序或命令,被叫做“进程”(process) 启动之后一只存在、常驻内存的进程,一般被称作“服务”(service) 5.3 systemctl 基本语法:systemctl start | stop | restart | status 服务名 5.4 运行级别 CentOS 6 Linux系统有7种运行级别: 常用级别为

    2024年02月11日
    浏览(19)
  • Linux(实操篇二)

    1.3 时间日期类 基本语法 date [OPTION]… [+FORMAT] 选项说明 -d时间字符串 显示指定的“时间字符串”表示的时间,而非当前时间 -s日期时间 设置系统日期时间 参数说明 +日期时间格式 指定显示时使用的日期时间格式 1.3.1 date显示当前时间 基本语法 (1)date 功能描述:显示当前时

    2024年02月11日
    浏览(36)
  • 篇二:工厂方法模式:灵活创建对象

    篇二: “工厂方法模式:灵活创建对象” 开始本篇文章之前先推荐一个好用的学习工具,AIRIght,借助于AI助手工具,学习事半功倍。欢迎访问:http://airight.fun/。 另外有2本不错的关于设计模式的资料,分享出来与大家学习参考。 链接:https://pan.baidu.com/s/1RmhQF_o1CdK8U7s5KeILog?

    2024年02月14日
    浏览(32)
  • 后端扫盲系列 - vue入门指南

    组件化:用户界面分解为可重用的组件,这些组件可以使开发的页面更加模块化和可维护= 双向数据绑定:vue提供了一种轻松绑定数据和DOM元素之间的机制,意味着数据发送变化时,视图会自动更新,反之亦然 虚拟DOM:vue使用虚拟DOM来最小化实际DOM更新的次数,从而提高性能

    2024年02月19日
    浏览(24)
  • 微服务保护——Sentinel【实战篇二】

    线程隔离有两种方式实现: 线程池隔离 信号量隔离(Sentinel默认采用) 在添加限流规则时,可以选择两种阈值类型: QPS:就是每秒的请求数,在快速入门中已经演示过 线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现舱壁模式。 需求:给

    2024年02月15日
    浏览(29)
  • 篇二:部署GitLab-创建令牌与GitLab凭证

    ​ 在Kubernetes集群中完成GitLab服务的部署,完成后创建一个公开项目,这里起个名叫 springcloud ,以NodePort的方式映射了80端口到宿主机的30888,并在gtilab中上传项目。 3.1部署GitLab 下载并解压 Jenkins.tar.gz 编辑gitlab资源清单 3.2创建公开项目 3.3上传项目 官方示例项目地址:https:

    2024年02月03日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包