上海外贸网站建设公司/宁波seo推广服务电话
这里写自定义目录标题
- 1 Lua
- 1.1 简介
- 1.1.1 注释
- 1.1.2 变量
- 1.1.3 数据类型
- 1.1.4 控制结构
- 1.1.5 函数
- 1.1.6 模块
- 1.1.7 字符串操作
- 1.1.8 错误处理
- 1.1.9 标准库
- 1.2 Redis和Lua脚本结合优点
- 1.3 Lua脚本应用和调试
- 1.3.1 缓存更新
- 1.3.2 原子操作
- 1.3.3 数据处理
- 1.3.4 分布式锁
- 1.3.5 Redis中调试Lua
- 1.4 Lua脚本在Spring Boot中的实现
- 1.4.1 pom.xml和配置
- 1.4.2 创建Lua脚本
- 1.4.2.1 运行Lua脚本字符串
- 1.4.2.2 运行Lua脚本文件
- 1.4.3 使用Lua脚本限流
- 1.4.3.1 自定义注解
- 1.4.3.2 自定义redis配置类
- 1.4.3.3 自定义限流AOP类
- 1.4.3.4 自定义lua脚本
- 1.4.3.5 添加测试接口
- 1.5 使用Lua提高SpringBoot性能
- 1.5.1 减少网络开销
- 1.5.2 原子操作
- 1.5.3 复杂操作
- 1.5.4 事务
- 1.6 错误处理和安全性
- 1.6.1 错误处理
- 1.6.2 安全性
1 Lua
1.1 简介
当涉及Lua
编程时,以下是对前述12个关键概念的详细说明,附带Lua代码示例以帮助更深入了解这门编程语言
1.1.1 注释
注释在Lua
中用于添加说明和注解。单行注释以--
开始,多行注释则使用--[[ ... ]]
。
-- 这是一条单行注释--[[ 这是一个多行注释可以跨越多行
]]
1.1.2 变量
变量在Lua
中无需显式声明类型。使用local
关键字创建局部变量,全局变量直接声明。
local age = 30
name = "John" -- 全局变量
1.1.3 数据类型
基本数据类型包括整数、浮点数、字符串、布尔值和nil
其中表是一种非常灵活的数据结构,使用花括号 {} 或者 table
构造函数。
local num = 42
local str = "Hello, Lua!"
local flag = true
local empty = nil
local person = { name = "John", age = 30 }
表是Lua
的核心数据结构,使用花括号 {} 或者 table
构造函数。
表可以包含键值对,键和值可以是任何数据类型。
local person = { name = "John", age = 30, hobbies = {"Reading", "Gaming"} }
print("姓名:" .. person.name)
print("年龄:" .. person.age)
1.1.4 控制结构
条件语句:使用if、else和elseif
来实现条件分支。
if age < 18 thenprint("未成年")
elseif age >= 18 and age < 65 thenprint("成年")
elseprint("老年")
end
循环结构:Lua支持for循环、while循环和repeat…until循环。
for i = 1, 5 doprint(i)
endlocal count = 0
while count < 3 doprint("循环次数: " .. count)count = count + 1
endrepeatprint("至少执行一次")
until count > 5
1.1.5 函数
函数在Lua
中使用function
关键字定义,可以接受参数并返回值。
function add(a, b)return a + b
endlocal result = add(5, 3)
print("5 + 3 = " .. result)
1.1.6 模块
Lua
支持模块化编程,允许将相关功能封装在独立的模块中,并通过require
关键字加载它们
1.1.7 字符串操作
Lua
提供了许多字符串处理函数,例如string.sub
用于截取子串,string.find
用于查找字符串中的子串等。
local text = "Lua programming"
local sub = string.sub(text, 1, 3)
print(sub) -- 输出 "Lua"
1.1.8 错误处理
错误处理通常使用pcall
函数来包裹可能引发异常的代码块,以捕获并处理错误。这通常与assert
一起使用。
local success, result = pcall(function()error("出错了!")
end)if success thenprint("执行成功")
elseprint("错误信息: " .. result)
end
1.1.9 标准库
Lua
标准库包含丰富的功能,如文件操作、网络编程、正则表达式、时间处理等。可以通过内置的模块来使用这些功能,如io、socket等。
总之,Lua
是一种灵活的编程语言,其简洁性和强大的表格数据结构使其在各种应用中具有广泛的用途。这些示例代码应该有助于更好地理解Lua
的基本概念和语法。
1.2 Redis和Lua脚本结合优点
Lua
脚本在Redis
中的使用有许多优势,使其成为执行复杂操作的理想选择。以下是一些主要原因:
- 性能:
Lua
脚本在Redis
中执行,避免了多次的客户端与服务器之间的通信。这可以减少网络开销,提高性能,特别是在需要执行多个Redis
命令以完成一个操作时。
原子性:Redis
保证Lua
脚本的原子性执行,无需担心竞态条件或并发问题。 - 事务:
Lua
脚本可以与Redis
事务一起使用,确保一系列命令的原子性执行。这允许将多个操作视为一个单一的事务,要么全部成功,要么全部失败。 - 复杂操作:
Lua
脚本提供了一种在Redis
中执行复杂操作的方法,允许在一个脚本中组合多个Redis
命令。这对于处理复杂的业务逻辑非常有用,例如计算和更新分布式计数器、实现自定义数据结构等。 - 原子锁:
使用Lua
脚本,你可以实现复杂的原子锁,而不仅仅是使用Redis
的SETNX(set if not exists)
命令。这对于分布式锁的实现非常重要。 - 减少网络开销:
对于大批量的数据处理,Lua脚本可以减少客户端和服务器之间的往返次数,从而显著减少网络开销。 - 减少服务器负载:
通过将复杂的计算移至服务器端,可以减轻客户端的负担,降低服务器的负载。 - 原生支持:
Redis
天生支持Lua
脚本,因此不需要额外的插件或扩展。 - 可读性和维护性:
Lua
脚本是一种常见的脚本语言,易于编写和维护。将复杂逻辑封装在脚本中有助于提高代码的可读性。
总之,Lua
脚本在Redis
中的优势在于它可以原子性地执行复杂操作、减少网络通信、提高性能、减轻服务器负载,以及提高代码的可读性。这使得它成为执行一系列复杂操作的理想选择,尤其是在分布式系统中需要高性能和可伸缩性的场景下。通过Lua
脚本,Redis
不仅成为一个键值存储,还能执行复杂的数据操作。
1.3 Lua脚本应用和调试
Lua
脚本在Redis
中有广泛的应用场景,以下是一些示例场景,展示了Lua
脚本的实际用途
1.3.1 缓存更新
场景:在缓存中存储某些数据,但需要定期或基于条件更新这些数据,同时确保在更新期间不会发生并发问题。
示例:使用Lua
脚本,你可以原子性地检查数据的新鲜度,如果需要更新,可以在一个原子性操作中重新计算数据并更新缓存。
local cacheKey = KEYS[1] -- 获取缓存键
local data = redis.call('GET', cacheKey) -- 尝试从缓存获取数据
if not data then-- 数据不在缓存中,重新计算并设置data = calculateData()redis.call('SET', cacheKey, data)
end
return data
1.3.2 原子操作
场景:需要执行多个Redis
命令作为一个原子操作,确保它们在多线程或多进程环境下不会被中断。
示例:使用Lua
脚本,可以将多个命令组合成一个原子操作,如实现分布式锁、计数器、排行榜等。
local key = KEYS[1] -- 获取键名
local value = ARGV[1] -- 获取参数值
local current = redis.call('GET', key) -- 获取当前值
if not current or tonumber(current) < tonumber(value) then-- 如果当前值不存在或新值更大,设置新值redis.call('SET', key, value)
end
1.3.3 数据处理
场景:需要对Redis
中的数据进行复杂的处理,如统计、筛选、聚合等。
示例:使用Lua
脚本,可以在Redis
中执行复杂的数据处理,而不必将数据传输到客户端进行处理,减少网络开销。
local keyPattern = ARGV[1] -- 获取键名的匹配模式
local keys = redis.call('KEYS', keyPattern) -- 获取匹配的键
local result = {}
for i, key in ipairs(keys) dolocal data = redis.call('GET', key) -- 获取每个键对应的数据-- 处理数据并添加到结果中table.insert(result, processData(data))
end
return result
1.3.4 分布式锁
场景:实现分布式系统中的锁机制,确保只有一个客户端可以执行关键操作。
示例:使用Lua
脚本,你可以原子性地尝试获取锁,避免竞态条件,然后在完成后释放锁。
local lockKey = KEYS[1] --获取锁的键名
local lockValue = ARGV[1] -- 获取锁的值
local lockTimeout = ARGV[2] -- 获取锁的超时时间
if redis.call('SET', lockKey, lockValue, 'NX', 'PX', lockTimeout) then-- 锁获取成功,执行关键操作-- ...redis.call('DEL', lockKey) -- 释放锁return true
elsereturn false -- 无法获取锁
这些场景只是Lua
脚本在Redis
中的应用之一。Lua
脚本允许你在Redis
中执行更复杂的操作,而无需进行多次的网络通信,从而提高性能和可伸缩性,同时确保数据的一致性和原子性。这使得Lua
成为Redis
的强大工具,用于处理各种分布式系统需求。
1.3.5 Redis中调试Lua
在 Redis
的 Lua
脚本中,KEYS
和 ARGV
是两个特殊的全局变量,用于获取传递给脚本的键和参数。
KEYS
变量:
KEYS
是一个数组,包含了传递给脚本的所有键。可以使用KEYS
变量来访问这些键,并执行相应的操作,如获取值、修改值等。
例如:local value = redis.call("GET", KEYS[1])
在例中使用KEYS[1]
来获取传递给脚本的第一个键,并使用redis.call
函数来获取该键的值。ARGV
变量:
ARGV
是一个数组,包含了传递给脚本的所有参数。可以使用ARGV
变量来访问这些参数,并执行相应的操作,如解析参数、计算参数等。
redis
中验证 lua
脚本的两种方式:
- 登录
redis
后执行eval
命令:EVAL script numkeys key [key ...] arg [arg ...]
例如:EVAL "local key = KEYS[1]\nlocal value = ARGV[1]\nredis.call('SET', key, value)" 1 mykey myvalue
script
:是要执行的Lua脚本numkeys
:是脚本中用到的键的数量key [key ...]
:是脚本中用到的键的名称arg [arg ...]
:是脚本中用到的参数
- 不登录执行
--eval
命令,如果lua
脚本较长,可以使用redis-cli --eval
的方式,新建lua.lua
文件,在文件中输入:return KEYS[1]..ARGV[1]
在linux
中执行:redis-cli --eval 文件路径 keys , argvs
key和参数
间需要使用逗号(,
)隔开,并且逗号前后需要占用空格
1.4 Lua脚本在Spring Boot中的实现
在Spring Boot
中实现Lua
脚本的执行主要涉及Spring Data Redis
和Lettuce(或Jedis)
客户端的使用。以下是编写、加载和执行Lua脚本的步骤和示例:
1.4.1 pom.xml和配置
首先,在Spring Boot
项目的pom.xml
中,添加Spring Data Redis
和Lettuce(或Jedis)
的依赖。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>io.lettuce.core</groupId><artifactId>lettuce-core</artifactId> <!-- 或使用Jedis -->
</dependency>
配置Redis
连接:
在application.properties
或application.yml
中配置Redis连接属性,包括主机、端口、密码等。
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=yourPassword
1.4.2 创建Lua脚本
创建一个Lua
脚本,以执行你需要的操作。将脚本保存在Spring Boot
项目的合适位置。
例如,假设你有一个Lua脚本文件myscript.lua,它实现了一个简单的计算:
local a = tonumber(ARGV[1])
local b = tonumber(ARGV[2])
return a + b
编写Java代码:
在Spring Boot
应用中,编写Java代码以加载和执行Lua脚本。使用Spring Data Redis
提供的StringRedisTemplate
或LettuceConnectionFactory
。
提供两种不同的示例来执行Lua脚本,一种是直接运行Lua脚本字符串,另一种是运行脚本文件。以下是这两种示例:
1.4.2.1 运行Lua脚本字符串
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;@Service
public class LuaScriptService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;public Integer executeLuaScriptFromString() {String luaScript = "local a = tonumber(ARGV[1])\nlocal b = tonumber(ARGV[2])\nreturn a + b";RedisScript<Integer> script = new DefaultRedisScript<>(luaScript, Integer.class);String[] keys = new String[0]; // 通常情况下,没有KEYS部分Object[] args = new Object[]{10, 20}; // 传递给Lua脚本的参数Integer result = stringRedisTemplate.execute(script, keys, args);return result;}
}
1.4.2.2 运行Lua脚本文件
首先,将Lua
脚本保存到文件,例如myscript.lua。
然后,创建一个Java类来加载和运行该脚本文件:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;@Service
public class LuaScriptService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate ResourceLoader resourceLoader;public Integer executeLuaScriptFromFile() {Resource resource = resourceLoader.getResource("classpath:myscript.lua");String luaScript;try {luaScript = new String(resource.getInputStream().readAllBytes());} catch (Exception e) {throw new RuntimeException("Unable to read Lua script file.");}RedisScript<Integer> script = new DefaultRedisScript<>(luaScript, Integer.class);String[] keys = new String[0]; // 通常情况下,没有KEYS部分Object[] args = new Object[]{10, 20}; // 传递给Lua脚本的参数Integer result = stringRedisTemplate.execute(script, keys, args);return result;}
}
通过这两种示例,可以选择要执行Lua
脚本的方式,是直接在Java代码中定义脚本字符串,还是从文件中读取脚本。
1.4.3 使用Lua脚本限流
1.4.3.1 自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RedisLimitAnnotation {/*** key*/String key() default "";/*** Key的前缀*/String prefix() default "";/*** 一定时间内最多访问次数*/int count();/*** 给定的时间范围 单位(秒)*/int period(); }
1.4.3.2 自定义redis配置类
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;import java.io.Serializable;@Configuration
public class RedisConfiguration {@Beanpublic DefaultRedisScript<Long> redisluaScript() {DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("limit.lua")));redisScript.setResultType(Long.class);return redisScript;}@Bean("redisTemplate")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,ObjectMapper.DefaultTyping.NON_FINAL,JsonTypeInfo.As.WRAPPER_ARRAY);jackson2JsonRedisSerializer.setObjectMapper(om);//设置value的序列化方式为JSOn
// redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);//设置key的序列化方式为StringredisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.afterPropertiesSet();return redisTemplate;}}
1.4.3.3 自定义限流AOP类
import cn.annotation.RedisLimitAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;@Slf4j
@Configuration
public class LimitRestAspect {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Autowiredprivate DefaultRedisScript<Long> redisluaScript;@Pointcut(value = "@annotation(com.congge.config.limit.RedisLimitAnnotation)")public void rateLimit() {}@Around("rateLimit()")public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();RedisLimitAnnotation rateLimit = method.getAnnotation(RedisLimitAnnotation.class);if (rateLimit != null) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String ipAddress = getIpAddr(request);StringBuffer stringBuffer = new StringBuffer();stringBuffer.append(ipAddress).append("-").append(targetClass.getName()).append("- ").append(method.getName()).append("-").append(rateLimit.key());List<String> keys = Collections.singletonList(stringBuffer.toString());//调用lua脚本,获取返回结果,这里即为请求的次数Long number = redisTemplate.execute(redisluaScript,// 此处传参只要能转为Object就行(因为数字不能直接强转为String,所以不能用String序列化)//new GenericToStringSerializer<>(Object.class),// 结果的类型需要根据脚本定义,此处是数字--定义的是Long类型//new GenericToStringSerializer<>(Long.class)keys,rateLimit.count(),rateLimit.period());if (number != null && number.intValue() != 0 && number.intValue() <= rateLimit.count()) {logger.info("限流时间段内访问了第:{} 次", number.toString());return joinPoint.proceed();}} else {return joinPoint.proceed();}throw new RuntimeException("访问频率过快,被限流了");}/*** 获取请求的IP方法* @param request* @return*/private static String getIpAddr(HttpServletRequest request) {String ipAddress = null;try {ipAddress = request.getHeader("x-forwarded-for");if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("WL-Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddr();}// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割if (ipAddress != null && ipAddress.length() > 15) {if (ipAddress.indexOf(",") > 0) {ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));}}} catch (Exception e) {ipAddress = "";}return ipAddress;}
}
该类要做的事情和上面的两种限流措施类似,不过在这里核心的限流是通过读取lua脚步,通过参数传递给lua脚步实现的。
1.4.3.4 自定义lua脚本
在工程的 resources
目录下,添加如下的lua脚本
local key = "rate.limit:" .. KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")if current + 1 > limit thenreturn 0
else-- 没有超阈值,将当前访问数量+1,并设置2秒过期(可根据自己的业务情况调整)redis.call("INCRBY", key,"1")redis.call("expire", key,"2")return current + 1
end
1.4.3.5 添加测试接口
@RestController
public class RedisController {@GetMapping("/redis/limit")@RedisLimitAnnotation(key = "queryFromRedis",period = 1, count = 1)public String queryFromRedis(){return "success";}
}
为了模拟效果,这里将QPS设置为1 ,启动工程后(提前启动redis服务),调用一下接口,正常的效果如下,如果快速刷接口,超过每秒1次的请求时报错
1.5 使用Lua提高SpringBoot性能
使用Lua
脚本可以显著提高Spring Boot
应用程序的性能,尤其是在与Redis
交互方面。以下是如何使用Lua脚本来实现性能优化的几种方法:
1.5.1 减少网络开销
Redis
是内存数据库,数据存储在内存中,而网络通信通常是Redis
操作的性能瓶颈之一。通过使用Lua
脚本,你可以将多个操作组合成一个原子操作,从而减少了多次的网络往返次数。这对于需要执行多个Redis
命令以完成一个操作的情况非常有用。
1.5.2 原子操作
Lua
脚本的执行是原子的,这意味着在Lua
脚本执行期间,没有其他客户端可以插入其他操作。这使得Lua
脚本在实现诸如分布式锁、计数器、排行榜等需要原子操作的情况下非常有用。
例如,考虑一个计数器的场景,多个客户端需要原子性地增加计数。使用Lua脚本,你可以实现原子递增:
local key = KEYS[1]
local increment = ARGV[1]
return redis.call('INCRBY', key, increment)
1.5.3 复杂操作
Lua
脚本允许你在Redis
服务器端执行复杂的数据处理。这减少了将数据传输到客户端进行处理的开销,并允许你在Redis
中执行更复杂的逻辑,从而提高性能。
例如,可以使用Lua
脚本来处理存储在多个键中的数据并返回聚合结果:
local total = 0
for _, key in ipairs(KEYS) dolocal value = redis.call('GET', key)total = total + tonumber(value)
end
return total
1.5.4 事务
与Lua
脚本一起使用事务可以确保一系列Redis
命令的原子性执行。这对于需要一组操作要么全部成功,要么全部失败的情况非常重要。
例如,可以使用Lua
脚本在事务中执行一系列更新操作,如果其中一个操作失败,整个事务将回滚:
local key1 = KEYS[1]
local key2 = KEYS[2]
local value = ARGV[1]redis.call('SET', key1, value)
redis.call('INCRBY', key2, value)-- 如果这里的任何一步失败,整个事务将回滚
总之,使用Lua
脚本可以大大提高Spring Boot
应用程序与Redis
之间的性能。它减少了网络开销,允许执行原子操作,执行复杂操作并实现事务,这些都有助于提高应用程序的性能和可伸缩性。因此,Lua
脚本是在与Redis
交互时实现性能优化的有力工具。
1.6 错误处理和安全性
处理Lua
脚本中的错误和确保安全性在与Redis
交互时非常重要。以下是如何处理这些问题的一些建议:
1.6.1 错误处理
- 错误返回值:
Lua
脚本在执行期间可能会遇到错误,例如脚本本身存在语法错误,或者在脚本中的某些操作失败。Redis
执行Lua
脚本后,会返回脚本的执行结果。可以检查这个结果以查看是否有错误,通常返回值是一个特定的错误标识。例如,如果脚本执行成功,返回值通常是OK,否则会有相应的错误信息。 - 异常处理: 在
Spring Boot
应用程序中,可以使用异常处理来捕获Redis
执行脚本时可能抛出的异常。Spring Data Redis
提供了一些异常类,如RedisScriptExecutionException
,用于处理脚本执行期间的错误。可以使用try-catch
块来捕获这些异常并采取相应的措施,例如记录错误信息或执行备用操作。
1.6.2 安全性
- 参数验证: 在执行
Lua
脚本之前,始终验证传递给脚本的参数。确保参数是合法的,并且不包含恶意代码。避免将不受信任的用户输入直接传递给Lua脚本,因为它可能包含恶意的Lua代码。 - 限制权限: 在
Redis
服务器上配置适当的权限,以限制对Lua
脚本的执行。确保只有授权的用户能够执行脚本,并且不允许执行具有破坏性或不安全操作的脚本。 - 白名单: 如果你允许动态加载
Lua
脚本,确保只有受信任的脚本可以执行。可以创建一个白名单,只允许执行白名单中的脚本,防止执行未经审核的脚本。 - 沙盒模式: 一些
Redis
客户端库支持将Lua
脚本运行在沙盒模式下,以限制其访问和执行权限。在沙盒模式下,脚本无法执行危险操作,如文件访问。 - 监控日志: 记录
Redis
执行Lua
脚本的相关信息,包括谁执行了脚本以及执行的脚本内容。这有助于跟踪执行情况并发现潜在的安全问题。
总之,处理Lua
脚本中的错误和确保安全性是非常重要的。通过适当的错误处理和安全措施,可以确保Lua
脚本在与Redis
交互时不会引入潜在的问题,并提高应用程序的稳定性和安全性
相关文章:

