Lua脚本

news2024/11/17 16:26:32

目录

    • 说明
    • 什么是Lua脚本
      • 为什么要使用Lua脚本
      • Lua脚本的安装
    • Lua脚本的使用
      • Lua的变量
      • Lua脚本的算术运算符
      • Lua脚本的关系运算符
      • Lua脚本的逻辑运算符
      • Lua脚本不同的操作
      • Lua脚本的函数和标准库
    • Redis整合Lua脚本(重点)
      • 在Java集成Lua
      • 在SpringBoot项目中使用Redis集成Lua

说明

  我学习Lua脚本的初衷是为为了解决Redis分布式锁中出现的问题,Lua脚本具有原子型,要么同时执行,要么同时失败,所以某一些操作,特别是我们在分布式环境下的一些操作,我们需要让他具有原子性,要不然就会出现问题。

说明:Redis在2.6版本之后,支持在Redis里面写Lua脚本,也就是可以在Redis里面调用Lua脚本。

什么是Lua脚本

  Lua脚本是一个小巧的轻量级的脚本语言,它的设计目的你说为了通过嵌入到应用程序中从而为应用程序提供拓展和定制功能。Lua是由C语言来编写的,几乎所有的操作系统和平台都可以编译和运行Lua脚本语言。在所有脚本引擎中,Lua的速度是最快的。所有它才被作为嵌入式脚本语言。

Lua脚本很容易被C、C++代码调用,也可以反过来调用C、C++代码。

 
 

为什么要使用Lua脚本

我们使用一个东西,我们需要去知道,我们为什么要用这个东西,这个东西对我们来说有什么作用。
使用Lua脚本的好处:
1、减少网络开销。
本来我们在Redis里面需要去执行三四个命令,但是在Lua脚本里面我们只需要调用一个就可以达到我们的目的。
2、Redis里面,对Lua脚本执行是一个原子性的操作,要么同时执行,要么同时失败。
3、具有复用性。

 
 

Lua脚本的安装

我们这次使用 Linux 的Wget来安装Lua。在我们安装Lua之前,我们需要去搞懂Linux中的Wget命令:Linux的Wget命令。

获取Lua RPM包的地址:https://pkgs.org/download/lua(x86-64)
我们将它下载到我们的Linux环境中,然后进行RPM安装,顺便熟悉一下RPM安装的指令:

wget -P /opt/software/ http://mirror.centos.org/centos/7/os/x86_64/Packages/lua-5.1.4-15.el7.x86_64.rpm

下载之后安装,使用RPM环境会帮我们自动进行配置(这用来安装Java环境挺好的,不用去配置)

rpm -ivh lua-5.1.4-15.el7.x86_64.rpm

安装完成之后,我们使用lua命令进入Lua客户端,具体流程如图

在这里插入图片描述

Lua脚本的使用

Lua脚本语言是一种动态类型的语言,变量的类型是根据值去推到的,不像我们Java的强变量类型。

Lua的变量

全局变量、局部变量
全局变量:a = 1
局部变量:local b = 1

数组的定义
local xx = {“a”,“b”,“c”}

Lua脚本的算术运算符

+、-、*、/是和其他语言一样的

在这里插入图片描述

Lua脚本的关系运算符

== :判断两个数是否相等
~=:不等于

Lua脚本的逻辑运算符

and / or (相当于Java中的&& / || )

not (a and b)

Lua脚本不同的操作

1、连接两个字符串

local a = "hello"
local b = "world"
print(a..b)

2、计算当前字符串的长度 #xxx

