<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>系统架构 on 月盾的博客</title>
    <link>https://blog.hopefly.top/categories/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/</link>
    <description>Recent content in 系统架构 on 月盾的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Fri, 18 Oct 2024 15:26:24 +0800</lastBuildDate>
    <atom:link href="https://blog.hopefly.top/categories/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>socket.io多实例集群化实现</title>
      <link>https://blog.hopefly.top/2024/10/socketio-cluster/</link>
      <pubDate>Fri, 18 Oct 2024 15:26:24 +0800</pubDate>
      <guid>https://blog.hopefly.top/2024/10/socketio-cluster/</guid>
      <description>&lt;p&gt;Socket.io作为服务器推送的首要选择，可以很方便的在客户端和Web服务器之间实现双向通信。很多人在选择之初都会有个疑问：socket.io是有状态长链接服务，它能支撑多少个用户同时在线？&lt;/p&gt;&#xA;&lt;p&gt;虽然目前没有标准统一的答案，但是，在开发实践中已经证明，在单机情况下10万用户是没问题的。&lt;/p&gt;&#xA;&lt;p&gt;如果您觉得10万还是不够，那么可以通过多实例的方式来支持更多的用户也是可行的。实现方式几乎是零成本升级。&lt;/p&gt;&#xA;&lt;p&gt;您只需要将默认的适配器更换为redis或其他适配器就可以支持多实例：&lt;/p&gt;&#xA;&lt;p&gt;一个基于epxress和socket.io的多实例服务配置：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/usr/bin/env node&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;../app&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;debug&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;debug&amp;#39;&lt;/span&gt;)(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ws_server:www&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;createServer&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;http&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;Server&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;socket.io&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;createAdapter&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@socket.io/redis-adapter&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;Redis&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ioredis&amp;#34;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;normalizePort&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;PORT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3000&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;set&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;port&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;httpServer&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createServer&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * Normalize a port into a number, string, or false.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;normalizePort&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; parseInt(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (isNaN(&lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#75715e&#34;&gt;// named pipe&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#75715e&#34;&gt;// port number&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * Event listener for HTTP server &amp;#34;error&amp;#34; event.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;onError&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;syscall&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;listen&amp;#39;&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;string&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Pipe &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Port &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#75715e&#34;&gt;// handle specific listen errors with friendly messages&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;EACCES&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; requires elevated privileges&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;EADDRINUSE&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; is already in use&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * Event listener for HTTP server &amp;#34;listening&amp;#34; event.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;onListening&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;addr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;httpServer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;address&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;addr&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;string&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pipe &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;addr&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;port &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;addr&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Listening on &amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 重点部分&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pubClient&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Redis&lt;/span&gt;({&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;host&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;REDIS_HOST&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;REDIS_PORT&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;REDIS_PASSWORD&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subClient&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pubClient&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;duplicate&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;io&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Server&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;httpServer&lt;/span&gt;, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;adapter&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createAdapter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;pubClient&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;subClient&lt;/span&gt;),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;cors&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;origin&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://admin.socket.io&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://127.0.0.1:8080&amp;#34;&lt;/span&gt;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#75715e&#34;&gt;// allowedHeaders: [&amp;#34;Authorization&amp;#34;, &amp;#34;Cookie&amp;#34;],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;credentials&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;set&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;IO&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;io&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;httpServer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;listen&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;port&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;httpServer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;error&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;onError&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;httpServer&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;listening&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;onListening&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;经过上面的改动后，所有的socket.io服务器实例可以共享一个redis实例来存储连接的状态信息。&lt;/p&gt;</description>
    </item>
    <item>
      <title>gitlab-ci使用Harbor机器人账户</title>
      <link>https://blog.hopefly.top/2024/08/gitlab-cicd-harbor/</link>
      <pubDate>Tue, 06 Aug 2024 14:50:43 +0800</pubDate>
      <guid>https://blog.hopefly.top/2024/08/gitlab-cicd-harbor/</guid>
      <description>&lt;p&gt;gitlab ci/cd 中，需要使用到 docker registry 账号 push 镜像，这个可以设置为机器人，可以避免个人账号泄露&lt;/p&gt;&#xA;&lt;h2 id=&#34;配置harbor机器人&#34;&gt;配置Harbor机器人&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;登录 Harbor 管理界面，进入&lt;code&gt;系统管理→认证&lt;/code&gt;页面；&lt;/li&gt;&#xA;&lt;li&gt;选择&lt;code&gt;机器人 Token&lt;/code&gt;页签，点击左上角的&lt;code&gt;新建机器人&lt;/code&gt;，创建一个名为&lt;code&gt;gitlab-ci&lt;/code&gt;的机器人；&lt;/li&gt;&#xA;&lt;li&gt;给该机器人一个名称，并给该机器人添加相应的权限；&lt;/li&gt;&#xA;&lt;li&gt;机器人创建完成以后，点击右侧的&lt;code&gt;查看token&lt;/code&gt;来复制该机器人的 token，这个就是以后用 gitlab-ci 配置文件使用的账号密码；&lt;/li&gt;&#xA;&lt;li&gt;在gitlab 创建 &lt;code&gt;harbor-robot&lt;/code&gt; 用户；&lt;/li&gt;&#xA;&lt;li&gt;为该用户添加项目级别的 &lt;code&gt;开发&lt;/code&gt; 权限；&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;配置gitlab-ci&#34;&gt;配置gitlab-ci&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;在项目的设置中添加 docker registry 账号密码，仓库地址是harbor的；&lt;/li&gt;&#xA;&lt;li&gt;编辑&lt;code&gt;.gitlab-ci.yml&lt;/code&gt;文件，把账号密码改为之前复制的 token；&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;注意点&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;有些情况下创建的机器人账户会带上&lt;code&gt;$&lt;/code&gt;符号，在gitlab中是无法使用的，需要将&lt;code&gt;$&lt;/code&gt;改为两个&lt;code&gt;$$&lt;/code&gt;来表示&lt;code&gt;$&lt;/code&gt;；&lt;/p&gt;&#xA;&lt;h2 id=&#34;结束&#34;&gt;结束&lt;/h2&gt;&#xA;&lt;p&gt;这样gitlab-ci 就可以顺利跑测试、构建、推送镜像了；&lt;/p&gt;&#xA;&lt;p&gt;以上内容，只是简单的配了配 harbor，可以有很多其他方法完成该工作，这里只是简单的记录下而已；&lt;/p&gt;&#xA;&lt;p&gt;如果需要复杂的设置，欢迎移步&lt;a href=&#34;https://github.com/goharbor/harbor/blob/master/docs/user_guide.md&#34;&gt;harbor 文档&lt;/a&gt;，那里有更多的内容可以提供参考。&lt;/p&gt;</description>
    </item>
    <item>
      <title>kubernetes部署mysql8</title>
      <link>https://blog.hopefly.top/2023/11/kubernetes-deploy-mysql8/</link>
      <pubDate>Wed, 22 Nov 2023 13:32:34 +0800</pubDate>
      <guid>https://blog.hopefly.top/2023/11/kubernetes-deploy-mysql8/</guid>
      <description>&lt;p&gt;虽然很多文章说不建议将数据库部署在容器中，因为有性能问题。但我觉得这事还是要看具体使用场景来决定，而不是全盘否定。开发的过程中并不只有性能是最重要的，还有效率，易用性等也很重要。&lt;/p&gt;&#xA;&lt;p&gt;对于测试环境来说，数据库部署在容器中肯定是可以的。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;apps/v1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deployment&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;labels&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;app&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;progressDeadlineSeconds&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;600&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;replicas&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;revisionHistoryLimit&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;selector&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;matchLabels&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;app&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;strategy&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;rollingUpdate&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;maxSurge&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;25&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;%&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;maxUnavailable&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;25&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;%&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;RollingUpdate&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;template&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;labels&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;app&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;containers&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;xxx&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql:8.0.33&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;imagePullPolicy&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;IfNotPresent&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;ports&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;containerPort&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3306&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;protocol&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;TCP&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;resources&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;limits&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;cpu&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;memory&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;8Gi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;volumeMounts&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;mountPath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/var/lib/mysql&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;data&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;mountPath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/etc/mysql/conf.d/&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql-config&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;volumes&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;data&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;persistentVolumeClaim&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;claimName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;configMap&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;defaultMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;420&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql-config&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql-config&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;v1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ConfigMap&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;data&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;my.cnf&lt;/span&gt;: |-&lt;span style=&#34;color:#e6db74&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    [client]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    default-character-set = utf8mb4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    [mysql]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    default-character-set=utf8mb4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    [mysqld]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    character-set-server=utf8mb4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    collation_server=utf8mb4_unicode_ci&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    gtid_mode=ON&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    enforce_gtid_consistency=ON&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    lower_case_table_names=1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    open_files_limit=65535&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    slow_query_log = ON&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    long_query_time = 2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    max_heap_table_size = 32M&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    tmp_table_size = 2M&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    log-bin = mysql-bin&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    binlog_cache_size = 32K&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    max_binlog_cache_size = 1G&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    max_binlog_size = 1G&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    binlog-format = ROW&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    sync_binlog = 1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    log-slave-updates = 1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    expire_logs_days = 7&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    default-time_zone = +8:00&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    max_connect_errors = 65535&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    max_allowed_packet = 536870912&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    max_connections = 5120&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    innodb_buffer_pool_instances = 1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    innodb_buffer_pool_size = 6442450944&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:15G&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    server_id = 100&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mysql-config&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    <item>
      <title>常用SQL审计平台介绍</title>
      <link>https://blog.hopefly.top/2023/11/sql-audit/</link>
      <pubDate>Tue, 14 Nov 2023 15:53:33 +0800</pubDate>
      <guid>https://blog.hopefly.top/2023/11/sql-audit/</guid>
      <description>&lt;p&gt;数据安全一直是企业安全的重中之重，在企业系统开发过程中，数据库的安全日益增强，所以需要有个平台能够记录和审查数据库变更。&lt;/p&gt;&#xA;&lt;p&gt;SQL 审计平台的功能特点：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;实时监控和记录：SQL 审计平台能够实时监听数据库操作，记录所有的 SQL 查询和事务操作，确保数据变更的可追溯性。&lt;/li&gt;&#xA;&lt;li&gt;用户行为分析：通过分析用户的查询行为和访问模式，SQL 审计平台可以识别风险操作、异常查询和异常登录行为，帮助提前发现潜在的安全威胁。&lt;/li&gt;&#xA;&lt;li&gt;合规报告与警报：SQL 审计平台能够生成详尽的合规报告，满足合规要求，并根据预设的规则触发警报，帮助管理员及时采取应对措施。&lt;/li&gt;&#xA;&lt;li&gt;数据完整性保护：通过审计日志记录的数据变更信息，SQL 审计平台能够帮助恢复受损的数据或进行调查取证，确保数据库的完整性和可靠性。&lt;/li&gt;&#xA;&lt;li&gt;权限管理与访问控制：SQL 审计平台提供丰富的权限管理功能，可以对用户进行细粒度的访问控制，确保只有经过授权的用户能够访问敏感数据。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;常见的sql审计平台：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Yearning&lt;/li&gt;&#xA;&lt;li&gt;Archery&lt;/li&gt;&#xA;&lt;li&gt;bytebase&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;yearning&#34;&gt;Yearning&lt;/h2&gt;&#xA;&lt;p&gt;官网：&lt;a href=&#34;https://yearning.io/&#34;&gt;https://yearning.io&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;开发语言：golang&lt;/p&gt;&#xA;&lt;p&gt;功能：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;SQL 查询&#xA;&lt;ul&gt;&#xA;&lt;li&gt;查询工单&lt;/li&gt;&#xA;&lt;li&gt;导出&lt;/li&gt;&#xA;&lt;li&gt;自动补全，智能提示&lt;/li&gt;&#xA;&lt;li&gt;查询语句审计&lt;/li&gt;&#xA;&lt;li&gt;查询结果脱敏&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;SQL 审核&#xA;&lt;ul&gt;&#xA;&lt;li&gt;流程化工单&lt;/li&gt;&#xA;&lt;li&gt;SQL语句语法检测&lt;/li&gt;&#xA;&lt;li&gt;根据规则检测SQL语句合规性&lt;/li&gt;&#xA;&lt;li&gt;自动生成DDL/DML回滚语句&lt;/li&gt;&#xA;&lt;li&gt;历史审核记录&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;推送&#xA;&lt;ul&gt;&#xA;&lt;li&gt;E-mail 工单推送&lt;/li&gt;&#xA;&lt;li&gt;钉钉 webhook 机器人工单推送&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;用户权限及管理&#xA;&lt;ul&gt;&#xA;&lt;li&gt;角色划分&lt;/li&gt;&#xA;&lt;li&gt;基于用户的细粒度权限&lt;/li&gt;&#xA;&lt;li&gt;注册&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;其他&#xA;&lt;ul&gt;&#xA;&lt;li&gt;todoList&lt;/li&gt;&#xA;&lt;li&gt;LDAP 登录&lt;/li&gt;&#xA;&lt;li&gt;动态审核规则配置&lt;/li&gt;&#xA;&lt;li&gt;自定义审核层级&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;AutoTask 自动执行&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;部署&#34;&gt;部署&lt;/h3&gt;&#xA;&lt;p&gt;支持容器部署。&lt;/p&gt;&#xA;&lt;h2 id=&#34;archery&#34;&gt;Archery&lt;/h2&gt;&#xA;&lt;p&gt;官网：&lt;a href=&#34;https://archerydms.com/&#34;&gt;https://archerydms.com/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;开发语言：python&lt;/p&gt;&#xA;&lt;p&gt;功能：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;查询&lt;/th&gt;&#xA;          &lt;th&gt;审核&lt;/th&gt;&#xA;          &lt;th&gt;执行&lt;/th&gt;&#xA;          &lt;th&gt;备份&lt;/th&gt;&#xA;          &lt;th&gt;数据字典&lt;/th&gt;&#xA;          &lt;th&gt;慢日志&lt;/th&gt;&#xA;          &lt;th&gt;会话管理&lt;/th&gt;&#xA;          &lt;th&gt;账号管理&lt;/th&gt;&#xA;          &lt;th&gt;参数管理&lt;/th&gt;&#xA;          &lt;th&gt;数据归档&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;MySQL&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;MsSQL&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Redis&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;PgSQL&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Oracle&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;MongoDB&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Phoenix&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;ODPS&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;ClickHouse&lt;/td&gt;&#xA;          &lt;td&gt;√&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;          &lt;td&gt;×&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h3 id=&#34;sql审核&#34;&gt;SQL审核&lt;/h3&gt;&#xA;&lt;h4 id=&#34;mysql实例&#34;&gt;MySQL实例&lt;/h4&gt;&#xA;&lt;p&gt;基于Inception/goInception实现，集成审核、执行、备份&lt;/p&gt;</description>
    </item>
    <item>
      <title>微信开发基础设施准备</title>
      <link>https://blog.hopefly.top/2023/09/wechatchat-developement/</link>
      <pubDate>Sun, 24 Sep 2023 11:07:26 +0800</pubDate>
      <guid>https://blog.hopefly.top/2023/09/wechatchat-developement/</guid>
      <description>&lt;p&gt;多年前微信开发写过一篇关于内网穿透的文章&lt;a href=&#34;https://blog.hopefly.top/blogdetail/55ab0f6ed366f15412cbf596/&#34;&gt;《微信开发通过公网访问本地服务器》&lt;/a&gt;，随着时间的推移，有些方法已经不再适用。&lt;/p&gt;&#xA;&lt;h2 id=&#34;本地服务器地址&#34;&gt;本地服务器地址&lt;/h2&gt;&#xA;&lt;p&gt;前面提到的公网访问本地服务器还是有一些限制，那时候是基于路由器的内网映射实现的，不是所有路由器都具备内网映射功能，就像我现在使用的路由器是移动宽带送的，外面看着挺有内涵，实际功能少的可怜。还是需要通过其他方式实现内网穿透。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;不可用的方式——ngrok&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;最开始想通过ngrok来实现，结果发现怎么都无法通过token验证，原因是ngrok会在首次打开时展现广告页面。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;可用的方式——借助花生壳来实现&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/%E5%85%B1%E4%BA%AB%E7%9B%AE%E5%BD%95/09/wechatchat-devolep/%E8%8A%B1%E7%94%9F%E5%A3%B3%E5%9F%9F%E5%90%8D.png&#34; alt=&#34;花生壳域名.png&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;如果通过客户端无法选择htt和http协议，提示：“不支持web访问方式，如有需要请使用HTTP或HTTPS”，就去web页面配置即可 -&amp;gt; &lt;a href=&#34;https://console.hsk.oray.com/forward&#34;&gt;https://console.hsk.oray.com/forward&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;服务器配置&#34;&gt;服务器配置&lt;/h2&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/%E5%85%B1%E4%BA%AB%E7%9B%AE%E5%BD%95/09/wechatchat-devolep/%E5%BE%AE%E4%BF%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%85%8D%E7%BD%AE.png&#34; alt=&#34;微信服务器配置.png&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;此处的&lt;strong&gt;服务器地址(URL)&lt;/strong&gt; 不止用于验证token，如果在文档中看到这样的字样，就是指这个地址，比如它还可以用于接收各种消息。这个地址是生产服务器地址，如果是开发阶段也不用将开发服务器地址配置到这里，因为微信提供了专门用于测试的配置，往下看。&lt;/p&gt;&#xA;&lt;h2 id=&#34;测试号管理&#34;&gt;测试号管理&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&amp;amp;t=sandbox/index&#34;&gt;测试号管理&lt;/a&gt;点开后就会看到和上面服务器配置一样的配置内容。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/%E5%85%B1%E4%BA%AB%E7%9B%AE%E5%BD%95/09/wechatchat-devolep/%E5%BE%AE%E4%BF%A1%E6%B5%8B%E8%AF%95%E5%8F%B7%E7%AE%A1%E7%90%86.png&#34; alt=&#34;微信测试号管理.png&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;测试账号具备所有API权限，所以你不用为了测试接口而花300元认证，而且认证也不对个人开放。&lt;/p&gt;&#xA;&lt;h2 id=&#34;接口访问白名单&#34;&gt;接口访问白名单&lt;/h2&gt;&#xA;&lt;p&gt;设置路径：设置与开发 -&amp;gt; 安全中心 -&amp;gt; IP白名单&lt;/p&gt;&#xA;&lt;p&gt;这个IP是通过ping内网穿透得到的IP，不是你电脑的内网IP，也不是你的电脑公网IP。有可能会变如果接口突然访问不同请检测是否IP已变动。&lt;/p&gt;&#xA;&lt;p&gt;有了以上准备基本就可以正式进入开发阶段了。祝一切顺利！&lt;/p&gt;</description>
    </item>
    <item>
      <title>https证书生成</title>
      <link>https://blog.hopefly.top/2023/09/https-ca/</link>
      <pubDate>Tue, 12 Sep 2023 14:18:30 +0800</pubDate>
      <guid>https://blog.hopefly.top/2023/09/https-ca/</guid>
      <description>&lt;p&gt;生成crt证书&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;openssl req -new -x509 -key domain.key -out domain.crt -days 365&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;将CRT转换为CSR：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;openssl req -new -key domain.key -out domain.csr&lt;/p&gt;&lt;/blockquote&gt;</description>
    </item>
    <item>
      <title>事件驱动编程、消息驱动编程、数据驱动编程</title>
      <link>https://blog.hopefly.top/2021/07/event-message-data-driven/</link>
      <pubDate>Fri, 23 Jul 2021 14:31:17 +0800</pubDate>
      <guid>https://blog.hopefly.top/2021/07/event-message-data-driven/</guid>
      <description>&lt;h3 id=&#34;事件驱动&#34;&gt;事件驱动&lt;/h3&gt;&#xA;&lt;p&gt;事件驱动机制就是：&lt;br&gt;&#xA;让驴拉磨，它不拉，你用鞭抽一下，它就开始拉了。然后又停了，你再抽一下，它又继续拉了&lt;br&gt;&#xA;这叫用“鞭”驱动“驴”拉磨&lt;/p&gt;&#xA;&lt;p&gt;在程序里，程序停止在那不动，你点击一个按钮，它就有反应了，过一会，又没反应了，你再点一下，它又继续运行。&lt;br&gt;&#xA;这叫用“事件”驱动“程序”运行&lt;/p&gt;&#xA;&lt;h4 id=&#34;0-基本概念&#34;&gt;0. 基本概念&lt;/h4&gt;&#xA;&lt;pre&gt;&lt;code&gt;窗口/组件&#xA;&#xA;事件&#xA;&#xA;消息(队列)&#xA;&#xA;事件响应(服务处理程序)&#xA;&#xA;调度算法&#xA;&#xA;进程/线程&#xA;&#xA;非阻塞I/O&#xA;&#xA;程序的执行可以看成对CPU,内存,IO资源一次占用&#xA;&#xA;现代操作系统支持多任务,可以分时复用上述资源.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h4 id=&#34;1-为什么采用事件驱动模型&#34;&gt;1. 为什么采用事件驱动模型?&lt;/h4&gt;&#xA;&lt;p&gt;事件驱动模型也就是我们常说的观察者，或者发布-订阅模型；理解它的几个关键点：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;首先是一种对象间的一对多的关系；最简单的如交通信号灯，信号灯是目标（一方），行人注视着信号灯（多方）；&#xA;&#xA;当目标发送改变（发布），观察者（订阅者）就可以接收到改变；&#xA;&#xA;观察者如何处理（如行人如何走，是快走/慢走/不走，目标不会管的），目标无需干涉；所以就松散耦合了它们之间的关系。&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h4 id=&#34;2-代码执行流程&#34;&gt;2. 代码执行流程&lt;/h4&gt;&#xA;&lt;p&gt;在传统的或“过程化”的应用程序中，应用程序自身控制了执行哪一部分代码和按何种顺序执行代码。从第一行代码执行程序并按应用程序中预定的路径执行，必要时调用过程。&#xA;在事件驱动的应用程序中，代码不是按照预定的路径执行－而是在响应不同的事件时执行不同的代码片段。事件可以由用户操作触发、也可以由来自操作系统或其它应用程序调度算法的消息触发、甚至由应用程序本身的消息触发。这些事件的顺序决定了代码执行的顺序，因此应用程序每次运行时所经过的代码的路径都是不同的。&lt;/p&gt;&#xA;&lt;h4 id=&#34;3-事件驱动模型&#34;&gt;3. 事件驱动模型&lt;/h4&gt;&#xA;&lt;p&gt;在UI编程中，常常要对鼠标点击进行相应，首先如何获得鼠标点击呢？&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;方式一&lt;/strong&gt;：创建一个线程，该线程一直循环检测是否有鼠标点击，那么这个方式有以下几个缺点：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;CPU资源浪费，可能鼠标点击的频率非常小，但是扫描线程还是会一直循环检测，这会造成很多的CPU资源浪费；如果扫描鼠标点击的接口是阻塞的呢？&#xA;&#xA;如果是堵塞的，又会出现下面这样的问题，如果我们不但要扫描鼠标点击，还要扫描键盘是否按下，由于扫描鼠标时被堵塞了，那么可能永远不会去扫描键盘；&#xA;&#xA;如果一个循环需要扫描的设备非常多，这又会引来响应时间的问题；所以，该方式是非常不好的。&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;strong&gt;方式二&lt;/strong&gt;：就是事件驱动模型目前大部分的UI编程都是事件驱动模型，如很多UI平台都会提供onClick()事件，这个事件就代表鼠标按下事件。事件驱动模型大体思路如下：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;有一个事件（消息）队列；&#xA;&#xA;鼠标按下时，往这个队列中增加一个点击事件（消息）；&#xA;&#xA;有个循环，不断从队列取出事件，根据不同的事件，调用不同的函数，如onClick()、onKeyDown()等；&#xA;&#xA;事件（消息）一般都各自保存各自的处理函数指针，这样，每个消息都有独立的处理函数；如图:&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h4 id=&#34;4-事件驱动处理库&#34;&gt;4. 事件驱动处理库&lt;/h4&gt;&#xA;&lt;pre&gt;&lt;code&gt;select&#xA;&#xA;poll&#xA;&#xA;epoll&#xA;&#xA;libev&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h3 id=&#34;消息驱动&#34;&gt;消息驱动&lt;/h3&gt;&#xA;&lt;p&gt;事件驱动机制跟消息驱动机制相比&lt;/p&gt;&#xA;&lt;p&gt;消息驱动和事件驱动很类似，都是先有一个事件，然后产生一个相应的消息，再把消息放入消息队列，由需要的项目获取。他们的区别是消息是谁产生的&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;消息驱动&lt;/strong&gt;：鼠标管自己点击不需要和系统有过多的交互，消息由系统（第三方）循环检测，来捕获并放入消息队列。消息对于点击事件来说是被动产生的，高内聚。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;事件驱动&lt;/strong&gt;：鼠标点击产生点击事件后要向系统发送消息“我点击了”的消息，消息是主动产生的。再发送到消息队列中。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;事件&lt;/strong&gt;：按下鼠标，按下键盘，按下游戏手柄，将U盘插入USB接口，都将产生事件。比如说按下鼠标左键，将产生鼠标左键被按下的事件。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;消息&lt;/strong&gt;：当鼠标被按下，产生了鼠标按下事件，windows侦测到这一事件的发生，随即发出鼠标被按下的消息到消息队列中，这消息附带了一系列相关的事件信息，比如鼠标哪个键被按了，在哪个窗口被按的，按下点的坐标是多少？如此等等。&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;要理解事件驱动和程序，就需要与非事件驱动的程序进行比较。实际上，现代的程序大多是事件驱动的，比如多线程的程序，肯定是事件驱动的。早期则存在许多非事件驱动的程序，这样的程序，在需要等待某个条件触发时，会不断地检查这个条件，直到条件满足，这是很浪费cpu时间的。而事件驱动的程序，则有机会释放cpu从而进入睡眠态（注意是有机会，当然程序也可自行决定不释放cpu），当事件触发时被操作系统唤醒，这样就能更加有效地使用cpu.&lt;/li&gt;&#xA;&lt;li&gt;再说什么是事件驱动的程序。一个典型的事件驱动的程序，就是一个死循环，并以一个线程的形式存在，这个死循环包括两个部分，第一个部分是按照一定的条件接收并选择一个要处理的事件，第二个部分就是事件的处理过程。程序的执行过程就是选择事件和处理事件，而当没有任何事件触发时，程序会因查询事件队列失败而进入睡眠状态，从而释放cpu。&lt;/li&gt;&#xA;&lt;li&gt;事件驱动的程序，必定会直接或者间接拥有一个事件队列，用于存储未能及时处理的事件。&lt;/li&gt;&#xA;&lt;li&gt;事件驱动的程序的行为，完全受外部输入的事件控制，所以，事件驱动的系统中，存在大量这种程序，并以事件作为主要的通信方式。&lt;/li&gt;&#xA;&lt;li&gt;事件驱动的程序，还有一个最大的好处，就是可以按照一定的顺序处理队列中的事件，而这个顺序则是由事件的触发顺序决定的，这一特性往往被用于保证某些过程的原子化。&lt;/li&gt;&#xA;&lt;li&gt;目前windows,linux,nucleus,vxworks都是事件驱动的，只有一些单片机可能是非事件驱动的。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;事件模式耦合高，同模块内好用；消息模式耦合低，跨模块好用。事件模式集成其它语言比较繁琐，消息模式集成其他语言比较轻松。事件是侵入式设计，霸占你的主循环；消息是非侵入式设计，将主循环该怎样设计的自由留给用户。如果你在设计一个东西举棋不定，那么你可以参考win32的GetMessage，本身就是一个藕合度极低的接口，又足够自由，接口任何语言都很方便，具体应用场景再在其基础上封装成事件并不是难事，接口耦合较低，即便哪天事件框架调整，修改外层即可，不会伤经动骨。而如果直接实现成事件，那就完全反过来了。&lt;/p&gt;&#xA;&lt;h3 id=&#34;什么是数据驱动编程&#34;&gt;什么是数据驱动编程&lt;/h3&gt;&#xA;&lt;p&gt;最近在学习《Unix编程艺术》。以前粗略的翻过，以为是介绍unix工具的。现在认真的看了下，原来是介绍设计原则的。它的核心就是第一章介绍的unix的哲学以及17个设计原则，而后面的内容就是围绕它来展开的。以前说过，要学习适合自己的资料，而判断是否适合的一个方法就是看你是否能够读得下去。我对这本书有一种相见恨晚的感觉。推荐有4~6年工作经验的朋友可以读一下。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;正题&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;p&gt;作者在介绍Unix设计原则时，其中有一条为“&lt;strong&gt;表示原则：把知识叠入数据以求逻辑质朴而健壮&lt;/strong&gt;”。结合之前自己的一些经验，我对这个原则很有共鸣，所以先学习了数据驱动编程相关的内容，这里和大家分享出来和大家一起讨论。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;数据驱动编程的核心&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;数据驱动编程的核心出发点是&lt;strong&gt;相对于程序逻辑，人类更擅长于处理数据&lt;/strong&gt;。数据比程序逻辑更容易驾驭，所以我们应该尽可能的将设计的复杂度从程序代码转移至数据。&lt;/p&gt;</description>
    </item>
    <item>
      <title>gitpage Hugo统计每一篇文章浏览量</title>
      <link>https://blog.hopefly.top/2021/06/gitpage-hugo-page-view/</link>
      <pubDate>Fri, 18 Jun 2021 20:03:41 +0800</pubDate>
      <guid>https://blog.hopefly.top/2021/06/gitpage-hugo-page-view/</guid>
      <description>&lt;p&gt;接上篇&lt;a href=&#34;https://blog.hopefly.top/2021/06/blog-move-to-gitpage/&#34;&gt;《博客迁移至hugo gitpage》&lt;/a&gt;后，因为缺失了每一篇文章的浏览量，而hugo又不具备这样的功能，原因还是gitpage不具备数据存储能力，自然就没办法统计每一篇文章的浏览量了。原本想使用自己服务器提供一个接口来记录，但发现https协议不支持调用http协议的接口，会出现&lt;code&gt;block:mixed-content&lt;/code&gt;错误。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;错误：https页面去发送http请求报错(浏览器阻止https发送http请求)&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;问题是明确了，但是我也没办法提供https的接口，免费的证书也用在了www.yuedun.wang上了。&lt;/p&gt;&#xA;&lt;p&gt;后来想到了leancloud，直接在前端调用api，将数据存储在云端。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;//cdn.jsdelivr.net/npm/leancloud-storage@4.11.1/dist/av-min.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// https://leancloud.cn/docs/sdk_setup-js.html#hash14962003 &#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// https://leancloud.cn/docs/leanstorage_guide-js.html#hash813593086&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;appId&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xxx&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;appKey&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xxx&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;serverURL&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xxx&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;AV&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;appId&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;appKey&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;serverURL&lt;/span&gt; });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;updateCollect&lt;/span&gt;() {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;collect&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AV&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Query&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Collect&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;location&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;collect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;select&lt;/span&gt;([&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pv&amp;#39;&lt;/span&gt;])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;collect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;equalTo&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;collect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;first&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt;) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 声明 class&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Collect&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AV&lt;/span&gt;.Object.&lt;span style=&#34;color:#a6e22e&#34;&gt;extend&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Collect&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 构建对象&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;collect&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Collect&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 为属性赋值&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;collect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;set&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;collect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;set&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pv&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;collect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;save&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;increment&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pv&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;save&lt;/span&gt;();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 元素不存在的情况&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;querySelector&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.post__meta.meta&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pv&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;createElement&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;div&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;pv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setAttribute&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;class&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;meta__item-author meta__item&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;span&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;createElement&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;span&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;span&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setAttribute&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;class&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;meta__text&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;span&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;innerText&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;浏览（&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;col&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pv&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;）&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;pv&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;appendChild&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;span&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;appendChild&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;pv&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;updateCollect&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因为hugo-dpsg主题没有浏览量字段，所以通过js在对应的位置插入浏览量字段即可。&lt;/p&gt;</description>
    </item>
    <item>
      <title>博客迁移至hugo gitpage</title>
      <link>https://blog.hopefly.top/2021/06/blog-move-to-gitpage/</link>
      <pubDate>Thu, 17 Jun 2021 19:38:31 +0800</pubDate>
      <guid>https://blog.hopefly.top/2021/06/blog-move-to-gitpage/</guid>
      <description>&lt;h3 id=&#34;为什么迁移&#34;&gt;为什么迁移？&lt;/h3&gt;&#xA;&lt;p&gt;6月初的时候，3年前买的阿里云服务器到期了，又买了其他服务器，但是只有1年期，于是进行了一番数据，应用迁移，这么一顿操作下来还是挺累的，尤其是在linux上安装mysql，mongodb，安装后又是连接不上，用了好几天时间才搞定，挺烦的。&lt;br&gt;&#xA;再想到一年后又是一顿操作，不由得一个激灵。&lt;/p&gt;&#xA;&lt;p&gt;经过一番的思量后，决定将自建博客迁移至gitpage上。&lt;/p&gt;&#xA;&lt;h3 id=&#34;自建和gitpage优劣对比&#34;&gt;自建和gitpage优劣对比&lt;/h3&gt;&#xA;&lt;p&gt;首先列出自己博客具备的功能：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;首页 包含最近10篇文章的标题和部分内容，最近发表的5篇，分类，标签，友链&lt;/li&gt;&#xA;&lt;li&gt;目录 所有文章时间线&lt;/li&gt;&#xA;&lt;li&gt;留言 自维护留言系统&lt;/li&gt;&#xA;&lt;li&gt;微博 新浪微博，最近出问题不显示了&lt;/li&gt;&#xA;&lt;li&gt;速记 简单记录&lt;/li&gt;&#xA;&lt;li&gt;关于 自我介绍&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;整体比较简单，没什么复杂功能。而很多静态博客也具备这些功能，而且做的更好，所以基本不会有主要功能的缺失。所以迁移到gitpage上也不会有什么问题。&lt;/p&gt;&#xA;&lt;h4 id=&#34;劣势&#34;&gt;劣势&lt;/h4&gt;&#xA;&lt;p&gt;静态博客在一些细节功能上面确实会有缺失，比如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;评论功能，数据原来是存在自己服务器上的，gitpage不具备数据存储能力，所以需要对接第三方评论或留言功能。&lt;/li&gt;&#xA;&lt;li&gt;目录缺失，这点主要由博客主题决定，有些主题是没有目录功能的。&lt;/li&gt;&#xA;&lt;li&gt;PV/UV统计，同样是由于数据存储的缺失，所以gitpage也没有这样的统计功能，但是可以添加谷歌，百度统计之类的。&lt;/li&gt;&#xA;&lt;li&gt;自定义功能较弱，只能使用主题提供的页面模式，除非不使用静态博客工具，完全自己开发。&lt;/li&gt;&#xA;&lt;li&gt;不能在线编辑。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;优势&#34;&gt;优势&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;免费，没有服务器费用。&lt;/li&gt;&#xA;&lt;li&gt;可以自定义域名，只需要一丁点儿的域名费用。&lt;/li&gt;&#xA;&lt;li&gt;seo良好。&lt;/li&gt;&#xA;&lt;li&gt;规范的书写格式，使文章内容更统一美观。&lt;/li&gt;&#xA;&lt;li&gt;更强大的编辑器，可以选择自己喜欢的markdown编辑器。&lt;/li&gt;&#xA;&lt;li&gt;便于本地检索。&lt;/li&gt;&#xA;&lt;li&gt;数据更保险，不易丢失。&lt;/li&gt;&#xA;&lt;li&gt;安全，静态博客可以避免一些网络攻击。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;整体来说，除了数据存储功能缺失外，其他都是可以实现的。&lt;/p&gt;&#xA;&lt;h3 id=&#34;迁移过程&#34;&gt;迁移过程&lt;/h3&gt;&#xA;&lt;p&gt;将mongodb数据中的博客导出为本地markdown文件。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// mongodb数据转markdown&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;genMd() {&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Blog&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;find&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;1&lt;/span&gt; }, &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, { &lt;span style=&#34;color:#a6e22e&#34;&gt;sort&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; } })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;.&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;debug&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tags&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;tags&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;,&amp;#34;&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;blog&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`---&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;title: &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;title&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;description: &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;/&amp;lt;[^&amp;lt;&amp;gt;]+&amp;gt;/g&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;/\r\n/g&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;/\n/g&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;substr&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt;)&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;date: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;updatedAt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toISOString&lt;/span&gt;()&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;draft: false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;authorbox: false&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;categories:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  - &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;category&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;tags:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  - &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tags&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  - &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tags&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  - &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tags&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  - &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;tags&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;---&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    `&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;writeFileSync&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`f:/workspace/yuedun&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;\&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.wang/content/blogdetail/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.md`&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;blog&lt;/span&gt;, { &lt;span style=&#34;color:#a6e22e&#34;&gt;flag&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a+&amp;#39;&lt;/span&gt; })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#75715e&#34;&gt;// debug(b.createdAt.toISOString())&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#x9;&#x9;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;&#x9;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&#x9;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ok！完成。&lt;/p&gt;</description>
    </item>
    <item>
      <title>让web项目不再502</title>
      <link>https://blog.hopefly.top/blogdetail/5f119031148c8850e521735f/</link>
      <pubDate>Fri, 17 Jul 2020 11:49:05 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5f119031148c8850e521735f/</guid>
      <description>&lt;h3 id=&#34;前后端分离&#34;&gt;前后端分离&lt;/h3&gt;&#xA;&lt;p&gt;近几年比较流行的web项目开发架构是前后端分离，前后端分离架构在系统稳定性方面非常有优势，其中一点优势主要体现在用户感知上，即使服务端发生错误也不会展现在视图层，一般情况下用户是可以继续浏览网页，不会很突兀的显示这样的信息：&#xA;502 bad gateway&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/502.jpg&#34; alt=&#34;502 bad gateway&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在接口发生错误时虽然可能会获取不到一些数据，但是在用户体验上比直接显示502错误要好。&lt;/p&gt;&#xA;&lt;p&gt;部署也相对安全和方便。&lt;/p&gt;&#xA;&lt;p&gt;前后端分离架构虽好，但不是”银弹”，不是所有网站都能使用前后端分离架构来做。&lt;/p&gt;&#xA;&lt;h3 id=&#34;服务端渲染&#34;&gt;服务端渲染&lt;/h3&gt;&#xA;&lt;p&gt;至于分不分离都有诸多的优缺点，可根据实际场景选择，本文要说的是不分离情况下文章开始所提到的问题——难看的502，500错误。&lt;/p&gt;&#xA;&lt;p&gt;本文我将使用“服务端渲染”来代替不分离的架构。&lt;/p&gt;&#xA;&lt;p&gt;由于服务端渲染的所有数据处理都在服务端进行，发生任何错误都可能引起500错误，这种错误会直接体现在视图层，用户看到这样的信息很不友好。&#xA;500 Internal Server Error&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/500.png&#34; alt=&#34;500 Internal Server Error&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;遇到这种错误，可以使用适当的措施来弥补，带来的效果却非常良好。开发web最多遇到的错误码有：404，500，502。&lt;/p&gt;&#xA;&lt;p&gt;404错误很多网站都有默认页面，像这样的：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/404.png&#34; alt=&#34;404 not found&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;但是500，502错误却鲜有默认页面，我们何不也搞一个呢？&lt;/p&gt;&#xA;&lt;h4 id=&#34;500错误&#34;&gt;500错误&lt;/h4&gt;&#xA;&lt;p&gt;首先说一下500错误的应对措施，500发生在服务端，比如空指针，数组溢出，超时等都会引起web服务500错误，一般的500错误在应用层面是可以捕捉到的，在response返回之前捕获异常跳转到一个”合适的页面”，这样就可以避免出现500 Internal Server Error信息。虽然这个“合适的页面”不是本应该跳转的页面，但起码还是个正常页面。&lt;/p&gt;&#xA;&lt;h3 id=&#34;502错误&#34;&gt;502错误&lt;/h3&gt;&#xA;&lt;p&gt;再说一下502错误，发生502错误一般是应用直接宕机了，任何页面都是访问不到的，这样是无法在应用层面监听和处理的，但也不是没办法，我们可以在NGINX上处理，当NGINX发生502时跳转到”合适的页面”。这个页面需要是一个静态页，可以是友好的提示信息，可以是网站首页，根据需求自己定制即可。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error_page  502  /502.html;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        location = /502.html {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                root   /usr/share/nginx/html;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以上两种错误处理虽然不能应对所有网站，比如一些个性化网站，即使掩饰的再好，用户也知道出问题了，但是在有些网站却能起到良好的作用，比如有些web站点发布时会中断服务出现502，比起光秃秃的500和502错误，上面的处理是不是就好多了。&lt;/p&gt;</description>
    </item>
    <item>
      <title>你是拿锤子的前端开发吗？</title>
      <link>https://blog.hopefly.top/blogdetail/5eb778a2bd7e796e7100c718/</link>
      <pubDate>Sun, 10 May 2020 03:44:34 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5eb778a2bd7e796e7100c718/</guid>
      <description>&lt;h3 id=&#34;前言&#34;&gt;前言&lt;/h3&gt;&#xA;&lt;p&gt;接上篇《&lt;a href=&#34;https://blog.hopefly.top/blogdetail/5b66733ae7e37a672f015c10&#34;&gt;说道说道前后端分离&lt;/a&gt;》今天再次对前端现状作一次分析（吐槽）。&lt;/p&gt;&#xA;&lt;p&gt;再次引用一句《穷查理宝典》中的理论：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;在手里拿着锤子的人看来，所有的东西都会是钉子。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;因为有锤子的关系，遇到任何问题，都会先想如何用锤子解决。久而久之，陷入了一种思维定式。任何工具带来便利的同时，也带来了局限性。而这往往是用锤子的人很难看到的。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;h3 id=&#34;事出有因&#34;&gt;事出有因&lt;/h3&gt;&#xA;&lt;p&gt;这种现状在开发圈内决不少见，不仅限于前端。本文只说说前端的现状，原因是笔者最近在工作中遇到一个棘手的问题：性能优化。&lt;/p&gt;&#xA;&lt;p&gt;最近接手了多个现有的前端项目，是公司比较核心的移动端官网，作为门户网站访问量和用户量都比较大，但是随着项目的迭代出现了性能问题，页面加载速度在WiFi网络下达到3s,3G网络15s以上，更差的网络40s+。加载的资源小则3M，大则6M。如果一切往好处想，假设所有用户使用最好的网络，用户和公司都不在乎流量费，两三秒的加载速度也还挺快的，每次打开页面费个3M流量也不是个事。但如果考虑这些问题的话就会发现这不是小问题。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/1%E5%AF%B91M%E7%AB%99%E4%BC%98%E5%8C%96%E5%89%8D.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;对以上问题分析得出结论之一：&lt;strong&gt;资源过大&lt;/strong&gt;，有兴趣的可以打开&lt;a href=&#34;http://www.taobao.wang&#34;&gt;淘宝网&lt;/a&gt;看下首屏资源做下对比，可以看到资源不超过3M，时间不超过2s。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/taobao.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;而我们一个移动端网站的资源居然能超过3M，究其原因：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;图片大&lt;/li&gt;&#xA;&lt;li&gt;js大&lt;/li&gt;&#xA;&lt;li&gt;css大&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;图片大是因为图片基本没任何大小控制，都是使用了最高标准原图。js和css大基本是属于架构问题，一个项目中包含的上百的页面每个页面600多k的js是绕不过去的（vendor.js,app.js等打包资源，不包含其他引入资源）。&lt;/p&gt;&#xA;&lt;p&gt;看到&lt;code&gt;vendor.js&lt;/code&gt;,&lt;code&gt;app.js&lt;/code&gt;这两个名称很多人应该想到了，这是vue（react）框架开发的网站。&#xA;是的，就是用vue开发的移动端网站，使用vue开发网站本身也不是什么大问题，毕竟有实力的公司不需要SEO，直接竞价排名就行。而我要说的问题是，不是什么网站都可以用vue来开发，不信请继续往下看。&lt;/p&gt;&#xA;&lt;h3 id=&#34;问题分析&#34;&gt;问题分析&lt;/h3&gt;&#xA;&lt;p&gt;我司的移动端网站作用并不仅仅是用来展示公司形象的，更重要的是用户转化的，就是让用户注册的。而且是要和很多第三方机构合作投放引流，经常需要分析页面UI的不同对转化率的影响，所以需要的页面不是几个，而是几十上百个，还在不停增加，每周都有三五个页面增加。&#xA;由于vue主要是以开发单页SPA应用为主的，在开发人员不考虑真实需求的情况下自然会使用流行的技术，最终把网站开发成一个单页应用。单页应用的特点就是&lt;strong&gt;单页&lt;/strong&gt;，就是把不同的页面做成一个页面一次加载，加载完成后页面之间的切换就会很快，一般无需再加载资源，用户体验也会好很多，可以套用一句话：“一次等待，处处快速”。&lt;/p&gt;&#xA;&lt;p&gt;这个特点在管理后台项目中很合适，但是在只需要展示一次的项目中也合适吗？不合适。&lt;/p&gt;&#xA;&lt;p&gt;我们的网站项目是用来做很多落地页的，各个落地页之间没有关联性，不会A页面跳到B页面，从B页面跳到C页面，A页面中不需要B页面的资源，B页面也不需要C页面的资源。然而vue项目打包的时候会把每个页面独有的一些资源都融合在一起，形成公共资源。结果就显而易见了，一个页面总要加载一堆无关资源，不仅资源大，还有很长的白屏时间，用户体验下降。&lt;/p&gt;&#xA;&lt;p&gt;还有一点不该使用单页应用的原因是我们的页面是纯展示的页面，不需要很多数据交互，vue能起到什么作用？操作数据？驱动UI？模块化？通通不需要。现代html可以不借助第三方库和框架的情况下完全能实现。&lt;/p&gt;&#xA;&lt;h3 id=&#34;结论&#34;&gt;结论&lt;/h3&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;JavaScript 的最大优势之一是它不需要编译，所以可以在浏览器中直接运行。这样你就可以立刻获得编码的反馈。入门门槛很低；你只需一个文本编辑器和一个浏览器就能编写软件了。&#xA;不幸的是，这种简单性和可访问性已被称为过度工具链的风气破坏了。这种风气已经将 JavaScript 的开发工作变成了一场噩梦。我甚至看过一整套关于配置 Webpack 的课程。这种乱象需要有个尽头——生命苦短啊。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;VUE，React这类框架用于构建&lt;strong&gt;应用&lt;/strong&gt;方面很合适，但不太适合构建&lt;strong&gt;网站&lt;/strong&gt;。应用是需要有较多的UI和数据方面的交互，而网站则更多的是信息展示，你可能根本不需要JavaScript（框架）。&lt;/p&gt;&#xA;&lt;p&gt;追求新技术可以让我们获得新奇感，成就感，解决老问题，而不是带来新问题。复杂性才是造成软件问题的根本原因。——试问：离开框架的你还会开发网站吗？&lt;/p&gt;</description>
    </item>
    <item>
      <title>“中台”是架构的捷径吗？</title>
      <link>https://blog.hopefly.top/blogdetail/5e183a7a54cf7f406dcb28f5/</link>
      <pubDate>Fri, 10 Jan 2020 08:48:58 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5e183a7a54cf7f406dcb28f5/</guid>
      <description>&lt;p&gt;由于“中台”概念的推动，关心业务架构的读者越来越多，很多企业也对实施“中台”、“中台”方法论趋之若鹜。历史总是相似的，之前无论 SOA、微服务、DDD，还是敏捷开发、双模开发等热门技术概念出现时，都曾经给大家燃起“捷径”的希望。&#xA;然而，最终还是证明了&lt;strong&gt;软件领域没有“银弹”&lt;/strong&gt;，很多时候，反倒是应了北欧的一句民谚：捷径是迷路的最快方法。&#xA;&lt;strong&gt;架构没有捷径&lt;/strong&gt;，无论从架构的设计、架构的落地还是架构的学习方面来讲，都是如此。&lt;/p&gt;&#xA;&lt;h3 id=&#34;1架构设计没有捷径&#34;&gt;1.架构设计没有捷径&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;架构设计如同求医问诊，必须对症下药&lt;/strong&gt;。盲目相信任何已有架构设计成果都是很危险且极不负责任的。每个人的身体都各有特点，企业也是如此，而企业级转型、企业级工程是对企业现有能力的最大调整，需要企业在认清自己的基础上进行，任何忽略“向内看”过程的架构设计，都是为今后的混乱，甚至失败埋下伏笔。&#xA;对于复杂手术，不经过详细的诊断，不经过对术式反复揣摩，医生不会轻易为患者开刀，否则，不啻于用生命做试验。&lt;strong&gt;软件工程虽然少有“人命关天”的事情，但是，浪费时间也等同于浪费生命&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;没有为企业做过深入“体检”而轻易相信“领先实践”，很容易把在架构设计上节省的时间和精力，加倍“奉还”到实施过程中去。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;企业级架构设计往往给人以过于漫长、难以响应变化的印象，但是，人们恰恰忽略了由此带来的架构认知的积累，以及由量变到质变的过程。&#xA;当人们感慨一次“传统”的企业级业务架构设计方式可能耗时一到两年，而互联网时代非常追逐“快”时，其实并没有充分意识到，互联网企业的架构，比如阿里的“中台”也经历了十余年的积淀，而且十余年的积淀也还只是理清了一个方向，不然也不会有今日对“中台”概念的众说纷纭。&#xA;互联网架构并不代表架构设计上有“捷径”，反而证明了，任何“快”都来自于自身的积累，是充分“向内看”才有了外在的“快”。&lt;strong&gt;“快”源自对业务的深刻理解，“中台”对公共能力的沉淀正是来自于对业务能力的归纳和提炼&lt;/strong&gt;，所以阿里才十分重视业务架构师的培养，而对“中台”的探索绝不仅限于对公共能力的沉淀，最终会上升到对企业整体、对何为业务经营的认识，这是一个自然的过程。&#xA;笔者在最近与原阿里中台核心架构师毗卢老师的交流中了解到，他们在进行中台设计时也在反复思考技术要努力支持业务经营的快速变化。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;那业务经营到底是什么？其实，大家关注的问题从领域级逐步上升到企业级之后，是一定会思考到底企业是什么、企业如何运转这些问题的。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;互联网架构并非简单地快速试错，这种试错是对业务选择能力的支持&lt;/strong&gt;，而从技术视角看，则是对架构能力的不断提炼，是架构的强大才有了快速设计的可能。&#xA;所以，架构设计没有捷径，唯有积累，通过积累提高了对企业自身的了解、对架构设计的驾驭能力，才有了可以快的“资本”。&lt;/p&gt;&#xA;&lt;p&gt;此处，还得再说一次前边提到的北欧民谚：捷径是迷路的最快方法，无论是企业还是个人，切换架构设计方法前，都要对学习曲线有深刻的认识，否则，当别人在原来的方向上越走越远时，你可能还是在原地打转，只不过为别人提供了案例。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;笔者 2019 年在公司负责搭建风险管理体系，而该项工作再次让我认识到，架构不是可以“抄”的。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;风险管理是个很成熟的领域了，三道防线的体系设计方式，无论是金融企业还是科技企业、无论是国内企业还是国外企业都在使用，但是，你却无法直接把其他企业的防线设计简单套用过来，必须一个工作事项一个工作事项地分析自己企业的流程，确认风险点，确认工作事项的负责团队，落实具体的风控职责，之后，再考虑风控措施是否可以实现“机控”，而这一切又取决于该工作事项是否已经线上化。&#xA;这样才能形成一个与实际工作环境相符，并融合数字化风控方向的全面风险管理体系。这种深入细节的体系建设无法通过照搬任何现成经验来获得，否则会出现“削足适履”的情况。没有“捷径”，只有“路径”。&lt;/p&gt;&#xA;&lt;h3 id=&#34;2架构落地没有捷径&#34;&gt;2.架构落地没有捷径&lt;/h3&gt;&#xA;&lt;p&gt;经常有读者好奇企业级业务架构设计如何落地，笔者在书中、在 2019 年 12 月南京的中台大会上都直言，企业级业务架构设计的落地过程没有任何神秘和特殊，也不该有，今天笔者认为企业级业务架构日益重要，并不是因为它有什么落地的捷径，&lt;strong&gt;任何架构设计的落地过程都是靠一个逻辑一个逻辑、一个模块一个模块地实现的&lt;/strong&gt;。&#xA;企业级业务架构设计只是让业务端的整理更加的结构化、整体化了，不同于需求分析对局部细节的关注，也不同于产品分析的领域性特点，企业级业务架构关注的是企业能力的整体规划和结构化表达，但它并不意味着在实现层面有何特殊性，它只是提供了软件过程中的一个“指挥棒”，通过业务架构设计形成对软件功能划分的指导。&#xA;而更重要的是，通过业务和技术都能理解的业务架构模型，使企业内部形成可以交流、甚至可以跨领域交流的“共同语言”。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;这个“指挥棒”对于提升企业的整体性而言是必不可少的，管理学上一直在研究如何让企业内部形成管理合力。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;业务架构诞生初期，在上个世纪 80-90 年代，企业的信息化程度还不如今日这么高，业务和技术的深度融合还没有受到应用的重视，但是今天，淘宝、滴滴、美团、头条等跨界竞争者给传统行业的原有企业造成了极大的竞争压力，乃至很多人都认同未来企业大部分都将转型为科技企业，工行的领导者最近也发表了此类言论。&#xA;由此可见，加强业务与技术的深度融合已经十分必要了，而业务架构正是符合这种时代要求的工具，赋予企业清晰的能力视图，清晰的结构加上架构的演进，就可能会不断提升架构的弹性。&#xA;企业管理经常追求韧性，常说希望企业能够像拧毛巾一样，越拧越紧却不会拧断，而未来，鉴于企业都具有科技属性，这样的“韧性”可能就要来自于架构的“弹性”了。&#xA;提升企业的整体性犹如进行马拉松训练，业务架构虽然提供了一个有力的工具，但是马拉松还是得依靠训练者一步一步地跑完，成绩的提升完全取决于训练者自身的能力和毅力。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;回到软件工程上，架构落地即便是采用敏捷过程，也不意味着靠的是什么“捷径”，而只是对工程组织方式的改进和对效率的追求。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;笔者近日阅读了《敏捷革命》一书，与广为流传的“敏捷价值观”相比，敏捷方法的原创者其实更在意的是如何通过信息的充分获取与共享、良好的思维模型，以短周期的方式迅速提供核心价值，从而降低项目周期过长导致的项目失败风险，通过多轮短周期的可控“冲刺”替代长周期的不可控过程。&lt;/p&gt;&#xA;&lt;p&gt;原创者非常推崇 &lt;strong&gt;OODA 原则&lt;/strong&gt;，也就是飞行员训练中采用的**“观察 - 导向 - 决定 - 行动”模式**，其实每一次敏捷的 Scrum 中都体现了这一思想。&#xA;“观察”代表了对全面信息的迅速获取；“导向”是依靠思维模型进行快速分析，也就是快速的设计过程；“决定”就是确定结论，不再犹豫，“行动”就是将决定付诸实现。&#xA;原创者在书中也强调一个 Scrum 内，需求确定后就尽可能不动，这与飞行员的“决定”、“行动”的模式一样，因为空战时间太短了，几乎没有后悔重来的机会。&#xA;敏捷方法原创者十分推崇丰田的生产方式，笔者恰好最近也读了《新乡重夫谈丰田生产方式》一书。丰田的生产方式，又称“精益生产”、“Just-in-time”，是对拒绝“浪费”的极致追求，这个浪费指的不是原材料的浪费，而对是时间、效率的浪费。&#xA;比如，丰田在思考原材料在不同生产场地间搬运造成的浪费时，首先的解决思路是如何做到不搬运，通过这种思考去调整生产环境；再比如，在反思如何提高打磨零件毛刺工作的效率时，采用了引入欧洲真空加工技术，让零件根本不产生毛刺的方法。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;诸如此类的例子还有很多，正是通过这种对点滴效率提升的持续近 20 年的不断追求，才最终打造出丰田生产方式。&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;任何方法的形成都是一个长期积累和反思的过程，而应用这些方法也需要使用者付出合理的努力加以掌握，架构设计的落地说到底是软件工程问题，没有捷径，只有持之以恒的效率提升。无论是给予敏捷方法原创者灵感的丰田生产方式，还是敏捷方法原创者自己的实践历程，都是一个对方法持续改进、日益精熟的过程。&#xA;没有真正理解方法之前，根本谈不上效率，与其总是在方法之间换来换去地求“快”，不如真把自己已有的功夫练到极致，只要解决问题的效率高，你自己就是“一派”。“四万八千法门”都能成佛，能够在修炼过程中“博采众长”就更好了，其实敏捷方法的原创者也正是这样创立敏捷方法的。&lt;/p&gt;&#xA;&lt;h3 id=&#34;3架构学习没有捷径&#34;&gt;3架构学习没有捷径&lt;/h3&gt;&#xA;&lt;p&gt;没有成为架构师的捷径，只有勤学苦练。架构的学习需要很多基础性工作，需要很广泛的涉猎，这方面笔者在《六方面学习，帮你走上业务架构师之路》一文中有所介绍，在架构能力、流程优化、建模技术、软件过程、编程语言、整体思维方面，都有很多知识需要学习，也列出了一些参考书目，此处不再赘述。&#xA;&lt;strong&gt;无论是哪一种架构师，都需要深厚的积累，架构师都是项目堆出来的&lt;/strong&gt;。&#xA;不可否认的是，互联网企业架构师成长确实很快，这也许是企业机制提供了更多的考验机会给适任者，使其能够快速进步。如果说培养架构师有什么勉强可以称之为“捷径”的方法，对企业而言，就是认真思考下自己是否建立了快速发现人才、培养人才的机制吧，否则，阿里说过了，业务架构师只能自己培养，没有合适的人才是什么也干不成的。&#xA;最近在一部《Doctor X》的连续剧中，医术高超的女主角在一场难度极高的手术中，说了这样一段话：“就像河水流淌一样，反复的基本技巧，就能创造出美丽的最终术野，那就是理想的手术……最重要的是，不管手术再艰难，也不能抛弃患者”，笔者想，这也同样适用于架构领域吧。&#xA;架构没有捷径，有的只是前人的肩膀，努力学习，积极实践，消化理解，真正站在前人的肩膀上，才可能看到前进的方向，而前人的肩膀也不仅限于你所从事的行业。&lt;/p&gt;</description>
    </item>
    <item>
      <title> 一次商业web网站搭建的取舍过程</title>
      <link>https://blog.hopefly.top/blogdetail/5df30ce58803fb4044fa0651/</link>
      <pubDate>Fri, 13 Dec 2019 04:00:37 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5df30ce58803fb4044fa0651/</guid>
      <description>&lt;p&gt;最近为公司官网重构搭建项目，把遇到的问题总结一下。此处的“商业”并没有多神秘，说的有点夸张而已，不过是为了区分公司项目与个人项目罢了。在这之前，我自己搭建过的网站也不下于10个，其中有个人网站也有公司网站，那时候搭建的网站也能上线运行，也没有过多的条件限制，所以不会有什么纠结的地方。&lt;/p&gt;&#xA;&lt;p&gt;所以搭建一个网站并不复杂，复杂的是让其满足很多要求。有业务需求，有领导喜好，有同事对技术的接受度。业务说网站要支持SEO，支持IE浏览器，领导说我们要前后端分离，同事说我想使用主流新技术。最后经过几轮商讨下来自然是业务第一，领导第二，同事第三的优先级进行选择了。&lt;/p&gt;&#xA;&lt;p&gt;要支持SEO和IE浏览器，只能是服务端渲染，可选的技术就只有SSR和模板引擎。SSR能选择的也就同事熟悉的Vue技术栈nuxtjs，但是其只能首屏渲染，并不能完全满足整站的SEO需求，而且开发体验并不怎么好，编译时间长，调试难，占用内存高等缺点。最后只能选择一把刷技术jQuery和node模板引擎。&lt;/p&gt;&#xA;&lt;p&gt;选择了jQuery和模板引擎还不够，前端的模块化怎么做？目前前端模块化方案还是少的可怜，但并不是没有好的方案，只是在技术潮流下显得有点暗淡失色。比如requirejs，seajs，fis等都可以做模块化，~~最后在内心斗争一番后选择了layui自带的模块化（主要是使用了这一套UI）。layui本身是一套UI框架，为了尽量减少引入第三方js就直接使用其提供的模块化。~~原网站使用的是fis3也很好，但是如果继续使用的话等于又回去了，而且fis3也不再维护了。好在改造难度不大，只需要重新包装一下即可，其实fis3最后生成的代码也是类似于AMD/CMD规范。&lt;/p&gt;&#xA;&lt;p&gt;技术方案确定后就剩开发环境工程问题了，由于一些老项目的缘故，前端同事都习惯了使用less开发css，也需要引入。然后是公司项目不同于个人项目具有服务器完全管理权限，通常使用NGINX代理，这样会对前端文件进行缓存，而网站发布频率较大，前端文件变化了还有缓存，所以又需要对前端文件进行哈希处理，这样就有了编译过程，同时还有node服务需要同时运行，所以使用了gulp工作流。后续补充：layui在开发过程中没啥问题，但是要上线时对静态文件哈希处理不好，最终也放弃了,回归了最原始的开发方式。&lt;/p&gt;&#xA;&lt;p&gt;这样一顿操作下来也耗时一周才完成，远比搭建个人项目一天内费事多了。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Consul 入门教程</title>
      <link>https://blog.hopefly.top/blogdetail/5c739d01e4d2f35bdd267d2d/</link>
      <pubDate>Mon, 25 Feb 2019 07:45:05 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5c739d01e4d2f35bdd267d2d/</guid>
      <description>&lt;p&gt;一、什么是服务发现&lt;/p&gt;&#xA;&lt;p&gt;二、consul 简介&lt;/p&gt;&#xA;&lt;p&gt;三、consul的几个概念&lt;/p&gt;&#xA;&lt;p&gt;四、安装 Consul&lt;/p&gt;&#xA;&lt;p&gt;五、运行 Consul Agent&lt;/p&gt;&#xA;&lt;p&gt;六、集群成员&lt;/p&gt;&#xA;&lt;p&gt;七、停止 Agent&lt;/p&gt;&#xA;&lt;p&gt;八、注册服务&lt;/p&gt;&#xA;&lt;p&gt;九、Consul 集群&lt;/p&gt;&#xA;&lt;p&gt;十、健康检查&lt;/p&gt;&#xA;&lt;p&gt;十一、KV Data&lt;/p&gt;&#xA;&lt;p&gt;十二、Consul Web UI&lt;/p&gt;&#xA;&lt;h2 id=&#34;十三docker下安装consul&#34;&gt;十三、Docker下安装consul&lt;/h2&gt;&#xA;&lt;p&gt;作者：菲宇&lt;br&gt;&#xA;来源：CSDN&lt;br&gt;&#xA;原文：&lt;a href=&#34;https://blog.csdn.net/bbwangj/article/details/81116505&#34;&gt;https://blog.csdn.net/bbwangj/article/details/81116505 &lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>服务发现：Zookeeper vs etcd vs Consul </title>
      <link>https://blog.hopefly.top/blogdetail/5c735c58e4d2f35bdd267d22/</link>
      <pubDate>Mon, 25 Feb 2019 03:09:12 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5c735c58e4d2f35bdd267d22/</guid>
      <description>&lt;p&gt;【编者的话】本文对比了Zookeeper、etcd和Consul三种服务发现工具，探讨了最佳的服务发现解决方案，仅供参考。&lt;/p&gt;&#xA;&lt;p&gt;如果使用预定义的端口，服务越多，发生冲突的可能性越大，毕竟，不可能有两个服务监听同一个端口。管理一个拥挤的比方说被几百个服务所使用的所有端口的列表，本身就是一个挑战，添加到该列表后，这些服务需要的数据库和数量会日益增多。因此我们应该部署无需指定端口的服务，并且让Docker为我们分配一个随机的端口。唯一的问题是我们需要发现端口号，并且让别人知道。&lt;/p&gt;&#xA;&lt;p&gt;当我们开始在一个分布式系统上部署服务到其中一台服务器上时，事情会变得更加复杂，我们可以选择预先定义哪台服务器运行哪个服务的方式，但这会导致很多问题。我们应该尽我们所能尽量利用服务器资源，但是如果预先定义每个服务的部署位置，那么要实现尽量利用服务器资源是几乎不可能的。另一个问题是服务的自动伸缩将会非常困难，更不用说自动恢复了，比方说服务器故障。另一方面，如果我们将服务部署到某台只有最少数量的容器在运行的服务器上，我们需要添加IP地址到数据列表中，这些数据需要可以被发现并存储在某处。&lt;/p&gt;&#xA;&lt;p&gt;当我们需要存储和发现一些与正在工作的服务相关的信息时，还有很多其他的例子。&lt;/p&gt;&#xA;&lt;p&gt;为了能够定位服务，我们需要至少接下来的两个有用的步骤。&#xA;服务注册——该步骤存储的信息至少包括正在运行的服务的主机和端口信息&#xA;服务发现——该步骤允许其他用户可以发现在服务注册阶段存储的信息。&lt;/p&gt;&#xA;&lt;p&gt;除了上述的步骤，我们还需要考虑其他方面。如果一个服务停止工作并部署/注册了一个新的服务实例，那么该服务是否应该注销呢？当有相同服务的多个副本时咋办？我们该如何做负载均衡呢？如果一个服务器宕机了咋办？所有这些问题都与注册和发现阶段紧密关联。现在，我们限定只在服务发现的范围里（常见的名字，围绕上述步骤）以及用于服务发现任务的工具，它们中的大多数采用了高可用的分布式键/值存储。&lt;/p&gt;&#xA;&lt;h3 id=&#34;服务发现工具&#34;&gt;服务发现工具&lt;/h3&gt;&#xA;&lt;p&gt;服务发现工具的主要目标是用来服务查找和相互对话，为此该工具需要知道每个服务，这不是一个新概念，在Docker之前就已经存在很多类似的工具了，然而，容器带给了这些工具一个全新水平的需求。&lt;/p&gt;&#xA;&lt;p&gt;服务发现背后的基本思想是对于服务的每一个新实例（或应用程序），能够识别当前环境和存储相关信息。存储的注册表信息本身通常采用键/值对的格式，由于服务发现经常用于分布式系统，所以要求这些信息可伸缩、支持容错和分布式集群中的所有节点。这种存储的主要用途是给所有感兴趣的各方提供最起码诸如服务IP地址和端口这样的信息，用于它们之间的相互通讯，这些数据还经常扩展到其它类型的信息服务发现工具倾向于提供某种形式的API，用于服务自身的注册以及服务信息的查找。&lt;/p&gt;&#xA;&lt;p&gt;比方说我们有两个服务，一个是提供方，另一个是第一个服务的消费者，一旦部署了服务提供方，就需要在服务发现注册表中存储其信息。接着，当消费者试图访问服务提供者时，它首先查询服务注册表，使用获取到的IP地址和端口来调用服务提供者。为了与注册表中的服务提供方的具体实现解耦，我们常常采用某种代理服务。这样消费者总是向固定IP地址的代理请求信息，代理再依次使用服务发现来查找服务提供方信息并重定向请求，在本文中我们稍后通过反向代理来实现。现在重要的是要理解基于三种角色（服务消费者、提供者和代理）的服务发现流程。&lt;/p&gt;&#xA;&lt;p&gt;服务发现工具要查找的是数据，至少我们应该能够找出服务在哪里？服务是否健康和可用？配置是什么样的？既然我们正在多台服务器上构建一个分布式系统，那么该工具需要足够健壮，保证其中一个节点的宕机不会危及数据，同时，每个节点应该有完全相同的数据副本，进一步地，我们希望能够以任何顺序启动服务、杀死服务或者替换服务的新版本，我们还应该能够重新配置服务并且查看到数据相应的变化。&lt;/p&gt;&#xA;&lt;p&gt;让我们看一下一些常用的选项来完成我们上面设定的目标。&lt;/p&gt;&#xA;&lt;h3 id=&#34;手动配置&#34;&gt;手动配置&lt;/h3&gt;&#xA;&lt;p&gt;大多数服务仍然是需要手动管理的，我们预先决定在何处部署服务、如何配置和希望不管什么原因，服务都将继续正常工作，直到天荒地老。这样的目标不是可以轻易达到的。部署第二个服务实例意味着我们需要启动全程的手动处理，我们需要引入一台新的服务器，或者找出哪一台服务器资源利用率较低，然后创建一个新的配置集并启动服务。情况或许会变得越来越复杂，比方说，硬件故障导致的手动管理下的反应时间变得很慢。可见性是另外一个痛点，我们知道什么是静态配置，毕竟是我们预先准备好的，然而，大多数的服务有很多动态生成的信息，这些信息不是轻易可见的，也没有一个单独的地方供我们在需要时参考这些数据。&lt;/p&gt;&#xA;&lt;p&gt;反应时间会不可避免的变慢，鉴于存在许多需要手动处理的移动组件，故障恢复和监控也会变得非常难以管理。&lt;/p&gt;&#xA;&lt;p&gt;尽管在过去或者当服务/服务器数量很少的时候有借口不做这项工作，随着服务发现工具的出现，这个借口已经不存在了。&lt;/p&gt;&#xA;&lt;h3 id=&#34;zookeeper&#34;&gt;Zookeeper&lt;/h3&gt;&#xA;&lt;p&gt;Zookeeper是这种类型的项目中历史最悠久的之一，它起源于Hadoop，帮助在Hadoop集群中维护各种组件。它非常成熟、可靠，被许多大公司（YouTube、eBay、雅虎等）使用。其数据存储的格式类似于文件系统，如果运行在一个服务器集群中，Zookeper将跨所有节点共享配置状态，每个集群选举一个领袖，客户端可以连接到任何一台服务器获取数据。&lt;/p&gt;&#xA;&lt;p&gt;Zookeeper的主要优势是其成熟、健壮以及丰富的特性，然而，它也有自己的缺点，其中采用Java开发以及复杂性是罪魁祸首。尽管Java在许多方面非常伟大，然后对于这种类型的工作还是太沉重了，Zookeeper使用Java以及相当数量的依赖使其对于资源竞争非常饥渴。因为上述的这些问题，Zookeeper变得非常复杂，维护它需要比我们期望从这种类型的应用程序中获得的收益更多的知识。这部分地是由于丰富的特性反而将其从优势转变为累赘。应用程序的特性功能越多，就会有越大的可能性不需要这些特性，因此，我们最终将会为这些不需要的特性付出复杂度方面的代价。&lt;/p&gt;&#xA;&lt;p&gt;Zookeeper为其他项目相当大的改进铺平了道路，“大数据玩家“在使用它，因为没有更好的选择。今天，Zookeeper已经老态龙钟了，我们有了更好的选择。&lt;/p&gt;&#xA;&lt;h3 id=&#34;etcd&#34;&gt;etcd&lt;/h3&gt;&#xA;&lt;p&gt;etcd是一个采用HTTP协议的健/值对存储系统，它是一个分布式和功能层次配置系统，可用于构建服务发现系统。其很容易部署、安装和使用，提供了可靠的数据持久化特性。它是安全的并且文档也十分齐全。&lt;/p&gt;&#xA;&lt;p&gt;etcd比Zookeeper是比更好的选择，因为它很简单，然而，它需要搭配一些第三方工具才可以提供服务发现功能。&lt;/p&gt;&#xA;&lt;p&gt;现在，我们有一个地方来存储服务相关信息，我们还需要一个工具可以自动发送信息给etcd。但在这之后，为什么我们还需要手动把数据发送给etcd呢？即使我们希望手动将信息发送给etcd，我们通常情况下也不会知道是什么信息。记住这一点，服务可能会被部署到一台运行最少数量容器的服务器上，并且随机分配一个端口。理想情况下，这个工具应该监视所有节点上的Docker容器，并且每当有新容器运行或者现有的一个容器停止的时候更新etcd，其中的一个可以帮助我们达成目标的工具就是Registrator。&lt;/p&gt;&#xA;&lt;h3 id=&#34;registrator&#34;&gt;Registrator&lt;/h3&gt;&#xA;&lt;p&gt;Registrator通过检查容器在线或者停止运行状态自动注册和去注册服务，它目前支持etcd、Consul和SkyDNS 2。&lt;/p&gt;&#xA;&lt;p&gt;Registrator与etcd是一个简单但是功能强大的组合，可以运行很多先进的技术。每当我们打开一个容器，所有数据将被存储在etcd并传播到集群中的所有节点。我们将决定什么信息是我们的。&lt;/p&gt;&#xA;&lt;p&gt;上述的拼图游戏还缺少一块，我们需要一种方法来创建配置文件，与数据都存储在etcd，通过运行一些命令来创建这些配置文件。&lt;/p&gt;&#xA;&lt;h3 id=&#34;confd&#34;&gt;Confd&lt;/h3&gt;&#xA;&lt;p&gt;Confd是一个轻量级的配置管理工具，常见的用法是通过使用存储在etcd、consul和其他一些数据登记处的数据保持配置文件的最新状态，它也可以用来在配置文件改变时重新加载应用程序。换句话说，我们可以用存储在etcd（或者其他注册中心）的信息来重新配置所有服务。&lt;/p&gt;&#xA;&lt;p&gt;对于etcd、Registrator和Confd组合的最后的思考&#xA;当etcd、Registrator和Confd结合时，可以获得一个简单而强大的方法来自动化操作我们所有的服务发现和需要的配置。这个组合还展示了“小”工具正确组合的有效性，这三个小东西可以如我们所愿正好完成我们需要达到的目标，若范围稍微小一些，我们将无法完成我们面前的目标，而另一方面如果他们设计时考虑到更大的范围，我们将引入不必要的复杂性和服务器资源开销。&lt;/p&gt;&#xA;&lt;p&gt;在我们做出最后的判决之前，让我们看看另一个有相同目标的工具组合，毕竟，我们不应该满足于一些没有可替代方案的选择。&lt;/p&gt;&#xA;&lt;h3 id=&#34;consul&#34;&gt;Consul&lt;/h3&gt;&#xA;&lt;p&gt;Consul是强一致性的数据存储，使用gossip形成动态集群。它提供分级键/值存储方式，不仅可以存储数据，而且可以用于注册器件事各种任务，从发送数据改变通知到运行健康检查和自定义命令，具体如何取决于它们的输出。&lt;/p&gt;&#xA;&lt;p&gt;与Zookeeper和etcd不一样，Consul内嵌实现了服务发现系统，所以这样就不需要构建自己的系统或使用第三方系统。这一发现系统除了上述提到的特性之外，还包括节点健康检查和运行在其上的服务。&lt;/p&gt;&#xA;&lt;p&gt;Zookeeper和etcd只提供原始的键/值队存储，要求应用程序开发人员构建他们自己的系统提供服务发现功能。而Consul提供了一个内置的服务发现的框架。客户只需要注册服务并通过DNS或HTTP接口执行服务发现。其他两个工具需要一个亲手制作的解决方案或借助于第三方工具。&lt;/p&gt;&#xA;&lt;p&gt;Consul为多种数据中心提供了开箱即用的原生支持，其中的gossip系统不仅可以工作在同一集群内部的各个节点，而且还可以跨数据中心工作。&lt;/p&gt;&#xA;&lt;p&gt;Consul还有另一个不错的区别于其他工具的功能，它不仅可以用来发现已部署的服务以及其驻留的节点信息，还通过HTTP请求、TTLs（time-to-live）和自定义命令提供了易于扩展的健康检查特性。&lt;/p&gt;&#xA;&lt;h3 id=&#34;registrator-1&#34;&gt;Registrator&lt;/h3&gt;&#xA;&lt;p&gt;Registrator有两个Consul协议，其中consulkv协议产生类似于etcd协议的结果。&lt;/p&gt;&#xA;&lt;p&gt;除了通常的IP和端口存储在etcd或consulkv协议中之外，Registrator consul协议存储了更多的信息，我们可以得到服务运行节点的信息，以及服务ID和名称。我们也可以借助于一些额外的环境变量按照一定的标记存储额外的信息。&lt;/p&gt;&#xA;&lt;h3 id=&#34;consul-template&#34;&gt;Consul-template&lt;/h3&gt;&#xA;&lt;p&gt;confd可以像和etce搭配一样用于Consul，不过Consul有自己的模板服务，其更适配Consul。&lt;/p&gt;&#xA;&lt;p&gt;通过从Consul获得的信息，Consul-template是一个非常方便的创建文件的途径，还有一个额外的好处就是在文件更新后可以运行任意命令，正如confd，Consul-template也可以使用 Go模板格式。&lt;/p&gt;&#xA;&lt;h3 id=&#34;consul健康检查web界面和数据中心&#34;&gt;Consul健康检查、Web界面和数据中心&lt;/h3&gt;&#xA;&lt;p&gt;监控集群节点和服务的健康状态与测试和部署它们一样的重要。虽然我们应该向着拥有从来没有故障的稳定的环境努力，但我们也应该承认，随时会有意想不到的故障发生，时刻准备着采取相应的措施。例如我们可以监控内存使用情况，如果达到一定的阈值，那么迁移一些服务到集群中的另外一个节点，这将是在发生“灾难”前执行的一个预防措施。另一方面，并不是所有潜在的故障都可以被及时检测到并采取措施。单个服务可能会齿白，一个完整的节点也可能由于硬件故障而停止工作。在这种情况下我们应该准备尽快行动，例如一个节点替换为一个新的并迁移失败的服务。Consul有一个简单的、优雅的但功能强大的方式进行健康检查，当健康阀值达到一定数目时，帮助用户定义应该执行的操作。&lt;/p&gt;&#xA;&lt;p&gt;如果用户Google搜索“etcd ui”或者“etec dashboard”时，用户可能看到只有几个可用的解决方案，可能会问为什么我们还没有介绍给用户，这个原因很简单，etcd只是键/值对存储，仅此而已。通过一个UI呈现数据没有太多的用处，因为我们可以很容易地通过etcdctl获得这些数据。这并不意味着etcd UI是无用的，但鉴于其有限的使用范围，它不会产生多大影响。&lt;/p&gt;&#xA;&lt;p&gt;Consu不仅仅是一个简单的键/值对存储，正如我们已经看到的，除了存储简单的键/值对，它还有一个服务的概念以及所属的数据。它还可以执行健康检查，因此成为一个好的候选dashboard，在上面可以看到我们的节点的状态和运行的服务。最后，它支持了多数据中心的概念。所有这些特性的结合让我们从不同的角度看到引入dashboard的必要性。&lt;/p&gt;&#xA;&lt;p&gt;通过Consul Web界面，用户可以查看所有的服务和节点、监控健康检查状态以及通过切换数据中心读取设置键/值对数据。&lt;/p&gt;&#xA;&lt;h3 id=&#34;对于consulregistratortemplate健康检查和web-ui的最终思考&#34;&gt;对于Consul、Registrator、Template、健康检查和Web UI的最终思考&lt;/h3&gt;&#xA;&lt;p&gt;Consul以及上述我们一起探讨的工具在很多情况下提供了比etcd更好的解决方案。这是从内心深处为了服务架构和发现而设计的方案，简单而强大。它提供了一个完整的同时不失简洁的解决方案，在许多情况下，这是最佳的服务发现以及满足健康检查需求的工具。&#xA;结论&#xA;所有这些工具都是基于相似的原则和架构，它们在节点上运行，需要仲裁来运行，并且都是强一致性的，都提供某种形式的键/值对存储。&lt;/p&gt;&#xA;&lt;p&gt;Zookeeper是其中最老态龙钟的一个，使用年限显示出了其复杂性、资源利用和尽力达成的目标，它是为了与我们评估的其他工具所处的不同时代而设计的（即使它不是老得太多）。&lt;/p&gt;&#xA;&lt;p&gt;etcd、Registrator和Confd是一个非常简单但非常强大的组合，可以解决大部分问题，如果不是全部满足服务发现需要的话。它还展示了我们可以通过组合非常简单和特定的工具来获得强大的服务发现能力，它们中的每一个都执行一个非常具体的任务，通过精心设计的API进行通讯，具备相对自治工作的能力，从架构和功能途径方面都是微服务方式。&lt;/p&gt;&#xA;&lt;p&gt;Consul的不同之处在于无需第三方工具就可以原生支持多数据中心和健康检查，这并不意味着使用第三方工具不好。实际上，在这篇博客里我们通过选择那些表现更佳同时不会引入不必要的功能的的工具，尽力组合不同的工具。使用正确的工具可以获得最好的结果。如果工具引入了工作不需要的特性，那么工作效率反而会下降，另一方面，如果工具没有提供工作所需要的特性也是没有用的。Consul很好地权衡了权重，用尽量少的东西很好的达成了目标。&lt;/p&gt;&#xA;&lt;p&gt;Consul使用gossip来传播集群信息的方式，使其比etcd更易于搭建，特别是对于大的数据中心。将存储数据作为服务的能力使其比etcd仅仅只有健/值对存储的特性更加完整、更有用（即使Consul也有该选项）。虽然我们可以在etcd中通过插入多个键来达成相同的目标，Consul的服务实现了一个更紧凑的结果，通常只需要一次查询就可以获得与服务相关的所有数据。除此之外，Registrator很好地实现了Consul的两个协议，使其合二为一，特别是添加Consul-template到了拼图中。Consul的Web UI更是锦上添花般地提供了服务和健康检查的可视化途径。&lt;/p&gt;&#xA;&lt;p&gt;我不能说Consul是一个明确的赢家，而是与etcd相比其有一个轻微的优势。服务发现作为一个概念，以及作为工具都很新，我们可以期待在这一领域会有许多的变化。秉承开放的心态，大家可以对本文的建议持保留态度，尝试不同的工具然后做出自己的结论。&lt;/p&gt;</description>
    </item>
    <item>
      <title>nodejs-go内存占比</title>
      <link>https://blog.hopefly.top/blogdetail/5c6cbe294a7b7e6cd80869e5/</link>
      <pubDate>Wed, 20 Feb 2019 02:40:41 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5c6cbe294a7b7e6cd80869e5/</guid>
      <description>&lt;p&gt;同一台服务器上部署了两个功能差不多的服务，但是内存占比差距有点大。&#xA;&lt;img src=&#34;https://qn-img.hopefly.top/nodejs-go%E5%86%85%E5%AD%98%E5%8D%A0%E6%AF%94.png&#34; alt=&#34;nodejs-go内存占比&#34; title=&#34;nodejs-go内存占比&#34;&gt;&#xA;go占14.7M&#xA;nodejs占122.2M&lt;/p&gt;</description>
    </item>
    <item>
      <title>gRPC负载均衡</title>
      <link>https://blog.hopefly.top/blogdetail/5bc1bd1bf846d21847dc3014/</link>
      <pubDate>Sat, 13 Oct 2018 09:38:35 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5bc1bd1bf846d21847dc3014/</guid>
      <description>&lt;p&gt;gRPC是谷歌开发的跨语言（C, C++, Python, PHP, Nodejs, C#, Objective-C、Golang、Java）RPC框架，跨语言是指可以使用gRPC进行个语言之间的通信，例如：PHP可以对java进行远程调用。&lt;/p&gt;&#xA;&lt;p&gt;在系统架构中，我们会把多个系统公共的模块拆分出来做成单独的服务，可以提供RESTful接口，也可以为了低延迟快速响应而提供RPC接口。如果选择的是gRPC，上线后发现多个系统都请求这个RPC服务提供者，而且流量很大的时候负载过高导致崩溃。为了降低负载和提高可用性，理所当然的要做集群，用nginx作为代理服务器，幸运的是nginx版本为1.13及以上支持了gRPC的负载均衡。那么请看以下配置：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;upstream grpcservice {&#xA;    server localhost:50051;&#xA;    server localhost:50052;&#xA;}&#xA;server {&#xA;    listen       8080  http2;#需要加http2&#xA;    server_name  localhost;&#xA;&#xA;    location / {&#xA;        grpc_pass grpc://grpcservice;#以grpc为前缀&#xA;    }&#xA;&#xA;    grpc_connect_timeout 10;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;配置好nginx以后，客户端需要连接到localhost:8080来调用远程服务。&#xA;效果图：&#xA;&lt;img src=&#34;https://qn-img.hopefly.top/grpc_nginx.gif&#34; alt=&#34;grpc-nginx&#34; title=&#34;grpc-nginx&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;可以看到，一次任务的多个请求两个RPC服务器都有输出，证明请求被分配到了两台服务器上。&lt;/p&gt;&#xA;&lt;p&gt;虽然我们使用了两台服务器来保证性能和可用性，但是当其中一台服务器挂掉以后发现部分请求响应非常慢。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/grpc_nginx2.gif&#34; alt=&#34;grpc_nginx2&#34; title=&#34;grpc_nginx2&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;原因是服务器虽然宕机，但是请求还会发送到挂掉的服务器上，然后等待超时（默认1分钟），超时后再请求另外的服务器，重新请求以后可能还会再次分配到这台宕机的服务器。为了能加快响应，配置了&lt;code&gt;grpc_connect_timeout&lt;/code&gt;选项，把时间设为5秒，再次测试，大概5秒后就能返回。如果设置更小的时间响应时间会更短。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/grpc_nginx3.gif&#34; alt=&#34;grpc_nginx3&#34; title=&#34;grpc_nginx3&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>过早的优化是万恶之源</title>
      <link>https://blog.hopefly.top/blogdetail/5b1fcbf7e205c79ceab7a529/</link>
      <pubDate>Tue, 12 Jun 2018 13:34:47 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5b1fcbf7e205c79ceab7a529/</guid>
      <description>&lt;p&gt;“过早的优化是万恶之源”，这句话或多或少在哪听过，不过为什么优化也会带来问题，恐怕只有经历过的人才能理解其中的意义。&lt;/p&gt;&#xA;&lt;p&gt;关于程序优化，产品优化这些词在开发工作中一定有经历过。某位程序员做事一丝不苟，对程序开发特别注重性能问题，花大量时间来优化某段代码。某位产品在提出需求的时候总是有一些目前用不到的功能，说是为了方便将来扩展。&lt;/p&gt;&#xA;&lt;p&gt;那么实际工作中这种优化到底起到了什么作用？个人认为弊多于利。&lt;/p&gt;&#xA;&lt;h3 id=&#34;问题一过早引入新技术&#34;&gt;问题一：过早引入新技术&lt;/h3&gt;&#xA;&lt;p&gt;甲同学在做一个员工日常工作内容列表的接口时，认为该接口会产生性能问题，将来数据增多的时候会有性能瓶颈，于是利用了某项缓存技术，觉得缓存一定比直接查数据库速度快，于是就引入了缓存数据库。把数据拉到缓存中，先从缓存中读取数据，没有数据的时候从数据库中读取放到缓存中。这个思路看似挺好，不过为了更新数据就设置过期时间为1天。上线后发现很多员工的日常工作内容一天内会有变化，就设置过期时间为半天，结果半天过期时间还是会有部分员工工作内容更新频繁。这时候就有些纠结了，到底是再降低过期时间还是单独更新某个员工的数据？如果选择降低过期时间会导致所有数据都要从数据库重新拉取，选择单独更新又要对各个更新几口进行修改来更新缓存。这期间不免要折腾几回来适应业务需求，而且容易造成各个业务过度依赖缓存，为了保持缓存数据一致性需要做很多工作，然而很多的工作目的并没有提高系统可用性。&lt;/p&gt;&#xA;&lt;h3 id=&#34;问题二过早优化数据库&#34;&gt;问题二：过早优化数据库&lt;/h3&gt;&#xA;&lt;p&gt;有些同学在设计数据库的时候总喜欢多加些字段，为了方便未来使用，知道索引能加速查询，就使劲加索引，这基本属于不够成熟的思想，试问：等真正需要字段的时候加会晚吗？现在提前加了后面还能知道这个字段当初是准备怎么用来的吗？别的同学要加的时候发现已经有了一个类似的，这个字段到底有没有在用，不敢删又不敢用。索引加了一大堆本以为有积极作用，可不知道索引会占用空间，插入修改却慢了下来。&lt;/p&gt;&#xA;&lt;h3 id=&#34;问题三过早进行防范准备&#34;&gt;问题三：过早进行防范准备&lt;/h3&gt;&#xA;&lt;p&gt;在没有真实QPS数据和准确预估的情况，开发同学生生怕线上出现故障，要求多部署几台服务器做负载均衡，反正服务器多了也不会有问题。但是过多的服务器会带来其他方面的问题：&#xA;1、运维难度增加&#xA;2、企业成本增加&#xA;3、线上故障排查难度增加&lt;/p&gt;&#xA;&lt;h3 id=&#34;针对性优化&#34;&gt;针对性优化&lt;/h3&gt;&#xA;&lt;p&gt;在避免过度设计的情况下才进行针对性优化。性能问题的出现并不是经常性的，而是在某个访问高峰期出现，这时候影响范围还不是很大，但是该考虑进行优化了，如果是经常性突发并发数增加可以考虑缓存数据库。如果是日常单机承载能力高可以进行一定压力测试，评估并发压力增加合适的服务器。随着企业发展，数据量不断累积，此时数据库成为主要压力，此时对数据库索引，SQL进行优化，往往能有立竿见影的效果。&lt;/p&gt;&#xA;&lt;p&gt;总结：没有针对性的性能优化，只是按照假象场景来设计，最后不但不能起到优化作用反而为后期优化带来累赘。&lt;/p&gt;</description>
    </item>
    <item>
      <title>消息队列中参数的传递和存储</title>
      <link>https://blog.hopefly.top/blogdetail/5b1d2d8f2c36bd008e01f5a3/</link>
      <pubDate>Sun, 10 Jun 2018 13:54:23 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5b1d2d8f2c36bd008e01f5a3/</guid>
      <description>&lt;p&gt;消息队列（MQ）在系统架构中发挥了重要作用，其主要作用有系统解耦，流量削峰，异步消息存储，分布式系统最终一致性等。&#xA;本文介绍的是消息系统中消息发布者和订阅者间参数如何约定和传递？实际应用场景中，一种消息的发布者和订阅者都有可能是多个，也就是多对多的关系，这样就形成了消息参数传递的复杂性，就算一开始参数有约定，大家都按照最初约定来开发，但不免开发过程中各端需求变化带来的修改。例如最开始为了实现系统解耦而引进了消息队列，此时消息参与者较少，只有一个发布者和一个订阅者，这种情况最简单（其实最简单还是直接调接口），双方简单约定了需要传递的参数。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;userId&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;userName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;阿离&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;age&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;23&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;后来订阅方又需要性别，地址，工作等信息，要求发布方加参数，这种要求也不过分，那就加好了。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;userId&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;userName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;阿离&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;age&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;23&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gender&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;女&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;address&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;上海&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;job&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;测试&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再后来又有一个订阅者参与进了消息系统，要求在上述参数中再加“工作年限”参数，那么先不说什么了，加呗。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;userId&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;userName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;阿离&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;age&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;23&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;gender&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;女&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;address&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;上海&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;job&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;测试&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#a6e22e&#34;&gt;workingLife&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再后来又有不同的订阅者参与进消息系统，每个订阅者都可能有自己的参数要求，参数类型不同，随着系统的发展，连发布者也有多个。这时最初的文档或约定已经不奏效了，总会有人不能及时更新文档导致约定不能成为约束，各自为政，比如“学历”参数有的人要求叫“硕士”，有人要叫“研究生”，总之是很难统一。如果要继续修改发布参数就会有很多冗余出现，参数类型不统一出现系统故障。&lt;/p&gt;&#xA;&lt;p&gt;这时消息系统已经不是当初简单的两个发布者订阅者了，而是一群人在参数，所谓的众口难调。此时弊端已现，无论哪一方有变化都势必牵连所有人。所以看出的出这种消息参数传递有其不合理的地方，那么怎么传递更合理？此处的MQ最开始已经说了，是为了解耦而引进，说明该MQ是多个系统的衔接者，而多个系统终究还是系统，不是简单的前端和后端接口调用，例如某个前端A也是由自己的接口提供服务系统的，MQ不是直接为前端A提供参数的。应该是由前端A自己的服务端A订阅消息，前端A需要什么参数就有服务A查询需要的参数即可，前端B需要什么参数由服务B查询提供。而发布者A，发布者B等只需要传递关键参数&lt;code&gt;userId&lt;/code&gt;即可，其他姓名，性别，年龄什么的都不需要传。&lt;/p&gt;&#xA;&lt;p&gt;此时有人喜欢拿性能来说事，觉得应该由发布者一次性查出来传递并（作为日志）存储，订阅者不用每次都查询那么多相同参数，这是没理解MQ的意义，MQ应该是负责告诉其他人发布者做了什么，而不应该由订阅者决定发布者和MQ应该怎么做。因为既然是解耦系统，那么各个系统有不同需求，也可能有自己的数据库，就算是要记录日志也是由各系统自己负责存储，不应该由MQ来存储，如果所有参数都由发布者来查询那是不是会降低发布者的性能？就像上面的情况，MQ的参数随着各端要求不断增减，类型变化，最后存储的参数会变得乱七八糟，根本无法使用。&lt;/p&gt;&#xA;&lt;p&gt;总结：消息队列应该只传递和存储关键信息，如ID就足够了，如果传递多余信息而造成不一致也会出现扯皮的事。各个订阅方自己负责其他参数查询和日志存储，否则就不是解耦了。&lt;/p&gt;</description>
    </item>
    <item>
      <title>在docker中运行nodejs</title>
      <link>https://blog.hopefly.top/blogdetail/5a6021fa4bdc8ea471fd2db0/</link>
      <pubDate>Thu, 18 Jan 2018 04:26:34 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5a6021fa4bdc8ea471fd2db0/</guid>
      <description>&lt;p&gt;首先看项目目录：&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://qn-img.hopefly.top/projectdir.png&#34; alt=&#34;项目目录&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;再看Dockerfile文件内容：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 以最新的node为基础镜像&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FROM hub.c.163.com/library/node:latest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 工作目录为app&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WORKDIR /app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 拷贝当前所在项目根目录到app目录&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;COPY . /app&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 全局安装pm2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN npm install pm2 -g&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EXPOSE &lt;span style=&#34;color:#ae81ff&#34;&gt;8081&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#使用pm2启动nodejs，如果没有--no-daemon参数docker启动后就退出&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CMD &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pm2-runtime&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dest/server.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--no-daemon&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ENTRYPOINT [&amp;#34;node&amp;#34;, &amp;#34;server.js&amp;#34;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者在Dockerfile中不添加CMD命令，可以在启动docker时执行命令：&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;docker run --name ks -ti -p 8081:8081 kser:pm2 pm2-runtime dest/server.js&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;如果是后台运行的docker:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;docker run --name ks -d -p 8081:8081 kser:pm2 pm2-runtime dest/server.js&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;-d&lt;/code&gt;选项是后台运行&lt;/p&gt;&#xA;&lt;p&gt;需要进入到docker查看pm2运行情况&#xA;，可以通过&lt;code&gt;docker exec -ti ks /bin/sh&lt;/code&gt;查看运行的容器内部情况&#xA;&lt;img src=&#34;https://qn-img.hopefly.top/pm2running.png&#34; alt=&#34;pm2运行情况&#34;&gt;&lt;/p&gt;&#xA;&lt;h1 id=&#34;要不要在docker中使用pm2运行nodejs&#34;&gt;要不要在docker中使用pm2运行nodejs&lt;/h1&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;pm2可以监控nodejs进程，如果进程挂了，可以自动重启&lt;/li&gt;&#xA;&lt;li&gt;pm2可以设置启动的nodejs进程个数，提高服务性能&lt;/li&gt;&#xA;&lt;li&gt;pm2可以设置日志记录&lt;/li&gt;&#xA;&lt;li&gt;pm2可以设置端口，避免端口冲突&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;docker已经提供了自动重启的功能，可以这样启动nodejs服务：&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;docker run --name ks -d --restart=always -p 8081:8081 kser:pm2 pm2-runtime dest/server.js&lt;/code&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么需要RPC，而不是简单的HTTP接口</title>
      <link>https://blog.hopefly.top/blogdetail/5a375f52e9a4cf008860c09b/</link>
      <pubDate>Mon, 18 Dec 2017 06:25:22 +0000</pubDate>
      <guid>https://blog.hopefly.top/blogdetail/5a375f52e9a4cf008860c09b/</guid>
      <description>&lt;p&gt;目前有很多Java的RPC框架，有基于Json的，有基于XML，也有基于二进制对象的。&lt;/p&gt;&#xA;&lt;p&gt;论复杂度，RPC框架肯定是高于简单的HTTP接口的。但毋庸置疑，HTTP接口由于受限于HTTP协议，需要带HTTP请求头，导致传输起来效率或者说安全性不如RPC。&lt;/p&gt;&#xA;&lt;p&gt;现在问题是，遇到怎样的瓶颈了才需要或者说更适合用RPC（比如像阿里这么大的请求并发量，简单的HTTP肯定达不到预期），但问题是大家所在的公司，要有像阿里这么大的量是比较少的，甚至说1/1000的量可能都没有，那我们还需要使用RPC吗？&lt;/p&gt;&#xA;&lt;p&gt;技术应该不是为了使用新技术而去使用，而应该是旧技术存在某些瓶颈，存在难以支撑或者扩展性越老越差等问题暴露出来之后，用新技术来进行解决。&lt;/p&gt;&#xA;&lt;p&gt;那RPC最大的优点，或者说它相比简单的HTTP接口，它的优势、更适合它的业务场景是怎样呢？简单的HTTP又哪里不足，哪些场景明显不太适合呢？&lt;/p&gt;&#xA;&lt;p&gt;RPC=Remote Produce Call 是一种技术的概念名词. HTTP是一种协议,RPC可以通过HTTP来实现,也可以通过Socket自己实现一套协议来实现.所以楼主可以换一个问法,为何RPC还有除HTTP 之外的实现法,有何必要.毕竟除了HTTP实现外,私有协议不具备通用性.那么我想唯一的答案就在于HTTP不能满足其业务场景的地方,所以这个就要具体 案例具体分析了.&lt;/p&gt;&#xA;&lt;p&gt;http接口是在接口不多、系统与系统交互较少的情况下，解决信息孤岛初期常使用的一种通信手段；优点就是简单、直接、开发方便。利用现成的http协议 进行传输。但是如果是一个大型的网站，内部子系统较多、接口非常多的情况下，RPC框架的好处就显示出来了，首先就是长链接，不必每次通信都要像http 一样去3次握手什么的，减少了网络开销；其次就是RPC框架一般都有注册中心，有丰富的监控管理；发布、下线接口、动态扩展等，对调用方来说是无感知、统 一化的操作。第三个来说就是安全性。最后就是最近流行的服务化架构、服务化治理，RPC框架是一个强力的支撑。&lt;/p&gt;&#xA;&lt;p&gt;rpc是一种概念，http也是rpc实现的一种方式。论复杂度，dubbo/hessian用起来是超级简单的。最近用dubbo和hessian比较多，http的几乎都被废弃了。&lt;/p&gt;&#xA;&lt;p&gt;至于为什么用，其实很简单，业务场景不一样。我最早的单位所有的代码都在一个工程里，一次要发布几百m的代码。这种架构是非常有利于小程序的。但是我们为什么要应用rpc层呢，一个功能，一套代码下来不就解决了么？我觉得有几个好处：&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;1 灵活部署&lt;/strong&gt; &lt;strong&gt;2 解耦&lt;/strong&gt; 至于为什么，当你用到的时候，你会体会。&lt;/p&gt;&#xA;&lt;p&gt;系统做大了，肯定是需要做微服务的。 现在我们做电商就是这样，单独有一个订单系统，支付系统，商品系统，用户系统。都是分开部署，单独上线的。 但我们交互是用HTTP接口来交互的，我想转用RPC，但问题是我现在还没发现为什么需要用RPC，我还没能理解它的作用和意义。&lt;/p&gt;&#xA;&lt;p&gt;用http交互其实就已经属于rpc了。&lt;/p&gt;&#xA;&lt;p&gt;RPC:远程过程调用。RPC的核心并不在于使用什么协议。RPC的目的是让你在本地调用远程的方法，而对你来说这个调用是透明的，你并不知道这个调用的方法是部署哪里。通过RPC能解耦服务，这才是使用RPC的真正目的。RPC的原理主要用到了动态代理模式，至于http协议，只是传输协议而已。简单的实现可以参考spring remoting，复杂的实现可以参考dubbo。&lt;/p&gt;&#xA;&lt;p&gt;RPC是一个软件结构概念，是构建分布式应用的理论基础。就好比为啥你家可以用到发电厂发出来的电？是因为电是可以传输的。至于用铜线还是用铁丝还是其他 种类的导线，也就是用http还是用其他协议的问题了。这个要看什么场景，对性能要求怎么样。比如在java中的最基本的就是RMI技术，它是java原 生的应用层分布式技术。我们可以肯定的是在传输性能方面，RMI的性能是优于HTTP的。那为啥很少用到这个技术？那是因为用这个有很多局限性，首先它要 保证传输的两端都要要用java实现，且两边需要有相同的对象类型和代理接口，不需要容器，但是加大了编程的难度，在应用内部的各个子系统之间还是会看到 他的身影，比如EJB就是基于rmi技术的。这就与目前的bs架构的软件大相径庭。用http必须要服务端位于http容器里面，这样减少了网络传输方面 的开发，只需要关注业务开发即可。所以在架构一个软件的时候，不能一定根据需求选定技术。&lt;/p&gt;&#xA;&lt;p&gt;转自：https://www.cnblogs.com/winner-0715/p/5847638.html&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
