<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Redis on Yeqown</title>
    <link>https://www.yeqown.xyz/tags/Redis/</link>
    <description>Recent content in Redis on Yeqown</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <lastBuildDate>Tue, 22 Aug 2023 10:51:43 +0800</lastBuildDate>
    <atom:link href="https://www.yeqown.xyz/tags/Redis/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>C和lua互操作实践</title>
      <link>https://www.yeqown.xyz/2023/08/22/c%E5%92%8Clua%E4%BA%92%E6%93%8D%E4%BD%9C%E5%AE%9E%E8%B7%B5/</link>
      <pubDate>Tue, 22 Aug 2023 10:51:43 +0800</pubDate>
      <guid>https://www.yeqown.xyz/2023/08/22/c%E5%92%8Clua%E4%BA%92%E6%93%8D%E4%BD%9C%E5%AE%9E%E8%B7%B5/</guid>
      <description>&lt;p&gt;相信了解 redis 和 openresty 的小伙伴们都知道 lua 代码可以嵌入这两种程序中运行，极大的提高了软件的扩展性；尤其是 openresty 中，通过使用 lua 我们可以很快速（相比c）的定制web服务器，或者增强 nginx 的功能。那么 lua 是如何嵌入到这些程序中的呢？lua 和 c 是如何互操作的呢？&lt;/p&gt;&#xA;&lt;p&gt;下文的相关环境和工具版本为：Lua 5.4.6; Mac OS 13.4.1 (Darwin Kernel Version 22.5.0) arm64 M2 pro; Apple clang version 14.0.3 (clang-1403.0.22.14.1)&lt;/p&gt;&#xA;&lt;h3 id=&#34;redis-中的-lua&#34;&gt;redis 中的 lua&lt;a class=&#34;anchor&#34; href=&#34;#redis-%e4%b8%ad%e7%9a%84-lua&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;下面展示了一段 redis 中操作 lua API 的代码：&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;这里出现了很多 &lt;strong&gt;lua_&lt;/strong&gt; 开头的函数，这些函数都是 lua 库中的函数，redis 通过这些函数来操作 lua 环境，&#xA;这里先不展开讲，后面会详细介绍。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;更多的代码，如 luaRegisterRedisAPI 就不展示了，有兴趣的可以去看源码。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// redis-v7.2/src/eval.c#183&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/* 初始化 lua 环境&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; *&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * redis 首次启动时调用，此时 setup 为 1,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * 这个函数也会在 redis 的其他生命周期中被调用，此时 setup 为 0，但是被简化为 scriptingReset 调用。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scriptingInit&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; setup) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lua_State &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;lua &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_open&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (setup) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 首次启动时，初始化 lua 环境 和 ldb (Lua debugger) 的一些数据结构&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lctx.lua_client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; NULL;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        server.script_disable_deny_script &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;ldbInit&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* 初始化 lua 脚本字典，用于存储 sha1 -&amp;gt; lua 脚本的映射&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     * 用户使用 EVALSHA 命令时，从这个字典中查找对应的 lua 脚本。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lctx.lua_scripts &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dictCreate&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;shaScriptObjectDictType);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lctx.lua_scripts_mem &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* 注册 redis 的一些 api 到 lua 环境中 */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;luaRegisterRedisAPI&lt;/span&gt;(lua);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* 注册调试命令 */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_getglobal&lt;/span&gt;(lua,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;redis&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* redis.breakpoint */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_pushstring&lt;/span&gt;(lua,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;breakpoint&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_pushcfunction&lt;/span&gt;(lua,luaRedisBreakpointCommand);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_settable&lt;/span&gt;(lua, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* redis.debug */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_pushstring&lt;/span&gt;(lua,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;debug&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_pushcfunction&lt;/span&gt;(lua,luaRedisDebugCommand);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_settable&lt;/span&gt;(lua,&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* redis.replicate_commands */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_pushstring&lt;/span&gt;(lua, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;replicate_commands&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_pushcfunction&lt;/span&gt;(lua, luaRedisReplicateCommandsCommand);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_settable&lt;/span&gt;(lua, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_setglobal&lt;/span&gt;(lua,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;redis&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* 注册一个错误处理函数，用于在 lua 脚本执行出错时，打印出错信息。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     * 需要注意的是，当错误发生在 C 函数中时，我们需要打印出错的 lua 脚本的信息，&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     * 这样才能帮助用户调试 lua 脚本。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;errh_func &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;       &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;local dbg = debug&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;debug = nil&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;function __redis__err__handler(err)&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;  local i = dbg.getinfo(2,&amp;#39;nSl&amp;#39;)&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;  if i and i.what == &amp;#39;C&amp;#39; then&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;    i = dbg.getinfo(3,&amp;#39;nSl&amp;#39;)&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;  end&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;  if type(err) ~= &amp;#39;table&amp;#39; then&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;    err = {err=&amp;#39;ERR &amp;#39; .. tostring(err)}&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;  end&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;  if i then&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;    err[&amp;#39;source&amp;#39;] = i.source&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;    err[&amp;#39;line&amp;#39;] = i.currentline&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;  end&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;  return err&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;end&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;luaL_loadbuffer&lt;/span&gt;(lua,errh_func,&lt;span style=&#34;color:#a6e22e&#34;&gt;strlen&lt;/span&gt;(errh_func),&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@err_handler_def&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_pcall&lt;/span&gt;(lua,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* 创建一个 lua client (没有网络连接)，用于在 lua 环境中执行 redis 命令。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     * 这个客户端没必要在 scriptingReset() 调用时重新创建。&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;     */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (lctx.lua_client &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; NULL) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lctx.lua_client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createClient&lt;/span&gt;(NULL);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lctx.lua_client&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;flags &lt;span style=&#34;color:#f92672&#34;&gt;|=&lt;/span&gt; CLIENT_SCRIPT;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;/* We do not want to allow blocking commands inside Lua */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lctx.lua_client&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;flags &lt;span style=&#34;color:#f92672&#34;&gt;|=&lt;/span&gt; CLIENT_DENY_BLOCKING;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* Lock the global table from any changes */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_pushvalue&lt;/span&gt;(lua, LUA_GLOBALSINDEX);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;luaSetErrorMetatable&lt;/span&gt;(lua);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;/* Recursively lock all tables that can be reached from the global table */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;luaSetTableProtectionRecursively&lt;/span&gt;(lua);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;lua_pop&lt;/span&gt;(lua, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lctx.lua &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lua;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过这部分代码，应该对于 lua 的嵌入式使用有了一个大概的印象。这里可以回答以下的问题：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Redis主从复制</title>
      <link>https://www.yeqown.xyz/2020/03/29/redis%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6/</link>
      <pubDate>Sun, 29 Mar 2020 09:45:19 +0800</pubDate>
      <guid>https://www.yeqown.xyz/2020/03/29/redis%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6/</guid>
      <description>&lt;p&gt;redis主从复制是高可用方案中的一部分，那主从复制是如何进行的？又是如何实现的？怎么支撑了redis的高可用性？在主从模式下Master和Slave节点分别做了哪些事情？&lt;/p&gt;&#xA;&lt;h3 id=&#34;redis高可用方案是什么&#34;&gt;redis高可用方案是什么？&lt;a class=&#34;anchor&#34; href=&#34;#redis%e9%ab%98%e5%8f%af%e7%94%a8%e6%96%b9%e6%a1%88%e6%98%af%e4%bb%80%e4%b9%88&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;我理解的redis高可用的特点有：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;高QPS，主从 =&amp;gt; 读写分离&lt;/li&gt;&#xA;&lt;li&gt;高容量，集群分片 =&amp;gt; 高容量&lt;/li&gt;&#xA;&lt;li&gt;故障转移，sentinel =&amp;gt; 故障转移&lt;/li&gt;&#xA;&lt;li&gt;故障恢复，数据持久 =&amp;gt; 故障恢复 ～ 这里我简单的理解（RDB + AOF）= 故障恢复&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;主从复制&#34;&gt;主从复制&lt;a class=&#34;anchor&#34; href=&#34;#%e4%b8%bb%e4%bb%8e%e5%a4%8d%e5%88%b6&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;redis 主从复制有两个版本：旧版（Ver2.8-），新版（Ver2.8+，增加PSYNC命令来解决旧版中的问题）&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;p&gt;讨论复制时都需要考虑两种场景：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;场景1：从节点刚刚上线，需要去同步主节点时，这部分可以理解为 &lt;strong&gt;全量复制&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;li&gt;场景2：从节点掉线，恢复上线后需要同步数据，使自己和主节点达到一致状态。这部分在旧版复制里等价于&lt;strong&gt;全量复制&lt;/strong&gt;，在新版里可以理解为&lt;strong&gt;增量复制&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;当然你肯定会想到如果主节点掉线，这时候会怎么样？这个场景当然也在redis高可用方案中，之时不是本文的重点，属于Sentinel机制的内容了。&lt;/p&gt;&#xA;&lt;h4 id=&#34;旧版主从复制&#34;&gt;旧版主从复制&lt;a class=&#34;anchor&#34; href=&#34;#%e6%97%a7%e7%89%88%e4%b8%bb%e4%bb%8e%e5%a4%8d%e5%88%b6&#34;&gt;#&lt;/a&gt;&lt;/h4&gt;&#xA;&lt;p&gt;前文说过了，旧版主从复制只有全量复制用于应付上述两个场景，因此下面的流程也只有一份：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;从服务器向主服务器发送sync命令。&lt;/li&gt;&#xA;&lt;li&gt;主服务器在收到sync命令之后，调用bgsave命令生成最新的rdb文件，将这个文件同步给从服务器，这样从服务器载入这个rdb文件之后，状态就会和主服务器执行bgsave命令时候的一致。&lt;/li&gt;&#xA;&lt;li&gt;主服务器将保存在命令缓冲区中的写命令同步给从服务器，从服务器执行这些命令，这样从服务器的状态就跟主服务器当前状态一致了。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;如果你不知道redis中还有个缓冲区的话，建议系统的了解下redis中缓冲区的设计。这里缓冲区特指命令缓冲区，后面还会讲到复制缓冲区。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;img src=&#34;https://www.yeqown.xyz/images/redis-mlcopy-1.svg&#34; height=&#34;300px&#34;/&gt;&#xA;&lt;p&gt;但是这样的实现在 &lt;strong&gt;场景2&lt;/strong&gt; 下的缺点很明显：如果说从节点断线后迅速上线，这段时间内的产生的写命令很少，却要&lt;strong&gt;全量复制&lt;/strong&gt;主库的数据，传输了大量重复数据。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;SYNC命令产生的消耗：&#xA;1. 主节点生成RDB，需要消耗大量的CPU，内存和磁盘IO&#xA;2. 网络传输大量字节数据，需要消耗主从服务器的网络资源&#xA;3. 从节点需要从RDB文件恢复，会造成阻塞无法接受客户端请求&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;优点就是：简单暴力。个人看来在redis架构中不合适的用法，不代表说实际场景中也一定不合适，简单暴力也是一个很大的优点。&lt;/p&gt;&#xA;&lt;h4 id=&#34;新版主从复制&#34;&gt;新版主从复制&lt;a class=&#34;anchor&#34; href=&#34;#%e6%96%b0%e7%89%88%e4%b8%bb%e4%bb%8e%e5%a4%8d%e5%88%b6&#34;&gt;#&lt;/a&gt;&lt;/h4&gt;&#xA;&lt;p&gt;新版的主从复制跟旧版的区别就在于：对场景2的优化。&lt;/p&gt;&#xA;&lt;p&gt;场景2的缺点上文已经提到过了，那么优化的方向就是**“尽量不使用全量复制；增加增量复制(PSYNC)的功能”**。为此还要解决下列问题：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;如果某个从节点断线了，重新上线该从节点如何知道自己是否应该全量还是增量复制呢？&lt;/li&gt;&#xA;&lt;li&gt;该从节点断线恢复后，又怎么知道自己缺失了哪些数据呢？&lt;/li&gt;&#xA;&lt;li&gt;主节点又如何补偿该从节点在断线期间丢失的那部分数据呢？旧版的复制除了RDB，还有从命令缓冲区中的写命令来保持数据一致。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;为此新版中使用了以下概念：&lt;/p&gt;&#xA;&lt;h5 id=&#34;运行id---runid&#34;&gt;运行ID - runid&lt;a class=&#34;anchor&#34; href=&#34;#%e8%bf%90%e8%a1%8cid---runid&#34;&gt;#&lt;/a&gt;&lt;/h5&gt;&#xA;&lt;p&gt;每个redis服务器都有其runid，runid由服务器在启动时自动生成，主服务器会将自己的runid发送给从服务器，而从服务器会将主服务器的runid保存起来。从服务器redis断线重连之后进行同步时，就是根据runid来判断同步的进度：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;如果前后两次主服务器runid&lt;strong&gt;一致&lt;/strong&gt;，则认为这一次断线重连还是之前复制的主服务器，主服务器可以继续尝试部分同步操作。&lt;/li&gt;&#xA;&lt;li&gt;如果前后两次主服务器runid&lt;strong&gt;不相同&lt;/strong&gt;，则&lt;strong&gt;全同步流程&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h5 id=&#34;复制偏移量---offset&#34;&gt;复制偏移量 - offset&lt;a class=&#34;anchor&#34; href=&#34;#%e5%a4%8d%e5%88%b6%e5%81%8f%e7%a7%bb%e9%87%8f---offset&#34;&gt;#&lt;/a&gt;&lt;/h5&gt;&#xA;&lt;p&gt;主从节点，分别会维护一个复制偏移量：&#xA;主服务器每次向从服务器同步了N字节数据之后，将修改自己的复制偏移量+N。从服务器每次从主服务器同步了N字节数据之后，将修改自己的复制偏移量+N。通过对比主从节点的偏移量很容易就可以发现，主从节点是否处于一致状态。&lt;/p&gt;&#xA;&lt;h5 id=&#34;复制积压缓冲区---copybuffer&#34;&gt;复制（积压）缓冲区 - copybuffer&lt;a class=&#34;anchor&#34; href=&#34;#%e5%a4%8d%e5%88%b6%e7%a7%af%e5%8e%8b%e7%bc%93%e5%86%b2%e5%8c%ba---copybuffer&#34;&gt;#&lt;/a&gt;&lt;/h5&gt;&#xA;&lt;p&gt;一个固定长度（可配置）的FIFO队列，默认大小 = 1MB；预测值 = second * write_size_per_second。当从节点重新连上主节点时候，从节点会通过PSYNC命令将自己的复制偏移量（offset）发送给主服务器，主节点会根据偏移量会判断该执行何种同步：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