a = "hello"
b = "world"
print(#a..b)

逻辑判断:

if expression then 
elseif expression then 
else
end

循环语句:

for i=1,100 do
 print(i)
end 

foreach语句:

xx = {"a","b","c"}
for i,v in ipairs(xx) do
 print(i)
end

Lua脚本的函数和标准库

函数模板:

-function 函数名(params....return(可以有可以没有)
end

函数名 = function(params....return(可以有可以没有)
end

在这里插入图片描述
String 、Table(对数组的操作)

Redis整合Lua脚本(重点)

在Lua脚本里面是可以调用Redis中的命令的。
redis.call(‘set’,‘key’,value)
redis.call(‘get’,‘key’)

Redis中如何整合Lua脚本?
在Redis中内置了Lua脚本,所以我们在Redis中使用Lua脚本,我们需要使用命令:
eval 脚本内容 keynumbers key… args…
如果我们Lua脚本中有key和参数,是根据我们的KEYS[] 和 ARGV[]来获取
eval “redis.call(‘set’,‘name’,‘liu’)” keynumbers
在这里插入图片描述

在这里插入图片描述

redis客户端调用lua脚本时候命令:
redis-cli --eval luaFileDir key1 key2… (这里需要空格),(这里需要空格) value1 value2…
才不会报错

这里需要说明一下:Redis在运行Lua脚本的时候,不能运行Redis的其他命令,这样保证了Lua脚本的原子性。

在Java集成Lua

在普通的Java工程中,由于Redis中内置了Lua脚本,所以我们可以使用Jedis来调用Lua。
使用Jedis来运行Lua脚本有两种方式:
1、直接使用jedis.eval(String LuaScript,List KEYS,List ARGV)
2、使用jedis.scriptLoad(String LuaScript)将Lua脚本缓存到Redis中并返回一个经过sha加密的摘要,然后我们使用jedis.evalsha(String sha,List KEYS,List ARGV)来运行,这样子就不用每次都将Lua脚本网络传输到远程的Redis中,减少网络IO,经过测试,确实是缓存到Redis中了。

举个例子:
写一个限制一个IP地址访问的次数(或者是防止手机注册或者是登录刷短信,我们可以使用这个方法来限制)
ip_limit.lua

local num = redis.call('incr',KEYS[1])
if tonumber(num) == 1 then 
   redis.call('expire',KEYS[1],ARGV[1])
   return 1
elseif tonumber(num) > tonumber(ARGV[2])
   return 0
else 
   return 1
end

Jedis方式一:
1、通过类加载器获取Java工程项目中resources下的ip_limit.lua资源,并将该Lua脚本转换为字符串。
2、通过jedis.eval(String LuaScript,List KEYS,List ARGV),来运行

public class LuaTest {
       public static void main(String[] args) throws IOException {
	      Jedis jedis = JedisUtil.getJedisConnection();
	      String fileName = LuaTest.class.getClassLoader().getResource("ip_limit.lua").getPath();//获取文件路径
	      String lua = fileToString2(fileName);
	      String sha = jedis.scriptLoad(lua);
	      List<String> keys = new ArrayList<String>();
	      keys.add("127.0.0.1");
	      List<String> argv = new ArrayList<String>();
	      argv.add("60000");
	      argv.add("5");
	      System.out.println(jedis.evalsha(sha, keys, argv));
       }
       
       
       public static String fileToString2(String path) throws IOException {
	      FileReader reader = new FileReader(new File(path));
	      StringBuilder stringBuilder = new StringBuilder();
	      char[] buffer = new char[1024];
	      int size;
	      while ((size = reader.read(buffer)) != -1) {
		     stringBuilder.append(buffer, 0, size);
	      }
	      return stringBuilder.toString();
       }
       
       
}

方式二:
1、通过通过上述的方式来获取resources下的lua脚本然后转换称为字符串,之后通过jedis.scriptload(String LuaScript)将Lua脚本缓存到Redis中然后返回一个通过sha加密之后的摘要。(缓存到Redis之后,我们就不用再解析Lua脚本了直接使用那个sha加密之后的摘要就行,除非Redis重启)
2、然后通过jedis.evalsha(sha,List KEYS,List ARGV)来运行。

public class LuaTest {
       public static void main(String[] args) throws IOException {
	      Jedis jedis = JedisUtil.getJedisConnection();
	      String fileName = LuaTest.class.getClassLoader().getResource("ip_limit.lua").getPath();//获取文件路径
	      String lua = fileToString2(fileName);
	      String sha = jedis.scriptLoad(lua);
	      List<String> keys = new ArrayList<String>();
	      keys.add("127.0.0.1");
	      List<String> argv = new ArrayList<String>();
	      argv.add("60000");
	      argv.add("5");
	      System.out.println(jedis.evalsha(sha, keys, argv));
       }
       
       
       public static String fileToString2(String path) throws IOException {
	      FileReader reader = new FileReader(new File(path));
	      StringBuilder stringBuilder = new StringBuilder();
	      char[] buffer = new char[1024];
	      int size;
	      while ((size = reader.read(buffer)) != -1) {
		     stringBuilder.append(buffer, 0, size);
	      }
	      return stringBuilder.toString();
       }
}

在SpringBoot项目中使用Redis集成Lua

这里我先说一下我在集成的时候,遇到的坑:
1、由于急于集成,原本项目没有对RedisTemplate进行序列化配置,导致一直报错。所以解决方案是将Redis序列化配置

redisTemplate.opsForValue().increment()【自增和自减操作】就可能报错redis.clients.jedis.exceptions.JedisDataException:
ERR value is not an integer or out of range了。

2、DefaultRedisScript的构造器没有指定Long的反射对象,产生如下错误。
在这里插入图片描述

具体步骤:
1、配置好RedisTemplate的配置,Host、Port、序列化等。
2、如果将lua文件放到了resources下面,那么我们需要根据类加载器获取resources下面的Lua脚本的路径,然后根据流转成String.
3、创建DefaultRedisScript(LuaScript,Long.class)对象,然后调用redisTemplate.execute(redisScript,KEYS,Object…)

解决1:

package com.cheng.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * rides配置类
 * @author 15594
 */
@EnableCaching
@Configuration
public class RidesConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        //是否开启事务,true为开启
        template.setEnableTransactionSupport(true);

        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }

}


解决2:

        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript,Long.class);

 
 
 
  
  
  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/419640.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端PC端适配,网页端适配