Redis之Lua脚本讲解
这里写自定义目录标题 1 Lua1.1 简介1.1.1 注释1.1.2 变量1.1.3 数据类型1.1.4 控制结构1.1.5 函数1.1.6 模块1.1.7 字符串操作1.1.8 错误处理1.1.9 标准库 1.2 Redis和Lua脚本结合优点1.3 Lua脚本应用和调试1.3.1 缓存更新1.3.2 原子操作1.3.3 数据处理1.3.4 分布式锁1.3.5 Re…...

redis archive github
https://github.com/redis/redis/releases/tag/7.2.2https://github.com/redis/redis/releases/tag/7.2.2...

3台Centos7快速部署Kafka集群
首先,我要说,Kafka 是强依赖于 ZooKeeper 的,所以在设置 Kafka 集群之前,我们首先需要设置一个 ZooKeeper 集群。 部署ZooKeeper需要安装jdk yum install java-1.8.0-openjdk 安装完以后 下面是详细的步骤: 1. 安装和…...

最小栈(C++解法)
题目 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。i…...

Python 算法高级篇:堆排序的优化与应用
Python 算法高级篇:堆排序的优化与应用 引言 1. 什么是堆?2. 堆的性质3. 堆排序的基本原理4. 堆排序的 Python 实现5. 堆排序的性能和优化6. 堆排序的实际应用7. 总结 引言 堆排序是一种高效的排序算法,它基于数据结构中的堆这一概念。堆排序…...

