<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>M&amp;M</title>
  
  <subtitle>反也者 道之动也</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://www.azjoy.com/"/>
  <updated>2024-11-10T04:44:47.130Z</updated>
  <id>http://www.azjoy.com/</id>
  
  <author>
    <name>小马</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>MySQL常用SQL语句</title>
    <link href="http://www.azjoy.com/2024/11/10/mysql-sql/"/>
    <id>http://www.azjoy.com/2024/11/10/mysql-sql/</id>
    <published>2024-11-10T03:53:52.000Z</published>
    <updated>2024-11-10T04:44:47.130Z</updated>
    
    <content type="html"><![CDATA[<p>SQL语句大致可以分为四类，<code>DDL</code>(例如: CREATE、DROP、ALTER), <code>DML</code>(例如: INSERT、DELETE、UPDATE), <code>DCL</code>(例如: GRANT、REVOKE), <code>DQL</code>(例如: SELECT)</p><h5 id="DDL数据定义语言"><a href="#DDL数据定义语言" class="headerlink" title="DDL数据定义语言"></a>DDL数据定义语言</h5><h6 id="CREATE"><a href="#CREATE" class="headerlink" title="CREATE"></a><code>CREATE</code></h6><p>CREATE常用搭配：CREATE DATABASE、CREATE TABLE、CREATE INDEX、CREATE USER，分别是创建数据库、创建表、创建索引、创建用户。</p><ul><li><code>创建数据库</code>：</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">DATABASE</span> [<span class="keyword">IF</span> <span class="keyword">NOT</span> <span class="keyword">EXISTS</span>] database_name [<span class="built_in">CHARACTER</span> <span class="keyword">SET</span> charset_name] [<span class="keyword">COLLATE</span> collation_name];</span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">DATABASE</span> database_name;</span><br></pre></td></tr></table></figure><ul><li><code>创建表</code>：</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> table_name (</span><br><span class="line">    column1 datatype,</span><br><span class="line">    column2 datatype,</span><br><span class="line">    column3 datatype,</span><br><span class="line">   ....</span><br><span class="line">);</span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> table_name (</span><br><span class="line">  <span class="keyword">id</span> <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">unsigned</span> <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT <span class="keyword">COMMENT</span> <span class="string">'自增ID'</span>,</span><br><span class="line">  <span class="keyword">account</span> <span class="built_in">varchar</span>(<span class="number">500</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span> <span class="keyword">COMMENT</span> <span class="string">'用户’,</span></span><br><span class="line"><span class="string">  PRIMARY KEY (`id`),</span></span><br><span class="line"><span class="string">) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='</span>测试<span class="string">';</span></span><br></pre></td></tr></table></figure><ul><li><code>创建索引</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 普通索引</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">INDEX</span> index_name <span class="keyword">ON</span> table_name (column1, column2, …);</span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">INDEX</span> idx_account_level <span class="keyword">ON</span> table_name (<span class="keyword">account</span>, <span class="keyword">level</span>);</span><br><span class="line"><span class="comment">-- 唯一索引</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">UNIQUE</span> <span class="keyword">INDEX</span> index_name <span class="keyword">ON</span> table_name (column1, column2, …);</span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">UNIQUE</span> <span class="keyword">INDEX</span> uk_account_level <span class="keyword">ON</span> table_name (<span class="keyword">account</span>, <span class="keyword">level</span>);</span><br></pre></td></tr></table></figure><ul><li><code>创建用户</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">USER</span> [<span class="keyword">IF</span> <span class="keyword">NOT</span> <span class="keyword">EXISTS</span>] account_name <span class="keyword">IDENTIFIED</span> <span class="keyword">BY</span> <span class="string">'password'</span>;  </span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">USER</span> <span class="string">'username'</span>@<span class="string">'localhost'</span> <span class="keyword">IDENTIFIED</span> <span class="keyword">BY</span> <span class="string">'password'</span>;</span><br></pre></td></tr></table></figure><h6 id="DROP"><a href="#DROP" class="headerlink" title="DROP"></a><code>DROP</code></h6><p>DROP常用搭配：DROP DATABASE、DROP TABLE、DROP INDEX、DROP USER，分别是删除数据库、删除表、删除索引、删除用户。</p><ul><li><code>删除数据库</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">DATABASE</span> [<span class="keyword">IF</span> <span class="keyword">EXISTS</span>] db_name；</span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">DATABASE</span> database_name；</span><br></pre></td></tr></table></figure><ul><li><code>删除表</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> [<span class="keyword">IF</span> <span class="keyword">EXISTS</span>] table_name; </span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> table_name；</span><br></pre></td></tr></table></figure><ul><li><code>删除索引</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">INDEX</span> index_name <span class="keyword">ON</span> table_name;</span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">INDEX</span> idx_account_level <span class="keyword">ON</span> table_name;</span><br></pre></td></tr></table></figure><ul><li><code>删除用户</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">USER</span> [<span class="keyword">IF</span> <span class="keyword">EXISTS</span>] <span class="keyword">user</span> [, <span class="keyword">user</span>] …</span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">USER</span> <span class="string">'username'</span>@<span class="string">'localhost'</span>;</span><br></pre></td></tr></table></figure><h6 id="ALTER"><a href="#ALTER" class="headerlink" title="ALTER"></a><code>ALTER</code></h6><p>ALTER常用搭配：ALTER DATABASE、ALTER TABLE、ALTER USER，分别是更改数据库、更改表、更改用户。</p><ul><li><code>更改数据库</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">DATABASE</span> [database_name] alter_option …</span><br><span class="line"><span class="comment">-- 更改默认数据库字符集</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">DATABASE</span> database_name <span class="built_in">CHARACTER</span> <span class="keyword">SET</span>=utf8;</span><br><span class="line"><span class="comment">-- 更改默认数据库排序规则</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">DATABASE</span> database_name <span class="keyword">COLLATE</span> utf8_general_ci; </span><br><span class="line"><span class="comment">-- 数据库是否只读，1只读，0非只读</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">DATABASE</span> database_name <span class="keyword">READ</span> <span class="keyword">ONLY</span> = <span class="number">1</span>;</span><br></pre></td></tr></table></figure><ul><li><code>更改表</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name [alter_option [, alter_option] ...] [partition_options]</span><br><span class="line"><span class="comment">-- 修改表存储引擎更改为InnoDB</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span>;</span><br><span class="line"><span class="comment">-- 重置当前的自动增量值</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name AUTO_INCREMENT=<span class="number">13</span>;</span><br><span class="line"><span class="comment">-- 更改默认表字符集</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="built_in">CHARACTER</span> <span class="keyword">SET</span>=utf8mb4; </span><br><span class="line"><span class="comment">-- 添加或更改表注释</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">COMMENT</span>=<span class="string">'comment'</span>;</span><br><span class="line"><span class="comment">-- 删除表的列或多列 </span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">DROP</span> <span class="keyword">COLUMN</span> a, <span class="keyword">DROP</span> <span class="keyword">COLUMN</span> b; </span><br><span class="line"><span class="comment">-- a列重命名为b，并将其定义更改为BIGINT NOT NULL</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">CHANGE</span> a b <span class="built_in">BIGINT</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>; </span><br><span class="line"><span class="comment">-- 只更改b列的定义不改列名</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">CHANGE</span> b b <span class="built_in">INT</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>; </span><br><span class="line"><span class="comment">-- 只更改b列的定义不改列名，MODIFY只需要指定一次列名s</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">MODIFY</span> b <span class="built_in">INT</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>; </span><br><span class="line"><span class="comment">-- b列后面新增c列</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">ADD</span> c <span class="built_in">varchar</span>(<span class="number">10</span>) <span class="keyword">DEFAULT</span> <span class="string">''</span> <span class="keyword">COMMENT</span> <span class="string">'xxxxx'</span> <span class="keyword">AFTER</span> b; </span><br><span class="line"><span class="comment">-- 指定列字符集</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> table_name <span class="keyword">MODIFY</span> c <span class="built_in">VARCHAR</span>(<span class="number">10</span>) <span class="built_in">CHARACTER</span> <span class="keyword">SET</span> utf8mb4 <span class="keyword">DEFAULT</span> <span class="string">''</span> <span class="keyword">COMMENT</span> <span class="string">'xxxxx'</span></span><br></pre></td></tr></table></figure><ul><li><code>更改用户</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">USER</span> [<span class="keyword">IF</span> <span class="keyword">EXISTS</span>] user_identity [<span class="keyword">IDENTIFIED</span> <span class="keyword">BY</span> <span class="string">'password'</span>] [password_option | lock_option] [<span class="keyword">comment</span>]</span><br><span class="line"><span class="comment">-- 修改用户密码</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">USER</span> username@<span class="string">'%'</span> <span class="keyword">IDENTIFIED</span> <span class="keyword">BY</span> <span class="string">'123'</span>;</span><br><span class="line"><span class="comment">-- 修改用户注释</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">USER</span> username@<span class="string">'%'</span> <span class="keyword">COMMENT</span> <span class="string">"test user"</span></span><br><span class="line"><span class="comment">-- 锁定用户</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">USER</span> username@<span class="string">'%'</span> <span class="keyword">ACCOUNT</span> <span class="keyword">LOCK</span>;</span><br><span class="line"><span class="comment">-- 解锁用户</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">USER</span> username@<span class="string">'%'</span> <span class="keyword">ACCOUNT</span> <span class="keyword">UNLOCK</span>;</span><br></pre></td></tr></table></figure><h5 id="DML数据操作语言"><a href="#DML数据操作语言" class="headerlink" title="DML数据操作语言"></a>DML数据操作语言</h5><h6 id="INSERT"><a href="#INSERT" class="headerlink" title="INSERT"></a><code>INSERT</code></h6><ul><li><code>数据插入</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 插入一条或多条记录</span></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> table_name (column1, column2, column3, …) <span class="keyword">VALUES</span> (value1, value2, value3, …), (value1, value2, value3, …);</span><br><span class="line"><span class="comment">-- 复制表t1中所有列数据插入t2</span></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> t2 <span class="keyword">SELECT</span> * <span class="keyword">FROM</span> t1 <span class="keyword">WHERE</span> <span class="keyword">id</span>=<span class="number">1</span>;</span><br><span class="line"><span class="comment">-- 复制表t1中部分列数据插入t2</span></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> t2 (column1, column2, column3, …) <span class="keyword">SELECT</span> column1, column2, column3, … <span class="keyword">FROM</span> t1 <span class="keyword">WHERE</span> <span class="keyword">id</span>=<span class="number">1</span>;</span><br></pre></td></tr></table></figure><h6 id="DELETE"><a href="#DELETE" class="headerlink" title="DELETE"></a><code>DELETE</code></h6><ul><li><code>数据删除</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> table_name [<span class="keyword">WHERE</span> Clause]</span><br><span class="line"><span class="comment">-- 删除符合条件的记录</span></span><br><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> table_name <span class="keyword">WHERE</span> <span class="keyword">id</span>=<span class="number">1</span>;</span><br><span class="line"><span class="comment">-- 删除表所有记录</span></span><br><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> table_name;</span><br></pre></td></tr></table></figure><h6 id="UPDATE"><a href="#UPDATE" class="headerlink" title="UPDATE"></a><code>UPDATE</code></h6><ul><li><code>数据更新</code></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">UPDATE</span> table_name <span class="keyword">SET</span> column1=value1, column2=value2, … <span class="keyword">WHERE</span> condition;</span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">UPDATE</span> table_name <span class="keyword">SET</span> a=<span class="number">1</span>,b=<span class="string">'ok'</span> <span class="keyword">WHERE</span> <span class="keyword">id</span>=<span class="number">1</span>;</span><br></pre></td></tr></table></figure><h5 id="DCL数据控制语句"><a href="#DCL数据控制语句" class="headerlink" title="DCL数据控制语句"></a>DCL数据控制语句</h5><h6 id="GRANT"><a href="#GRANT" class="headerlink" title="GRANT"></a><code>GRANT</code></h6><ul><li>授权用户数据库权限或角色</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GRANT</span> privilege1, privilege2, privilege3...ON object_type <span class="keyword">TO</span> user_or_role1, user_or_role2, user_or_role3...[<span class="keyword">WITH</span> <span class="keyword">GRANT</span> <span class="keyword">OPTION</span>]</span><br><span class="line"><span class="comment">-- 赋予用户数据库所有表数据增、删、改、查的权限</span></span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">select</span>, <span class="keyword">insert</span>, <span class="keyword">update</span>, <span class="keyword">delete</span> <span class="keyword">ON</span> dbname.* <span class="keyword">TO</span> username@<span class="string">'%'</span>;</span><br><span class="line"><span class="comment">-- 赋予用户修改数据库所有表结构创建、删除、修改的权限</span></span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">create</span>,<span class="keyword">alter</span>,<span class="keyword">drop</span> <span class="keyword">ON</span> dbname.* <span class="keyword">TO</span> username@<span class="string">'%'</span>;</span><br><span class="line"><span class="comment">-- 赋予用户操作索引的权限</span></span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">index</span> <span class="keyword">ON</span> dbname.* <span class="keyword">TO</span> username@<span class="string">'%'</span>;</span><br><span class="line"><span class="comment">-- 赋予用户数据库所有权限</span></span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">ALL</span> <span class="keyword">PRIVILEGES</span> <span class="keyword">ON</span> dbname <span class="keyword">TO</span> username@<span class="string">'%'</span>;</span><br><span class="line"><span class="comment">-- 被赋予权限的用户，如果需要分享权限给其它用户，需要加上 WITH GRANT OPTION;</span></span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">select</span>, <span class="keyword">insert</span>, <span class="keyword">update</span>, <span class="keyword">delete</span> <span class="keyword">ON</span> dbname.* <span class="keyword">TO</span> username@<span class="string">'%'</span> <span class="keyword">WITH</span> <span class="keyword">GRANT</span> <span class="keyword">OPTION</span>;</span><br><span class="line"><span class="comment">-- 刷新权限立即生效</span></span><br><span class="line"><span class="keyword">FLUSH</span> <span class="keyword">PRIVILEGES</span></span><br></pre></td></tr></table></figure><h6 id="REVOKE"><a href="#REVOKE" class="headerlink" title="REVOKE"></a><code>REVOKE</code></h6><ul><li>回收用户数据库权限或角色</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">REVOKE</span> privilege1, privilege2, privilege3...ON object_type <span class="keyword">FROM</span> user_or_role1, user_or_role2, user_or_role3...[<span class="keyword">WITH</span> <span class="keyword">GRANT</span> <span class="keyword">OPTION</span>]</span><br><span class="line"><span class="comment">/* 用法与GRANT类似，之需要将关键字TO换成FROM即可 */</span></span><br><span class="line"><span class="comment">-- 回收用户数据库所有表数据增、删、改、查的权限</span></span><br><span class="line"><span class="keyword">REVOKE</span> <span class="keyword">select</span>, <span class="keyword">insert</span>, <span class="keyword">update</span>, <span class="keyword">delete</span> <span class="keyword">ON</span> dbname.* <span class="keyword">FROM</span> username@<span class="string">'%'</span>;</span><br><span class="line"><span class="comment">-- 刷新权限立即生效</span></span><br><span class="line"><span class="keyword">FLUSH</span> <span class="keyword">PRIVILEGES</span></span><br></pre></td></tr></table></figure><h5 id="DQL数据查询语言"><a href="#DQL数据查询语言" class="headerlink" title="DQL数据查询语言"></a>DQL数据查询语言</h5><h6 id="SELECT"><a href="#SELECT" class="headerlink" title="SELECT"></a><code>SELECT</code></h6><ul><li>查询数据</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 单个表数据查询</span></span><br><span class="line"><span class="keyword">SELECT</span> column1, column2, … <span class="keyword">FROM</span> table_name [<span class="keyword">WHERE</span> condition] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> column_name [<span class="keyword">ASC</span> | <span class="keyword">DESC</span>]] [<span class="keyword">HAVING</span> condition] [<span class="keyword">ORDER</span> <span class="keyword">BY</span> column_name [<span class="keyword">ASC</span> | <span class="keyword">DESC</span>]] [<span class="keyword">LIMIT</span> <span class="built_in">number</span> <span class="keyword">OFFSET</span> <span class="keyword">offset</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">--- JOIN连接多个表数据关联查询：</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">INNER JOIN：获取两个表中字段匹配关系的记录,INNER可以省略不写</span></span><br><span class="line"><span class="comment">LEFT JOIN：获取左表所有记录，即使右表没有对应匹配的记录</span></span><br><span class="line"><span class="comment">RIGHT JOIN：获取右表所有记录，即使左表没有对应匹配的记录</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">SELECT</span> a.column1, b.column2, … <span class="keyword">FROM</span> table1 a <span class="keyword">INNER</span> <span class="keyword">JOIN</span> table2 b <span class="keyword">ON</span> a.colx = b.coly [<span class="keyword">WHERE</span> condition];</span><br><span class="line"></span><br><span class="line"><span class="comment">-- UNION合并多个SELECT查询结果集:</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">UNION连接两个及以上SELECT结果集并去除重复的记录</span></span><br><span class="line"><span class="comment">每个SELECT语句的列数和对应位置的数据类型必须相同</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">SELECT</span> column1, column2, … <span class="keyword">FROM</span> table1 <span class="keyword">WHERE</span> condition1 <span class="keyword">UNION</span> [<span class="keyword">ALL</span> | <span class="keyword">DISTINCT</span>] <span class="keyword">SELECT</span> column1, column2, … <span class="keyword">FROM</span> table2 <span class="keyword">WHERE</span> condition2 [<span class="keyword">UNION</span> [<span class="keyword">ALL</span> | <span class="keyword">DISTINCT</span>] <span class="keyword">SELECT</span> ...]</span><br></pre></td></tr></table></figure><h5 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h5><h6 id="数据删除"><a href="#数据删除" class="headerlink" title="数据删除"></a><code>数据删除</code></h6><p>DELETE、TRUNCATE、DROP删除表数据的区别</p><ul><li>DELETE是DML可以回滚，TRUNCATE、DROP是DDL不能回滚</li><li>DELETE、TRUNCATE只是删除数据，DROP删除数据和表结构</li><li>执行速度DROP&gt;TRUNCATE&gt;DELETE</li></ul><h6 id="SHOW"><a href="#SHOW" class="headerlink" title="SHOW"></a><code>SHOW</code></h6><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 列出实例数据库</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">DATABASES</span> [like_or_where]</span><br><span class="line"><span class="comment">-- 查看建库语句</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">CREATE</span> <span class="keyword">DATABASE</span> dbname</span><br><span class="line"><span class="comment">-- 查看数据库表详细信息</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">TABLE</span> <span class="keyword">STATUS</span> [<span class="keyword">FROM</span> db_name] [like_or_where]</span><br><span class="line"><span class="comment">-- 列出数据库表</span></span><br><span class="line"><span class="keyword">SHOW</span> [<span class="keyword">FULL</span>] <span class="keyword">TABLES</span> [<span class="keyword">FROM</span> db_name] [like_or_where]</span><br><span class="line"><span class="comment">-- 查看建表语句</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">CREATE</span> <span class="keyword">TABLE</span> tablename</span><br><span class="line"><span class="comment">-- 查看当前打开的非临时表</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">OPEN</span> <span class="keyword">TABLES</span> [<span class="keyword">FROM</span> db_name] [like_or_where]</span><br><span class="line"><span class="comment">-- 查看实例支持的存储引擎</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">ENGINES</span></span><br><span class="line"><span class="comment">-- 查看存储引擎状态</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">ENGINE</span> engine_name &#123;<span class="keyword">STATUS</span> | <span class="keyword">MUTEX</span>&#125;</span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">ENGINE</span> <span class="keyword">INNODB</span> <span class="keyword">STATUS</span>;</span><br><span class="line"><span class="comment">-- 查看用户权限</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">GRANTS</span> <span class="keyword">FOR</span> <span class="keyword">user</span></span><br><span class="line"><span class="comment">-- 例如</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">GRANTS</span> <span class="keyword">FOR</span> username@<span class="string">'%'</span>;</span><br><span class="line"><span class="comment">-- 查看表索引信息</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">INDEX</span> <span class="keyword">FROM</span> tablename</span><br><span class="line"><span class="comment">-- 查看master状态</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">MASTER</span> <span class="keyword">STATUS</span></span><br><span class="line"><span class="comment">-- 查看slave状态</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">SLAVE</span> <span class="keyword">STATUS</span> [<span class="keyword">FOR</span> CHANNEL channel]</span><br><span class="line"><span class="comment">-- 查看实例所有插件信息</span></span><br><span class="line"><span class="keyword">SHOW</span> PLUGINS</span><br><span class="line"><span class="comment">-- 查看权限表</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">PRIVILEGES</span></span><br><span class="line"><span class="comment">-- 查看当前实例活跃线程</span></span><br><span class="line"><span class="keyword">SHOW</span> [<span class="keyword">FULL</span>] <span class="keyword">PROCESSLIST</span></span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;SQL语句大致可以分为四类，&lt;code&gt;DDL&lt;/code&gt;(例如: CREATE、DROP、ALTER), &lt;code&gt;DML&lt;/code&gt;(例如: INSERT、DELETE、UPDATE), &lt;code&gt;DCL&lt;/code&gt;(例如: GRANT、REVOKE), &lt;c
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="MySQL" scheme="http://www.azjoy.com/categories/Ops/MySQL/"/>
    
    
      <category term="MySQL" scheme="http://www.azjoy.com/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>GO交叉编译</title>
    <link href="http://www.azjoy.com/2024/11/09/go-build/"/>
    <id>http://www.azjoy.com/2024/11/09/go-build/</id>
    <published>2024-11-09T03:23:45.000Z</published>
    <updated>2024-11-09T04:31:51.469Z</updated>
    
    <content type="html"><![CDATA[<h5 id="直接编译"><a href="#直接编译" class="headerlink" title="直接编译"></a>直接编译</h5><p>如果项目代码或者引用的包没有用到<code>CGO</code>代码，则可以直接指定系统<code>GOOS</code>和平台<code>GOARCH</code>，编译即可。</p><p>参数说明</p><ul><li><code>CGO_ENABLED</code> : CGO_ENABLED表示是否使用了CGO，交叉编译如果没有则关闭即可</li><li><code>GOOS</code>: 目标系统平台<ul><li>Mac对应<code>darwin</code></li><li>Linux对应<code>linux</code></li><li>Windows对应<code>windows</code></li></ul></li><li><code>GOARCH</code>: 目标架构平台<code>386</code>、<code>amd64</code>、<code>arm</code>、<code>arm64</code>等,个人电脑一般都是amd64架构<ul><li><code>386</code>也称x86对应32位操作系统</li><li><code>amd64</code>也称x64对应64位操作系统</li><li><code>arm</code>与<code>arm64</code>这种架构一般用于嵌入式开发</li></ul></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ GOOS=linux GOARCH=amd64 go build -mod=vendor -ldflags <span class="string">"-s -w"</span> -o name</span><br></pre></td></tr></table></figure><h5 id="CGO代码编译"><a href="#CGO代码编译" class="headerlink" title="CGO代码编译"></a>CGO代码编译</h5><p><code>CGO</code>作为<code>Go</code>语言和<code>C</code>语言之间的桥梁，其使用场景可以分为两种：Go调用C程序和C调用Go程序。</p><p>参数说明</p><ul><li><code>GOARM</code>, 只有GOARCH是arm64才有效, 表示arm的版本, 只能是5,6,7其中之一, 默认6</li><li><code>CGO_ENABLED</code>, 是否支持CGO交叉汇编, 值只能是0, 1, 默认情况下是0, 启用交叉汇编比较麻烦</li><li><code>CC</code>, 当支持交叉汇编时(即 CGO_ENABLED=1), 编译目标文件使用的<code>C</code>编译器，例如: <code>arm-linux-gnueabi-gcc</code></li><li><code>CXX</code>, 当支持交叉汇编时(即 CGO_ENABLED=1), 编译目标文件使用的<code>C++</code>编译器，例如: <code>arm-linux-gnueabi-g++</code></li><li><code>AR</code>, 当支持交叉汇编时(即 CGO_ENABLED=1), 编译目标文件使用的创建库文件命令</li></ul><h6 id="ARM32"><a href="#ARM32" class="headerlink" title="ARM32"></a>ARM32</h6><ul><li>交叉编译，目标为ARM32位Linux系统平台，C编译器指定为<code>arm-linux-gnueabi-gcc</code></li><li>当前项目使用了CGO，目标文件使用了C编译器，故需要指定C编译器<code>arm-linux-gnueabi-gcc</code></li><li><code>ARM32位</code>编译器<a href="https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabi/" target="_blank" rel="noopener">下载地址</a>，下载解压后，将对应bin目录添加到环境变量即可</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ GOOS=linux GOARCH=arm CGO_ENABLED=1  CC=arm-linux-gnueabi-gcc go build</span><br></pre></td></tr></table></figure><h6 id="ARM64"><a href="#ARM64" class="headerlink" title="ARM64"></a>ARM64</h6><ul><li>交叉编译，目标为ARM64位Linux系统平台，C编译器指定为<code>aarch64-linux-gnu-gcc</code></li><li>当前项目使用了CGO，目标文件使用了C编译器，故需要指定C编译器<code>arm-linux-gnueabi-gcc</code></li><li><code>ARM64位</code>编译器<a href="https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/" target="_blank" rel="noopener">下载地址</a>，下载解压，将对应bin目录添加到环境变量即可</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc go build</span><br></pre></td></tr></table></figure><h5 id="特定编译"><a href="#特定编译" class="headerlink" title="特定编译"></a>特定编译</h5><p>如果项目用到了特定的C语言库库，可能是需要对该库单独编译处理的，比如<code>libpcap</code>这个C语言库。</p><p>参数说明</p><ul><li><code>CGO_LDFLAGS</code> 链接参数，主要包含要链接库的检索目录和要链接库的名字, 因为历史遗留问题，链接库不支持相对路径，我们必须为链接库指定绝对路径</li><li><code>CGO_CFLAGS</code> 编译参数，主要是头文件的检索路径</li></ul><p>1、先编译目标系统架构的<code>libpcap</code>版本，只编译不安装，以<code>arm64</code>为例</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">cd</span> /tmp</span><br><span class="line">$ wget http://www.tcpdump.org/release/libpcap-1.9.0.tar.gz</span><br><span class="line">$ tar -zxvf libpcap-1.9.0.tar.gz</span><br><span class="line">$ <span class="built_in">cd</span> libpcap-1.9.0</span><br><span class="line">$ <span class="built_in">export</span> CC=aarch64-linux-gnu-gcc</span><br><span class="line">$ ./configure --host=aarch64-linux --with-pcap=linux</span><br><span class="line">$ make</span><br></pre></td></tr></table></figure><p>2、然后再交叉编译中，指定编译好的链接库或头文件位置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc CGO_LDFLAGS=<span class="string">"-L/tmp/libpcap-1.9.0"</span> CGO_CFLAGS=<span class="string">"-I/tmp/libpcap-1.9.0"</span> go build</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h5 id=&quot;直接编译&quot;&gt;&lt;a href=&quot;#直接编译&quot; class=&quot;headerlink&quot; title=&quot;直接编译&quot;&gt;&lt;/a&gt;直接编译&lt;/h5&gt;&lt;p&gt;如果项目代码或者引用的包没有用到&lt;code&gt;CGO&lt;/code&gt;代码，则可以直接指定系统&lt;code&gt;GOOS&lt;/code&gt;和
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="Tools" scheme="http://www.azjoy.com/categories/Ops/Tools/"/>
    
    
      <category term="go" scheme="http://www.azjoy.com/tags/go/"/>
    
  </entry>
  
  <entry>
    <title>Vagrant共享目录双向同步等问题</title>
    <link href="http://www.azjoy.com/2020/07/29/vagrant/"/>
    <id>http://www.azjoy.com/2020/07/29/vagrant/</id>
    <published>2020-07-29T07:05:08.000Z</published>
    <updated>2024-11-05T14:28:33.564Z</updated>
    
    <content type="html"><![CDATA[<h4 id="vagrant共享目录不能双向同步文件"><a href="#vagrant共享目录不能双向同步文件" class="headerlink" title="vagrant共享目录不能双向同步文件"></a>vagrant共享目录不能双向同步文件</h4><p>Vagrant 如果在共享目录的时候无法双向同步，在启动的时日志提示信息，提示系统缺失<code>Virtualbox Guest Additions</code>插件</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">No guest additions were detected on the base box <span class="keyword">for</span> this VM!</span><br></pre></td></tr></table></figure><h5 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h5><p>安装<code>vagrant-vbguest</code>插件，重新启动虚拟机后会自动在虚拟机里面编译安装<code>Virtualbox Guest Additions</code>插件</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ vagrant plugin install vagrant-vbguest</span><br><span class="line"><span class="comment"># 可选</span></span><br><span class="line">$ vagrant vbguest</span><br></pre></td></tr></table></figure><p>关闭默认挂载目录</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ config.vm.synced_folder <span class="string">"."</span>,<span class="string">"/vagrant"</span>,disabled:<span class="literal">true</span></span><br></pre></td></tr></table></figure><p>配置共享目录，虚拟机的挂载位置不能使用默认目录<code>/vagrant</code>，这里用<code>/share</code>目录</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ config.vm.synced_folder <span class="string">"./code"</span>, <span class="string">"/share"</span></span><br></pre></td></tr></table></figure><p>重启虚拟机，完成挂载配置更新</p><h4 id="vagrant默认磁盘空间不够"><a href="#vagrant默认磁盘空间不够" class="headerlink" title="vagrant默认磁盘空间不够"></a>vagrant默认磁盘空间不够</h4><p>vagrant创建虚拟机，默认磁盘只有8GB，可能不够用</p><h5 id="解决方法-1"><a href="#解决方法-1" class="headerlink" title="解决方法"></a>解决方法</h5><p>安装插件<code>vagrant-disksize</code>，默认由8G变为40G，另外还可以手动配置调整到更大空间</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ vagrant plugin install vagrant-disksize</span><br></pre></td></tr></table></figure><h4 id="vagrant-CPU负载飙高大于100"><a href="#vagrant-CPU负载飙高大于100" class="headerlink" title="vagrant CPU负载飙高大于100%+"></a>vagrant CPU负载飙高大于100%+</h4><p><code>Vagrant</code> + <code>VirtualBox</code> 搭建的开发环境，如果<code>VirtualBox</code>进程CPU负载莫名其妙飙高到100%+，电脑风扇响个不停</p><h5 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h5><p>这个貌似是<code>VirtualBox</code>的<code>BUG</code>，手动在<code>VirtualBox</code>里面关掉虚拟机的声卡(虚拟机-&gt;设置-&gt;声音-&gt;勾掉启用声音选项)，即可</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;vagrant共享目录不能双向同步文件&quot;&gt;&lt;a href=&quot;#vagrant共享目录不能双向同步文件&quot; class=&quot;headerlink&quot; title=&quot;vagrant共享目录不能双向同步文件&quot;&gt;&lt;/a&gt;vagrant共享目录不能双向同步文件&lt;/h4&gt;&lt;p&gt;Va
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="Problem" scheme="http://www.azjoy.com/categories/Ops/Problem/"/>
    
    
      <category term="vagrant" scheme="http://www.azjoy.com/tags/vagrant/"/>
    
  </entry>
  
  <entry>
    <title>开源一个生成API项目的模板</title>
    <link href="http://www.azjoy.com/2019/10/31/DjangoApiTemplate/"/>
    <id>http://www.azjoy.com/2019/10/31/DjangoApiTemplate/</id>
    <published>2019-10-31T12:42:31.000Z</published>
    <updated>2024-11-06T11:52:49.364Z</updated>
    
    <content type="html"><![CDATA[<h5 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h5><p>用 <code>Django + Django-REST-framework</code> 开发后端API接口有一段时间了，工作中每开发一个新的微服务，都需要配置同样的项目结构或设置相同的配置项，不仅浪费时间并且有还容易出错。<br>用<code>cookiecutter</code>可以基于模板生成项目，这样就可以解决这些问题了。<br>我从日常的项目中抽离了一些最基本的功能做成了一个模板，希望能帮助遇到同样困惑的朋友，如果能提供一个解决问题的思路或方向就是最大的收获了。</p><h5 id="地址"><a href="#地址" class="headerlink" title="地址"></a>地址</h5><p>项目传送门 <a href="https://github.com/xiaomastack/django_api_template" title="Django RestFramework API 项目模板" target="_blank" rel="noopener">Django RestFramework API 项目模板</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h5 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h5&gt;&lt;p&gt;用 &lt;code&gt;Django + Django-REST-framework&lt;/code&gt; 开发后端API接口有一段时间了，工作中每开发一个
      
    
    </summary>
    
      <category term="Dev" scheme="http://www.azjoy.com/categories/Dev/"/>
    
      <category term="Django" scheme="http://www.azjoy.com/categories/Dev/Django/"/>
    
    
      <category term="微服务" scheme="http://www.azjoy.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Django" scheme="http://www.azjoy.com/tags/Django/"/>
    
  </entry>
  
  <entry>
    <title>Iris非对称签名认证</title>
    <link href="http://www.azjoy.com/2019/10/21/IrisSignatureAuth/"/>
    <id>http://www.azjoy.com/2019/10/21/IrisSignatureAuth/</id>
    <published>2019-10-21T02:58:58.000Z</published>
    <updated>2024-11-05T14:28:33.518Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>API接口有多种认证方式，例如<code>基础认证(用户名/密码)</code>、<code>apikey(秘钥)</code>、<code>token(令牌)</code>、<code>JWT(令牌)</code>、<code>signature(签名)</code>等。非对称签名认证应用广泛，接下来从客户端到服务端简单实现非对称签名，签名算法采用<code>HMAC SHA256</code>。</p></blockquote><h5 id="数字签名"><a href="#数字签名" class="headerlink" title="数字签名"></a>数字签名</h5><p>数字签名技术是将摘要信息用发送者的私钥加密，与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息，然后用HASH函数对收到的原文产生一个摘要信息，与解密的摘要信息对比。如果相同，则说明收到的信息是完整的，否则说明信息被修改，因此数字签名能保证信息传输过程中完整性、提供信息发送者的身份认证和不可抵赖性。可以用数字签名来做API认证。</p><h5 id="签名过程"><a href="#签名过程" class="headerlink" title="签名过程"></a>签名过程</h5><p>使用随机函数生成公私钥对，例如</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">公钥: <span class="string">"8thcm24furn0likp"</span></span><br><span class="line">私钥: <span class="string">"PnA4tIBlKBbUV5iVQloLrvBoTbMR03el"</span></span><br></pre></td></tr></table></figure><p>选择合适的摘要签名信息，可以采用当前时间</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">摘要信息: <span class="string">"date: 2019-10-21 10:58:58"</span></span><br></pre></td></tr></table></figure><p>将摘要信息用<code>HMAC SHA256</code>进程签名，并对原始的二进制数据进行<code>Base64编码</code>，得到签名值</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">签名值: <span class="string">"33xn46YabD78N372yw+WV4NvNSTRrRbmAV7mcz0vDUQ="</span></span><br></pre></td></tr></table></figure><p>客户端<code>header</code>组装，<code>x-api-key</code>用来传输公钥，<code>date</code>用来传输摘要信息，<code>authorization</code>用来传输完整的签名信息</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'x-api-key'</span>: <span class="string">'8thcm24furn0likp'</span>,</span><br><span class="line"><span class="string">'date'</span>: <span class="string">'2019-10-21 10:58:58'</span>,</span><br><span class="line"><span class="string">'authorization'</span>: <span class="string">'Signature keyId="8thcm24furn0likp",algorithm="hmac-sha256",signature="33xn46YabD78N372yw+WV4NvNSTRrRbmAV7mcz0vDUQ="'</span></span><br></pre></td></tr></table></figure><h5 id="签名实现"><a href="#签名实现" class="headerlink" title="签名实现"></a>签名实现</h5><p><code>python</code>可以基于<code>HTTPSignatureAuth</code>来实现</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> unicode_literals</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> httpsig.requests_auth <span class="keyword">import</span> HTTPSignatureAuth</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SignatureAuthHttp</span><span class="params">(object)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, key=<span class="string">'8thcm24furn0likp'</span>, secret=<span class="string">'PnA4tIBlKBbUV5iVQloLrvBoTbMR03el'</span>)</span>:</span></span><br><span class="line">        self.auth = HTTPSignatureAuth(key=key, secret=secret)</span><br><span class="line">        self.header = &#123;</span><br><span class="line">            <span class="string">'Content-Type'</span>: <span class="string">'application/json'</span>,</span><br><span class="line">            <span class="string">'X-Api-Key'</span>: key,</span><br><span class="line">            <span class="string">'Date'</span>: datetime.datetime.now().strftime(<span class="string">"%Y-%m-%d %H:%M:%S"</span>)</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">httpGet</span><span class="params">(self, url)</span>:</span></span><br><span class="line">        <span class="keyword">return</span> requests.get(url, auth=self.auth, header=self.header)</span><br></pre></td></tr></table></figure><p>如果是Kong的上游请求需要进行签名认证，可以基于<code>openssl.hmac</code>和<code>ngx</code>模块实现</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> BasePlugin = <span class="built_in">require</span> <span class="string">"kong.plugins.base_plugin"</span></span><br><span class="line"><span class="keyword">local</span> req_set_header = ngx.req.set_header</span><br><span class="line"><span class="keyword">local</span> openssl_hmac = <span class="built_in">require</span> <span class="string">"openssl.hmac"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> UpstreamAuthSignatureHandler = BasePlugin:extend()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UpstreamAuthSignatureHandler:new</span><span class="params">()</span></span></span><br><span class="line">  UpstreamAuthSignatureHandler.super.new(self, <span class="string">"upstream-auth-signature"</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UpstreamAuthSignatureHandler:access</span><span class="params">(conf)</span></span></span><br><span class="line">  UpstreamAuthSignatureHandler.super.access(self)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">local</span> key = conf.key</span><br><span class="line">  <span class="keyword">local</span> secret = conf.secret</span><br><span class="line">  <span class="keyword">local</span> <span class="built_in">date</span> = <span class="built_in">os</span>.<span class="built_in">date</span>(<span class="string">"%Y-%m-%d %H:%M:%S"</span>, <span class="built_in">os</span>.<span class="built_in">time</span>())</span><br><span class="line">  <span class="keyword">local</span> signature_salt = <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">"date: %s"</span>, <span class="built_in">date</span>)</span><br><span class="line">  <span class="keyword">local</span> signature = openssl_hmac.new(secret, <span class="string">"sha256"</span>):final(signature_salt)</span><br><span class="line">  <span class="keyword">local</span> authorization = <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">'Signature keyId="%s",algorithm="hmac-sha256",signature="%s"'</span>, key, ngx.encode_base64(signature))</span><br><span class="line"></span><br><span class="line">  req_set_header(<span class="string">"date"</span>, <span class="built_in">date</span>)</span><br><span class="line">  req_set_header(<span class="string">"x-api-key"</span>, key)</span><br><span class="line">  req_set_header(<span class="string">"authorization"</span>, authorization)</span><br><span class="line"></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">UpstreamAuthSignatureHandler.PRIORITY = <span class="number">850</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> UpstreamAuthSignatureHandler</span><br></pre></td></tr></table></figure><h5 id="验证签名"><a href="#验证签名" class="headerlink" title="验证签名"></a>验证签名</h5><p>首先，服务端通过客户端请求传输的<code>header</code>信息，获取摘要信息、签名值、公钥并找到本地对应的私钥，然后使用同样的算法用私钥对摘要信息进行签名，最后通过对比传输的签名值和本地签名值是否一致，来判断认证的有效性。</p><p>基于Iris框架的实现</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// SignatureAuth 判断签名是否有效</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">SignatureAuth</span><span class="params">(ctx iris.Context)</span></span> &#123;</span><br><span class="line">  <span class="comment">// 通过header获取签名信息</span></span><br><span class="line">  auth := ctx.GetHeader(<span class="string">"Authorization"</span>)</span><br><span class="line">  date := ctx.GetHeader(<span class="string">"date"</span>)</span><br><span class="line">  authKey := ctx.GetHeader(<span class="string">"x-api-key"</span>)</span><br><span class="line">  <span class="keyword">if</span> auth == <span class="string">""</span> || date == <span class="string">""</span> || authKey == <span class="string">""</span> &#123;</span><br><span class="line">    ctx.StatusCode(<span class="number">401</span>)</span><br><span class="line">    ctx.JSON(iris.Map&#123;<span class="string">"code"</span>: <span class="number">1</span>, <span class="string">"msg"</span>: <span class="string">"error"</span>, <span class="string">"data"</span>: <span class="string">"invalid authentication"</span>&#125;)</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 设置签名信息失效时间，这里为10s</span></span><br><span class="line">  loc, _ := time.LoadLocation(<span class="string">"PRC"</span>)</span><br><span class="line">  t, _ := time.ParseInLocation(<span class="string">"2006-01-02 15:04:05"</span>, date, loc)</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">if</span> time.Since(t).Seconds() &gt; <span class="number">10</span> &#123;</span><br><span class="line">    ctx.StatusCode(<span class="number">401</span>)</span><br><span class="line">    ctx.JSON(iris.Map&#123;<span class="string">"code"</span>: <span class="number">1</span>, <span class="string">"msg"</span>: <span class="string">"error"</span>, <span class="string">"data"</span>: <span class="string">"invalid authentication"</span>&#125;)</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 获取签名值</span></span><br><span class="line">  authInfo := strings.Split(auth, <span class="string">","</span>)</span><br><span class="line">  authSignature := strings.Split(authInfo[<span class="number">2</span>], <span class="string">"\""</span>)[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 获取本地私钥</span></span><br><span class="line">  userProfile, err := models.GetAPISecret(authKey)</span><br><span class="line">  <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">    ctx.StatusCode(<span class="number">401</span>)</span><br><span class="line">    ctx.JSON(iris.Map&#123;<span class="string">"code"</span>: <span class="number">1</span>, <span class="string">"msg"</span>: <span class="string">"error"</span>, <span class="string">"data"</span>: <span class="string">"invalid authentication"</span>&#125;)</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 使用同样的算法用私钥对摘要信息进行签名</span></span><br><span class="line">  mac := hmac.New(sha256.New, []<span class="keyword">byte</span>(userProfile.APISecret))</span><br><span class="line">  mac.Write([]<span class="keyword">byte</span>(<span class="string">"date: "</span> + date))</span><br><span class="line">  signature := base64.StdEncoding.EncodeToString(mac.Sum(<span class="literal">nil</span>))</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 对比传输的签名值和本地签名值</span></span><br><span class="line">  <span class="keyword">if</span> authSignature == signature &#123;</span><br><span class="line">    ctx.Next()</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    ctx.StatusCode(<span class="number">401</span>)</span><br><span class="line">    ctx.JSON(iris.Map&#123;<span class="string">"code"</span>: <span class="number">1</span>, <span class="string">"msg"</span>: <span class="string">"error"</span>, <span class="string">"data"</span>: <span class="string">"invalid authentication"</span>&#125;)</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>给需要认证的API添加拦截器</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">before</span><span class="params">(ctx iris.Context)</span></span> &#123;</span><br><span class="line">  SignatureAuth(ctx)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">InitRouter</span><span class="params">()</span> *<span class="title">iris</span>.<span class="title">Application</span></span> &#123;</span><br><span class="line">  r := iris.New()</span><br><span class="line">  r.Use(<span class="built_in">recover</span>.New())</span><br><span class="line"></span><br><span class="line">  r.Use(before)</span><br><span class="line"></span><br><span class="line">  api := r.Party(<span class="string">"/api/xxx"</span>)</span><br><span class="line">  &#123;</span><br><span class="line">    ... ...</span><br><span class="line">    api.Get(<span class="string">"/xxx"</span>, v.xxx)</span><br><span class="line">    ... ...</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> r</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试，未通过签名认证认返回信息</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"code"</span>: 1,</span><br><span class="line">  <span class="string">"data"</span>: <span class="string">"invalid authentication"</span>,</span><br><span class="line">  <span class="string">"msg"</span>: <span class="string">"error"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;API接口有多种认证方式，例如&lt;code&gt;基础认证(用户名/密码)&lt;/code&gt;、&lt;code&gt;apikey(秘钥)&lt;/code&gt;、&lt;code&gt;token(令牌)&lt;/code&gt;、&lt;code&gt;JWT(令牌)&lt;/code&gt;、&lt;code&gt;signature
      
    
    </summary>
    
      <category term="Dev" scheme="http://www.azjoy.com/categories/Dev/"/>
    
      <category term="Iris" scheme="http://www.azjoy.com/categories/Dev/Iris/"/>
    
    
      <category term="微服务" scheme="http://www.azjoy.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Go" scheme="http://www.azjoy.com/tags/Go/"/>
    
      <category term="Iris" scheme="http://www.azjoy.com/tags/Iris/"/>
    
  </entry>
  
  <entry>
    <title>Iris服务端跨域</title>
    <link href="http://www.azjoy.com/2019/09/17/IrisCors/"/>
    <id>http://www.azjoy.com/2019/09/17/IrisCors/</id>
    <published>2019-09-17T01:53:41.000Z</published>
    <updated>2024-11-06T11:52:49.359Z</updated>
    
    <content type="html"><![CDATA[<h5 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h5><p><a href="https://segmentfault.com/a/1190000012550346" target="_blank" rel="noopener">Nginx配置跨域请求 Access-Control-Allow-Origin *</a><br><a href="https://blog.csdn.net/m0_37886429/article/details/83618506" target="_blank" rel="noopener">Nginx配置跨域访问</a><br><a href="https://studyiris.com/example/exper/cors.html" target="_blank" rel="noopener">CORS跨域资源共享</a></p><h5 id="CORS"><a href="#CORS" class="headerlink" title="CORS"></a>CORS</h5><blockquote><p><code>CROS</code>全称是跨域资源共享 (Cross-origin resource sharing)，跨域资源共享(CORS)标准新增了一组 HTTP 首部字段，允许服务器声明哪些源站有权限访问哪些资源。<code>另外，规范要求，对那些可能对服务器数据产生副作用的HTTP请求方法（特别是GET以外的HTTP请求，或者搭配某些MIME类型的POST请求），浏览器必须首先使用OPTIONS方法发起一个预检请求（preflight request），从而获知服务端是否允许该跨域请求</code>。服务器确认允许之后，才发起实际的 HTTP 请求。在预检请求的返回中，服务器端也可以通知客户端，是否需要携带身份凭证（包括 Cookies 和 HTTP 认证相关数据）。</p></blockquote><h5 id="问题和解决方法"><a href="#问题和解决方法" class="headerlink" title="问题和解决方法"></a>问题和解决方法</h5><p>服务端默认是不被允许跨域的，当浏览器跨域访问出现<code>403</code>跨域错误的时候<code>No &#39;Access-Control-Allow-Origin&#39; header is present on the requested resource</code>。两种解决方法，<code>使用Nginx作为反向代理服务器，对响应添加对应的头信息</code>，<code>服务端自身实现，允许跨域</code>。不管哪种方式都是添加响应头信息<code>Access-Control-Allow-Origin *</code>，表示服务端可以接受所有的请求源<code>Origin</code>，即接受所有跨域的请求。</p><h5 id="服务端允许跨域"><a href="#服务端允许跨域" class="headerlink" title="服务端允许跨域"></a>服务端允许跨域</h5><p>通过<code>nginx</code>作为反向代理服务器，解决跨域问题网上有很多教程，这里就不不累述了。主要描述下怎样在服务端自身实现跨域，这样是比较理想的解决方法，可以通过<code>Iris cors 中间件</code>实现，也可以自己代码实现。</p><h6 id="Iris-cors-中间件"><a href="#Iris-cors-中间件" class="headerlink" title="Iris cors 中间件"></a>Iris cors 中间件</h6><p>实例代码可以参考文章 <a href="https://studyiris.com/example/exper/cors.html" target="_blank" rel="noopener">CORS跨域资源共享</a></p><h6 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h6><p>自定义<code>middleware</code>包新增函数<code>Cors</code>。给所有请求添加头信息<code>Access-Control-Allow-Origin *</code>，如果是<code>OPTIONS</code>方法的预请求，成功响应并添加对应的头信息，状态码响应<code>204</code>或者<code>200</code>。不设置状态码默认响应<code>301</code>，预请求响应失败会造成最后跨域访问失败。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> middleware</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">  <span class="string">"github.com/kataras/iris"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Cors 实现服务端跨域</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Cors</span><span class="params">(ctx iris.Context)</span></span> &#123;</span><br><span class="line">  ctx.Header(<span class="string">"Access-Control-Allow-Origin"</span>, <span class="string">"*"</span>)</span><br><span class="line">  <span class="keyword">if</span> ctx.Request().Method == <span class="string">"OPTIONS"</span> &#123;</span><br><span class="line">    ctx.Header(<span class="string">"Access-Control-Allow-Methods"</span>, <span class="string">"GET,POST,PUT,DELETE,PATCH,OPTIONS"</span>)</span><br><span class="line">    ctx.Header(<span class="string">"Access-Control-Allow-Headers"</span>, <span class="string">"Content-Type, Accept, Authorization"</span>)</span><br><span class="line">    ctx.StatusCode(<span class="number">204</span>)</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  &#125;</span><br><span class="line">  ctx.Next()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果API接口没有实现<code>OPTIONS</code>方法，需要给所有接口设置缺省的<code>OPTIONS</code>方法，不然捕捉不到对应API接口的<code>OPTIONS</code>请求。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> routers</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">  <span class="string">"github.com/kataras/iris"</span></span><br><span class="line">  <span class="string">"github.com/kataras/iris/middleware/recover"</span></span><br><span class="line">  ...</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// InitRouter 路由初始化</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">InitRouter</span><span class="params">()</span> *<span class="title">iris</span>.<span class="title">Application</span></span> &#123;</span><br><span class="line">  r := iris.New()</span><br><span class="line">  r.Use(<span class="built_in">recover</span>.New())</span><br><span class="line"></span><br><span class="line">  ...</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// common</span></span><br><span class="line">  common := r.Party(<span class="string">"/"</span>)</span><br><span class="line">  &#123;</span><br><span class="line">    common.Options(<span class="string">"*"</span>, <span class="function"><span class="keyword">func</span><span class="params">(ctx iris.Context)</span></span> &#123;</span><br><span class="line">      ctx.Next()</span><br><span class="line">    &#125;)</span><br><span class="line">    ...</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  ...</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> r</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后在所有请求前面添加拦截器，使用自定义的中间件<code>r.Use(cors)</code>。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> routers</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">  <span class="string">"github.com/kataras/iris"</span></span><br><span class="line">  <span class="string">"github.com/kataras/iris/middleware/recover"</span></span><br><span class="line">  <span class="string">"github.com/xiaomastack/hobby/middleware"</span></span><br><span class="line">  ...</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">cors</span><span class="params">(ctx iris.Context)</span></span> &#123;</span><br><span class="line">  middleware.Cors(ctx)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// InitRouter 路由初始化</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">InitRouter</span><span class="params">()</span> *<span class="title">iris</span>.<span class="title">Application</span></span> &#123;</span><br><span class="line">  r := iris.New()</span><br><span class="line">  r.Use(<span class="built_in">recover</span>.New())</span><br><span class="line"></span><br><span class="line">  <span class="comment">// cors</span></span><br><span class="line">  r.Use(cors)</span><br><span class="line">  ...</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// common</span></span><br><span class="line">  common := r.Party(<span class="string">"/"</span>)</span><br><span class="line">  &#123;</span><br><span class="line">    common.Options(<span class="string">"*"</span>, <span class="function"><span class="keyword">func</span><span class="params">(ctx iris.Context)</span></span> &#123;</span><br><span class="line">      ctx.Next()</span><br><span class="line">    &#125;)</span><br><span class="line">    ...</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  ...</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> r</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>用<code>OPTIONS</code>方法请求任一API接口，可以测试是否生效，如下返回的结果显示已生效。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X OPTIONS -vv http://localhost:8080/hs</span><br><span class="line">*   Trying ::1...</span><br><span class="line">* TCP_NODELAY <span class="built_in">set</span></span><br><span class="line">* Connected to localhost (::1) port 8080 (<span class="comment">#0)</span></span><br><span class="line">&gt; OPTIONS /hs HTTP/1.1</span><br><span class="line">&gt; Host: localhost:8080</span><br><span class="line">&gt; User-Agent: curl/7.54.0</span><br><span class="line">&gt; Accept: */*</span><br><span class="line">&gt;</span><br><span class="line">&lt; HTTP/1.1 204 No Content</span><br><span class="line">&lt; Access-Control-Allow-Headers: Content-Type, Accept, Authorization</span><br><span class="line">&lt; Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,OPTIONS</span><br><span class="line">&lt; Access-Control-Allow-Origin: *</span><br><span class="line">&lt; Date: Tue, 17 Sep 2019 03:03:18 GMT</span><br><span class="line">&lt;</span><br><span class="line">* Connection <span class="comment">#0 to host localhost left intact</span></span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h5 id=&quot;参考文章&quot;&gt;&lt;a href=&quot;#参考文章&quot; class=&quot;headerlink&quot; title=&quot;参考文章&quot;&gt;&lt;/a&gt;参考文章&lt;/h5&gt;&lt;p&gt;&lt;a href=&quot;https://segmentfault.com/a/1190000012550346&quot; target=&quot;
      
    
    </summary>
    
      <category term="Dev" scheme="http://www.azjoy.com/categories/Dev/"/>
    
      <category term="Iris" scheme="http://www.azjoy.com/categories/Dev/Iris/"/>
    
    
      <category term="微服务" scheme="http://www.azjoy.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Go" scheme="http://www.azjoy.com/tags/Go/"/>
    
      <category term="Iris" scheme="http://www.azjoy.com/tags/Iris/"/>
    
  </entry>
  
  <entry>
    <title>Kong多认证插件</title>
    <link href="http://www.azjoy.com/2019/08/21/KongAuth/"/>
    <id>http://www.azjoy.com/2019/08/21/KongAuth/</id>
    <published>2019-08-21T13:24:51.000Z</published>
    <updated>2024-11-05T14:28:33.519Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p><code>Kong</code>支持 <code>同时启用</code>多个身份认证 <code>(Auth)</code>插件，允许客户端使用不同的身份验证方法来访问指定的服务 <code>(Services)</code>或路由<code>(Routes)</code>。</p></blockquote><h4 id="创建一个消费者"><a href="#创建一个消费者" class="headerlink" title="创建一个消费者"></a>创建一个消费者</h4><h5 id="创建消费者"><a href="#创建消费者" class="headerlink" title="创建消费者"></a>创建消费者</h5><p>创建消费者 <code>devops</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X POST --url http://localhost:8001/consumers/ --data <span class="string">"username=devops"</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"custom_id"</span>:null,</span><br><span class="line">  <span class="string">"created_at"</span>:1566380171,</span><br><span class="line">  <span class="string">"username"</span>:<span class="string">"devops"</span>,</span><br><span class="line">  <span class="string">"id"</span>:<span class="string">"a1b6c169-f6e3-482e-a477-2d09d14dce8b"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="生成key-auth认证秘钥"><a href="#生成key-auth认证秘钥" class="headerlink" title="生成key-auth认证秘钥"></a>生成key-auth认证秘钥</h5><p>为消费者 <code>devops</code>生成秘钥凭证，可以指定<code>key</code>值，也可以让插件自动生成<code>key</code>值，建议插件自动生成</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X POST --url http://localhost:8001/consumers/devops/key-auth/</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"key"</span>: <span class="string">"OLCgYhkbjfZueFBQ7qe3nOYuWiUdOiiN"</span>,</span><br><span class="line">  <span class="string">"created_at"</span>: 1566382283,</span><br><span class="line">  <span class="string">"consumer"</span>: &#123;</span><br><span class="line">    <span class="string">"id"</span>: <span class="string">"a1b6c169-f6e3-482e-a477-2d09d14dce8b"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">"id"</span>: <span class="string">"e9dafd25-5e18-4ec1-a41d-b4fc9ea20208"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以通过接口<code>/key-auths</code>查看所有消费者的秘钥，通过<code>/consumers/{consumer}/key-auth</code>接口查看指定消费者的秘钥。</p><h5 id="生成JWT凭证"><a href="#生成JWT凭证" class="headerlink" title="生成JWT凭证"></a>生成JWT凭证</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X POST http://localhost:8001/consumers/devops/jwt \</span><br><span class="line">&gt; -H <span class="string">"Content-Type: application/x-www-form-urlencoded"</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"rsa_public_key"</span>: null,</span><br><span class="line">  <span class="string">"created_at"</span>: 1566382780,</span><br><span class="line">  <span class="string">"consumer"</span>: &#123;</span><br><span class="line">    <span class="string">"id"</span>: <span class="string">"a1b6c169-f6e3-482e-a477-2d09d14dce8b"</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">"id"</span>: <span class="string">"c0296bae-b9bd-4835-b433-95dff237bb4b"</span>,</span><br><span class="line">  <span class="string">"algorithm"</span>: <span class="string">"HS256"</span>,</span><br><span class="line">  <span class="string">"secret"</span>: <span class="string">"EGUUr9v99DMGwIk9SpBxvigjzFi5GsBZ"</span>,</span><br><span class="line">  <span class="string">"key"</span>: <span class="string">"GiweuLOEAwSO9cxkibug7MaBfdJE4NPB"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以通过<code>/jwts</code>接口查看所有消费者的<code>jwt</code>凭证，通过<code>/consumers/{consumer}/jwt</code>接口查看指定消费者的凭证。</p><h4 id="启用多个认证插件"><a href="#启用多个认证插件" class="headerlink" title="启用多个认证插件"></a>启用多个认证插件</h4><p>插件可以应用于<code>Route</code>，<code>Service</code>，<code>Global</code>，这里以<code>Global</code>为例。并同时启动<code>key-auth</code>和<code>jwt</code>认证插件。</p><h5 id="启用key-auth认证插件"><a href="#启用key-auth认证插件" class="headerlink" title="启用key-auth认证插件"></a>启用key-auth认证插件</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X POST --url http://localhost:8001/plugins/ --data <span class="string">"name=key-auth"</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"created_at"</span>: 1566381363,</span><br><span class="line">  <span class="string">"config"</span>: &#123;</span><br><span class="line">    <span class="string">"key_names"</span>: [</span><br><span class="line">      <span class="string">"apikey"</span></span><br><span class="line">    ],</span><br><span class="line">    <span class="string">"run_on_preflight"</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="string">"anonymous"</span>: null,</span><br><span class="line">    <span class="string">"hide_credentials"</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="string">"key_in_body"</span>: <span class="literal">false</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">"id"</span>: <span class="string">"947db416-1f3a-4a54-a5ff-75b6a55206d7"</span>,</span><br><span class="line">  <span class="string">"service"</span>: null,</span><br><span class="line">  <span class="string">"enabled"</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="string">"run_on"</span>: <span class="string">"first"</span>,</span><br><span class="line">  <span class="string">"consumer"</span>: null,</span><br><span class="line">  <span class="string">"route"</span>: null,</span><br><span class="line">  <span class="string">"name"</span>: <span class="string">"key-auth"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="启用jwt认证插件"><a href="#启用jwt认证插件" class="headerlink" title="启用jwt认证插件"></a>启用jwt认证插件</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X POST --url http://localhost:8001/plugins/ --data <span class="string">"name=jwt"</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"created_at"</span>: 1566383438,</span><br><span class="line">  <span class="string">"config"</span>: &#123;</span><br><span class="line">    <span class="string">"secret_is_base64"</span>: <span class="literal">false</span>,</span><br><span class="line">    <span class="string">"key_claim_name"</span>: <span class="string">"iss"</span>,</span><br><span class="line">    <span class="string">"cookie_names"</span>: [],</span><br><span class="line">    <span class="string">"maximum_expiration"</span>: 0,</span><br><span class="line">    <span class="string">"claims_to_verify"</span>: null,</span><br><span class="line">    <span class="string">"anonymous"</span>: null,</span><br><span class="line">    <span class="string">"run_on_preflight"</span>: <span class="literal">true</span>,</span><br><span class="line">    <span class="string">"uri_param_names"</span>: [</span><br><span class="line">      <span class="string">"jwt"</span></span><br><span class="line">    ]</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">"id"</span>: <span class="string">"fc54429b-73cd-4215-8f4d-35a21c6a389e"</span>,</span><br><span class="line">  <span class="string">"service"</span>: null,</span><br><span class="line">  <span class="string">"enabled"</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="string">"run_on"</span>: <span class="string">"first"</span>,</span><br><span class="line">  <span class="string">"consumer"</span>: null,</span><br><span class="line">  <span class="string">"route"</span>: null,</span><br><span class="line">  <span class="string">"name"</span>: <span class="string">"jwt"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="多重认证-AND"><a href="#多重认证-AND" class="headerlink" title="多重认证-AND"></a>多重认证-AND</h4><p><code>Kong</code>启用多个认证插件后，默认的执行逻辑是 <code>AND</code>。例如启用了两个认证插件<code>key-auth</code>和<code>jwt</code>，那么每次的请求必须提供这两个插件的认证信息，必须通过所有认证插件的认证，才会将下游的请求转发到上游。</p><p>使用<code>key-auth</code> 认证方式，请求状态<code>401</code>，返回<code>jwt</code>插件的未认证信息提示<code>{&quot;message&quot;:&quot;Unauthorized&quot;}</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ curl -i -X GET --url http://192.168.1.100:8000/api/<span class="built_in">test</span>/ \</span><br><span class="line">-H <span class="string">"apikey: OLCgYhkbjfZueFBQ7qe3nOYuWiUdOiiN"</span></span><br><span class="line">HTTP/1.1 401 Unauthorized</span><br><span class="line">... ... ...</span><br><span class="line"></span><br><span class="line">&#123;<span class="string">"message"</span>:<span class="string">"Unauthorized"</span>&#125;</span><br></pre></td></tr></table></figure><p>使用 <code>jwt</code> 认证方式，请求状态<code>401</code>，返回<code>key-auth</code>插件的未认证信息提示<code>{&quot;message&quot;:&quot;No API key found in request&quot;}</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ curl -i -X GET --url http://192.168.1.100:8000/api/<span class="built_in">test</span>/ \</span><br><span class="line">&gt; -H <span class="string">"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJHaXdldUxPRUF3U085Y3hraWJ1ZzdNYUJmZEpFNE5QQiJ9.UjxuXIu4yI6VyZeoDIfrMLpqggKE4n63o_IQMbygPFA"</span></span><br><span class="line">HTTP/1.1 401 Unauthorized</span><br><span class="line">... ... ...</span><br><span class="line"></span><br><span class="line">&#123;<span class="string">"message"</span>:<span class="string">"No API key found in request"</span>&#125;</span><br></pre></td></tr></table></figure><p>同时使用<code>key-auth</code>和<code>jwt</code> 认证方式，成功返回请求数据，请求状态<code>200</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ curl -i -X GET --url http://192.168.1.100:8000/api/<span class="built_in">test</span>/ \</span><br><span class="line">&gt; -H <span class="string">"apikey: OLCgYhkbjfZueFBQ7qe3nOYuWiUdOiiN"</span> \</span><br><span class="line">&gt; -H <span class="string">"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJHaXdldUxPRUF3U085Y3hraWJ1ZzdNYUJmZEpFNE5QQiJ9.UjxuXIu4yI6VyZeoDIfrMLpqggKE4n63o_IQMbygPFA"</span></span><br><span class="line">HTTP/1.1 200 OK</span><br><span class="line">... ... ...</span><br></pre></td></tr></table></figure><h4 id="多种认证-OR"><a href="#多种认证-OR" class="headerlink" title="多种认证-OR"></a>多种认证-OR</h4><p><code>Kong</code>启用多个认证插件后，默认的执行逻辑是 <code>AND</code>，当认证插件都启用匿名访问后，执行逻辑将会变为 <code>OR</code>。</p><blockquote><p>在不启用匿名访问的时候，<code>auth插件</code>将始终执行身份验证，如果未经过验证，则返回<code>40x</code>响应。当启用匿名访问后<code>auth插件</code>只会在未经过身份验证的情况下执行身份验证，如果身份验证失败，它不会返回<code>40x</code>响应，而是将匿名使用者设置为有效的消费者，那么当调用多个<code>auth插件</code>时，就会导致 <code>OR + 匿名访问</code>的逻辑。如果配合<code>request-termination plugin</code>插件拦截匿名访问的请求，禁止匿名访问，就可以达到多种认证 <code>OR</code>的效果。</p></blockquote><h5 id="创建匿名消费者"><a href="#创建匿名消费者" class="headerlink" title="创建匿名消费者"></a>创建匿名消费者</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X POST --url http://localhost:8001/consumers/ \</span><br><span class="line">&gt; --data <span class="string">"username=anonymous"</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"custom_id"</span>: null,</span><br><span class="line">  <span class="string">"created_at"</span>: 1566388995,</span><br><span class="line">  <span class="string">"username"</span>: <span class="string">"anonymous"</span>,</span><br><span class="line">  <span class="string">"id"</span>: <span class="string">"958e85d7-e39d-4d2c-b8a9-888e25dbeed5"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="启用匿名访问"><a href="#启用匿名访问" class="headerlink" title="启用匿名访问"></a>启用匿名访问</h5><p><code>key-auth</code>插件启用匿名访问，插件<code>id</code>为<code>947db416-1f3a-4a54-a5ff-75b6a55206d7</code>，匿名用户<code>id</code>为<code>958e85d7-e39d-4d2c-b8a9-888e25dbeed5</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X PATCH --url http://localhost:8001/plugins/947db416-1f3a-4a54-a5ff-75b6a55206d7/ \</span><br><span class="line">&gt; --data <span class="string">"config.anonymous=958e85d7-e39d-4d2c-b8a9-888e25dbeed5"</span></span><br></pre></td></tr></table></figure><p><code>jwt</code>插件启用匿名访问，插件<code>id</code>为<code>fc54429b-73cd-4215-8f4d-35a21c6a389e</code>，匿名用户<code>id</code>为<code>958e85d7-e39d-4d2c-b8a9-888e25dbeed5</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X PATCH --url http://localhost:8001/plugins/fc54429b-73cd-4215-8f4d-35a21c6a389e/ \</span><br><span class="line">&gt; --data <span class="string">"config.anonymous=958e85d7-e39d-4d2c-b8a9-888e25dbeed5"</span></span><br></pre></td></tr></table></figure><p>启用匿名访问后，请求将不需要认证</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ curl -i -X GET --url http://192.168.1.100:8000/api/<span class="built_in">test</span>/</span><br><span class="line">HTTP/1.1 200 OK</span><br><span class="line">... ... ...</span><br></pre></td></tr></table></figure><h5 id="拦截匿名请求"><a href="#拦截匿名请求" class="headerlink" title="拦截匿名请求"></a>拦截匿名请求</h5><p>启用<code>request-termination</code>插件</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X POST http://localhost:8001/plugins/ \</span><br><span class="line">&gt; --data <span class="string">"name=request-termination"</span> \</span><br><span class="line">&gt; --data <span class="string">"config.status_code=401"</span> \</span><br><span class="line">&gt; --data <span class="string">"config.content_type=application/json; charset=utf-8"</span> \</span><br><span class="line">&gt; --data <span class="string">"config.body=&#123;\"message\": \"Authentication required\"&#125;"</span></span><br><span class="line"></span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">"created_at"</span>: 1566391156,</span><br><span class="line">  <span class="string">"config"</span>: &#123;</span><br><span class="line">    <span class="string">"status_code"</span>: 401,</span><br><span class="line">    <span class="string">"content_type"</span>: <span class="string">"application/json; charset=utf-8"</span>,</span><br><span class="line">    <span class="string">"body"</span>: <span class="string">"&#123;\"message\": \"Authentication required\"&#125;"</span>,</span><br><span class="line">    <span class="string">"message"</span>: null</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">"id"</span>: <span class="string">"e5ff19cf-006d-4fcd-ae00-5837bc5d6938"</span>,</span><br><span class="line">  <span class="string">"service"</span>: null,</span><br><span class="line">  <span class="string">"enabled"</span>: <span class="literal">true</span>,</span><br><span class="line">  <span class="string">"run_on"</span>: <span class="string">"first"</span>,</span><br><span class="line">  <span class="string">"consumer"</span>: null,</span><br><span class="line">  <span class="string">"route"</span>: null,</span><br><span class="line">  <span class="string">"name"</span>: <span class="string">"request-termination"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>匿名消费者<code>anonymous</code>启用该拦截插件，插件<code>id</code>为<code>e5ff19cf-006d-4fcd-ae00-5837bc5d6938</code>，匿名消费者<code>id</code>为<code>958e85d7-e39d-4d2c-b8a9-888e25dbeed5</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ curl -X PATCH http://localhost:8001/plugins/e5ff19cf-006d-4fcd-ae00-5837bc5d6938/ \</span><br><span class="line">--data <span class="string">"consumer.id=958e85d7-e39d-4d2c-b8a9-888e25dbeed5"</span></span><br></pre></td></tr></table></figure><p>此时匿名请求已被拦截</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ curl -i -X GET --url http://192.168.1.100:8000/api/<span class="built_in">test</span>/</span><br><span class="line">HTTP/1.1 401 Unauthorized</span><br><span class="line">... ... ...</span><br><span class="line"></span><br><span class="line">&#123;<span class="string">"message"</span>: <span class="string">"Authentication required"</span>&#125;</span><br></pre></td></tr></table></figure><p>但是只要通过任一种验证，即可成功请求数据</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># key-auth</span></span><br><span class="line">$ curl -i -X GET http://192.168.1.100:8000/api/<span class="built_in">test</span>/ \</span><br><span class="line">&gt; -H <span class="string">"apikey: OLCgYhkbjfZueFBQ7qe3nOYuWiUdOiiN"</span></span><br><span class="line">HTTP/1.1 200 OK</span><br><span class="line">... ... ...</span><br><span class="line"><span class="comment"># jwt</span></span><br><span class="line">$ curl -i -X GET http://192.168.1.100:8000/api/<span class="built_in">test</span>/ \</span><br><span class="line">&gt; -H <span class="string">"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJHaXdldUxPRUF3U085Y3hraWJ1ZzdNYUJmZEpFNE5QQiJ9.UjxuXIu4yI6VyZeoDIfrMLpqggKE4n63o_IQMbygPFA"</span></span><br><span class="line">HTTP/1.1 200 OK</span><br><span class="line">... ...</span><br></pre></td></tr></table></figure><p>此时的多认证插件<code>(Auth)</code>执行逻辑是 <code>OR</code></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Kong&lt;/code&gt;支持 &lt;code&gt;同时启用&lt;/code&gt;多个身份认证 &lt;code&gt;(Auth)&lt;/code&gt;插件，允许客户端使用不同的身份验证方法来访问指定的服务 &lt;code&gt;(Services)&lt;/code&gt;或路由&lt;code&gt;(
      
    
    </summary>
    
      <category term="Dev" scheme="http://www.azjoy.com/categories/Dev/"/>
    
      <category term="Tools" scheme="http://www.azjoy.com/categories/Dev/Tools/"/>
    
    
      <category term="微服务" scheme="http://www.azjoy.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Kong" scheme="http://www.azjoy.com/tags/Kong/"/>
    
  </entry>
  
  <entry>
    <title>Kong高可用方案</title>
    <link href="http://www.azjoy.com/2019/08/16/KongHA/"/>
    <id>http://www.azjoy.com/2019/08/16/KongHA/</id>
    <published>2019-08-16T03:14:29.000Z</published>
    <updated>2024-11-05T14:28:33.519Z</updated>
    
    <content type="html"><![CDATA[<h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>只要 <code>kong</code> 的后端数据库做到了高可用，<code>kong</code> 的高可用水到渠成。</p><h4 id="架构图"><a href="#架构图" class="headerlink" title="架构图"></a>架构图</h4><p><img src="/images/2019/08/Kong高可用.png" alt="Kong高可用"></p><h4 id="安装Kong"><a href="#安装Kong" class="headerlink" title="安装Kong"></a>安装Kong</h4><p>到 <code>Kong</code>官网下载相应的<code>rpm</code>安装包或者获取下载链接。<br>下载安装包</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ wget https://bintray.com/kong/kong-community-edition-rpm/download_file?file_path=centos/6/kong-community-edition-1.0.0.el6.noarch.rpm</span><br></pre></td></tr></table></figure><p>安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install kong-community-edition-1.0.0.el6.noarch.rpm</span><br></pre></td></tr></table></figure><p>配置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ grep -Ev <span class="string">'^#|^$|^ '</span> kong.conf</span><br><span class="line">plugins = bundled,upstream-auth-signature,upstream-url-penetrate             <span class="comment"># kong的可选自定义插件</span></span><br><span class="line">proxy_listen = 0.0.0.0:6002, 0.0.0.0:6443 ssl             <span class="comment"># kong的服务地址</span></span><br><span class="line">admin_listen = 127.0.0.1:6001, 127.0.0.1:6444 ssl             <span class="comment"># kong的后台管理地址</span></span><br><span class="line">pg_host = 192.168.1.100             <span class="comment"># 这里填写PostgreSQL集群Pgpool的VIP地址</span></span><br><span class="line">pg_port = 9999                  <span class="comment"># Pgpool服务端口</span></span><br><span class="line">pg_user = kong                  <span class="comment"># 用户名</span></span><br><span class="line">pg_password = kong              <span class="comment"># 用户密码</span></span><br><span class="line">pg_database = kong              <span class="comment"># 数据库名</span></span><br></pre></td></tr></table></figure><p>用其中<code>某个</code>节点，来初始化数据库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Kong版本高于0.14.1</span><br><span class="line">$ kong migrations bootstrap</span><br><span class="line"></span><br><span class="line">Kong版本低于0.15.0</span><br><span class="line">$ kong migrations up [-c /etc/kong/kong.conf]</span><br></pre></td></tr></table></figure><p>各个节点启动服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ kong start [-c /etc/kong/kong.conf]</span><br></pre></td></tr></table></figure><p>各个节点配置nginx反向代理</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">cat kong.conf</span><br><span class="line">server &#123;</span><br><span class="line">    listen 80;</span><br><span class="line">    server_name kong.domain.com;</span><br><span class="line"></span><br><span class="line">    location / &#123;</span><br><span class="line">        proxy_connect_timeout 120s;</span><br><span class="line">        proxy_read_timeout 120s;</span><br><span class="line">        proxy_send_timeout 120s;</span><br><span class="line">        proxy_pass http://192.168.1.10:6002;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>测试服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ curl http://kong.domain.com/</span><br><span class="line">&#123;<span class="string">"message"</span>:<span class="string">"no route and no API found with those values"</span>&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h4&gt;&lt;p&gt;只要 &lt;code&gt;kong&lt;/code&gt; 的后端数据库做到了高可用，&lt;code&gt;kong&lt;/code&gt; 的高可用水到渠成。&lt;/p&gt;
&lt;h4 
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/categories/Ops/PostgreSQL/"/>
    
    
      <category term="Kong" scheme="http://www.azjoy.com/tags/Kong/"/>
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/tags/PostgreSQL/"/>
    
      <category term="Pgpool-II" scheme="http://www.azjoy.com/tags/Pgpool-II/"/>
    
  </entry>
  
  <entry>
    <title>Pgpool-II集群的高可用</title>
    <link href="http://www.azjoy.com/2019/08/16/pgpoolHA/"/>
    <id>http://www.azjoy.com/2019/08/16/pgpoolHA/</id>
    <published>2019-08-16T03:07:27.000Z</published>
    <updated>2024-11-05T14:28:33.540Z</updated>
    
    <content type="html"><![CDATA[<h4 id="集群准备"><a href="#集群准备" class="headerlink" title="集群准备"></a>集群准备</h4><table><thead><tr><th style="text-align:center">role</th><th style="text-align:center">ip</th><th style="text-align:center">PostgreSQL</th><th style="text-align:center">Pgpool-II</th><th style="text-align:center">port</th></tr></thead><tbody><tr><td style="text-align:center">master</td><td style="text-align:center">192.168.1.3</td><td style="text-align:center">10.9</td><td style="text-align:center">4.0.5</td><td style="text-align:center">5432</td></tr><tr><td style="text-align:center">slave</td><td style="text-align:center">192.168.1.2</td><td style="text-align:center">10.9</td><td style="text-align:center">4.0.5</td><td style="text-align:center">5432</td></tr><tr><td style="text-align:center">vip</td><td style="text-align:center">192.168.1.100</td><td style="text-align:center">–</td><td style="text-align:center">–</td><td style="text-align:center">–</td></tr></tbody></table><p>其中<code>watchdog</code>端口<code>9000</code>，<code>pcp</code>端口<code>9898</code>，<code>psql</code>服务连接端口<code>9999</code></p><p>查看集群节点状态 <code>show pool_nodes;</code> </p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$ psql -p 9999 -h 192.168.1.100 -U postgres</span><br><span class="line">Password for user postgres:</span><br><span class="line">psql (10.6, server 10.9)</span><br><span class="line">Type "<span class="keyword">help</span><span class="string">" for help.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">postgres=# show pool_nodes;</span></span><br><span class="line"><span class="string"> node_id |   hostname    | port | status | lb_weight |  role   | select_cnt | load_balance_node </span></span><br><span class="line"><span class="string">---------+---------------+------+--------+-----------+---------+------------+-------------------</span></span><br><span class="line"><span class="string"> 0       | 192.168.1.3   | 5432 | up     | 0.500000  | primary | 5          | true                             </span></span><br><span class="line"><span class="string"> 1       | 192.168.1.2   | 5432 | up     | 0.500000  | standby | 0          | false                             </span></span><br><span class="line"><span class="string">(2 rows)</span></span><br></pre></td></tr></table></figure><h4 id="Pgpool服务挂了"><a href="#Pgpool服务挂了" class="headerlink" title="Pgpool服务挂了"></a>Pgpool服务挂了</h4><p><code>vip</code>目前在<code>master</code>端，先测试<code>非vip端pgpool</code>服务不可用，再测试<code>vip端pgpool</code>服务不可用。</p><h5 id="非vip端pgpool不可用"><a href="#非vip端pgpool不可用" class="headerlink" title="非vip端pgpool不可用"></a>非vip端pgpool不可用</h5><p>停止<code>pgpool</code>服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl stop pgpool.service</span><br></pre></td></tr></table></figure><p>查看节点状态，服务未中断</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$ psql -p 9999 -h 192.168.1.100 -U postgres</span><br><span class="line">Password for user postgres:</span><br><span class="line">psql (10.6, server 10.9)</span><br><span class="line">Type "<span class="keyword">help</span><span class="string">" for help.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">postgres=# show pool_nodes;</span></span><br><span class="line"><span class="string"> node_id |   hostname    | port | status | lb_weight |  role   | select_cnt | load_balance_node </span></span><br><span class="line"><span class="string">---------+---------------+------+--------+-----------+---------+------------+-------------------</span></span><br><span class="line"><span class="string"> 0       | 192.168.1.3   | 5432 | up     | 0.500000  | primary | 5          | false                             </span></span><br><span class="line"><span class="string"> 1       | 192.168.1.2   | 5432 | up     | 0.500000  | standby | 0          | true                             </span></span><br><span class="line"><span class="string">(2 rows)</span></span><br></pre></td></tr></table></figure><h5 id="vip端pgpool不可用"><a href="#vip端pgpool不可用" class="headerlink" title="vip端pgpool不可用"></a>vip端pgpool不可用</h5><p>停止<code>pgpool</code>服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl stop pgpool.service</span><br></pre></td></tr></table></figure><p>此时 <code>非vip端的pgpool</code> 会主动接管<code>vip</code>继续对外提供服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> ifconfig eth0:0</span></span><br><span class="line">eth0:0: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500</span><br><span class="line">        inet 192.168.1.100  netmask 255.255.255.0  broadcast 192.168.1.255</span><br><span class="line">        ether fa:f2:7d:42:75:00  txqueuelen 1000  (Ethernet)</span><br></pre></td></tr></table></figure><p>查看节点状态，服务被接管后，继续对外提供服务</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$ psql -p 9999 -h 192.168.1.100 -U postgres</span><br><span class="line">Password for user postgres:</span><br><span class="line">psql (10.6, server 10.9)</span><br><span class="line">Type "<span class="keyword">help</span><span class="string">" for help.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">postgres=# show pool_nodes;</span></span><br><span class="line"><span class="string"> node_id |   hostname    | port | status | lb_weight |  role   | select_cnt | load_balance_node </span></span><br><span class="line"><span class="string">---------+---------------+------+--------+-----------+---------+------------+-------------------</span></span><br><span class="line"><span class="string"> 0       | 192.168.1.3   | 5432 | up     | 0.500000  | primary | 5          | true                             </span></span><br><span class="line"><span class="string"> 1       | 192.168.1.2   | 5432 | up     | 0.500000  | standby | 0          | false                             </span></span><br><span class="line"><span class="string">(2 rows)</span></span><br></pre></td></tr></table></figure><h4 id="PostgreSQL挂了"><a href="#PostgreSQL挂了" class="headerlink" title="PostgreSQL挂了"></a>PostgreSQL挂了</h4><h5 id="standby不可用"><a href="#standby不可用" class="headerlink" title="standby不可用"></a>standby不可用</h5><p>停止<code>postgresql</code>服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ pg_ctl stop</span><br><span class="line">waiting <span class="keyword">for</span> server to shut down.... <span class="keyword">done</span></span><br><span class="line">server stopped</span><br></pre></td></tr></table></figure><p>此时集群节点状态会发生变化，<code>standby</code>节点变为<code>down</code>，此时集群仍然可以继续对外提供服务。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$ psql -p 9999 -h 192.168.1.100 -U postgres</span><br><span class="line">Password for user postgres:</span><br><span class="line">psql (10.6, server 10.9)</span><br><span class="line">Type "<span class="keyword">help</span><span class="string">" for help.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">postgres=# show pool_nodes;</span></span><br><span class="line"><span class="string"> node_id |   hostname    | port | status | lb_weight |  role   | select_cnt | load_balance_node </span></span><br><span class="line"><span class="string">---------+---------------+------+--------+-----------+---------+------------+-------------------</span></span><br><span class="line"><span class="string"> 0       | 192.168.1.3   | 5432 | up     | 0.500000  | primary | 5          | true                             </span></span><br><span class="line"><span class="string"> 1       | 192.168.1.2   | 5432 | down     | 0.500000  | standby | 0          | false                             </span></span><br><span class="line"><span class="string">(2 rows)</span></span><br></pre></td></tr></table></figure><p>重新启动 <code>standby</code> 节点，模拟节点修复，重新将该节点加入集群</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ pg_ctl start</span><br><span class="line">... ... ...</span><br><span class="line"><span class="keyword">done</span></span><br><span class="line">server started</span><br><span class="line"></span><br><span class="line">$ pcp_attach_node -h 192.168.1.100 -p 9898 -U postgres -n 1</span><br><span class="line">Password:</span><br><span class="line">pcp_attach_node -- Command Successful</span><br></pre></td></tr></table></figure><p><code>standby</code> 重新加入集群后，状态变为<code>up</code>，继续对外服务。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$ psql -p 9999 -h 192.168.1.100 -U postgres</span><br><span class="line">Password <span class="keyword">for</span> user postgres:</span><br><span class="line">psql (10.6, server 10.9)</span><br><span class="line">Type <span class="string">"help"</span> <span class="keyword">for</span> <span class="built_in">help</span>.</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># show pool_nodes;</span></span><br><span class="line"> node_id |   hostname    | port | status | lb_weight |  role   | select_cnt | load_balance_node </span><br><span class="line">---------+---------------+------+--------+-----------+---------+------------+-------------------</span><br><span class="line"> 0       | 192.168.1.3   | 5432 | up     | 0.500000  | primary | 5          | <span class="literal">true</span>                             </span><br><span class="line"> 1       | 192.168.1.2   | 5432 | up     | 0.500000  | standby | 0          | <span class="literal">false</span>                             </span><br><span class="line">(2 rows)</span><br></pre></td></tr></table></figure><h5 id="primary不可用"><a href="#primary不可用" class="headerlink" title="primary不可用"></a>primary不可用</h5><p>当前 <code>192.168.1.2</code> 是 <code>primary</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$ psql -p 9999 -h 192.168.1.100 -U postgres</span><br><span class="line">Password <span class="keyword">for</span> user postgres:</span><br><span class="line">psql (10.6, server 10.9)</span><br><span class="line">Type <span class="string">"help"</span> <span class="keyword">for</span> <span class="built_in">help</span>.</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># show pool_nodes;</span></span><br><span class="line"> node_id |   hostname    | port | status | lb_weight |  role   | select_cnt | load_balance_node </span><br><span class="line">---------+---------------+------+--------+-----------+---------+------------+-------------------</span><br><span class="line"> 0       | 192.168.1.3   | 5432 | up     | 0.500000  | standby | 5          | <span class="literal">true</span>                             </span><br><span class="line"> 1       | 192.168.1.2   | 5432 | up     | 0.500000  | primary | 0          | <span class="literal">false</span>                             </span><br><span class="line">(2 rows)</span><br></pre></td></tr></table></figure><p>停止<code>192.168.1.2</code> 上面的 <code>postgresql</code>服务 </p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ pg_ctl stop</span><br><span class="line">waiting <span class="keyword">for</span> server to shut down.... <span class="keyword">done</span></span><br><span class="line">server stopped</span><br></pre></td></tr></table></figure><p>此时查看节点状态，集群检查到 <code>192.168.1.2</code> 服务不可用后，自动切换 <code>192.168.1.3</code> 为 <code>primary</code>，继续对外提供服务，此时集群可读写。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">$ psql -p 9999 -h 192.168.1.100 -U postgres</span><br><span class="line">Password <span class="keyword">for</span> user postgres:</span><br><span class="line">psql (10.6, server 10.9)</span><br><span class="line">Type <span class="string">"help"</span> <span class="keyword">for</span> <span class="built_in">help</span>.</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># show pool_nodes;</span></span><br><span class="line"> node_id |   hostname    | port | status | lb_weight |  role   | select_cnt | load_balance_node </span><br><span class="line">---------+---------------+------+--------+-----------+---------+------------+-------------------</span><br><span class="line"> 0       | 192.168.1.3   | 5432 | up     | 0.500000  | primary | 5          | <span class="literal">true</span>                             </span><br><span class="line"> 1       | 192.168.1.2   | 5432 | down   | 0.500000  | standby | 0          | <span class="literal">false</span>                             </span><br><span class="line">(2 rows)</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># create database kong;</span></span><br><span class="line">CREATE DATABASE</span><br><span class="line">postgres=<span class="comment"># drop database kong;</span></span><br><span class="line">DROP DATABASE</span><br></pre></td></tr></table></figure><p>修复节点 <code>192.168.1.2</code> 的服务，然后重新加入集群作为 <code>standby</code> 继续对外提供服务。如果主备时间线不同步，需要用 <code>pg_rewind</code> 命令修复时间线，然后再启动服务。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ mv recovery.done recovery.conf</span><br><span class="line">$ pg_ctl start</span><br><span class="line">$ pcp_attach_node -h 192.168.1.100 -p 9898 -U postgres -n 1</span><br><span class="line">Password:</span><br><span class="line">pcp_attach_node -- Command Successful</span><br></pre></td></tr></table></figure><p>节点成功加入集群，其状态为 <code>up</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$ psql -p 9999 -h 192.168.1.100 -U postgres</span><br><span class="line">Password <span class="keyword">for</span> user postgres:</span><br><span class="line">psql (10.6, server 10.9)</span><br><span class="line">Type <span class="string">"help"</span> <span class="keyword">for</span> <span class="built_in">help</span>.</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># show pool_nodes;</span></span><br><span class="line"> node_id |   hostname    | port | status | lb_weight |  role   | select_cnt | load_balance_node </span><br><span class="line">---------+---------------+------+--------+-----------+---------+------------+-------------------</span><br><span class="line"> 0       | 192.168.1.3   | 5432 | up     | 0.500000  | primary | 5          | <span class="literal">true</span>                             </span><br><span class="line"> 1       | 192.168.1.2   | 5432 | up     | 0.500000  | standby | 0          | <span class="literal">false</span>                             </span><br><span class="line">(2 rows)</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;集群准备&quot;&gt;&lt;a href=&quot;#集群准备&quot; class=&quot;headerlink&quot; title=&quot;集群准备&quot;&gt;&lt;/a&gt;集群准备&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:center&quot;&gt;role&lt;/th&gt;
&lt;th 
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/categories/Ops/PostgreSQL/"/>
    
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/tags/PostgreSQL/"/>
    
      <category term="Pgpool-II" scheme="http://www.azjoy.com/tags/Pgpool-II/"/>
    
  </entry>
  
  <entry>
    <title>基于Pgpool-II的PostgreSQL集群</title>
    <link href="http://www.azjoy.com/2019/08/16/postgresql%E9%9B%86%E7%BE%A4/"/>
    <id>http://www.azjoy.com/2019/08/16/postgresql集群/</id>
    <published>2019-08-16T02:36:57.000Z</published>
    <updated>2024-11-05T14:28:33.542Z</updated>
    
    <content type="html"><![CDATA[<h4 id="安装Pgpool-II"><a href="#安装Pgpool-II" class="headerlink" title="安装Pgpool-II"></a>安装Pgpool-II</h4><blockquote><p><code>Pgpool-II</code> 是一个位于 <code>PostgreSQL服务器</code>和 <code>PostgreSQL数据库客户端</code>之间的中间件，<code>Pgpool-II</code>提供了连接池（Connection Pooling）、复制（Replication）、负载均衡（Load Balancing）、缓存（In Memory Query Cache）、看门狗（Watchdog）、超出限制链接（Limiting Exceeding Connections）等功能，可以基于这些特性来搭建<code>PostgreSQL</code>高可用集群。</p></blockquote><h5 id="yum安装"><a href="#yum安装" class="headerlink" title="yum安装"></a>yum安装</h5><p>官方参考地址<br><a href="http://pgpool.net/mediawiki/index.php/Yum_Repository" target="_blank" rel="noopener">Yum Repository</a><br><a href="http://www.pgpool.net/docs/latest/en/html/install-rpm.html" target="_blank" rel="noopener">Installation from RPM</a></p><ol><li>安装CentOS7的yum源。</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install http://www.pgpool.net/yum/rpms/4.0/redhat/rhel-7-x86_64/pgpool-II-release-4.0-1.noarch.rpm</span><br></pre></td></tr></table></figure><ol start="2"><li>安装支持PostgreSQL10版本的pgpool-II</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ yum install pgpool-II-pg10</span><br><span class="line">$ yum install pgpool-II-pg10-debuginfo</span><br><span class="line">$ yum install pgpool-II-pg10-devel</span><br><span class="line">$ yum install pgpool-II-pg10-extensions</span><br></pre></td></tr></table></figure><ol start="3"><li>服务开机启动</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl <span class="built_in">enable</span> pgpool.service</span><br></pre></td></tr></table></figure><ol start="4"><li>启动/停止服务</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl start pgpool.service </span><br><span class="line">$ systemctl stop pgpool.service</span><br></pre></td></tr></table></figure><h4 id="配置Pgpool-II"><a href="#配置Pgpool-II" class="headerlink" title="配置Pgpool-II"></a>配置Pgpool-II</h4><p>参考文章</p><p><a href="https://my.oschina.net/u/3308173/blog/900093" target="_blank" rel="noopener">pgpool-II的安装及使用</a></p><p><a href="https://www.jianshu.com/p/ef183d0a9213" target="_blank" rel="noopener">PGPool-II+PG流复制实现HA主备切换</a></p><h5 id="实例准备"><a href="#实例准备" class="headerlink" title="实例准备"></a>实例准备</h5><table><thead><tr><th style="text-align:center">role</th><th style="text-align:center">ip</th><th style="text-align:center">PostgreSQL</th><th style="text-align:center">Pgpool-II</th><th style="text-align:center">port</th></tr></thead><tbody><tr><td style="text-align:center">master</td><td style="text-align:center">192.168.1.3</td><td style="text-align:center">10.9</td><td style="text-align:center">4.0.5</td><td style="text-align:center">5432</td></tr><tr><td style="text-align:center">slave</td><td style="text-align:center">192.168.1.2</td><td style="text-align:center">10.9</td><td style="text-align:center">4.0.5</td><td style="text-align:center">5432</td></tr><tr><td style="text-align:center">vip</td><td style="text-align:center">192.168.1.100</td><td style="text-align:center">–</td><td style="text-align:center">–</td><td style="text-align:center">–</td></tr></tbody></table><p><code>watchdog</code>端口<code>9000</code>，<code>pcp</code>端口<code>9898</code>，<code>psql</code>服务连接端口<code>9999</code></p><p>架构图<br><img src="/images/2019/08/Pgpool-II集群.png" alt="Pgpool-II集群"></p><h5 id="配置系统环境"><a href="#配置系统环境" class="headerlink" title="配置系统环境"></a>配置系统环境</h5><p>配置程序运行的用户和组为 <code>postgres</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建或修改目录权限</span></span><br><span class="line">$ chown -R postgres.postgres /etc/pgpool-II</span><br><span class="line">$ mkdir /var/run/pgpool/</span><br><span class="line">$ chown postgres.postgres /var/run/pgpool/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 修改pgpool运行用户和组为postgres</span></span><br><span class="line">$ vi /usr/lib/systemd/system/pgpool.service</span><br><span class="line"></span><br><span class="line">User=postgres</span><br><span class="line">Group=postgres</span><br></pre></td></tr></table></figure><h5 id="配置认证方式"><a href="#配置认证方式" class="headerlink" title="配置认证方式"></a>配置认证方式</h5><p>配置<code>pool_hba.conf</code>，要么都是<code>trust</code>，要么都是<code>md5</code>验证方式，这里采用了<code>md5</code>验证方式如下设置</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># <span class="string">"local"</span> <span class="keyword">is</span> <span class="keyword">for</span> Unix domain socket connections <span class="keyword">only</span></span><br><span class="line">local   <span class="keyword">all</span>         <span class="keyword">all</span>                               md5</span><br><span class="line"># IPv4 local connection<span class="variable">s:</span></span><br><span class="line">host    <span class="keyword">all</span>         <span class="keyword">all</span>         <span class="number">127.0</span>.<span class="number">0.1</span>/<span class="number">32</span>          md5</span><br><span class="line">host    <span class="keyword">all</span>         <span class="keyword">all</span>         ::<span class="number">1</span>/<span class="number">128</span>               md5</span><br><span class="line">host    <span class="keyword">all</span>         <span class="keyword">all</span>         <span class="number">0.0</span>.<span class="number">0.0</span>/<span class="number">0</span>             md5</span><br></pre></td></tr></table></figure><p>配置<code>pg_hba.conf</code>，认证方式保持一致</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># <span class="string">"local"</span> <span class="keyword">is</span> <span class="keyword">for</span> Unix domain socket connections <span class="keyword">only</span></span><br><span class="line">local   <span class="keyword">all</span>             <span class="keyword">all</span>                                     md5</span><br><span class="line"># IPv4 local connection<span class="variable">s:</span></span><br><span class="line">host    <span class="keyword">all</span>             <span class="keyword">all</span>             <span class="number">127.0</span>.<span class="number">0.1</span>/<span class="number">32</span>            md5</span><br><span class="line">host    <span class="keyword">all</span>             <span class="keyword">all</span>             <span class="number">0.0</span>.<span class="number">0.0</span>/<span class="number">0</span>               md5</span><br><span class="line"># IPv6 local connection<span class="variable">s:</span></span><br><span class="line">host    <span class="keyword">all</span>             <span class="keyword">all</span>             ::<span class="number">1</span>/<span class="number">128</span>                 md5</span><br></pre></td></tr></table></figure><p>非必要可选步骤，为了集群可扩展性，可以将复制的认证条件放宽</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># Allow replication connections from localhost, by <span class="keyword">a</span> user with the</span><br><span class="line"># replication privilege.</span><br><span class="line">local   replication     <span class="keyword">all</span>                                     md5</span><br><span class="line">host    replication     <span class="keyword">all</span>             <span class="number">127.0</span>.<span class="number">0.1</span>/<span class="number">32</span>            md5</span><br><span class="line">host    replication     <span class="keyword">all</span>             ::<span class="number">1</span>/<span class="number">128</span>                 md5</span><br><span class="line">host    replication     <span class="keyword">all</span>         <span class="number">192.168</span>.<span class="number">1.0</span>/<span class="number">24</span>        md5</span><br></pre></td></tr></table></figure><h5 id="配置pcp"><a href="#配置pcp" class="headerlink" title="配置pcp"></a>配置pcp</h5><p><code>pcp.conf</code> 配置用于<code>pgpool</code>自己登陆管理使用的，一些操作<code>pgpool</code>的工具会要求提供密码等，配置如下</p><h6 id="配置用户名密码"><a href="#配置用户名密码" class="headerlink" title="配置用户名密码"></a>配置用户名密码</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ pg_md5 postgres</span><br><span class="line">e8a48653851e28c69d0506508fb27fc5</span><br><span class="line"></span><br><span class="line">$ vi pcp.conf</span><br><span class="line"><span class="comment"># USERID:MD5PASSWD</span></span><br><span class="line">postgres:e8a48653851e28c69d0506508fb27fc5</span><br></pre></td></tr></table></figure><h6 id="添加pg数据库用户密码"><a href="#添加pg数据库用户密码" class="headerlink" title="添加pg数据库用户密码"></a>添加pg数据库用户密码</h6><p>在pgpool中添加pg数据库的用户名和密码，数据库登录用户是postgres，这里输入登录密码</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ pg_md5 -p -m -u postgres pool_passwd</span><br><span class="line">password:</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输入密码后，会生成一个pool_passwd文件</span></span><br><span class="line">$ cat pool_passwd</span><br><span class="line">postgres:md53175bce1d3201d16594cebf9d7eb3f9d</span><br></pre></td></tr></table></figure><h5 id="配置系统命令权限"><a href="#配置系统命令权限" class="headerlink" title="配置系统命令权限"></a>配置系统命令权限</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ chmod +s /sbin/ifconfig</span><br><span class="line">$ chmod +s /sbin/ip</span><br><span class="line">$ chmod +s /sbin/ifup</span><br><span class="line">$ chmod +s /bin/ping</span><br><span class="line">$ chmod +s /sbin/arping</span><br></pre></td></tr></table></figure><h5 id="配置-pgpool-conf"><a href="#配置-pgpool-conf" class="headerlink" title="配置 pgpool.conf"></a>配置 <code>pgpool.conf</code></h5><h6 id="primary"><a href="#primary" class="headerlink" title="primary"></a><code>primary</code></h6><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="keyword">cp</span> pgpool.<span class="keyword">conf</span>.sample-stream pgpool.<span class="keyword">conf</span></span><br></pre></td></tr></table></figure><p>编辑内容如下</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"># - pgpool Connection Settings -</span><br><span class="line">listen_addresses = <span class="string">'*'</span></span><br><span class="line">port = <span class="number">9999</span></span><br><span class="line"></span><br><span class="line"># - pgpool Communication Manager Connection Settings -</span><br><span class="line">pcp_listen_addresses = <span class="string">'*'</span></span><br><span class="line">pcp_port = <span class="number">9898</span></span><br><span class="line"></span><br><span class="line"># - Backend Connection Settings -</span><br><span class="line">backend_hostname0 = <span class="string">'192.168.1.3'</span></span><br><span class="line">backend_port0 = <span class="number">5432</span></span><br><span class="line">backend_weight0 = <span class="number">1</span></span><br><span class="line">backend_data_directory0 = <span class="string">'/var/lib/pgsql/10/data'</span></span><br><span class="line">backend_flag0 = <span class="string">'ALLOW_TO_FAILOVER'</span></span><br><span class="line"></span><br><span class="line">backend_hostname1 = <span class="string">'192.168.1.2'</span></span><br><span class="line">backend_port1 = <span class="number">5432</span></span><br><span class="line">backend_weight1 = <span class="number">1</span></span><br><span class="line">backend_data_directory1 = <span class="string">'/var/lib/pgsql/10/data'</span></span><br><span class="line">backend_flag1 = <span class="string">'ALLOW_TO_FAILOVER'</span></span><br><span class="line"></span><br><span class="line"># - Authentication -</span><br><span class="line">enable_pool_hba = <span class="keyword">on</span></span><br><span class="line">pool_passwd = <span class="string">'pool_passwd'</span></span><br><span class="line">authentication_timeout = <span class="number">60</span></span><br><span class="line"></span><br><span class="line"># FILE LOCATIONS</span><br><span class="line">pid_file_name = <span class="string">'/var/run/pgpool/pgpool.pid'</span></span><br><span class="line"></span><br><span class="line"># CONNECTION POOLING</span><br><span class="line">connection_cache = <span class="keyword">on</span></span><br><span class="line"></span><br><span class="line"># REPLICATION MODE</span><br><span class="line">replication_mode = off</span><br><span class="line"></span><br><span class="line"># LOAD BALANCING MODE</span><br><span class="line">load_balance_mode = <span class="keyword">on</span></span><br><span class="line"></span><br><span class="line"># MASTER/SLAVE MODE</span><br><span class="line">master_slave_mode = <span class="keyword">on</span></span><br><span class="line">master_slave_sub_mode = <span class="string">'stream'</span></span><br><span class="line"></span><br><span class="line"># - Streaming -</span><br><span class="line">sr_check_period = <span class="number">5</span></span><br><span class="line">sr_check_user = <span class="string">'repuser'</span></span><br><span class="line">sr_check_password = <span class="string">'repuser'</span></span><br><span class="line">sr_check_database = <span class="string">'postgres'</span></span><br><span class="line"></span><br><span class="line"># HEALTH CHECK</span><br><span class="line">health_check_period = <span class="number">10</span></span><br><span class="line">health_check_timeout = <span class="number">10</span></span><br><span class="line">health_check_user = <span class="string">'postgres'</span></span><br><span class="line">health_check_password = <span class="string">'postgres'</span></span><br><span class="line">health_check_database = <span class="string">'postgres'</span></span><br><span class="line"></span><br><span class="line"># FAILOVER AND FAILBACK</span><br><span class="line">failover_command = <span class="string">'/var/lib/pgsql/10/failover_stream.sh %H'</span></span><br><span class="line"></span><br><span class="line"># WATCHDOG</span><br><span class="line">use_watchdog = <span class="keyword">on</span></span><br><span class="line">wd_hostname = <span class="string">'192.168.1.3'</span></span><br><span class="line">wd_port = <span class="number">9000</span></span><br><span class="line">delegate_IP = <span class="string">'192.168.1.100'</span></span><br><span class="line">if_cmd_path = <span class="string">'/sbin'</span></span><br><span class="line">if_up_cmd = <span class="string">'ifconfig eth0:0 inet $_IP_$ netmask 255.255.255.0'</span></span><br><span class="line">if_down_cmd = <span class="string">'ifconfig eth0:0 down'</span></span><br><span class="line"></span><br><span class="line"># -- heartbeat <span class="keyword">mode</span> --</span><br><span class="line">heartbeat_destination0 = <span class="string">'192.168.1.2'</span></span><br><span class="line">heartbeat_device0 = <span class="string">'eth0'</span></span><br><span class="line"></span><br><span class="line"># - Other pgpool Connection Settings -</span><br><span class="line">other_pgpool_hostname0 = <span class="string">'192.168.1.2'</span></span><br><span class="line">other_pgpool_port0 = <span class="number">9999</span></span><br><span class="line">other_wd_port0 = <span class="number">9000</span></span><br></pre></td></tr></table></figure><h6 id="standby"><a href="#standby" class="headerlink" title="standby"></a><code>standby</code></h6><p>编辑内容如下</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"># - pgpool Connection Settings -</span><br><span class="line">listen_addresses = <span class="string">'*'</span></span><br><span class="line">port = <span class="number">9999</span></span><br><span class="line"></span><br><span class="line"># - pgpool Communication Manager Connection Settings -</span><br><span class="line">pcp_listen_addresses = <span class="string">'*'</span></span><br><span class="line">pcp_port = <span class="number">9898</span></span><br><span class="line"></span><br><span class="line"># - Backend Connection Settings -</span><br><span class="line">backend_hostname0 = <span class="string">'192.168.1.3'</span></span><br><span class="line">backend_port0 = <span class="number">5432</span></span><br><span class="line">backend_weight0 = <span class="number">1</span></span><br><span class="line">backend_data_directory0 = <span class="string">'/var/lib/pgsql/10/data'</span></span><br><span class="line">backend_flag0 = <span class="string">'ALLOW_TO_FAILOVER'</span></span><br><span class="line"></span><br><span class="line">backend_hostname1 = <span class="string">'192.168.1.2'</span></span><br><span class="line">backend_port1 = <span class="number">5432</span></span><br><span class="line">backend_weight1 = <span class="number">1</span></span><br><span class="line">backend_data_directory1 = <span class="string">'/var/lib/pgsql/10/data'</span></span><br><span class="line">backend_flag1 = <span class="string">'ALLOW_TO_FAILOVER'</span></span><br><span class="line"></span><br><span class="line"># - Authentication -</span><br><span class="line">enable_pool_hba = <span class="keyword">on</span></span><br><span class="line">pool_passwd = <span class="string">'pool_passwd'</span></span><br><span class="line">authentication_timeout = <span class="number">60</span></span><br><span class="line"></span><br><span class="line"># FILE LOCATIONS</span><br><span class="line">pid_file_name = <span class="string">'/var/run/pgpool/pgpool.pid'</span></span><br><span class="line"></span><br><span class="line"># CONNECTION POOLING</span><br><span class="line">connection_cache = <span class="keyword">on</span></span><br><span class="line"></span><br><span class="line"># REPLICATION MODE</span><br><span class="line">replication_mode = off</span><br><span class="line"></span><br><span class="line"># LOAD BALANCING MODE</span><br><span class="line">load_balance_mode = <span class="keyword">on</span></span><br><span class="line"></span><br><span class="line"># MASTER/SLAVE MODE</span><br><span class="line">master_slave_mode = <span class="keyword">on</span></span><br><span class="line">master_slave_sub_mode = <span class="string">'stream'</span></span><br><span class="line"></span><br><span class="line"># - Streaming -</span><br><span class="line">sr_check_period = <span class="number">5</span></span><br><span class="line">sr_check_user = <span class="string">'repuser'</span></span><br><span class="line">sr_check_password = <span class="string">'repuser'</span></span><br><span class="line">sr_check_database = <span class="string">'postgres'</span></span><br><span class="line"></span><br><span class="line"># HEALTH CHECK</span><br><span class="line">health_check_period = <span class="number">10</span></span><br><span class="line">health_check_timeout = <span class="number">10</span></span><br><span class="line">health_check_user = <span class="string">'postgres'</span></span><br><span class="line">health_check_password = <span class="string">'postgres'</span></span><br><span class="line">health_check_database = <span class="string">'postgres'</span></span><br><span class="line"></span><br><span class="line"># FAILOVER AND FAILBACK</span><br><span class="line">failover_command = <span class="string">'/var/lib/pgsql/10/failover_stream.sh %H'</span></span><br><span class="line"></span><br><span class="line"># WATCHDOG</span><br><span class="line">use_watchdog = <span class="keyword">on</span></span><br><span class="line">wd_hostname = <span class="string">'192.168.1.2'</span></span><br><span class="line">wd_port = <span class="number">9000</span></span><br><span class="line">delegate_IP = <span class="string">'192.168.1.100'</span></span><br><span class="line">if_cmd_path = <span class="string">'/sbin'</span></span><br><span class="line">if_up_cmd = <span class="string">'ifconfig eth0:0 inet $_IP_$ netmask 255.255.255.0'</span></span><br><span class="line">if_down_cmd = <span class="string">'ifconfig eth0:0 down'</span></span><br><span class="line"></span><br><span class="line"># -- heartbeat <span class="keyword">mode</span> --</span><br><span class="line">heartbeat_destination0 = <span class="string">'192.168.1.3'</span></span><br><span class="line">heartbeat_device0 = <span class="string">'eth0'</span></span><br><span class="line"></span><br><span class="line"># - Other pgpool Connection Settings -</span><br><span class="line">other_pgpool_hostname0 = <span class="string">'192.168.1.3'</span></span><br><span class="line">other_pgpool_port0 = <span class="number">9999</span></span><br><span class="line">other_wd_port0 = <span class="number">9000</span></span><br></pre></td></tr></table></figure><h6 id="failover-stream-sh"><a href="#failover-stream-sh" class="headerlink" title="failover_stream.sh"></a><code>failover_stream.sh</code></h6><p>配置<code>failover_stream.sh</code>脚本，内容如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">pwd</span></span><br><span class="line">/var/lib/pgsql/10</span><br><span class="line">$ touch failover_stream.sh</span><br><span class="line">$ chmod u+x failover_stream.sh</span><br><span class="line">$ cat failover_stream.sh</span><br><span class="line"><span class="meta">#! /bin/sh</span></span><br><span class="line"><span class="comment"># Failover command for streaming replication.</span></span><br><span class="line"><span class="comment"># Arguments: $1: new master hostname.</span></span><br><span class="line"></span><br><span class="line">new_master=<span class="variable">$1</span></span><br><span class="line">trigger_command=<span class="string">"/usr/bin/pg_ctl promote -D /var/lib/pgsql/10/data"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Prompte standby database.</span></span><br><span class="line">/usr/bin/ssh -T <span class="variable">$new_master</span> <span class="variable">$trigger_command</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">exit</span> 0;</span><br></pre></td></tr></table></figure><h6 id="设置主机互信"><a href="#设置主机互信" class="headerlink" title="设置主机互信"></a>设置主机互信</h6><p>配置<code>ssh</code>秘钥，分别在<code>master、slave</code>上生成<code>ssh密钥对</code>，并设置主机互信。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">$ ssh-keygen -t rsa -b 1024</span><br><span class="line">$ <span class="built_in">cd</span> ~/.ssh</span><br><span class="line">$ <span class="built_in">pwd</span></span><br><span class="line">/var/lib/pgsql/.ssh</span><br><span class="line">$ touch authorized_keys</span><br><span class="line">$ chmod 600 authorized_keys</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将master公钥id_rsa.pub内容拷贝到salve认证文件authorized_keys，同理将slave公钥id_rsa.pub内容拷贝到master认证文件authorized_keys</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试主机互信</span></span><br><span class="line"><span class="comment"># salve</span></span><br><span class="line">$ ssh postgres@192.168.1.3</span><br><span class="line"><span class="comment"># master</span></span><br><span class="line">$ ssh postgres@192.168.1.2</span><br></pre></td></tr></table></figure><p>如果这个脚本的执行目标是本地，并且ssh本地登陆没有设置免秘钥，那么这个脚本会一直卡在输入密码的阶段，这时候主备自动切换过程就阻塞了。如果 <code>pgpool</code>和 <code>postgresql</code>在同一台机器部署，需要添加本地登陆免秘钥。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">pwd</span></span><br><span class="line">/var/lib/pgsql</span><br><span class="line">$ <span class="built_in">cd</span> .ssh/</span><br><span class="line">$ ls</span><br><span class="line">authorized_keys  id_rsa  id_rsa.pub</span><br><span class="line">$ cat id_rsa.pub &gt;&gt; authorized_keys</span><br></pre></td></tr></table></figure><h6 id="启动集群"><a href="#启动集群" class="headerlink" title="启动集群"></a>启动集群</h6><p>分别启动<code>master</code>、<code>slave</code>的<code>PostgreSQL</code>服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl start postgresql-10.service</span><br></pre></td></tr></table></figure><p>分别启动各节点<code>Pgpool-II</code>服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl start pgpool.service</span><br></pre></td></tr></table></figure><h6 id="集群状态"><a href="#集群状态" class="headerlink" title="集群状态"></a>集群状态</h6><p>用<code>vip</code>登录集群，查看状态</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$ psql -p 9999 -h 192.168.1.100 -U postgres</span><br><span class="line">Password for user postgres:</span><br><span class="line">psql (10.9)</span><br><span class="line">Type "<span class="keyword">help</span><span class="string">" for help.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">postgres=# show pool_nodes;</span></span><br><span class="line"><span class="string"> node_id |   hostname    | port | status | lb_weight |  role   | select_cnt | load_balance_node | replication_delay | last_status_change</span></span><br><span class="line"><span class="string">---------+---------------+------+--------+-----------+---------+------------+-------------------+-------------------+---------------------</span></span><br><span class="line"><span class="string"> 0       | 192.168.1.3   | 5432 | up     | 0.500000  | primary | 5          | true              | 0                 | 2019-08-08 16:11:26</span></span><br><span class="line"><span class="string"> 1       | 192.168.1.2   | 5432 | up     | 0.500000  | standby | 3          | false             | 0                 | 2019-08-08 16:13:14</span></span><br><span class="line"><span class="string">(2 rows)</span></span><br></pre></td></tr></table></figure><p>至此，基于 <code>Pgpool-II</code> 中间件的 <code>PostgreSQL</code> 集群搭建完成。</p><h6 id="PCP命令"><a href="#PCP命令" class="headerlink" title="PCP命令"></a><code>PCP</code>命令</h6><ul><li><p>获取节点数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 获取192.168.1.3可见节点数</span></span><br><span class="line">$ pcp_node_count -h 192.168.1.3 -p 9898 -U postgres</span><br><span class="line">Password:</span><br><span class="line">2</span><br><span class="line"><span class="comment"># 获取192.168.1.2可见节点数</span></span><br><span class="line">$ pcp_node_count -h 192.168.1.2 -p 9898 -U postgres</span><br><span class="line">Password:</span><br><span class="line">2</span><br></pre></td></tr></table></figure></li><li><p>获取节点信息</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看节点0信息</span></span><br><span class="line">$ pcp_node_info -h 192.168.1.3 -p 9898 -U postgres 0</span><br><span class="line">Password:</span><br><span class="line">192.168.1.3 5432 2 0.500000 up primary 0 2019-08-08 16:11:26</span><br><span class="line"><span class="comment"># 查看节点1信息</span></span><br><span class="line">$ pcp_node_info -h 192.168.1.3 -p 9898 -U postgres 1</span><br><span class="line">Password:</span><br><span class="line">192.168.1.2 5432 2 0.500000 up standby 0 2019-08-08 16:13:14</span><br><span class="line">$</span><br></pre></td></tr></table></figure></li><li><p>从<code>pgpool-II</code>中脱离一个节点<br>该命令将节点<code>slave</code>从<code>pgpool-II</code>中脱离。一般如果需要维护某个数据库节点、或不希望<code>pgpool-II</code>将连接分发到该节点时，需要将该节点从<code>pgpool-II</code>中用该命令脱离。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pcp_detach_node -h 192.168.1.100 -p 9898 -U postgres -n 1</span><br></pre></td></tr></table></figure></li><li><p>为<code>pgpool-II</code>关联一个节点<br>该命令将节点<code>slave</code>关联到<code>pgpool-II</code>中。当维护结束，或新添加一个节点后，可以将节点添加到<code>pgpool-II</code>。<br>另外，如果该节点由于主机或数据库故障导致检测到数据库为启动时，即使后期服务器重新修复、数据库手工启动，也需要执行<code>attach</code>操作。同时需要注意从两个节点上观察是否节点都已经<code>attach</code>。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pcp_attach_node -h 192.168.1.100 -p 9898 -U postgres -n 1</span><br></pre></td></tr></table></figure></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;安装Pgpool-II&quot;&gt;&lt;a href=&quot;#安装Pgpool-II&quot; class=&quot;headerlink&quot; title=&quot;安装Pgpool-II&quot;&gt;&lt;/a&gt;安装Pgpool-II&lt;/h4&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Pgpool-II&lt;/code
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/categories/Ops/PostgreSQL/"/>
    
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/tags/PostgreSQL/"/>
    
      <category term="Pgpool-II" scheme="http://www.azjoy.com/tags/Pgpool-II/"/>
    
  </entry>
  
  <entry>
    <title>PostgreSQL主备切换</title>
    <link href="http://www.azjoy.com/2019/08/16/postgresql%E4%B8%BB%E5%A4%87%E5%88%87%E6%8D%A2/"/>
    <id>http://www.azjoy.com/2019/08/16/postgresql主备切换/</id>
    <published>2019-08-16T02:17:37.000Z</published>
    <updated>2024-11-05T14:28:33.541Z</updated>
    
    <content type="html"><![CDATA[<h5 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h5><blockquote><p>PostgreSQL双节点高可用构架中，如果主库宕机或挂了，高可用系统会提升备库为新主库对外继续服务。对于原主库的处理，可以删掉后重搭新备库，也可以降级为备库继续服务。</p></blockquote><h5 id="触发方式"><a href="#触发方式" class="headerlink" title="触发方式"></a>触发方式</h5><p>PostgreSQL热备<code>(HOT-Standby)</code>如果主库出现异常，备库如何激活，来替换主库工作，有2种方式可以选择</p><ul><li>备库配置文件 <code>recovery.conf</code> 中有配置项 <code>trigger_file</code> ，它是激活从库的触发文件，当它存在就会激活从库。</li><li>在备库上执行 <code>pg_ctl promote</code> 命令激活。</li></ul><h6 id="文件触发"><a href="#文件触发" class="headerlink" title="文件触发"></a>文件触发</h6><p>如果备库配置文件 <code>recovery.conf</code> 配置项 <code>trigger_file</code> 不为空，例如：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">trigger_file = <span class="string">'/var/lib/pgsql/10/data/trigger_standby'</span></span><br></pre></td></tr></table></figure><p>创建文件<code>/var/lib/pgsql/10/data/trigger_standby</code>，就可以激活备库。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ touch /var/lib/pgsql/10/data/trigger_standby</span><br></pre></td></tr></table></figure><h6 id="命令触发"><a href="#命令触发" class="headerlink" title="命令触发"></a>命令触发</h6><p>在备库上执行命令 <code>pg_ctl promote</code> 就可以激活备库。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pg_ctl promote</span><br></pre></td></tr></table></figure><h5 id="主备切换"><a href="#主备切换" class="headerlink" title="主备切换"></a>主备切换</h5><h6 id="状态标识"><a href="#状态标识" class="headerlink" title="状态标识"></a>状态标识</h6><p>数据字典表<code>pg_stat_replication</code> 、命令 <code>pg_controldata</code>、进程、自带的函数 <code>pg_is_in_recovery()</code> 都可以区别或判断实例的主备状态。</p><p>主库标识 <code>in production</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ pg_controldata | grep <span class="string">'Database cluster state'</span></span><br><span class="line">Database cluster state:               <span class="keyword">in</span> production</span><br></pre></td></tr></table></figure><p>备库标识 <code>in archive recovery</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ pg_controldata | grep <span class="string">'Database cluster state'</span></span><br><span class="line">Database cluster state:               <span class="keyword">in</span> archive recovery</span><br></pre></td></tr></table></figure><h6 id="关闭主库"><a href="#关闭主库" class="headerlink" title="关闭主库"></a>关闭主库</h6><p>在主库执行 <code>pg_ctl stop</code> 模拟主库宕机。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ pg_ctl stop</span><br><span class="line">waiting <span class="keyword">for</span> server to shut down.... <span class="keyword">done</span></span><br><span class="line">server stopped</span><br></pre></td></tr></table></figure><p>这时备库日志会报错，提示 <code>primary</code> 主库连接不上</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ tailf /var/lib/pgsql/10/data/<span class="built_in">log</span>/postgresql-Tue.log</span><br><span class="line">TCP/IP connections on port 5432?</span><br><span class="line">2019-08-06 18:00:19.399 CST [21594] FATAL:  could not connect to the primary server: could not connect to server: Connection refused</span><br><span class="line">Is the server running on host <span class="string">"192.168.1.2"</span> and accepting</span><br></pre></td></tr></table></figure><h6 id="激活备库"><a href="#激活备库" class="headerlink" title="激活备库"></a>激活备库</h6><p>在备库执行 <code>pg_ctl promote</code> 激活备库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ pg_ctl promote</span><br><span class="line">waiting <span class="keyword">for</span> server to promote.... <span class="keyword">done</span></span><br><span class="line">server promoted</span><br></pre></td></tr></table></figure><p>备库激活后可以插入数据，变为可读写。这时配置文件 <code>recovery.conf</code> 变为 <code>recovery.done</code>。</p><p>备库被激活，标识变为 <code>in production</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ pg_controldata | grep <span class="string">'Database cluster state'</span></span><br><span class="line">Database cluster state:               <span class="keyword">in</span> production</span><br><span class="line">-bash-4.2$</span><br></pre></td></tr></table></figure><p>此时备库已被激活，可以读写</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">postgres=<span class="comment"># create database test;</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">DATABASE</span></span><br></pre></td></tr></table></figure><h5 id="重做备库"><a href="#重做备库" class="headerlink" title="重做备库"></a>重做备库</h5><p>将宕机原主库修复好后，重新作为新主库的备库</p><h6 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h6><ol><li>编辑新主库认证文件 <code>pg_hba.conf</code>，新增对原主库的认证方式，如果存在可以跳过此步骤。</li></ol><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">host    <span class="keyword">all</span>             <span class="keyword">all</span>          <span class="number">192.168</span>.<span class="number">1.2</span>/<span class="number">32</span>        trust</span><br><span class="line">host    replication     repuser         <span class="number">192.168</span>.<span class="number">1.2</span>/<span class="number">32</span>        md5</span><br></pre></td></tr></table></figure><p><code>pg_ctl reload</code> 使配置生效</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ pg_ctl reload</span><br><span class="line">server signaled</span><br></pre></td></tr></table></figure><ol start="3"><li>新主库新增归档目录 <code>pg_archive</code>，如果存在可以跳过此步骤。</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ mkdir /var/lib/pgsql/10/pg_archive</span><br></pre></td></tr></table></figure><ol start="3"><li>新备库新增文件认证文件 <code>.pgpass</code> ，如果存在可以跳过此步骤。</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">pwd</span></span><br><span class="line">/var/lib/pgsql</span><br><span class="line">$ cat .pgpass</span><br><span class="line">192.168.1.3:5432:postgres:repuser:repuser</span><br></pre></td></tr></table></figure><ol start="4"><li>新备库配置文件需要确认以下参数</li></ol><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">hot_standby = <span class="keyword">on</span></span><br><span class="line">max_standby_streaming_delay = <span class="number">30</span>s</span><br><span class="line">wal_receiver_status_interval = <span class="number">10</span>s</span><br><span class="line">hot_standby_feedback = <span class="keyword">on</span></span><br></pre></td></tr></table></figure><h6 id="删除再搭建备库"><a href="#删除再搭建备库" class="headerlink" title="删除再搭建备库"></a>删除再搭建备库</h6><ol><li>删除原主库数据</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ rm -rf /var/lib/pgsql/10/data/*</span><br></pre></td></tr></table></figure><ol start="2"><li>从新主库拷贝数据</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ pg_basebackup -h 192.168.1.3 -U repuser -D /var/lib/pgsql/10/data/ -X stream -P</span><br><span class="line">Password:</span><br><span class="line">40584/40584 kB (100%), 1/1 tablespace</span><br></pre></td></tr></table></figure><ol start="3"><li>拷贝配置文件 <code>recovery.conf.sample</code> 或 <code>recovery.done</code> 为 <code>recovery.conf</code></li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp /usr/pgsql-10/share/recovery.conf.sample /var/lib/pgsql/10/data/recovery.conf</span><br></pre></td></tr></table></figure><p>更新配置文件 <code>recovery.conf</code></p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">standby_mode = <span class="keyword">on</span></span><br><span class="line">primary_conninfo = <span class="string">'host=192.168.1.3 port=5432 user=repuser password=repuser application_name=standby002'</span></span><br><span class="line">recovery_target_timeline = <span class="string">'latest'</span></span><br></pre></td></tr></table></figure><ol start="4"><li>启动新备库</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pg_ctl start</span><br></pre></td></tr></table></figure><h6 id="直接降级为备库"><a href="#直接降级为备库" class="headerlink" title="直接降级为备库"></a>直接降级为备库</h6><ol><li>拷贝配置文件 <code>recovery.conf.sample</code> 或 <code>recovery.done</code> 为 <code>recovery.conf</code></li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp /usr/pgsql-10/share/recovery.conf.sample /var/lib/pgsql/10/data/recovery.conf</span><br></pre></td></tr></table></figure><p>更新配置文件 <code>recovery.conf</code></p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">standby_mode = <span class="keyword">on</span></span><br><span class="line">primary_conninfo = <span class="string">'host=192.168.1.3 port=5432 user=repuser password=repuser application_name=standby002'</span></span><br><span class="line">recovery_target_timeline = <span class="string">'latest'</span></span><br></pre></td></tr></table></figure><ol start="2"><li>同步时间线，由于数据发生了变化，可能造成当前主备的时间线不一致，可以先用 <code>pg_rewind</code> 同步下时间线</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ pg_rewind --target-pgdata=/var/lib/pgsql/10/data --<span class="built_in">source</span>-server=<span class="string">'host=192.168.1.3 port=5432 user=postgres dbname=postgres'</span> -P</span><br><span class="line">connected to server</span><br><span class="line"><span class="built_in">source</span> and target cluster are on the same timeline</span><br><span class="line">no rewind required</span><br></pre></td></tr></table></figure><ol start="3"><li>启动新备库</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pg_ctl start</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h5 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h5&gt;&lt;blockquote&gt;
&lt;p&gt;PostgreSQL双节点高可用构架中，如果主库宕机或挂了，高可用系统会提升备库为新主库对外继续服务。对于原主库的
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/categories/Ops/PostgreSQL/"/>
    
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/tags/PostgreSQL/"/>
    
  </entry>
  
  <entry>
    <title>PostgreSQL同步复制</title>
    <link href="http://www.azjoy.com/2019/08/06/postgresql%E5%90%8C%E6%AD%A5%E5%A4%8D%E5%88%B6/"/>
    <id>http://www.azjoy.com/2019/08/06/postgresql同步复制/</id>
    <published>2019-08-06T05:56:41.000Z</published>
    <updated>2024-11-05T14:28:33.541Z</updated>
    
    <content type="html"><![CDATA[<h5 id="同步与异步区别"><a href="#同步与异步区别" class="headerlink" title="同步与异步区别"></a>同步与异步区别</h5><blockquote><p>PostgreSQL流复制默认是异步的。如果主服务器崩溃，那么某些已被提交的事务可能还没有被复制到从服务器，会导致数据丢失。数据的丢失量与故障转移时的复制延迟成比例。</p></blockquote><blockquote><p>同步复制能够保证一个事务的所有修改都能被传送到同步的从服务器。提高了事务提交标准的持久化级别，这种保护级别被称为2-safe复制。</p></blockquote><blockquote><p><code>同步复制时</code>，写事务的每次提交将一直等待，直到该提交在主服务器和从服务器上都已被写入到磁盘上的预写日志中。<code>数据会被丢失的唯一可能性是主服务器和后备服务器在同一时间都崩溃</code>。 </p></blockquote><p>一旦流复制已经被配置，配置同步复制就只需要一个额外的配置步骤，将<code>synchronous_standby_names</code>设置为一个非空值。 <code>synchronous_commit</code>也必须被设置为<code>on</code>，但由于这是默认值，通常不需要改变。<code>同步模式比异步模式增加了响应时间，用性能换取了安全</code>。</p><h5 id="设置application-name"><a href="#设置application-name" class="headerlink" title="设置application_name"></a>设置application_name</h5><p>更新备库配置文件 <code>recovery.conf</code> 中的 <code>primary_conninfo</code> 参数，默认值为 <code>walreceiver</code> ，需要指定该实例的 <code>application_name</code> 。</p><p>更新前</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">primary_conninfo = <span class="string">'host=192.168.1.2 port=5432 user=repuser password=repuser'</span></span><br></pre></td></tr></table></figure><p>主库查询复制状态为</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> application_name, client_addr, sync_state <span class="keyword">from</span> pg_stat_replication;</span><br><span class="line"> application_name |  client_addr  | sync_state</span><br><span class="line"><span class="comment">------------------+---------------+------------</span></span><br><span class="line"> walreceiver      | 192.168.1.3 | async</span><br><span class="line">(1 row)</span><br></pre></td></tr></table></figure><p>更新后</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">primary_conninfo = <span class="string">'host=192.168.1.2 port=5432 user=repuser password=repuser application_name=standby003'</span></span><br></pre></td></tr></table></figure><p>重启从库服务，主库查询复制状态为</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> application_name, client_addr, sync_state <span class="keyword">from</span> pg_stat_replication;</span><br><span class="line"> application_name |  client_addr  | sync_state</span><br><span class="line"><span class="comment">------------------+---------------+------------</span></span><br><span class="line"> standby003       | 192.168.1.3 | async</span><br><span class="line">(1 row)</span><br></pre></td></tr></table></figure><p>根据 <code>application_name</code> 参数，可以看出实例名更改已经生效为 <code>standby003</code> </p><h5 id="设置synchronous-standby-names"><a href="#设置synchronous-standby-names" class="headerlink" title="设置synchronous_standby_names"></a>设置synchronous_standby_names</h5><p>更新主服务器的<code>postgresql.conf</code>文件</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">synchronous_standby_names = <span class="string">'standby003'</span></span><br></pre></td></tr></table></figure><p>重启主库服务，主库查询复制状态为</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> application_name, client_addr, sync_state <span class="keyword">from</span> pg_stat_replication;</span><br><span class="line"> application_name |  client_addr  | sync_state</span><br><span class="line"><span class="comment">------------------+---------------+------------</span></span><br><span class="line"> standby003       | 192.168.1.3 | sync</span><br><span class="line">(1 row)</span><br></pre></td></tr></table></figure><p>根据<code>sync_state</code>参数，可以看出复制模式是同步状态。</p><h5 id="synchronous-standby-names"><a href="#synchronous-standby-names" class="headerlink" title="synchronous_standby_names"></a>synchronous_standby_names</h5><p>同步复制支持一个或多个同步从服务器，提交的事务会一直等待，直到所有同步从服务器都确认收到了数据为止。该参数支持三种配置方式。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[FIRST] num_sync ( standby_name [, ...] )</span><br></pre></td></tr></table></figure><p>关键字<code>FIRST</code>加上<code>num_sync</code>，指定基于优先级的同步复制。 例如，<code>FIRST 3 (s1, s2, s3, s4)</code> 名称出现在列表前面<code>3</code>个的从服务器被赋予更高的优先级，并将被视为同步复制。在列表后面出现的其它从服务器为潜在的同步从服务器。如果当前的某些同步从服务器断开连接，列表后面的从服务器，优先级将被提高，从而转变为同步从服务器，其中关键字<code>FIRST</code>是可选的。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ANY num_sync ( standby_name [, ...] )</span><br></pre></td></tr></table></figure><p>关键字<code>ANY</code>加上<code>num_sync</code>，指定基于数量的同步复制，例如，<code>ANY 3 (s1, s2, s3, s4)</code> 每个提交至少被<code>s1</code>、<code>s2</code>、 <code>s3</code> 和<code>s4</code>中的任意三个从服务器同步复制。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">standby_name [, ...]</span><br></pre></td></tr></table></figure><p>与第一个使用<code>FIRST</code>和<code>num_sync</code>等于<code>1</code>的语法相同。例如，<code>FIRST 1 (s1, s2)</code>和<code>s1, s2</code>具有相同的含义：选择<code>s1</code>或<code>s2</code>作为同步服务器。特殊项<code>*</code>匹配任何备用服务器名称。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h5 id=&quot;同步与异步区别&quot;&gt;&lt;a href=&quot;#同步与异步区别&quot; class=&quot;headerlink&quot; title=&quot;同步与异步区别&quot;&gt;&lt;/a&gt;同步与异步区别&lt;/h5&gt;&lt;blockquote&gt;
&lt;p&gt;PostgreSQL流复制默认是异步的。如果主服务器崩溃，那么某些已被提交
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/categories/Ops/PostgreSQL/"/>
    
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/tags/PostgreSQL/"/>
    
  </entry>
  
  <entry>
    <title>PostgreSQL流复制热备</title>
    <link href="http://www.azjoy.com/2019/08/06/postgresql%E6%B5%81%E5%A4%8D%E5%88%B6/"/>
    <id>http://www.azjoy.com/2019/08/06/postgresql流复制/</id>
    <published>2019-08-06T05:48:15.000Z</published>
    <updated>2024-11-05T14:28:33.541Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>PostgreSQL在9.0之后引入了主从的流复制机制，从服务器通过<code>tcp</code>流从主服务器中同步相应的数据。流复制允许备库更新，同时也能提供只读服务，流复制默认是异步的。</p></blockquote><h5 id="实例准备"><a href="#实例准备" class="headerlink" title="实例准备"></a>实例准备</h5><table><thead><tr><th style="text-align:center">role</th><th style="text-align:center">ip</th><th style="text-align:center">port</th><th style="text-align:center">version</th></tr></thead><tbody><tr><td style="text-align:center">master</td><td style="text-align:center">192.168.1.2</td><td style="text-align:center">5432</td><td style="text-align:center">10.9</td></tr><tr><td style="text-align:center">slave</td><td style="text-align:center">192.168.1.3</td><td style="text-align:center">5432</td><td style="text-align:center">10.9</td></tr></tbody></table><p>架构图<br><img src="/images/2019/08/PostgreSQL流复制.png" alt="PostgreSQL流复制"></p><h5 id="配置主库"><a href="#配置主库" class="headerlink" title="配置主库"></a>配置主库</h5><h6 id="创建复制用户"><a href="#创建复制用户" class="headerlink" title="创建复制用户"></a>创建复制用户</h6><p>初始化数据库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ rm -rf /var/lib/pgsql/10/data/*</span><br><span class="line">$ /usr/pgsql-10/bin/postgresql-10-setup initdb</span><br><span class="line">Initializing database ... OK</span><br></pre></td></tr></table></figure><p>启动数据库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl start postgresql-10</span><br></pre></td></tr></table></figure><p>创建用户repuser，并赋予复制和登录的权限。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ su - postgres</span><br><span class="line">$ psql</span><br><span class="line">psql (10.9)</span><br><span class="line">Type "<span class="keyword">help</span><span class="string">" for help.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">postgres=# CREATE USER repuser REPLICATION LOGIN CONNECTION LIMIT 2 ENCRYPTED PASSWORD 'repuser';</span></span><br><span class="line"><span class="string">CREATE ROLE</span></span><br><span class="line"><span class="string">postgres=# \q</span></span><br></pre></td></tr></table></figure><h6 id="更新认证方式"><a href="#更新认证方式" class="headerlink" title="更新认证方式"></a>更新认证方式</h6><p>编辑配置文件<code>pg_hba.conf</code>，新增</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">host    all             all          192.168.1.3/32        trust</span><br><span class="line">host    replication     repuser         192.168.1.3/32        md5</span><br></pre></td></tr></table></figure><h6 id="更新PostgreSQL配置"><a href="#更新PostgreSQL配置" class="headerlink" title="更新PostgreSQL配置"></a>更新PostgreSQL配置</h6><p>编辑配置文件<code>postgresql.conf</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">listen_addresses = <span class="string">'*'</span></span><br><span class="line">wal_log_hints = on</span><br><span class="line">archive_mode = on</span><br><span class="line">archive_command = <span class="string">'cp %p /var/lib/pgsql/10/pg_archive/%f'</span></span><br><span class="line">wal_keep_segments = 64</span><br></pre></td></tr></table></figure><p>新增归档目录 <code>pg_archive</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ mkdir /var/lib/pgsql/10/pg_archive</span><br></pre></td></tr></table></figure><h6 id="重启主库"><a href="#重启主库" class="headerlink" title="重启主库"></a>重启主库</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl restart postgresql-10</span><br></pre></td></tr></table></figure><h5 id="配置从库"><a href="#配置从库" class="headerlink" title="配置从库"></a>配置从库</h5><h6 id="初始化数据库"><a href="#初始化数据库" class="headerlink" title="初始化数据库"></a>初始化数据库</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ rm -rf /var/lib/pgsql/10/data/*</span><br><span class="line">$ /usr/pgsql-10/bin/postgresql-10-setup initdb</span><br><span class="line">Initializing database ... OK</span><br></pre></td></tr></table></figure><h6 id="新增文件-pgpass"><a href="#新增文件-pgpass" class="headerlink" title="新增文件.pgpass"></a>新增文件<code>.pgpass</code></h6><p>在<code>postgres</code>用户根目录下创建<code>.pgpass</code>，并追加认证信息</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ touch .pgpass</span><br><span class="line">$ chmod 0600 .pgpass</span><br><span class="line">$ cat .pgpass</span><br><span class="line">192.168.1.2:5432:postgres:repuser:repuser</span><br></pre></td></tr></table></figure><h6 id="从主节点拷贝数据到从节点"><a href="#从主节点拷贝数据到从节点" class="headerlink" title="从主节点拷贝数据到从节点"></a>从主节点拷贝数据到从节点</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ su - postgres</span><br><span class="line">Last login: Mon Aug  5 17:27:20 CST 2019 on pts/0</span><br><span class="line">$ pg_basebackup -h 192.168.1.2 -U repuser -D /var/lib/pgsql/10/data/ -X stream -P</span><br><span class="line">Password:</span><br><span class="line">24308/24308 kB (100%), 1/1 tablespace</span><br></pre></td></tr></table></figure><h6 id="配置recovery-conf"><a href="#配置recovery-conf" class="headerlink" title="配置recovery.conf"></a>配置recovery.conf</h6><p>拷贝配置文件<code>recovery.conf.sample</code>为<code>recovery.conf</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ cp /usr/pgsql-10/share/recovery.conf.sample /var/lib/pgsql/10/data/recovery.conf</span><br></pre></td></tr></table></figure><p>更新配置文件recovery.conf</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">standby_mode = on</span><br><span class="line">primary_conninfo = <span class="string">'host=192.168.1.2 port=5432 user=repuser password=repuser'</span></span><br><span class="line">recovery_target_timeline = <span class="string">'latest'</span></span><br></pre></td></tr></table></figure><h6 id="配置postgresql-conf"><a href="#配置postgresql-conf" class="headerlink" title="配置postgresql.conf"></a>配置postgresql.conf</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">listen_addresses = <span class="string">'*'</span></span><br><span class="line">wal_log_hints = on</span><br><span class="line">wal_keep_segments = 64</span><br><span class="line">hot_standby_feedback = on</span><br></pre></td></tr></table></figure><h6 id="重启从库"><a href="#重启从库" class="headerlink" title="重启从库"></a>重启从库</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl restart postgresql-10</span><br></pre></td></tr></table></figure><h5 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h5><p>在主节点上命令验证</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">postgres=<span class="comment"># select application_name, client_addr, sync_state from pg_stat_replication;</span></span><br><span class="line"> application_name |  client_addr  | sync_state</span><br><span class="line"><span class="comment">------------------+---------------+------------</span></span><br><span class="line"> walreceiver      | 192.168.1.3 | async</span><br><span class="line">(1 row)</span><br></pre></td></tr></table></figure><p>说明<code>192.168.1.3</code>是从服务器，在接收流，而且是异步流复制。</p><p>可以分别在主、从节点上运行 <code>ps -ef | grep postgres</code>来查看进程验证</p><p>主服务器有一个 <code>wal sender</code> 进程</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ ps -ef | grep postgres</span><br><span class="line">postgres 20645 19653  0 22:14 ?        00:00:00 postgres: wal sender process repuser 192.168.1.3(59176) streaming 0/70029E0</span><br></pre></td></tr></table></figure><p>从服务器有一个 <code>wal receiver</code> 进程</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ ps -ef | grep postgres</span><br><span class="line">postgres 18019 18012  0 22:14 ?        00:00:00 postgres: wal receiver process   streaming 0/7002A18</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;PostgreSQL在9.0之后引入了主从的流复制机制，从服务器通过&lt;code&gt;tcp&lt;/code&gt;流从主服务器中同步相应的数据。流复制允许备库更新，同时也能提供只读服务，流复制默认是异步的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5 id=&quot;实
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/categories/Ops/PostgreSQL/"/>
    
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/tags/PostgreSQL/"/>
    
  </entry>
  
  <entry>
    <title>CentOS7安装PostgreSQL</title>
    <link href="http://www.azjoy.com/2019/08/05/postgresql/"/>
    <id>http://www.azjoy.com/2019/08/05/postgresql/</id>
    <published>2019-08-05T05:49:54.000Z</published>
    <updated>2024-11-05T14:28:33.540Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>关于PostgreSQL与MySQL的比较，可以参考 <a href="https://www.zhihu.com/question/20010554" target="_blank" rel="noopener">PostgreSQL 与 MySQL 相比，优势何在？</a></p></blockquote><h5 id="环境"><a href="#环境" class="headerlink" title="环境"></a>环境</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">CentOS Linux release 7.4.1708 (Core)</span><br><span class="line">PostgreSQL 10.9</span><br></pre></td></tr></table></figure><h5 id="系统设置"><a href="#系统设置" class="headerlink" title="系统设置"></a>系统设置</h5><h6 id="修改最大用户进程数"><a href="#修改最大用户进程数" class="headerlink" title="修改最大用户进程数"></a>修改最大用户进程数</h6><p><code>vi /etc/security/limits.conf</code>添加</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">*          soft     nproc          65535</span><br><span class="line">*          hard     nproc          65535</span><br><span class="line">*          soft     nofile         65535</span><br><span class="line">*          hard     nofile         65535</span><br></pre></td></tr></table></figure><h6 id="修改文件句柄数"><a href="#修改文件句柄数" class="headerlink" title="修改文件句柄数"></a>修改文件句柄数</h6><p><code>vi  /etc/sysctl.conf</code>添加</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fs.file-max = 65535</span><br></pre></td></tr></table></figure><h5 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h5><p>不同系统，不同版本安装都有差异。在官网选择对应的系统环境和安装版本，页面会给出对应的安装指导，<a href="https://www.postgresql.org/download/linux/redhat/" target="_blank" rel="noopener">官方指导</a><br>下面按照官方指导一步一步安装</p><h6 id="安装PostgreSQL仓库"><a href="#安装PostgreSQL仓库" class="headerlink" title="安装PostgreSQL仓库"></a>安装PostgreSQL仓库</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm</span><br></pre></td></tr></table></figure><h6 id="安装客户端"><a href="#安装客户端" class="headerlink" title="安装客户端"></a>安装客户端</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install postgresql10</span><br></pre></td></tr></table></figure><h6 id="安装服务端"><a href="#安装服务端" class="headerlink" title="安装服务端"></a>安装服务端</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install postgresql10-server</span><br></pre></td></tr></table></figure><h6 id="加入开机启动项"><a href="#加入开机启动项" class="headerlink" title="加入开机启动项"></a>加入开机启动项</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl <span class="built_in">enable</span> postgresql-10.service</span><br></pre></td></tr></table></figure><h6 id="初始化数据库"><a href="#初始化数据库" class="headerlink" title="初始化数据库"></a>初始化数据库</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ /usr/pgsql-10/bin/postgresql-10-setup initdb</span><br></pre></td></tr></table></figure><h6 id="启动数据库服务"><a href="#启动数据库服务" class="headerlink" title="启动数据库服务"></a>启动数据库服务</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl start postgresql-10</span><br></pre></td></tr></table></figure><h6 id="检查运行状态"><a href="#检查运行状态" class="headerlink" title="检查运行状态"></a>检查运行状态</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl status postgresql-10</span><br><span class="line">● postgresql-10.service - PostgreSQL 10 database server</span><br><span class="line">   Loaded: loaded </span><br><span class="line"> ... ... ...</span><br><span class="line">Aug 02 17:59:44 wytestssssfat02042202 systemd[1]: Started PostgreSQL 10 database server.</span><br><span class="line">Hint: Some lines were ellipsized, use -l to show <span class="keyword">in</span> full.</span><br></pre></td></tr></table></figure><h6 id="设置用户密码"><a href="#设置用户密码" class="headerlink" title="设置用户密码"></a>设置用户密码</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$ su - postgres</span><br><span class="line">Last login: Mon Aug  5 11:09:14 CST 2019 on pts/0</span><br><span class="line"><span class="comment"># 默认用户postgres</span></span><br><span class="line">-bash-4.2$ psql</span><br><span class="line">psql (10.9)</span><br><span class="line">Type <span class="string">"help"</span> <span class="keyword">for</span> <span class="built_in">help</span>.</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># \password</span></span><br><span class="line"><span class="comment"># 设置密码为 '******'</span></span><br><span class="line">Enter new password:</span><br><span class="line">Enter it again:</span><br><span class="line"><span class="comment"># 保存退出</span></span><br><span class="line">postgres=<span class="comment"># \q</span></span><br></pre></td></tr></table></figure><h6 id="修改监听地址"><a href="#修改监听地址" class="headerlink" title="修改监听地址"></a>修改监听地址</h6><p>编辑配置文件<code>postgresql.conf</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">listen_addresses = <span class="string">'*'</span></span><br><span class="line"><span class="comment">#listen_addresses = 'localhost'</span></span><br></pre></td></tr></table></figure><h6 id="修改客户端认证方式"><a href="#修改客户端认证方式" class="headerlink" title="修改客户端认证方式"></a>修改客户端认证方式</h6><p>编辑配置文件<code>pg_hba.conf</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># IPv4 local connections:</span></span><br><span class="line">host    all             all             127.0.0.1/32            ident</span><br><span class="line">host    all             all             192.168.1.0/24          md5</span><br></pre></td></tr></table></figure><blockquote><p>配置文件<code>pg_hba.conf</code>，配置项说明，可以参考 <a href="https://scarletsky.github.io/2017/04/26/client-authentication-in-postgresql/" target="_blank" rel="noopener">PostgreSQL 中的客户端认证</a></p></blockquote><ul><li><code>TYPE</code> 连接类型，表示允许用哪些方式连接数据库，它允许以下几个值：<ul><li><code>local</code>通过 Unix socket 的方式连接。</li><li><code>host</code>通过 TCP/IP 的方式连接，它能匹配 SSL 和 non-SSL 连接。</li><li><code>hostssl</code>只允许 SSL 连接。</li><li><code>hostnossl</code>只允许 non-SSL 连接。</li></ul></li><li><code>DATABASE</code> 可连接的数据库，它有以下几个特殊值：<ul><li><code>all</code> 匹配所有数据库。</li><li><code>sameuser</code> 可连接和用户名相同的数据库。</li><li><code>samerole</code> 可连接和角色名相同的数据库。</li><li><code>replication</code> 允许复制连接，用于集群环境下的数据库同步。 除了上面这些特殊值之外，我们可以写特定的数据库，可以用逗号 (,) 来分割多个数据库。</li></ul></li><li><code>USER</code> 可连接数据库的用户，值有三种写法：<ul><li><code>all</code> 匹配所有用户。</li><li>特定数据库用户名。</li><li>特定数据库用户组，需要在前面加上 <code>+</code> (如：<code>+admin</code>)。</li></ul></li><li><code>ADDRESS</code> 可连接数据库的地址，有以下几种形式：<ul><li><code>all</code> 匹配所有 IP 地址。</li><li><code>samehost</code> 匹配该服务器的 IP 地址。</li><li><code>samenet</code> 匹配该服务器子网下的 IP 地址。</li><li><code>ipaddress/netmask</code> (如：172.20.143.89⁄32)，支持 IPv4 与 IPv6。</li><li>如果上面几种形式都匹配不上，就会被当成是 <code>hostname</code>。 注意: 只有 host, hostssl, hostnossl 会应用个字段。</li></ul></li><li><code>METHOD</code> 连接数据库时的认证方式，常见的有几个特殊值：<ul><li><code>trust</code> 无条件通过认证。</li><li><code>reject</code> 无条件拒绝认证。</li><li><code>md5</code> 用 md5 加密密码进行认证。</li><li><code>password</code> 用明文密码进行认证，不建议在不信任的网络中使用。</li><li><code>ident</code> 从一个 ident 服务器 (RFC1413) 获得客户端的操作系统用户名并且用它作为被允许的数据库用户名来认证，只能用在 TCP/IP 的类型中 (即 host, hostssl, hostnossl)。</li><li><code>peer</code> 从内核获得客户端的操作系统用户名并把它用作被允许的数据库用户名来认证，只能用于本地连接 (即 local)。</li><li>其他特殊值可以在 官方文档 中查阅。 简单来说，ident 和 peer 都要求客户端操作系统中存在对应的用户。 注意: 上面列举的只有 <code>md5</code> 和 <code>password</code> 是需要密码的，其他方式都不需要输入密码认证。</li></ul></li></ul><h6 id="重启服务"><a href="#重启服务" class="headerlink" title="重启服务"></a>重启服务</h6><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ systemctl restart postgresql-10</span><br></pre></td></tr></table></figure><h6 id="连接测试"><a href="#连接测试" class="headerlink" title="连接测试"></a>连接测试</h6><p><code>psql</code> 是 PostgreSQL 的客户端程序，要连接 PostgreSQL 数据库，我们需要指定以下内容：</p><ul><li><code>-d</code> or <code>--dbname</code> 数据库名</li><li><code>-h</code> or <code>--host</code> 主机名</li><li><code>-p</code> or <code>--port</code> 端口号，默认5432 端口</li><li><code>-U</code> or <code>--username</code> 用户名 </li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ psql -h 192.168.1.2 -U postgres</span><br><span class="line">Password <span class="keyword">for</span> user postgres:</span><br><span class="line">psql (10.9)</span><br><span class="line">Type <span class="string">"help"</span> <span class="keyword">for</span> <span class="built_in">help</span>.</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># \q</span></span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;关于PostgreSQL与MySQL的比较，可以参考 &lt;a href=&quot;https://www.zhihu.com/question/20010554&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PostgreSQL 与 MyS
      
    
    </summary>
    
      <category term="Ops" scheme="http://www.azjoy.com/categories/Ops/"/>
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/categories/Ops/PostgreSQL/"/>
    
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/tags/PostgreSQL/"/>
    
  </entry>
  
  <entry>
    <title>写一个Kong前端URI通配符传递插件</title>
    <link href="http://www.azjoy.com/2019/01/19/kong-plugin-upstream-url-penetrate/"/>
    <id>http://www.azjoy.com/2019/01/19/kong-plugin-upstream-url-penetrate/</id>
    <published>2019-01-19T07:33:18.000Z</published>
    <updated>2024-11-06T11:52:49.370Z</updated>
    
    <content type="html"><![CDATA[<h5 id="场景"><a href="#场景" class="headerlink" title="场景"></a>场景</h5><p>如果Kong前端uris配置正则表达式，虽然Kong能够捕获正则表达式的值，但是它不会用该值去替换上游upstream_url里面预设变量，例如：<br>前端uris配置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/api/testmap/(?&lt;id&gt;\d+)</span><br></pre></td></tr></table></figure><p>上游upstream_url配置类似这样</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://192.168.1.3:10000/api/testmap/&#123;id&#125;</span><br></pre></td></tr></table></figure><p>前端发起请求</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">curl http://10.0.0.111/api/testmap/1/</span><br><span class="line">curl http://10.0.0.111/api/testmap/2/</span><br><span class="line">curl http://10.0.0.111/api/testmap/3/</span><br></pre></td></tr></table></figure><p>上游upstream_url的值都是</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://192.168.1.3:10000/api/testmap/&#123;id&#125;</span><br></pre></td></tr></table></figure><h5 id="解决思路"><a href="#解决思路" class="headerlink" title="解决思路"></a>解决思路</h5><p>既然Kong能够捕获正则表达式的值，只需要用Kong捕获的值，对upstream_url进行正则替换即可。</p><h6 id="获取"><a href="#获取" class="headerlink" title="获取"></a>获取</h6><p>如果，Kong前端uris配置为</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/api/testmap/(?&lt;id&gt;\d+)</span><br></pre></td></tr></table></figure><p>那么，Kong将捕获的值，以table类型存储在变量<code>ngx.ctx.router_matches.uri_captures</code>中。<br>如果，前端发起的请求为</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl http://10.0.0.111/api/testmap/2/</span><br></pre></td></tr></table></figure><p>那么<code>ngx.ctx.router_matches.uri_captures</code>变量的值为</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="string">"2"</span>, id = <span class="string">"2"</span>&#125;</span><br></pre></td></tr></table></figure><p>需要注意的是，如果uris里面没有正则表达式，那么变量<code>ngx.ctx.router_matches.uri_captures</code>的值为<code>nil</code>，而不是一个<code>table</code>。</p><h6 id="替换"><a href="#替换" class="headerlink" title="替换"></a>替换</h6><p>定义upstream_url里面的变量时候，需要有一定的特征。<br>例如，uris定义为</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/api/testmap/(?&lt;id&gt;\d+)</span><br></pre></td></tr></table></figure><p>那么，upstream_url定义的时候，就将<code>id</code>用特需符号标识，比如用<code>{}</code>来标识这是一个待替换的变量</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://192.168.1.3:10000/api/testmap/&#123;id&#125;</span><br></pre></td></tr></table></figure><p>做一个<code>table</code>的循环，<code>k</code>是待替换的变量，<code>v</code>是最后替换的值。</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> k, v <span class="keyword">in</span> <span class="built_in">pairs</span>(uri_captures) <span class="keyword">do</span></span><br><span class="line">  <span class="keyword">if</span> <span class="built_in">type</span>(k) == <span class="string">"string"</span> <span class="keyword">then</span></span><br><span class="line">    <span class="keyword">local</span> tag_string = <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">"&#123;%s&#125;"</span>, k)</span><br><span class="line">    upstream_uri = <span class="built_in">string</span>.<span class="built_in">gsub</span>(upstream_uri, tag_string, v)</span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><h6 id="重置"><a href="#重置" class="headerlink" title="重置"></a>重置</h6><p>变量替换好后，需要重置<code>upstream_uri</code>的值。有个坑就是，Kong版本大于等于<code>0.11.0</code>后，不支持用<code>ngx.req.set_uri()</code>重置<code>upstream_uri</code>的值，需要用<code>ngx.var.upstream_uri</code>重置<code>upstream_uri</code>。<br>官方更新日志是这么说的：</p><blockquote><p>The upstream URI is now determined via the Nginx $upstream_uri variable. Custom plugins using the ngx.req.set_uri() API will not be taken into consideration anymore. One must now set the ngx.var.upstream_uri variable from the Lua land.</p></blockquote><h5 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h5><p>插件的核心代码<code>handler.lua</code></p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> BasePlugin = <span class="built_in">require</span> <span class="string">"kong.plugins.base_plugin"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> UpstreamUrlPenetrateHandler = BasePlugin:extend()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UpstreamUrlPenetrateHandler:new</span><span class="params">()</span></span></span><br><span class="line">  UpstreamUrlPenetrateHandler.super.new(self, <span class="string">"upstream-url-penetrate"</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UpstreamUrlPenetrateHandler:access</span><span class="params">()</span></span></span><br><span class="line">  UpstreamUrlPenetrateHandler.super.access(self)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">local</span> uri_captures = ngx.ctx.router_matches.uri_captures</span><br><span class="line">  <span class="keyword">local</span> upstream_uri = ngx.var.upstream_uri</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> <span class="built_in">type</span>(uri_captures) == <span class="string">"table"</span> <span class="keyword">then</span></span><br><span class="line">    <span class="keyword">for</span> k, v <span class="keyword">in</span> <span class="built_in">pairs</span>(uri_captures) <span class="keyword">do</span></span><br><span class="line">      <span class="keyword">if</span> <span class="built_in">type</span>(k) == <span class="string">"string"</span> <span class="keyword">then</span></span><br><span class="line">        <span class="keyword">local</span> tag_string = <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">"&#123;%s&#125;"</span>, k)</span><br><span class="line">        upstream_uri = <span class="built_in">string</span>.<span class="built_in">gsub</span>(upstream_uri, tag_string, v)</span><br><span class="line">      <span class="keyword">end</span></span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line">    ngx.var.upstream_uri = upstream_uri</span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">UpstreamUrlPenetrateHandler.PRIORITY = <span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> UpstreamUrlPenetrateHandler</span><br></pre></td></tr></table></figure><blockquote><p>该插件也适用于uris包含多个正则表达式的场景。<br>插件GitHub开源地址<a href="https://github.com/xiaomastack/kong-plugin-upstream-url-penetrate" target="_blank" rel="noopener">kong-plugin-upstream-url-penetrate</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h5 id=&quot;场景&quot;&gt;&lt;a href=&quot;#场景&quot; class=&quot;headerlink&quot; title=&quot;场景&quot;&gt;&lt;/a&gt;场景&lt;/h5&gt;&lt;p&gt;如果Kong前端uris配置正则表达式，虽然Kong能够捕获正则表达式的值，但是它不会用该值去替换上游upstream_url里面预设变量，
      
    
    </summary>
    
      <category term="Dev" scheme="http://www.azjoy.com/categories/Dev/"/>
    
      <category term="Tools" scheme="http://www.azjoy.com/categories/Dev/Tools/"/>
    
    
      <category term="微服务" scheme="http://www.azjoy.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Kong" scheme="http://www.azjoy.com/tags/Kong/"/>
    
  </entry>
  
  <entry>
    <title>写一个Kong上游转发签名认证插件</title>
    <link href="http://www.azjoy.com/2019/01/17/kong-plugin-upstream-auth-signature/"/>
    <id>http://www.azjoy.com/2019/01/17/kong-plugin-upstream-auth-signature/</id>
    <published>2019-01-17T05:39:31.000Z</published>
    <updated>2024-11-06T11:52:49.362Z</updated>
    
    <content type="html"><![CDATA[<h5 id="场景"><a href="#场景" class="headerlink" title="场景"></a>场景</h5><p>微服务API接口认证的方式有很多，Kong官网关于上游API认证的插件只有一个<a href="https://docs.konghq.com/hub/revolution_systems/upstream-auth-basic/" title="Upstream HTTP Basic Authentication" target="_blank" rel="noopener">Upstream HTTP Basic Authentication</a>，是基于基本的用户名和密码认证的。其它的都是前端认证认证插件。如果后端微服务API采用的是<code>HMAC-SHA256</code>签名认证，就接入不了了，怎么办？首先Google了一番，没有找到此类插件。那么问题来了，如果要用这种认证方式接入Kong，那么就要自己写插件造轮子了。</p><h5 id="相关知识"><a href="#相关知识" class="headerlink" title="相关知识"></a>相关知识</h5><p>先看看Lua的基本语法，由于是菜鸟，就去看了<a href="http://www.runoob.com/lua/lua-tutorial.html" target="_blank" rel="noopener">Lua 菜鸟教程</a>。<br>再熟悉学习了Kong的插件大致怎么写，大概看了以下文章:<br><a href="https://www.cnblogs.com/huxianglin/p/7263964.html" target="_blank" rel="noopener">kong插件官方文档翻译</a><br><a href="https://www.lijiaocn.com/%E9%A1%B9%E7%9B%AE/2018/11/09/kong-features-07-write-plugins.html#%E5%88%9B%E5%BB%BA%E6%95%B0%E6%8D%AE%E5%BA%93%E8%A1%A8%E4%B8%8E%E6%95%B0%E6%8D%AE%E5%BA%93%E6%A3%80%E6%9F%A5migrations" target="_blank" rel="noopener">API网关Kong学习笔记（十一）：自己动手写一个插件</a><br><a href="http://www.yuxiumin.com/2018/07/09/kong-api-gateway-plugin/" target="_blank" rel="noopener">Kong Api网关简介(二) 插件</a><br><a href="https://zhuanlan.zhihu.com/p/52402537" target="_blank" rel="noopener">kong 网关插件快速开发指南</a><br>好了，看过这几篇文章后，大概了解了Kong的插件是一个怎样的套路了。<br>还需要了解ngx模块里面的<code>var、ctx、req</code>等内置变量和方法。</p><h5 id="签名认证过程"><a href="#签名认证过程" class="headerlink" title="签名认证过程"></a>签名认证过程</h5><p>API的签名认证用的是Python的<code>httpsig</code>模块，签名算法采用<code>HMAC-SHA256</code>。由于是相对于后端来说，Kong的转发相当于客户端请求，要完成这个插件就需要了解<code>HMAC-SHA256</code>签名的详细认证过程。<br>通过阅读和调试<code>httpsig</code>模块相关代码，签名的过程是以下这样的:</p><h6 id="客户端获取自己的公私钥对"><a href="#客户端获取自己的公私钥对" class="headerlink" title="客户端获取自己的公私钥对"></a>客户端获取自己的公私钥对</h6><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> key = conf.key</span><br><span class="line"><span class="keyword">local</span> secret = conf.secret</span><br></pre></td></tr></table></figure><h6 id="计算加密消息"><a href="#计算加密消息" class="headerlink" title="计算加密消息"></a>计算加密消息</h6><p>加密的消息一般采用当前时间<code>date</code></p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> <span class="built_in">date</span> = <span class="built_in">os</span>.<span class="built_in">date</span>(<span class="string">"%Y-%m-%d %H:%M:%S"</span>, <span class="built_in">os</span>.<span class="built_in">time</span>())</span><br></pre></td></tr></table></figure><h6 id="加密"><a href="#加密" class="headerlink" title="加密"></a>加密</h6><p>将自己的密钥和加密消息，用<code>HMAC-SHA256</code>算法进行签名。</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> signature_salt = <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">"date: %s"</span>, <span class="built_in">date</span>)</span><br><span class="line"><span class="keyword">local</span> signature = openssl_hmac.new(secret, <span class="string">"sha256"</span>):final(signature_salt)</span><br></pre></td></tr></table></figure><h6 id="组装和注入"><a href="#组装和注入" class="headerlink" title="组装和注入"></a>组装和注入</h6><p>将<code>date</code>，<code>x-api-key</code>，<code>authorization</code>三个值注入到HTTP头字段<code>HTTP header fields</code>。其中<code>authorization</code>字段格式有一定的要求，必须包含公钥<code>Signature keyId</code>、签名算法<code>algorithm</code>、签名<code>signature</code>，类似这样的字符串</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'authorization'</span>: <span class="string">u'Signature keyId="d1eaffbcb543r357",algorithm="hmac-sha256",signature="pp18H0zla/kvKZP/jAKCTkgqiVO92RRAqTsybZJao/o="'</span></span><br></pre></td></tr></table></figure><p>组装<code>authorization</code>字段，<code>signature</code>的值需要对原始签名的二进制数据进行Base64编码。</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> authorization = <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">'Signature keyId="%s",algorithm="hmac-sha256",signature="%s"'</span>, key, ngx.encode_base64(signature))</span><br></pre></td></tr></table></figure><p>用<code>ngx.req.set_header</code>注入相应值。其中的<code>date</code>字段值是签名时用的加密消息，<code>x-api-key</code>是自己的公钥。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">req_set_header(&quot;date&quot;, date)</span><br><span class="line">req_set_header(&quot;x-api-key&quot;, key)</span><br><span class="line">req_set_header(&quot;authorization&quot;, authorization)</span><br></pre></td></tr></table></figure><p>最后得到的HTTP头字段类似这样</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">'accept-encoding'</span>: <span class="string">'gzip, deflate'</span>, <span class="string">'accept'</span>: <span class="string">'*/*'</span>, \</span><br><span class="line">  <span class="string">'user-agent'</span>: <span class="string">'xxxxxx/xxx'</span>, <span class="string">'connection'</span>: <span class="string">'keep-alive'</span>, \</span><br><span class="line">  <span class="string">'x-api-key'</span>: <span class="string">'d1eaffbcb543r357'</span>, <span class="string">'date'</span>: <span class="string">'2019-01-16 17:22:46'</span>,\</span><br><span class="line">  <span class="string">'content-type'</span>: <span class="string">'application/json'</span>, \</span><br><span class="line">  <span class="string">'authorization'</span>: <span class="string">u'Signature keyId="d1eaffbcb543r357",algorithm="hmac-sha256",signature="pp18H0zla/kvKZP/jAKCTkgqiVO92RRAqTsybZJao/o="'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h6 id="认证"><a href="#认证" class="headerlink" title="认证"></a>认证</h6><p>后端微服务拿到公钥、加密消息、签名算法后，先查询公钥对应的私钥，在用一样的签名算法，计算私钥和加密消息，如果得到的结果一样，那么就认证成功，如果不一样那么就认证失败，返回相应错误。</p><h5 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h5><p>插件的核心代码<code>handler.lua</code>，目前只支持<code>HMAC-SHA256</code>签名认证，自己动手改一改就可以支持其它签名认证了。</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> BasePlugin = <span class="built_in">require</span> <span class="string">"kong.plugins.base_plugin"</span></span><br><span class="line"><span class="keyword">local</span> req_set_header = ngx.req.set_header</span><br><span class="line"><span class="keyword">local</span> openssl_hmac = <span class="built_in">require</span> <span class="string">"openssl.hmac"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> UpstreamAuthSignatureHandler = BasePlugin:extend()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UpstreamAuthSignatureHandler:new</span><span class="params">()</span></span></span><br><span class="line">  UpstreamAuthSignatureHandler.super.new(self, <span class="string">"upstream-auth-signature"</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UpstreamAuthSignatureHandler:access</span><span class="params">(conf)</span></span></span><br><span class="line">  UpstreamAuthSignatureHandler.super.access(self)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">local</span> key = conf.key</span><br><span class="line">  <span class="keyword">local</span> secret = conf.secret</span><br><span class="line">  <span class="keyword">local</span> <span class="built_in">date</span> = <span class="built_in">os</span>.<span class="built_in">date</span>(<span class="string">"%Y-%m-%d %H:%M:%S"</span>, <span class="built_in">os</span>.<span class="built_in">time</span>())</span><br><span class="line">  <span class="keyword">local</span> signature_salt = <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">"date: %s"</span>, <span class="built_in">date</span>)</span><br><span class="line">  <span class="keyword">local</span> signature = openssl_hmac.new(secret, <span class="string">"sha256"</span>):final(signature_salt)</span><br><span class="line">  <span class="keyword">local</span> authorization = <span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">'Signature keyId="%s",algorithm="hmac-sha256",signature="%s"'</span>, key, ngx.encode_base64(signature))</span><br><span class="line"></span><br><span class="line">  req_set_header(<span class="string">"date"</span>, <span class="built_in">date</span>)</span><br><span class="line">  req_set_header(<span class="string">"x-api-key"</span>, key)</span><br><span class="line">  req_set_header(<span class="string">"authorization"</span>, authorization)</span><br><span class="line"></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">UpstreamAuthSignatureHandler.PRIORITY = <span class="number">850</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> UpstreamAuthSignatureHandler</span><br></pre></td></tr></table></figure><blockquote><p>插件GitHub开源地址<a href="https://github.com/xiaomastack/kong-plugin-upstream-auth-signature" target="_blank" rel="noopener">kong-plugin-upstream-auth-signature</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h5 id=&quot;场景&quot;&gt;&lt;a href=&quot;#场景&quot; class=&quot;headerlink&quot; title=&quot;场景&quot;&gt;&lt;/a&gt;场景&lt;/h5&gt;&lt;p&gt;微服务API接口认证的方式有很多，Kong官网关于上游API认证的插件只有一个&lt;a href=&quot;https://docs.konghq.co
      
    
    </summary>
    
      <category term="Dev" scheme="http://www.azjoy.com/categories/Dev/"/>
    
      <category term="Tools" scheme="http://www.azjoy.com/categories/Dev/Tools/"/>
    
    
      <category term="微服务" scheme="http://www.azjoy.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Kong" scheme="http://www.azjoy.com/tags/Kong/"/>
    
  </entry>
  
  <entry>
    <title>Kong API Gateway 安装</title>
    <link href="http://www.azjoy.com/2019/01/14/kong/"/>
    <id>http://www.azjoy.com/2019/01/14/kong/</id>
    <published>2019-01-14T11:22:25.000Z</published>
    <updated>2024-11-05T14:28:33.531Z</updated>
    
    <content type="html"><![CDATA[<h4 id="安装PostgreSQL"><a href="#安装PostgreSQL" class="headerlink" title="安装PostgreSQL"></a>安装PostgreSQL</h4><h5 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h5><p>不同系统，不同版本安装都有差异。上官网选择对应的系统环境和安装版本，页面会给出对应的安装指导。<br><a href="https://www.postgresql.org/download/linux/redhat/" title="PostgreSQL官网" target="_blank" rel="noopener">PostgreSQL官网</a><br>比如选择版本10，系统CentOS，平台64位。<br><a href="/images/2019/01/postgresql.jpeg"><img src="/images/2019/01/postgresql.jpeg" alt></a> </p><h5 id="安装详情"><a href="#安装详情" class="headerlink" title="安装详情"></a>安装详情</h5><p>安装PostgreSQL仓库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-6-x86_64/pgdg-centos10-10-2.noarch.rpm</span><br></pre></td></tr></table></figure><p>安装客户端</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install postgresql10</span><br></pre></td></tr></table></figure><p>安装服务端</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install postgresql10-server</span><br></pre></td></tr></table></figure><p>加入开机启动项</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ chkconfig postgresql-10 on</span><br></pre></td></tr></table></figure><p>为了登录不报以下错误:</p><blockquote><p>psql: FATAL:  Peer authentication failed for user “xxxx”<br>psql: FATAL:  Ident authentication failed for user “xxxx”</p></blockquote><p>需要修改<code>/var/lib/pgsql/10/data/pg_hba.conf</code>配置文件</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">将以下两行配置peer和ident都修改为trust</span><br><span class="line"></span><br><span class="line">修改前</span><br><span class="line"></span><br><span class="line"># &quot;local&quot; is for Unix domain socket connections only</span><br><span class="line">local   all             all                                     peer</span><br><span class="line"># IPv4 local connections:</span><br><span class="line">host    all             all             127.0.0.1/32            ident</span><br><span class="line"></span><br><span class="line">修改后</span><br><span class="line"></span><br><span class="line"># &quot;local&quot; is for Unix domain socket connections only</span><br><span class="line">local   all             all                                     trust</span><br><span class="line"># IPv4 local connections:</span><br><span class="line">host    all             all             127.0.0.1/32            trust</span><br></pre></td></tr></table></figure><p>初始化数据库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ service postgresql-10 initdb</span><br></pre></td></tr></table></figure><p>启动数据库服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ service postgresql-10 start</span><br></pre></td></tr></table></figure><h5 id="新建数据库kong"><a href="#新建数据库kong" class="headerlink" title="新建数据库kong"></a>新建数据库kong</h5><p>新建数据库<code>kong</code>，用户和密码分别为<code>kong</code>和<code>test</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">$ su - postgres</span><br><span class="line">-bash-4.1$ psql</span><br><span class="line">psql (10.6)</span><br><span class="line">Type <span class="string">"help"</span> <span class="keyword">for</span> <span class="built_in">help</span>.</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># create user kong with password 'test';</span></span><br><span class="line">CREATE ROLE</span><br><span class="line">postgres=<span class="comment"># create database kong owner kong;</span></span><br><span class="line">CREATE DATABASE</span><br><span class="line">postgres=<span class="comment"># grant all privileges on database kong to kong;</span></span><br><span class="line">GRANT</span><br><span class="line">postgres=<span class="comment"># \q</span></span><br><span class="line">-bash-4.1$ <span class="built_in">exit</span></span><br><span class="line">$</span><br></pre></td></tr></table></figure><p>测试数据库连接性</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">$ psql --username=kong -h127.0.0.1 --password</span><br><span class="line">用户 kong 的口令：</span><br><span class="line">psql (10.6)</span><br><span class="line">输入 &quot;help&quot; 来获取帮助信息.</span><br><span class="line">kong=&gt; \q</span><br><span class="line"></span><br><span class="line">$ psql --username=kong --password</span><br><span class="line">用户 kong 的口令：</span><br><span class="line">psql (10.6)</span><br><span class="line">输入 &quot;help&quot; 来获取帮助信息.</span><br><span class="line"></span><br><span class="line">kong=&gt; \q</span><br><span class="line"></span><br><span class="line">$ psql --username=kong</span><br><span class="line">psql (10.6)</span><br><span class="line">输入 &quot;help&quot; 来获取帮助信息.</span><br><span class="line"></span><br><span class="line">kong=&gt; \q</span><br></pre></td></tr></table></figure><p>至此，数据库准备完毕。</p><h4 id="安装Kong"><a href="#安装Kong" class="headerlink" title="安装Kong"></a>安装Kong</h4><p>到Kong官网下载相应的rpm安装包<a href="https://konghq.com/install/" title="Kong下载" target="_blank" rel="noopener">“Kong下载”</a>，或者获取下载链接。<br>下载安装包</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ wget https://bintray.com/kong/kong-community-edition-rpm/download_file?file_path=centos/6/kong-community-edition-1.0.0.el6.noarch.rpm</span><br></pre></td></tr></table></figure><p>安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ yum install kong-community-edition-1.0.0.el6.noarch.rpm</span><br></pre></td></tr></table></figure><p>配置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">cd</span> /etc/kong/</span><br><span class="line">$ cp kong.conf.default kong.conf</span><br><span class="line"></span><br><span class="line">修改admin_listen的ip为0.0.0.0</span><br><span class="line"></span><br><span class="line">修改前</span><br><span class="line"><span class="comment">#admin_listen = 127.0.0.1:8001, 127.0.0.1:8444 ssl</span></span><br><span class="line"></span><br><span class="line">修改后</span><br><span class="line">admin_listen = 0.0.0.1:8001, 0.0.0.1:8444 ssl</span><br></pre></td></tr></table></figure><p>修改<code>kong.conf</code>配置文件，配置数据库连接</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">pg_host = 127.0.0.1             <span class="comment"># The PostgreSQL host to connect to.</span></span><br><span class="line">pg_port = 5432                  <span class="comment"># The port to connect to.</span></span><br><span class="line">pg_timeout = 5000               <span class="comment"># Defines the timeout (in ms), for connecting,</span></span><br><span class="line">                                 <span class="comment"># reading and writing.</span></span><br><span class="line"></span><br><span class="line">pg_user = kong                  <span class="comment"># The username to authenticate if required.</span></span><br><span class="line">pg_password = <span class="built_in">test</span>              <span class="comment"># The password to authenticate if required.</span></span><br><span class="line">pg_database = kong              <span class="comment"># The database name to connect to.</span></span><br></pre></td></tr></table></figure><p>初始化数据库并启动服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Kong版本高于0.14.1</span><br><span class="line">$ kong migrations bootstrap</span><br><span class="line">$ kong start [-c /etc/kong/kong.conf]</span><br><span class="line"></span><br><span class="line">Kong版本低于0.15.0</span><br><span class="line">$ kong migrations up [-c /etc/kong/kong.conf]</span><br><span class="line">$ kong start [-c /etc/kong/kong.conf]</span><br></pre></td></tr></table></figure><p>测试服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ curl http://localhost:8001/</span><br><span class="line">或者</span><br><span class="line">$ curl http://192.168.1.3:8001/</span><br></pre></td></tr></table></figure><p>测试返回一串json格式数据，则安装启动服务成功。<br>Kong还有可视化的控制面板<code>kong-dashboard</code>，项目地址是<a href="https://github.com/PGBI/kong-dashboard" title="kong-dashboard" target="_blank" rel="noopener">kong-dashboard</a>。由于Kong提供了完善的<a href="https://docs.konghq.com/0.8.x/admin-api/" title="REST API接口" target="_blank" rel="noopener">REST API接口</a>，习惯接口操作就不需要安装<code>kong-dashboard</code>了。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;安装PostgreSQL&quot;&gt;&lt;a href=&quot;#安装PostgreSQL&quot; class=&quot;headerlink&quot; title=&quot;安装PostgreSQL&quot;&gt;&lt;/a&gt;安装PostgreSQL&lt;/h4&gt;&lt;h5 id=&quot;说明&quot;&gt;&lt;a href=&quot;#说明&quot; class=&quot;
      
    
    </summary>
    
      <category term="Dev" scheme="http://www.azjoy.com/categories/Dev/"/>
    
      <category term="Tools" scheme="http://www.azjoy.com/categories/Dev/Tools/"/>
    
    
      <category term="微服务" scheme="http://www.azjoy.com/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="Kong" scheme="http://www.azjoy.com/tags/Kong/"/>
    
      <category term="PostgreSQL" scheme="http://www.azjoy.com/tags/PostgreSQL/"/>
    
  </entry>
  
  <entry>
    <title>用logging + Middleware记录Django API请求日志</title>
    <link href="http://www.azjoy.com/2019/01/11/record-api-log/"/>
    <id>http://www.azjoy.com/2019/01/11/record-api-log/</id>
    <published>2019-01-11T07:48:12.000Z</published>
    <updated>2024-11-05T14:28:33.547Z</updated>
    
    <content type="html"><![CDATA[<h4 id="场景"><a href="#场景" class="headerlink" title="场景"></a>场景</h4><p>用<code>Django REST framework</code>来构建<code>Result API</code>，需要记录用户请求的时间、方法、数据、响应状态等。</p><h4 id="实现方法"><a href="#实现方法" class="headerlink" title="实现方法"></a>实现方法</h4><p>用Python的<code>logging</code>模块结合Django框架的<code>Middleware</code>，来将每次API请求的详细信息记录下来。</p><h5 id="settings新增LOGGING配置"><a href="#settings新增LOGGING配置" class="headerlink" title="settings新增LOGGING配置"></a>settings新增LOGGING配置</h5><p>定义一个专门记录API日志的logger，命名为<code>api</code>。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># LOGGING settings</span></span><br><span class="line">LOG_DIR = BASE_DIR + <span class="string">"/log"</span></span><br><span class="line">LOGGING = &#123;</span><br><span class="line">    <span class="string">'version'</span>: <span class="number">1</span>,</span><br><span class="line">    <span class="string">'disable_existing_loggers'</span>: <span class="literal">True</span>,</span><br><span class="line">    <span class="string">'formatters'</span>: &#123;</span><br><span class="line">        <span class="string">'standard'</span>: &#123;</span><br><span class="line">            <span class="string">'format'</span>: <span class="string">'%(asctime)s FuncName:%(funcName)s LINE:%(lineno)d [%(levelname)s]- %(message)s'</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">'simple'</span>: &#123;</span><br><span class="line">            <span class="string">'format'</span>: <span class="string">'%(levelname)s %(message)s'</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">'verbose'</span>: &#123;</span><br><span class="line">            <span class="string">'format'</span>: <span class="string">'%(levelname)s %(asctime)s %(module)s %(funcName)s %(message)s'</span></span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">'filters'</span>: &#123;</span><br><span class="line">        <span class="string">'require_debug_false'</span>: &#123;</span><br><span class="line">            <span class="string">'()'</span>: <span class="string">'django.utils.log.RequireDebugFalse'</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">'handlers'</span>: &#123;</span><br><span class="line">        <span class="string">'console'</span>: &#123;</span><br><span class="line">            <span class="string">'level'</span>: <span class="string">'DEBUG'</span>,</span><br><span class="line">            <span class="string">'class'</span>: <span class="string">'logging.StreamHandler'</span>,</span><br><span class="line">            <span class="string">'formatter'</span>: <span class="string">'standard'</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">'default'</span>: &#123;</span><br><span class="line">            <span class="string">'level'</span>: <span class="string">'INFO'</span>,</span><br><span class="line">            <span class="string">'class'</span>: <span class="string">'logging.handlers.RotatingFileHandler'</span>,</span><br><span class="line">            <span class="string">'filename'</span>: os.path.join(LOG_DIR, <span class="string">'info.log'</span>),</span><br><span class="line">            <span class="string">'maxBytes'</span>: <span class="number">1024</span>*<span class="number">1024</span>*<span class="number">50</span>,   <span class="comment"># 50 MB</span></span><br><span class="line">            <span class="string">'backupCount'</span>: <span class="number">2</span>,</span><br><span class="line">            <span class="string">'formatter'</span>: <span class="string">'standard'</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">'default_debug'</span>: &#123;</span><br><span class="line">            <span class="string">'level'</span>: <span class="string">'DEBUG'</span>,</span><br><span class="line">            <span class="string">'class'</span>: <span class="string">'logging.handlers.RotatingFileHandler'</span>,</span><br><span class="line">            <span class="string">'filename'</span>: os.path.join(LOG_DIR, <span class="string">'debug.log'</span>),</span><br><span class="line">            <span class="string">'maxBytes'</span>: <span class="number">1024</span>*<span class="number">1024</span>*<span class="number">50</span>,  <span class="comment"># 50 MB</span></span><br><span class="line">            <span class="string">'backupCount'</span>: <span class="number">2</span>,</span><br><span class="line">            <span class="string">'formatter'</span>: <span class="string">'standard'</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">'request_handler'</span>: &#123;</span><br><span class="line">            <span class="string">'level'</span>: <span class="string">'DEBUG'</span>,</span><br><span class="line">            <span class="string">'class'</span>: <span class="string">'logging.handlers.RotatingFileHandler'</span>,</span><br><span class="line">            <span class="string">'filename'</span>: os.path.join(LOG_DIR, <span class="string">'common.log'</span>),</span><br><span class="line">            <span class="string">'maxBytes'</span>: <span class="number">1024</span>*<span class="number">1024</span>*<span class="number">50</span>,  <span class="comment"># 50 MB</span></span><br><span class="line">            <span class="string">'backupCount'</span>: <span class="number">2</span>,</span><br><span class="line">            <span class="string">'formatter'</span>: <span class="string">'standard'</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">'restful_api'</span>: &#123;</span><br><span class="line">            <span class="string">'level'</span>: <span class="string">'DEBUG'</span>,</span><br><span class="line">            <span class="string">'class'</span>: <span class="string">'logging.handlers.RotatingFileHandler'</span>,</span><br><span class="line">            <span class="string">'filename'</span>: os.path.join(LOG_DIR, <span class="string">'api.log'</span>),</span><br><span class="line">            <span class="string">'maxBytes'</span>: <span class="number">1024</span>*<span class="number">1024</span>*<span class="number">50</span>,  <span class="comment"># 50 MB</span></span><br><span class="line">            <span class="string">'backupCount'</span>: <span class="number">2</span>,</span><br><span class="line">            <span class="string">'formatter'</span>: <span class="string">'verbose'</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">'loggers'</span>: &#123;</span><br><span class="line">        <span class="string">'django'</span>: &#123;</span><br><span class="line">            <span class="string">'handlers'</span>: [<span class="string">'console'</span>, <span class="string">'default_debug'</span>],</span><br><span class="line">            <span class="string">'level'</span>: <span class="string">'INFO'</span>,</span><br><span class="line">            <span class="string">'propagate'</span>: <span class="literal">False</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">'django.request'</span>: &#123;</span><br><span class="line">            <span class="string">'handlers'</span>: [<span class="string">'request_handler'</span>],</span><br><span class="line">            <span class="string">'level'</span>: <span class="string">'INFO'</span>,</span><br><span class="line">            <span class="string">'propagate'</span>: <span class="literal">False</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">'common'</span>: &#123;</span><br><span class="line">            <span class="string">'handlers'</span>: [<span class="string">'default'</span>, <span class="string">'console'</span>],</span><br><span class="line">            <span class="string">'level'</span>: <span class="string">'INFO'</span>,</span><br><span class="line">            <span class="string">'propagate'</span>: <span class="literal">True</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">'api'</span>: &#123;</span><br><span class="line">            <span class="string">'handlers'</span>: [<span class="string">'restful_api'</span>],</span><br><span class="line">            <span class="string">'level'</span>: <span class="string">'INFO'</span>,</span><br><span class="line">            <span class="string">'propagate'</span>: <span class="literal">True</span></span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="自定义日志记录中间件ApiLoggingMiddleware"><a href="#自定义日志记录中间件ApiLoggingMiddleware" class="headerlink" title="自定义日志记录中间件ApiLoggingMiddleware"></a>自定义日志记录中间件ApiLoggingMiddleware</h5><p>在合适的位置新建文件<code>middleware.py</code>，自定义日志记录中间件<code>ApiLoggingMiddleware</code>。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> unicode_literals</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> logging</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ApiLoggingMiddleware</span><span class="params">(object)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, get_response)</span>:</span></span><br><span class="line">        self.get_response = get_response</span><br><span class="line">        self.apiLogger = logging.getLogger(<span class="string">'api'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__call__</span><span class="params">(self, request)</span>:</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            body = json.loads(request.body)</span><br><span class="line">        <span class="keyword">except</span> Exception:</span><br><span class="line">            body = dict()</span><br><span class="line">        body.update(dict(request.POST))</span><br><span class="line">        response = self.get_response(request)</span><br><span class="line">        <span class="keyword">if</span> request.method != <span class="string">'GET'</span>:</span><br><span class="line">            self.apiLogger.info(<span class="string">"&#123;&#125; &#123;&#125; &#123;&#125; &#123;&#125; &#123;&#125; &#123;&#125;"</span>.format(</span><br><span class="line">                request.user, request.method, request.path, body,</span><br><span class="line">                response.status_code, response.reason_phrase))</span><br><span class="line">        <span class="keyword">return</span> response</span><br></pre></td></tr></table></figure><h5 id="在settings中加入中间件ApiLoggingMiddleware"><a href="#在settings中加入中间件ApiLoggingMiddleware" class="headerlink" title="在settings中加入中间件ApiLoggingMiddleware"></a>在settings中加入中间件ApiLoggingMiddleware</h5><p>由于中间件对<code>request</code>的处理是顺序执行的，对<code>response</code>处理是逆序执行的，故将<code>ApiLoggingMiddlewar</code>放在MIDDLEWARE_CLASSES的最后面。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">MIDDLEWARE_CLASSES = [</span><br><span class="line">    <span class="string">'django.middleware.security.SecurityMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.contrib.sessions.middleware.SessionMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.middleware.common.CommonMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.middleware.csrf.CsrfViewMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.contrib.auth.middleware.AuthenticationMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.contrib.messages.middleware.MessageMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.middleware.clickjacking.XFrameOptionsMiddleware'</span>,</span><br><span class="line">    <span class="string">'lib.mypath.middleware.ApiLoggingMiddleware'</span>,</span><br><span class="line">]</span><br></pre></td></tr></table></figure><h5 id="记录效果"><a href="#记录效果" class="headerlink" title="记录效果"></a>记录效果</h5><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">INFO 2019-01-11 15:28:50,652 middleware process_response username POST /api/product/ &#123;u<span class="string">'csrfmiddlewaretoken'</span>: [u<span class="string">'jAlRDXXXXXXXXzK8UnluvSzaz2yacdDzTwRE005aCVvYnINSG7xvraqi0Pu5QHur'</span>], u<span class="string">'code'</span>: [u<span class="string">'test'</span>], u<span class="string">'name'</span>: [u<span class="string">'\u53d1\u5e03\u7cfb\u7edf'</span>]&#125; 201 Created</span><br><span class="line">INFO 2019-01-11 15:29:10,598 middleware process_response username GET /api/product/9/ &#123;&#125; 200 OK</span><br><span class="line">INFO 2019-01-11 15:29:15,252 middleware process_response username PUT /api/product/9/ &#123;u<span class="string">'code'</span>: [u<span class="string">'test'</span>], u<span class="string">'name'</span>: [u<span class="string">'\u53d1\u5e03'</span>]&#125; 200 OK</span><br><span class="line">INFO 2019-01-11 15:29:27,603 middleware process_response username DELETE /api/product/9/ &#123;&#125; 200 OK</span><br><span class="line">INFO 2019-01-11 15:29:37,961 middleware process_response username GET /api/product/ &#123;&#125; 200 OK</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;场景&quot;&gt;&lt;a href=&quot;#场景&quot; class=&quot;headerlink&quot; title=&quot;场景&quot;&gt;&lt;/a&gt;场景&lt;/h4&gt;&lt;p&gt;用&lt;code&gt;Django REST framework&lt;/code&gt;来构建&lt;code&gt;Result API&lt;/code&gt;，需要记录用户
      
    
    </summary>
    
      <category term="Dev" scheme="http://www.azjoy.com/categories/Dev/"/>
    
      <category term="Django" scheme="http://www.azjoy.com/categories/Dev/Django/"/>
    
    
      <category term="Django" scheme="http://www.azjoy.com/tags/Django/"/>
    
  </entry>
  
  <entry>
    <title>用pyenv管理Python开发环境</title>
    <link href="http://www.azjoy.com/2018/12/26/pyenv/"/>
    <id>http://www.azjoy.com/2018/12/26/pyenv/</id>
    <published>2018-12-26T05:31:19.000Z</published>
    <updated>2024-11-05T14:28:33.542Z</updated>
    
    <content type="html"><![CDATA[<p>Python项目开发的时候，不同项目用到的Python版本不一样或用到的依赖包不一样。这时候需要做开发环境隔离，给每个项目一个单独的python开发环境。一般用 <a href="https://github.com/pyenv/pyenv" target="_blank" rel="noopener">pyenv</a> 配合 <a href="https://github.com/pyenv/pyenv-virtualenv" target="_blank" rel="noopener">pyenv-virtualenv</a> 插件使用，可以实现Python多开发环境的隔离。</p><h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><h5 id="pyenv"><a href="#pyenv" class="headerlink" title="pyenv"></a>pyenv</h5><p>pyenv可以用git克隆安装，也可以下载zip后解压安装。下载zip包解压安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ wget https://github.com/pyenv/pyenv/archive/v1.2.7.tar.gz</span><br><span class="line">$ tar xvfz v1.2.7.tar.gz</span><br><span class="line">$ mv pyenv-1.2.7 ~/.pyenv</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'export PYENV_ROOT="$HOME/.pyenv"'</span> &gt;&gt; ~/.bash_profile</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'export PATH="$PYENV_ROOT/bin:$PATH"'</span> &gt;&gt;~/.bash_profile</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'eval "$(pyenv init -)"'</span> &gt;&gt;~/.bash_profile</span><br><span class="line">$ <span class="built_in">source</span> .bash_profile</span><br></pre></td></tr></table></figure><p>pyenv安装完成，用<code>pyenv install --list</code>命令查看提供可安装的python版本</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv install --list</span><br><span class="line">Available versions:</span><br><span class="line">  2.1.3</span><br><span class="line">  2.2.3</span><br><span class="line">  2.3.7</span><br><span class="line">  2.4</span><br><span class="line">  2.4.1</span><br><span class="line">  ... ...</span><br><span class="line">  2.7.15</span><br><span class="line">  ... ...</span><br></pre></td></tr></table></figure><h5 id="Python"><a href="#Python" class="headerlink" title="Python"></a>Python</h5><p>安装或更新python所需依赖</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ yum install zlib-devel bzip2-devel openssl-devel ncurses-devel \</span><br><span class="line">  sqlite-devel readline-devel tk-devel gdbm-devel db4-devel \</span><br><span class="line">  libpcap-devel xz-devel patch curl nss</span><br></pre></td></tr></table></figure><p>比如安装2.7.15版本的python</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv install 2.7.15 -v</span><br></pre></td></tr></table></figure><p>为所有已安装的可执行文件创建<code>shims</code>，如：<code>~/.pyenv/versions/*/bin/*</code>，因此，每当增删了Python版本或带有可执行文件的包(例如<code>celery, uwsgi, pip</code>)以后，都应该执行一次该命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv <span class="built_in">rehash</span></span><br></pre></td></tr></table></figure><p>查看当前已安装python的版本</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv versions</span><br><span class="line">  system</span><br><span class="line">* 2.7.15 (set by /root/.pyenv/version)</span><br></pre></td></tr></table></figure><p>设置系统全局python版本为2.7.15</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv global 2.7.15</span><br></pre></td></tr></table></figure><h5 id="优先级"><a href="#优先级" class="headerlink" title="优先级"></a>优先级</h5><p>pyenv优先级顺序<code>shell &gt; local &gt; global</code><br><code>global</code>全局Python版本，通过命令<code>pyenv global</code>设置，手动将版本号写入<code>~/.pyenv/version</code>也可以。作用范围是系统。<br><code>local</code>本地的Python版本，通过命令<code>pyenv local</code>设置，手动将版本号写入当前目录下的<code>.python-version</code>也可以。作用范围是当前目录，优先级较global高。<br><code>shell</code>当前终端的Python版本，通过命令<code>pyenv local</code>设置，手动定义当前终端的<code>PYENV_VERSION</code>环境变量也可以。作用范围是当前终端，优先级比local和global都要高。</p><h5 id="pyenv-virtualenv"><a href="#pyenv-virtualenv" class="headerlink" title="pyenv-virtualenv"></a>pyenv-virtualenv</h5><p>采用git安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git <span class="built_in">clone</span> https://github.com/pyenv/pyenv-virtualenv.git   ~/.pyenv/plugins/pyenv-virtualenv</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'eval "$(pyenv virtualenv-init -)"'</span> &gt;&gt; ~/.bash_profile</span><br></pre></td></tr></table></figure><h4 id="环境隔离"><a href="#环境隔离" class="headerlink" title="环境隔离"></a>环境隔离</h4><p>创建隔离环境命令格式</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv virtualenv Pyhton版本 环境名称</span><br></pre></td></tr></table></figure><p>例如，创建一个隔离环境<code>devtest</code>，Python版本<code>2.7.15</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv virtualenv 2.7.15 devtest</span><br><span class="line">New python executable <span class="keyword">in</span> /root/.pyenv/versions/2.7.15/envs/devtest/bin/python2.7</span><br><span class="line">Also creating executable <span class="keyword">in</span> /root/.pyenv/versions/2.7.15/envs/devtest/bin/python</span><br><span class="line">Installing setuptools, pip, wheel...done.</span><br><span class="line">Requirement already satisfied: setuptools <span class="keyword">in</span> /root/.pyenv/versions/2.7.15/envs/devtest/lib/python2.7/site-packages</span><br><span class="line">Requirement already satisfied: pip <span class="keyword">in</span> /root/.pyenv/versions/2.7.15/envs/devtest/lib/python2.7/site-packages</span><br></pre></td></tr></table></figure><p>切换到环境devtest，用<code>pip list</code>可以看到新的环境安装包只有<code>pip、setuptools、wheel</code>。这是一个干净的环境，可以开始一个新的Python项目开发。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv activate devtest</span><br><span class="line">pyenv-virtualenv: prompt changing will be removed from future release. configure `<span class="built_in">export</span> PYENV_VIRTUALENV_DISABLE_PROMPT=1` to simulate the behavior.</span><br><span class="line"></span><br><span class="line">(devtest) $ pip list</span><br><span class="line">Package    Version</span><br><span class="line">---------- -------</span><br><span class="line">pip        18.1</span><br><span class="line">setuptools 40.6.3</span><br><span class="line">wheel      0.32.3</span><br></pre></td></tr></table></figure><p>退出当前环境</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv deactivate</span><br></pre></td></tr></table></figure><p>列出所有的环境</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv virtualenvs</span><br></pre></td></tr></table></figure><p>删除环境<code>devtest</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ pyenv virtualenv-delete devtest</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Python项目开发的时候，不同项目用到的Python版本不一样或用到的依赖包不一样。这时候需要做开发环境隔离，给每个项目一个单独的python开发环境。一般用 &lt;a href=&quot;https://github.com/pyenv/pyenv&quot; target=&quot;_blank&quot;
      
    
    </summary>
    
      <category term="Dev" scheme="http://www.azjoy.com/categories/Dev/"/>
    
      <category term="Tools" scheme="http://www.azjoy.com/categories/Dev/Tools/"/>
    
    
      <category term="Python" scheme="http://www.azjoy.com/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>风水(堪舆)疑惑解答</title>
    <link href="http://www.azjoy.com/2018/12/21/%E9%A3%8E%E6%B0%B4%E8%A7%A3%E6%83%91/"/>
    <id>http://www.azjoy.com/2018/12/21/风水解惑/</id>
    <published>2018-12-21T11:34:39.000Z</published>
    <updated>2024-11-06T11:52:49.369Z</updated>
    
    <content type="html"><![CDATA[<h6 id="风水是不是迷信"><a href="#风水是不是迷信" class="headerlink" title="风水是不是迷信"></a>风水是不是迷信</h6><p>什么是迷信，百度百科是这样解释的<br><a href="/images/2018/12/shanghai.jpeg"><img src="/images/2018/12/mixin.jpeg" alt></a><br>个人认为风水不是迷信而是中华文明传承的一部分，前面专门写过一篇文章简单论证了这个话题 <a href="http://www.azjoy.com/2018/12/17/%E9%A3%8E%E6%B0%B4%E4%B8%8D%E6%98%AF%E8%BF%B7%E4%BF%A1/" title="风水是迷信,还是你太年轻">风水是迷信,还是你太年轻</a>，其实过度相信科学，不也是一种迷信。</p><h6 id="古时候风水这么厉害，为什么没有永垂不朽的帝王或王朝"><a href="#古时候风水这么厉害，为什么没有永垂不朽的帝王或王朝" class="headerlink" title="古时候风水这么厉害，为什么没有永垂不朽的帝王或王朝"></a>古时候风水这么厉害，为什么没有永垂不朽的帝王或王朝</h6><p>其实可以反问一句，现在的人都是知识分子都信科学，科学这么厉害，为什么我们不能长生不老，依旧摆脱不了生老病死轮回。风水的核心是气和平衡。为了保持平衡那么它是不会朝极端发展，从而就不会出现永垂不朽的神迹。不管是科学还是大家嗤之以鼻的传统文化，如果它走向极端必将灭亡，平衡才能发展。</p><h6 id="风水到底作用多大"><a href="#风水到底作用多大" class="headerlink" title="风水到底作用多大"></a>风水到底作用多大</h6><p>风水据个人验证是有作用的，究竟能有多大，能逆天改命还是能巨富石崇比？风水的作用绝对不是这些。一般就是让不好的事情变好；让好的事情变得更好；让失业的人能找到一份工作；让生病的人缓解下痛苦；让家庭变得更和睦；让店铺营业额多点等。退一万步讲，如果相信风水，有很厉害的地师能逆天改命改运，别人也是不会轻易为你做这些，你也不要强求师傅做这些，因为师傅为你们做到了这些，他一定也会失去某些对他很重要的东西。</p><h6 id="看风水到底要不要给红包或礼物"><a href="#看风水到底要不要给红包或礼物" class="headerlink" title="看风水到底要不要给红包或礼物"></a>看风水到底要不要给红包或礼物</h6><p>这个一定是要给的，作为一种<code>信息</code>的交换，最好有这个交换过程。师傅将布置改造的<code>信息</code>给了福主，那么福主应该用红包或礼物作为交换。金额或礼品分量随意，1块钱也行，师傅开盘了，一定不要他空手而归。</p><h6 id="同一个单元楼，户型相同，它们的风水影响一样吗"><a href="#同一个单元楼，户型相同，它们的风水影响一样吗" class="headerlink" title="同一个单元楼，户型相同，它们的风水影响一样吗"></a>同一个单元楼，户型相同，它们的风水影响一样吗</h6><p>不一样，有很大的区别，天下没有相同的两片叶子，也没有相同的风水环境。<br>为什么不一样？因为存在这些可能，造成了它们的不一样。</p><ol><li>入住装修的时间不一样可能造成元运不一样；</li><li>同一位置不同楼层罗盘量出的方向不一样；</li><li>不同的住户他们的装修不一样；</li><li>床头朝向，沙发摆放位置，地板颜色不一样；</li><li>等等；</li></ol><h6 id="风水物品到底有没有用"><a href="#风水物品到底有没有用" class="headerlink" title="风水物品到底有没有用"></a>风水物品到底有没有用</h6><p>有用，但是不用这些风水物品同样能达到一样的效果甚至更佳。因为生活可见的物品都可以归于五行之中。<br>例如：如果调理风水需要放置水，那么用黑色或灰色地毯、鱼缸、饮水机都是可以的。当然买那种流水的风水物品也是有用的，但是没有什么必要。<br>古人是不大用现代那些眼花缭乱的风水物品的。调理风水如果要风水物品，用平常用到的物品完全可以。最好的风水调理是不见斧凿之痕。<br><code>最后提醒一下，网上卖的那些风水物品，貔貅、麒麟是不能乱摆放的，不能放卧室、不靠厕所、不对厨房等，切记不要乱摆放</code>。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h6 id=&quot;风水是不是迷信&quot;&gt;&lt;a href=&quot;#风水是不是迷信&quot; class=&quot;headerlink&quot; title=&quot;风水是不是迷信&quot;&gt;&lt;/a&gt;风水是不是迷信&lt;/h6&gt;&lt;p&gt;什么是迷信，百度百科是这样解释的&lt;br&gt;&lt;a href=&quot;/images/2018/12/shang
      
    
    </summary>
    
      <category term="风水" scheme="http://www.azjoy.com/categories/%E9%A3%8E%E6%B0%B4/"/>
    
      <category term="杂谈" scheme="http://www.azjoy.com/categories/%E9%A3%8E%E6%B0%B4/%E6%9D%82%E8%B0%88/"/>
    
    
      <category term="风水" scheme="http://www.azjoy.com/tags/%E9%A3%8E%E6%B0%B4/"/>
    
  </entry>
  
</feed>