问题背景 由于我司是使用的大屏&#xff0c;且设计稿尺寸为19201080。但是需要适配各种分辨率&#xff0c; 比如12801024(5:4)、1366768(16&#xff1a;10)、16801050&#xff08;16&#xff1a;10&#xff09;。在尝试了多种方法之后&#xff0c;最终确定主要的适配方法为rem…

【vue3】04-vue基础语法补充及阶段案例

文章目录vue基础语法补充vue的computedvue的watch侦听书籍购物车案例vue基础语法补充 vue的computed computed&#xff1a;用于声明要在组件实例上暴露的计算属性。&#xff08;官方文档描述&#xff09; 我们已经知道&#xff0c;在模板中可以直接通过插值语法显示一些data中…

科学的演变:从笛卡尔到生成式人工智能

编者按&#xff1a;本文主要介绍了科学的演变历史&#xff0c;从笛卡尔到生成式人工智能。文章探讨了数学在验证科学原理中的作用&#xff0c;并介绍了新机器学习工具如何验证新的科学。 文中提到&#xff0c;将生成式人工智能与Excel或iPhone进行比较是低估了这一新技术的潜在…

【AI】PaddlePaddle实现自动语音识别

文章目录文档背景安装环境Python版本pip环境安装模型需要的环境项目目录结构数据准备生成数据字典数据预处理训练模型创建模型构建模型的目的模型黑盒在模型中充当什么角色解码方法总结文档背景 学习AI的过程中&#xff0c;难免会出现各种各样的问题。比如&#xff0c;什么样的…

制造业生产管理系统(500强制造企业数字化实践)

前言 制造业是国民经济的支柱产业之一&#xff0c;随着科技和数字化的发展&#xff0c;制造业正在经历着一场新的变革。传统的制造模式已经无法满足市场的快速变化和客户的多样化需求&#xff0c;制造企业急需通过数字化和智能化转型升级&#xff0c;提高生产效率和质量水平&a…

第十四届蓝桥杯嵌入式详解

目录 第一部分 客观试题&#xff08;15 分&#xff09; 不定项选择&#xff08;1.5 分/题&#xff09; 第二部分 程序设计试题&#xff08;85 分&#xff09; 2.1 STM32CubeMX初始化配置 2.1.1 配置GPIO 2.1.2 配置ADC 2.1.3 配置RCC 2.1.4 配置定时器TIM 2.1.5 配置ADC1、AD…

【从零开始学Skynet】基础篇(二):了解Skynet

1、节点和服务 在下图所示的服务端系统中&#xff0c;每个Skynet进程&#xff08;操作系统进程&#xff09;都称为一个节点&#xff0c;每个节点都可以开启数千个Lua服务&#xff0c;每个服务都是一个Actor。不同节点可以部署在不同的物理机上&#xff0c;提供分布式集群的能力…

Velocity入门到精通(上篇)

最近自己所做的项目使用到这个Velocity模板引擎&#xff0c;分享一下在互联网找的学习资料。 目录 一. velocity简介 1. velocity简介 2. 应用场景 3. velocity 组成结构 二. 快速入门 1. 需求分析 2. 步骤分析 3. 代码实现 3.1 创建maven工程 3.2 引入坐标 3.3 编…

Redis锁的租约问题

目录Redis的租约问题Redis租约问题的想法Redis租约问题的解决方案Redis的租约问题 首先我们先来说一说什么是Redis的租约问题。   在我们实现Redis分布式锁的时候&#xff0c;我们会出现Redis锁的时间<业务执行执行时间&#xff0c;这其实就是一个典型的租约问题&#xf…

