直接贴出代码,实现执行lua脚本的方法,用到的第三方类库是 StackExchange.Redis(nuget上有)
注:下面的代码是简化后的,实际使用要修改,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using StackExchange.Redis; namespace TL.Cloud.KV
{
public class Kv:IKv
{ private ConfigurationOptions _redisConfig;// = new ConfigurationOptions
private IDatabaseAsync _db;//=Connection.GetDatabase();
private IServer _server;// = GetServer();
private string _keyPrefix;// 区分前缀 /// <summary>
/// 每个微服务一个kv云实例,或共享云实例中(无独立前缀,服务间Key取名需要自行防止冲突)
/// </summary>
/// <param name="hostServiceName">宿主微服务</param>
public Kv(PublicCloudKvConfig config)
{
Init(config);
} private void Init(PublicCloudKvConfig config)
{
//独特的区分dev/100前缀,online可以没有
_keyPrefix = string.IsNullOrWhiteSpace(config.KeyPrefix)
?null
:config.KeyPrefix.Trim().ToLower()+':';//小写带冒号分割
//ali kv
var kvUrl = config.KvUrl; _redisConfig = ConfigurationOptions.Parse(kvUrl); _redisConfig.Password = config.KvPassword;
_redisConfig.SetDefaultPorts(); //自动填充默认端口
var connection = ConnectionMultiplexer.Connect(_redisConfig);
_db = connection.GetDatabase();
_server = GetServer(); } private IServer GetServer()
{
var config = new ConfigurationOptions
{
KeepAlive = ,
EndPoints = { _redisConfig.EndPoints[]},
AbortOnConnectFail = false,
AllowAdmin = true
};
var conn = ConnectionMultiplexer.Connect(config);
return conn.GetServer(config.EndPoints[]);
} public Task<RedisResult> EvalLua(string lua, IList<RedisKey> keys, IList<RedisValue> values)
{
if (_keyPrefix != null)
keys = keys.Select(p => p.Prepend(_keyPrefix)).ToList();//加上前缀
return _db.ScriptEvaluateAsync(lua, keys.ToArray(), values.ToArray());
} public async Task<RedisResult> EvalLua(byte[] luaSha1, IList<RedisKey> keys, IList<RedisValue> values)
{
if (_keyPrefix != null)
keys = keys.Select(p => p.Prepend(_keyPrefix)).ToList();//加上前缀
return await _db.ScriptEvaluateAsync(luaSha1, keys.ToArray(), values.ToArray());
} public async Task<byte[]> LoadLuaToServerAsync(string lua)
{
var sha1 = lua.CalcLuaSha1();//本地计算
if(!await _server.ScriptExistsAsync(sha1))//服务器上不存在
sha1 = await _server.ScriptLoadAsync(lua);//应该和计算的相同 return sha1;
}
}
}
下面是测试代码段
//下面是测试代码
[Test]
public async Task LoadLuaToServerAsync()
{
var key = "TestEvalLua10010";
var fieldContent = "testlua10011";
const string lua =
"redis.call('SET', KEYS[1], ARGV[1])\n" +
"return redis.call('GET', KEYS[1])\n"; var sha2 = lua.CalcLuaSha1();
var sha1 = await _kv.LoadLuaToServerAsync(lua);
Assert.AreEqual(, sha1.Length);
for (var i = ; i < ; i++)
Assert.AreEqual(sha1[i],sha2[i]); var keys = new List<RedisKey> { key };
var values = new List<RedisValue> { fieldContent };
var result = await _kv.EvalLua(sha1, keys, values);
Assert.AreEqual(fieldContent, (string)result);
}
计算sha1用到的方法
//计算lua的sha1结果,作为执行lua的参数
public static byte[] CalcLuaSha1(this string lua)
{
SHA1 sha1 = new SHA1CryptoServiceProvider();
var bytesSha1In = Encoding.Default.GetBytes(lua);
return sha1.ComputeHash(bytesSha1In);
}