视频下载软件 Downie4 mac中文介绍
Downie mac是一款Mac平台上非常实用的视频下载工具。它支持下载各种视频网站上的视频,并且具有快速、稳定、易于使用的特点。 Downie支持下载各种视频网站上的视频,包括YouTube、Vimeo、Netflix、Hulu、Amazon等等。它具有快速、稳定的下载速度ÿ…...

计算机操作系统重点概念整理-第一章 计算机系统概述【期末复习|考研复习】
第一章 计算机系统概述 【期末复习|考研复习】 计算机操作系统系列文章传送门: 第一章 计算机系统概述 第二章 进程管理 第三章 进程同步 第四章 内存管理 第五章 文件管理 第六章 输出输出I/O管理 文章目录 第一章 计算机系统概述 【期末复习|考研复习】前言一、计…...

树莓派基金会近日发布了新版基于 Debian 的树莓派操作系统
树莓派基金会(Raspberry Pi Foundation)近日发布了新版基于 Debian 的树莓派操作系统(Raspberry Pi OS),为树莓派单板电脑带来了新的书虫基础和一些重大变化。 新版 Raspberry Pi OS 的最大变化是它现在基于最新的 Deb…...

Web项目如何做单元测试
你可能会用单元测试框架,python的unittest、pytest,Java的Junit、testNG等。 那么你会做单元测试么!当然了,这有什么难的? test_demo.py def inc(x):return x 1def test_answer():assert inc(3) 4 inc() 是定义的…...