【C++】你了解命名空间吗?

C语言之父&#xff1a;Bjarne Stroustrup博士(本贾尼) 当我们在编写代码的时候&#xff0c;可能会产生一些命名冲突&#xff0c;为了解决这一冲突我们引出命名空间的概念 (ps:命名冲突的产生主要包括两个方面原因&#xff1a;1、与库函数名冲突&#xff1b;2、相互之间的冲突&…

【LeetCode】剑指 Offer 51. 数组中的逆序对 p249 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/ 1. 题目介绍&#xff08;51. 数组中的逆序对&#xff09; 在数组中的两个数字&#xff0c;如果前面一个数字大于后面的数字&#xff0c;则这两个数字组成一个逆序对。输入一个数组&#xf…

python3 DataFrame一些好玩且高效的操作

pandas在处理Excel/DBs中读取出来&#xff0c;处理为DataFrame格式的数据时&#xff0c;处理方式和性能上有很大差异&#xff0c;下面是一些高效&#xff0c;方便处理数据的方法。 map/apply/applymaptransformagg遍历求和/求平均shift/diff透视表切片&#xff0c;索引&#x…

VS Code 将推出更多 AI 功能给 Java 开发者

大家好&#xff0c;欢迎来到我们的二月更新&#xff01;我们将为您带来与 JUnit 5 并行测试相关的新功能以及用于 Spring Boot Dashboard 的过滤功能。另外&#xff0c;OpenAI 和 ChatGPT 是最近的热点&#xff0c;所以在 GitHub Copilot 方面也有一些令人激动的消息&#xff0…

【郭东白架构课 模块二:创造价值】19|节点二:架构活动的目标为什么常常被忽略?

你好&#xff0c;我是郭东白。从这节课开始&#xff0c;我们就进入到架构活动第二个环节的学习&#xff0c;那就是目标确认。 为架构活动确认一个正确目标&#xff0c;是架构师能为架构活动做出最大贡献的环节。从我的个人经验来看&#xff0c;一大半架构活动的目标都不具备正…

类文件具有错误的版本 55.0, 应为 52.0

最近在编译时报如下错误 java: 无法访问com.xx错误的类文件: /xxx.jar!/aa.class类文件具有错误的版本 55.0, 应为 52.0请删除该文件或确保该文件位于正确的类路径子目录中。 原来我依赖的jar包的编译版本是jdk11,而我本地代码编译的版本的jdk1.8,两个版本不一致&#xff0c;所…

C++类和对象终章——友元函数 | 友元类 | 内部类 | 匿名对象 | 关于拷贝对象时一些编译器优化

文章目录&#x1f490;专栏导读&#x1f490;文章导读&#x1f337;友元&#x1f33a;概念&#x1f33a;友元函数&#x1f341;友元函数的重要性质&#x1f33a;友元类&#x1f341;友元类的重要性质&#x1f337;内部类&#xff08;不常用&#xff09;&#x1f33a;内部类的性…

Ubuntu 下载并切换Python默认版本(无痛顺畅版)

Ubuntu 下载并切换Python默认版本的方法 文章目录Ubuntu 下载并切换Python默认版本的方法一&#xff0c;前言二&#xff0c;在ubantu中下载指定python版本1&#xff0c;更新apt版本为最新2&#xff0c;安装software-properties-common3&#xff0c;将 deadsnakes PPA 添加到你的…

并发 并行 进程 线程

并发 并行 进程 线程 进程和线程介绍 程序、进程和线程的关系示意图 并发和并行 1)多线程程序在单核上运行&#xff0c;就是并发 2)多线程程序在多核上运行&#xff0c;就是并行 示意图: 小结

大模型时代的“Linux”生态,开启人工智能新十年

演讲 | 林咏华 智源人工智能研究院副院长 整理 | 何苗出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;2018 年以来&#xff0c;超大规模预训练模型的出现推动了 AI 科研范式从面向特定应用场景、训练专有模型&#xff0c;转变为大模型微调模型服务的AI工业化开…

016 - 如何写一个 C++ 类

到目前为止&#xff0c;我们学了类 class&#xff0c;本期我们要尝试着从头开始写一个类。 本期不会讲的太深。我们不会写非常复杂的类&#xff0c;我们要会完成一个基本的 log 类&#xff0c;来演示一下我们已经学过的相关知识。 接下来的几期&#xff0c;我们会继续学习类。…