MySQL主从复制(基于GTID--事务ID方式)
目录 一、GTID相关概念1.GTID 是什么?2.GTID主从复制方式概念3.GTID的优缺点 二、GTID工作原理三、部署主从复制四、测试同步1.主库上新建数据库2.从库上查看是否同步成功 五、重设从库六、常见故障七、故障切换八、GTID的一些疑问1.为什么基于GTID的同步也要打开bi…...

3.72 Command Buffer及URP概述
一、Command Buffer 1.概念 CommandBuffer携带一系列的渲染命令,依赖相机,用来拓展渲染管线的渲染效果。而且可以指定在相机渲染的某个点执行本身的拓展渲染。Command buffers也可以结合屏幕后期效果使用。 简单来说就是可以在渲染流程中插入一些自定…...

分布式理论和分布式锁知识点总结
文章目录 (一) 分布式理论算法和协议1)CAP理论总结 2)BASE理论BASE 理论的核心思想基本可用软状态最终一致性 3)Paxos算法Basic Paxos 算法4) Raft算法1 拜占庭将军 5)Gossip协议 (二) 分布式锁分布式锁应该具备哪些条…...

IOC课程整理-17 Spring事件
1. Java 事件/监听器编程模型 2. 面向接口的事件/监听器设计模式 3. 面向注解的事件/监听器设计模式 4. Spring 标准事件-ApplicationEvent 5. 基于接口的 Spring 事件监听器 6. 基于注解的 Spring 事件监听器 7. 注册 Spring ApplicationListener 8. Spring 事件发布器 9. Spr…...

大数据Flink(一百零五):SQL性能调优
文章目录 SQL性能调优 一、 MiniBatch 聚合...

ESP8266,手机与电脑之间的TCP通讯
电脑端运行通讯猫调试助手,作为服务端: 电脑端 电脑的IP地址是: 192.168.2.232 手机与电脑之间的TCP通讯 手机端运行网络调试精灵,作为客户端: 手机端 如果从手机端点击"发送"按钮,则也会将"ghhh东方红广场"几个字发送到电脑上(服务端). ESP8266作为客户…...

vue的数据监听是如何实现的?
Vue的数据监听是通过数据劫持和发布订阅模式来实现的。 数据劫持:Vue通过使用Object.defineProperty()方法来劫持数据对象的属性,并使用getter和setter来监听属性的变化。当属性被修改时,setter方法会被调用,从而触发相应的监听函…...

埋点日志解决方案——Golang+Gin+Sarama VS Java+SpringCloudGateway+ReactorKafka
埋点日志解决方案——GolangGinSarama VS JavaSpringCloudGatewayReactorKafka 之前我就写过几篇OpenRestylua-kafka-client将埋点数据写入Kafka的文章,如下: Lua将Nginx请求数据写入Kafka——埋点日志解决方案 python定时任务执行shell脚本切割Nginx…...

LeetCode 541 反转字符串 II 简单
题目 - 点击直达 1. 541 反转字符串 II 简单1. 题目详情1. 原题链接2. 题目要求3. 基础框架 2. 解题思路1. 思路分析2. 时间复杂度3. 代码实现 1. 541 反转字符串 II 简单 1. 题目详情 给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个…...

从入门到精通:深入了解CSS中的Grid网格布局技巧和应用!
🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 ⭐ 专栏简介 📘 文章引言 一…...

Android Studio Giraffe 添加 maven { url “https://jitpack.io“ }报错
Android Studio Giraffe 添加 maven { url “https://jitpack.io” }报错 settings.gradle.kts:13:21: Unexpected tokens (use ; to separate expressions on the same line)解决方法 新版maven写法发生了改变: maven { url uri("https://jitpack.io"…...

Linux C/C++ 实现网络流量分析(性能工具)
网络流量分析的原理基于对数据包的捕获、解析和统计分析,通过对网络流量的细致观察和分析,帮助管理员了解和优化网络的性能、提高网络安全性,并快速排查和解决网络故障和问题。 Linux中的网络流量常见类型 在Linux中,网络流量可以…...

python门牌制作,统计某个数字出现的次数
题目: 小蓝要为一条街的住户制作门牌号。 这条街一共有 2022位住户,门牌号从 1 到 2022 编号。 小蓝制作门牌的方法是先制作 0 到 9 这几个数字字符,最后根据需要将字符粘贴到门牌上,例如门牌 1017 需要依次粘贴字符 1、0、1、…...

轻量封装WebGPU渲染系统示例<7>-材质多pass(源码)
当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/version-1.01/src/voxgpu/sample/MultiMaterialPass.ts 此示例渲染系统实现的特性: 1. 用户态与系统态隔离。 2. 高频调用与低频调用隔离。 3. 面向用户的易用性封装。 4. 渲染数据和渲染机制分离。 …...

0030Java程序设计-积分管理系统论文
文章目录 摘 要**目 录**系统实现系统功能需求3.2.1 管理员功能3.2.2 柜员功能 开发环境 摘 要 随着计算机和网络的不断革新,世界已经进入了前所未有的电子时代。作为实用性强、应用范围广泛的会员管理系统也正在被越来越多的各类企业用于消费管理领域。然…...

H5游戏源码分享-考眼力游戏猜猜金币在哪
H5游戏源码分享-考眼力游戏猜猜金币在哪 <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><meta charset"UTF-8"><meta name"apple-mobile-web-app-capa…...

2023 年值得关注的国外网络安全初创公司
网络安全初创公司试图解决的问题往往有点超前于主流。他们可以比大多数老牌公司更快地填补空白或新兴需求。初创公司通常可以更快地创新,因为它们不受安装基础的限制。 当然,缺点是初创公司往往缺乏资源和成熟度。公司致力于初创公司的产品或平台是有风…...

搞定蓝牙-第六篇(HID
搞定蓝牙-第六篇(HID) ble与HIDHOGPGAPP与HID ESP32程序分析 ble与HID HOGP 我们发现,电脑连接了蓝牙键盘就可以直接使用了,不需要配置任何东西,那么,这两者是怎么通讯的呢。我们使用的电脑windows系统内…...

Open3D(C++) 最小二乘拟合平面(直接求解法)
目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。 一、算法原理 平面方程的一般表达式为: A x + B y + C...

lua移植及使用
编译环境:Ubuntu16.04 64位 交叉编译工具:arm-hisiv500-linux-gcc 文章目录 1. 项目背景2. lua开源版本选择3. 封装代码3.1 源码简介3.2 封装类3.2.1 头文件3.2.2 类的实现3.3.3 sample代码 1. 项目背景 使用lua脚本,读取key对应的值&#x…...

【鸿蒙软件开发】ArkTS基础组件之Select(下拉菜单)、Slider(滑动条)
文章目录 前言一、Select下拉菜单1.1 子组件1.2 接口参数 1.3 属性1.4 事件1.5 示例代码 二、Slider2.1 子组件2.2 接口参数:SliderStyle枚举说明 2.3 属性2.4 事件SliderChangeMode枚举说明 2.5 示例代码 总结 前言 Select组件:提供下拉选择菜单&#…...