<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>个人生活记录分享</title>
  
  <subtitle>尺有所短，寸有所长。物有所不足，智有所不明。</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://www.bkduck.cn/"/>
  <updated>2022-03-04T16:00:00.000Z</updated>
  <id>https://www.bkduck.cn/</id>
  
  <author>
    <name>bkduck</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Hexo博客SEO优化-百度收录</title>
    <link href="https://www.bkduck.cn/web/hexo-seo/"/>
    <id>https://www.bkduck.cn/web/hexo-seo/</id>
    <published>2022-04-13T16:00:00.000Z</published>
    <updated>2022-03-04T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本文讲解的SEO主要基于<code>Hexo</code> + <code>Fluid</code>博库主题，收集整理相关百度收录和Google收录的操作步骤。至于<code>Hexo</code>的相关知识，同学们需要自行度娘，主要涉及sitemap自动提交的npm安装。对于百度站长和Google站长管理平台收录操作，相信都是通用的！</p><blockquote><p>搜索引擎优化（英语：search engine optimization，缩写为SEO），是一种通过了解搜索引擎的运作规则来调整网站，以及提高目的网站在有关搜索引擎内排名的方式。由于不少研究发现，搜索引擎的用户往往只会留意搜索结果最前面的几个条目，所以不少网站都希望通过各种形式来影响搜索引擎的排序，让自己的网站可以有优秀的搜索排名。当中尤以各种依靠广告维生的网站为甚。</p><p><strong>搜索引擎优化</strong><a href="https://zh.wikipedia.org/wiki/搜尋引擎最佳化">维基百科</a></p></blockquote><h2 id="准备事项"><a href="#准备事项" class="headerlink" title="准备事项"></a>准备事项</h2><h3 id="账号注册"><a href="#账号注册" class="headerlink" title="账号注册"></a>账号注册</h3><ul><li>注册百度站长，先注册百度账号 <a href="https://passport.baidu.com/">百度</a></li><li>注册google账号 <a href="https://myaccount.google.com/">google</a></li><li>查看网站收录情况 <code>site:www.yoursite.com</code> 对应搜索引擎输入您的站点域名</li></ul><p><img src="https://static.bkduck.cn/img/blog/202204141643221649925802_gmC1TD.png" alt="image-20220414120246816"></p><h3 id="安装-sitemap"><a href="#安装-sitemap" class="headerlink" title="安装 sitemap"></a>安装 sitemap</h3><p>为了能够让我们的站点被收录，我们需要为我们的hexo博客安装相关支持的包。这两个包分别为：</p><ul><li>hexo-generator-sitemap 【该包用于支持Google搜索引擎】</li><li>hexo-generator-baidu-sitemap 【该包用于支持百度搜索引擎】</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install hexo-generator-sitemap --save<br>npm install hexo-generator-baidu-sitemap --save<br></code></pre></td></tr></table></figure><blockquote><p>sitemap：中文译为“站点地图”</p><p>站点地图是一种文件，您可以通过该文件列出您网站上的网页，从而将您网站内容的组织架构告知 Google 和其他搜索引擎。Googlebot 等搜索引擎网页抓取工具会读取此文件，以便更加智能地抓取您的网站。</p></blockquote><h3 id="hexo-配置文件"><a href="#hexo-配置文件" class="headerlink" title="hexo 配置文件"></a>hexo 配置文件</h3><ol><li><p>根目录_config.yaml添加如下的配置（注意每行的空格）生成对应xml文件</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># sitemap</span><br><span class="hljs-attr">sitemap:</span><br> <span class="hljs-attr">path:</span> <span class="hljs-string">sitemap.xml</span><br><span class="hljs-attr">baidusitemap:</span><br> <span class="hljs-attr">path:</span> <span class="hljs-string">baidusitemap.xml</span><br></code></pre></td></tr></table></figure></li><li><p>修改文章链接,找到关键字 <code>permalink</code>，并修改为如下形式：(此步骤按个人喜好)</p><p>SEO搜索引擎优化认为，网站的最佳结构是用户从首页点击三次就可以到达任何一个页面，但是我们使用hexo编译的站点默认打开文章的url是“sitename/year/mounth/day/title”四层的结构，这样的url结构很不利于SEO，爬虫就会经常爬不到我们的文章，于是，我们可以将url直接改成“sitename/title”的形式，并且title最好是用英文（中文的url会出现好多乱码，我这方面还有待改进）。</p></li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># 基于以上原因，我在根目录的站点配置文件下修改url设置如下：</span><br><span class="hljs-attr">url:</span> <span class="hljs-string">https://www.bkduck.cn</span><br><span class="hljs-attr">root:</span> <span class="hljs-string">/</span><br><span class="hljs-attr">permalink:</span> <span class="hljs-string">:title/</span><br><span class="hljs-attr">permalink_defaults:</span><br></code></pre></td></tr></table></figure><ol start="3"><li><p>博客根目录中的 source 文件夹下，添加蜘蛛协议 “robots.txt” 的文件，内容如下：</p><figure class="highlight dts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs dts">User-agent: *<br><span class="hljs-symbol">Allow:</span> /<br><span class="hljs-symbol">Allow:</span> <span class="hljs-meta-keyword">/categories/</span><br><span class="hljs-symbol">Allow:</span> <span class="hljs-meta-keyword">/tags/</span><br><span class="hljs-symbol">Allow:</span> <span class="hljs-meta-keyword">/archives/</span><br><span class="hljs-symbol">Allow:</span> <span class="hljs-meta-keyword">/about/</span><br><span class="hljs-symbol"></span><br><span class="hljs-symbol">Disallow:</span> <span class="hljs-meta-keyword">/vendors/</span><br><span class="hljs-symbol">Disallow:</span> <span class="hljs-meta-keyword">/js/</span><br><span class="hljs-symbol">Disallow:</span> <span class="hljs-meta-keyword">/css/</span><br><span class="hljs-symbol">Disallow:</span> <span class="hljs-meta-keyword">/fonts/</span><br><span class="hljs-symbol">Disallow:</span> <span class="hljs-meta-keyword">/vendors/</span><br><span class="hljs-symbol">Disallow:</span> <span class="hljs-meta-keyword">/fancybox/</span><br><br><span class="hljs-meta"># 记得替换成你的域名</span><br><span class="hljs-symbol">Sitemap:</span> http:<span class="hljs-comment">//yoursite.com/sitemap.xml</span><br><span class="hljs-symbol">Sitemap:</span> http:<span class="hljs-comment">//yoursite.com/baidusitemap.xml主动推送</span><br></code></pre></td></tr></table></figure></li></ol><h3 id="主动推送"><a href="#主动推送" class="headerlink" title="主动推送"></a>主动推送</h3><p>新链接由我们主动推送给百度，此步骤需提前注册百度账号并拿到权限token</p><ol><li><p>安装插件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell"><span class="hljs-meta">#</span><span class="bash"> 会生成baidu_urls.txt目录</span><br>npm install hexo-baidu-url-submit --save<br></code></pre></td></tr></table></figure></li><li><p>根目录站点配置文件_config.yaml中新增如下字段</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">baidu_url_submit:</span><br>  <span class="hljs-attr">count:</span> <span class="hljs-number">100</span> <span class="hljs-comment"># 提交最新的一个链接</span><br>  <span class="hljs-attr">host:</span> <span class="hljs-string">www.bkduck.cn</span> <span class="hljs-comment"># 在百度站长平台中注册的域名</span><br>  <span class="hljs-attr">token:</span> <span class="hljs-string">lY..........Fk</span> <span class="hljs-comment"># 请注意这是您的秘钥，所以请不要把博客源代码发布在公众仓库里!</span><br>  <span class="hljs-attr">path:</span> <span class="hljs-string">baidu_urls.txt</span> <span class="hljs-comment"># 文本文档的地址，新链接会保存在此文本文档里</span><br></code></pre></td></tr></table></figure></li><li><p>域名和秘钥可以在站长工具平台的连接提交中的接口调用地址中找到，即对应host与token后面的字段。<a href="https://ziyuan.baidu.com/linksubmit/index?site=">link</a></p></li></ol><p><img src="https://static.bkduck.cn/img/blog/202204141643281649925808_7a1bMM.png" alt="image-20220414161106646"></p><ol start="4"><li><p>根目录_config.yaml, 修改deploy配置</p><p>以后每次执行<code>hexo d</code>，新的链接就会主动推送给百度，然后百度就会更快地派爬虫来发现你站点中的新链接，可以在第一时间收录新建的链接。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># Deployment</span><br><span class="hljs-comment"># 使用hexo deploy指令后，自动推送给百度</span><br><span class="hljs-comment">## Docs: https://hexo.io/docs/deployment.html</span><br><span class="hljs-attr">deploy:</span><br>  <span class="hljs-attr">type:</span> <span class="hljs-string">&#x27;baidu_url_submitter&#x27;</span><br></code></pre></td></tr></table></figure></li></ol><h2 id="百度收录"><a href="#百度收录" class="headerlink" title="百度收录"></a>百度收录</h2><ol><li><p>建立站点管理，打开<a href="https://ziyuan.baidu.com/site/siteadd#/">link</a>, 并填写你的域名</p><p><img src="https://static.bkduck.cn/img/blog/202204141643321649925812_j7ELh4.png" alt="image-20220414163159782"></p></li><li><p>勾选对应站点属性</p></li></ol><p><img src="https://static.bkduck.cn/img/blog/202204141643371649925817_aNZSl8.png" alt="image-20220414163401616"></p><ol start="3"><li><p>下载认证文件，并放在网站根目录,若是html文件,hexo需要在html文件添加<code>layout: false</code>,防止转换</p><p><img src="https://static.bkduck.cn/img/blog/202204141643431649925823_sjpOup.png" alt="image-20220414163450833"></p></li></ol><h2 id="google收录"><a href="#google收录" class="headerlink" title="google收录"></a>google收录</h2><p>google收录就相对简单点，只要先按上面步骤[安装sitemap](# 安装sitemap)生成好sitemap.xml，然后提交到google后台就ok👌.</p><ol><li><p>验证抓取域名</p><p>打开<a href="https://search.google.com/search-console/welcome?hl=zh-CN&utm_source=wmx&utm_medium=deprecation-pane&utm_content=home">google search console</a>, 并添加需要抓取的域名，并把googlexxx.html文件放在网站根目录 如下图</p></li></ol><p><img src="https://static.bkduck.cn/img/blog/202204141643481649925828_f169lC.png" alt="image-20220414105359381"></p><ol start="2"><li>然后点击<code>验证</code>按钮，成功则返回下图：</li></ol><p><img src="https://static.bkduck.cn/img/blog/202204141643531649925833_NUq7ew.png" alt="image-20220414105808142"></p><ol start="3"><li>点击上图<code>前往资源页面</code>，或通过<a href="https://search.google.com/search-console/sitemaps?resource_id=">link</a>进入，并提交你的<code>sitemap.xml</code></li></ol><p><img src="https://static.bkduck.cn/img/blog/202204141643581649925838_kwcZUY.png" alt="image-20220414110347708"></p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>若以上步骤都完成了，网站就能正常被收录啦，赶紧用<code>site:www.yoursite.com</code>看看具体情况呗，相信网站流量很快破百万，升职ceo，迎娶白富美！！！💪🏻💪🏻💪🏻</p>]]></content>
    
    <summary type="html">
    
      本文讲解的SEO主要基于`Hexo` + `Fluid`博库主题，收集整理相关百度收录和Google收录的操作步骤。至于`Hexo`的相关知识，同学们需要自行度娘，主要涉及sitemap自动提交的npm安装。对于百度站长和Google站长管理平台收录操作，相信都是通用的！
    
    </summary>
    
    
      <category term="hexo" scheme="https://www.bkduck.cn/categories/hexo/"/>
    
    
      <category term="seo" scheme="https://www.bkduck.cn/tags/seo/"/>
    
      <category term="hexo" scheme="https://www.bkduck.cn/tags/hexo/"/>
    
  </entry>
  
  <entry>
    <title>监控系统-prometheus</title>
    <link href="https://www.bkduck.cn/microservices/prometheus/"/>
    <id>https://www.bkduck.cn/microservices/prometheus/</id>
    <published>2022-03-27T16:00:00.000Z</published>
    <updated>2022-03-27T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-Prometheus介绍"><a href="#1-Prometheus介绍" class="headerlink" title="1. Prometheus介绍"></a>1. Prometheus介绍</h2><p>Prometheus是一个开源的系统监控和警报工具包。自2012年启动以来，许多公司和组织都采用了Prometheus，该项目拥有非常活跃的开发人员和用户社区。它现在是一个独立的开源项目，独立于任何公司进行维护。Prometheus于2016年加入云原生计算基金会，成为继Kubernetes之后的第二个托管项目。</p><h2 id="2-Prometheus-特点"><a href="#2-Prometheus-特点" class="headerlink" title="2. Prometheus 特点"></a>2. Prometheus 特点</h2><p>Prometheus是一个开源的完整的监控解决方案，基于的是中央化的规则计算、统一分析和告警的新模型。相对于传统监控系统，Prometheus有以下优点。</p><h3 id="2-1-主要功能"><a href="#2-1-主要功能" class="headerlink" title="2.1 主要功能"></a>2.1 主要功能</h3><ul><li>多维 <a href="https://prometheus.io/docs/concepts/data_model/">数据模型</a>（时序由 metric 名字和 k/v 的 labels 构成）。</li><li>灵活的查询语句（<a href="https://prometheus.io/docs/querying/basics/">PromQL</a>）。</li><li>无依赖存储，支持 local 和 remote 不同模型。</li><li>采用 http 协议，使用 pull 模式，拉取数据，简单易懂。</li><li>监控目标，可以采用服务发现或静态配置的方式。</li><li>支持多种统计数据模型，图形化友好。</li><li>复杂环境，支持手动push模式，通过pushgateway组件收集，server拉取pushgateway</li></ul><h3 id="2-2-主要组件"><a href="#2-2-主要组件" class="headerlink" title="2.2 主要组件"></a>2.2 主要组件</h3><ul><li><p>Prometheus Server：主服务器，负责收集和存储时间序列数据</p></li><li><p>Client libraries：应用程序代码插桩，将监控指标嵌入到被监控应用程序中</p></li><li><p>Pushgateway：推送网关，为支持short-lived作业提供一个推送网关</p></li><li><p>Exporter：专门为一些应用开发的数据摄取组件-exporter，例如：HAProxy、StatsD、Graphite等等 <a href="https://prometheus.io/docs/instrumenting/exporters/">exporter</a></p></li><li><p>Altermanager：专门用于处理alert的组件 <a href="https://github.com/prometheus/alertmanager">altermanager</a></p></li></ul><h3 id="2-3-原理图"><a href="#2-3-原理图" class="headerlink" title="2.3 原理图"></a>2.3 原理图</h3><p>从这个架构图，也可以看出 Prometheus 的主要模块包含， Server, Exporters, Pushgateway, PromQL, Alertmanager, WebUI 等</p><p><img src="https://static.bkduck.cn/img/blog/202204111048431649645323_3LqGKa.png" alt="image-20220408170507337"></p><p>它大致使用逻辑是这样：</p><ol><li>Prometheus server 定期从静态配置的 targets 或者服务发现的 targets 拉取数据。 （pull 模式 + pushgateway）</li><li>当新拉取的数据大于配置内存缓存区的时候，Prometheus 会将数据持久化到磁盘（如果使用 remote storage 将持久化到云端）。</li><li>Prometheus 可以配置 rules，然后定时查询数据，当条件触发的时候，会将 alert 推送到配置的 Alertmanager。</li><li>Alertmanager 收到警告的时候，可以根据配置，聚合，去重，降噪，最后发送警告。</li><li>可以使用 API， Prometheus Console 或者 Grafana 查询和聚合数据。</li></ol><p><strong>注意事项</strong></p><ul><li>Prometheus 的数据是基于时序的 float64 的值，如果你的数据值有更多类型，无法满足。</li><li>Prometheus 不适合做审计计费，因为它的数据是按一定时间采集的，关注的更多是系统的运行瞬时状态以及趋势，即使有少量数据没有采集也能容忍，但是审计计费需要记录每个请求，并且数据长期存储，这个 Prometheus 无法满足，可能需要采用专门的审计系统。</li></ul><h2 id="静态配置和动态配置"><a href="#静态配置和动态配置" class="headerlink" title="静态配置和动态配置"></a>静态配置和动态配置</h2><h3 id="静态配置"><a href="#静态配置" class="headerlink" title="静态配置"></a>静态配置</h3><p>Prometheus通过static_configs来设定静态方式的监控对象，比如如下Node Exporter的监控数据抓取的配置，其特点是Node Exporter提供的监控数据的/metrics的HTTP服务的IP和端口号都是固定的，在这种情况下可以使用静态配置</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">scrape_configs:</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">&#x27;node_exporter&#x27;</span><br>    <span class="hljs-attr">static_configs:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">&#x27;192.168.31.242:9100&#x27;</span>]<br></code></pre></td></tr></table></figure><h3 id="动态配置-服务自发现"><a href="#动态配置-服务自发现" class="headerlink" title="动态配置-服务自发现"></a>动态配置-服务自发现</h3><p>动态配置是相较于静态配置的说法，在Prometheus中则是结合服务发现来实现的，将服务发现的这个功能和机制分离出去，直接利用现有的服务发现的功能框架比如consul来确认可能会动态变化的监控对象，这样就做到了所谓的动态配置。</p><p><img src="https://static.bkduck.cn/img/blog/202204111049071649645347_kz37SU.png" alt="image-20220411100501507"></p><p>常见的动态配置的使用方式如上图所示：</p><ol><li>各种服务直接在SD（服务发现能力提供者：比如consule）上进行服务注册 </li><li>Prometheus不直接管理哪些对象需要监控，它直接向SD去获取需要监控的对象的列表</li><li>SD会返回给Prometheus需要监控的对象列表</li><li>Prometheus根据返回的监控对象列表使用Pull的方式进行监控数据的收集</li></ol><h4 id="基于consul服务发现"><a href="#基于consul服务发现" class="headerlink" title="基于consul服务发现"></a>基于consul服务发现</h4><p>Consul提供了服务发现和注册的功能，而Prometheus也支持通过Consul的方式来进行动态配置</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">consul_sd_configs:</span><br>     <span class="hljs-bullet">-</span> <span class="hljs-attr">server:</span> <span class="hljs-string">Consul服务的IP:Consul服务的端口号</span><br>       <span class="hljs-attr">services:</span><br>         <span class="hljs-bullet">-</span> <span class="hljs-string">node_exporter</span><br></code></pre></td></tr></table></figure><h4 id="基本etcd-confd"><a href="#基本etcd-confd" class="headerlink" title="基本etcd+confd"></a>基本etcd+confd</h4><p>根据服务注册metrics到<a href="https://so.csdn.net/so/search?q=etcd&spm=1001.2101.3001.7020">etcd</a>，confd 拉取etcd的配置，自动生成配置文件到promethoues，然后热加载来提供自动的指标监控。 <a href="https://github.com/ywanbing/golearning/tree/master/etcd_confd_prometheus">github</a></p><ol><li>启动confd</li></ol><p>设置 confd 读取的配置和模版的文件夹，并监听 etcd 的变化。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">confd -confdir ./confd/ -config-file ./confd/conf.d/prometheus.json.toml -backend etcdv3  -watch -node http://127.0.0.1:2379  &amp;<br></code></pre></td></tr></table></figure><ol start="2"><li><p>启动 prometheus</p><p>覆盖 /etc/prometheus/prometheus.yml 的文件, 并在 prometheus 的 bin 下面启动:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sh">&gt;&gt; mkdir /prometheus<br>&gt;&gt; prometheus --web.enable-lifecycle \<br>    --config.file=/etc/prometheus/prometheus.yml \<br>    --storage.tsdb.path=/prometheus<br></code></pre></td></tr></table></figure></li><li><p>Prometheus 配置文件</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">global:</span><br>  <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">15s</span><br>  <span class="hljs-attr">scrape_timeout:</span> <span class="hljs-string">10s</span><br>  <span class="hljs-attr">evaluation_interval:</span> <span class="hljs-string">15s</span><br><br><span class="hljs-attr">scrape_configs:</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">&#x27;test&#x27;</span><br>    <span class="hljs-attr">file_sd_configs:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">files:</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">/tmp/prometheus/targets/target_*.json</span><br></code></pre></td></tr></table></figure><h3 id="其他配置"><a href="#其他配置" class="headerlink" title="其他配置"></a>其他配置</h3><p>更多有效<a href="https://github.com/prometheus/prometheus/blob/release-2.12/config/testdata/conf.good.yml">配置示例</a></p><figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs 1c"><span class="hljs-meta">#Prometheus数据源的配置主要分为静态配置和动态发现, 常用的为以下几类</span><br><span class="hljs-number">1</span>）static_configs: <span class="hljs-meta">#静态服务发现</span><br><span class="hljs-number">2</span>）file_sd_configs: <span class="hljs-meta">#文件服务发现</span><br><span class="hljs-number">3</span>）dns_sd_configs: DNS <span class="hljs-meta">#服务发现</span><br><span class="hljs-number">4</span>）kubernetes_sd_configs: <span class="hljs-meta">#Kubernetes 服务发现</span><br><span class="hljs-number">5</span>）consul_sd_configs: Consul <span class="hljs-meta">#服务发现</span><br></code></pre></td></tr></table></figure></li></ol>]]></content>
    
    <summary type="html">
    
      Prometheus是一个开源的系统监控和警报工具包。自2012年启动以来，许多公司和组织都采用了Prometheus，该项目拥有非常活跃的开发人员和用户社区。它现在是一个独立的开源项目，独立于任何公司进行维护。Prometheus于2016年加入云原生计算基金会，成为继Kubernetes之后的第二个托管项目。
    
    </summary>
    
    
      <category term="微服务" scheme="https://www.bkduck.cn/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    
      <category term="微服务" scheme="https://www.bkduck.cn/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="prometheus" scheme="https://www.bkduck.cn/tags/prometheus/"/>
    
      <category term="监控系统" scheme="https://www.bkduck.cn/tags/%E7%9B%91%E6%8E%A7%E7%B3%BB%E7%BB%9F/"/>
    
  </entry>
  
  <entry>
    <title>链路追踪系统-zipkin</title>
    <link href="https://www.bkduck.cn/microservices/zipkin/"/>
    <id>https://www.bkduck.cn/microservices/zipkin/</id>
    <published>2022-03-23T16:00:00.000Z</published>
    <updated>2021-03-23T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="OpenTracing原理"><a href="#OpenTracing原理" class="headerlink" title="OpenTracing原理"></a>OpenTracing原理</h2><p>分布式链路追踪就是将一次分布式请求还原成调用链路，将一次分布式请求的调用情况集中展示，比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等。</p><p><strong>链路跟踪主要功能：</strong></p><ul><li><strong>故障快速定位</strong>：可以通过调用链结合业务日志快速定位错误信息。</li><li><strong>链路性能可视化</strong>：各个阶段链路耗时、服务依赖关系可以通过可视化界面展现出来。</li><li><strong>链路分析</strong>：通过分析链路耗时、服务依赖关系可以得到用户的行为路径，汇总分析应用在很多业务场景。</li></ul><h3 id="span概念"><a href="#span概念" class="headerlink" title="span概念"></a>span概念</h3><p>图中一条完整的链路是：chrome -&gt; 服务A -&gt; 服务B -&gt; 服务C -&gt; 服务D -&gt; 服务E -&gt; 服务C -&gt; 服务A -&gt; chrome。服务间经过的局部链路构成了一条完整的链路，其中每一条局部链路都用一个全局唯一的traceid来标识。</p><p>同一层级parent id相同，span id不同，span id从小到大表示请求的顺序，从下图中可以很明显看出服务A是先调了服务B然后再调用了C。</p><p>上下层级代表调用关系，如下图服务C的span id为2，服务D的parent id为2，这就表示服务C和服务D形成了父子关系，很明显是服务C调用了服务D。</p><p><img src="https://static.bkduck.cn/img/blog/202203242252241648133544_PN6gL8.png" alt=""></p><p><strong>总结：通过事先在日志中埋点，找出相同traceId的日志，再加上parent id和span id就可以将一条完整的请求调用链串联起来。</strong></p><h3 id="Annotations"><a href="#Annotations" class="headerlink" title="Annotations"></a>Annotations</h3><p>Dapper中还定义了annotation的概念，用于用户自定义事件，用来辅助定位问题。</p><p><strong>通常包含四个注解信息</strong>：</p><p>cs：Client Start，表示客户端发起请求；</p><p>sr：ServerReceived，表示服务端收到请求；</p><p>ss：Server Send，表示服务端完成处理，并将结果发送给客户端；</p><p>cr：ClientReceived，表示客户端获取到服务端返回信息；</p><p><img src="https://static.bkduck.cn/img/blog/202203242255211648133721_XCA8Ag.png" alt=""></p><h3 id="采样"><a href="#采样" class="headerlink" title="采样"></a>采样</h3><p>由于每一个请求都会生成一个链路，为了减少性能消耗，避免存储资源的浪费，dapper并不会上报所有的span数据，而是使用采样的方式。举个例子，每秒有1000个请求访问系统，如果设置采样率为1/1000，那么只会上报一个请求到存储端。</p><p><img src="https://static.bkduck.cn/img/blog/202203242257231648133843_eHEXle.png" alt=""></p><h2 id="zipkin原理"><a href="#zipkin原理" class="headerlink" title="zipkin原理"></a>zipkin原理</h2><h3 id="基本结构"><a href="#基本结构" class="headerlink" title="基本结构"></a>基本结构</h3><p>zipkin(服务端)包含四个组件，分别是collector、storage、search、web UI。</p><ul><li>collector 就是信息收集器,作为一个守护进程，它会时刻等待客户端传递过来的追踪数据，对这些数据进行验证、存储以及创建查询需要的索引。</li><li>storage  是存储组件。zipkin 默认直接将数据存在内存中，此外支持使用Cassandra、ElasticSearch 和 Mysql。</li><li>search 是一个查询进程，它提供了简单的JSON API来供外部调用查询。</li><li>web UI 是zipkin的服务端展示平台，主要调用search提供的接口，用图表将链路信息清晰地展示给开发人员。</li></ul><p><img src="https://static.bkduck.cn/img/blog/202203242249081648133348_HibGyT.awebp" alt=""></p><h3 id="zipkin请求过程"><a href="#zipkin请求过程" class="headerlink" title="zipkin请求过程"></a>zipkin请求过程</h3><p>一个 span 表示一次服务调用，那么追踪器必定是被服务调用发起的动作触发，生成基本信息，同时为了追踪服务提供方对其他服务的调用情况，便需要传递本次追踪链路的traceId和本次调用的span-id。服务提供方完成服务将结果响应给调用方时，需要根据调用发起时记录的时间戳与当前时间戳计算本次服务的持续时间进行记录，至此这次调用的追踪span完成，就可以发送给zipkin服务端了。但是需要注意的是，发送span给zipkin collector不得影响此次业务结果，其发送成功与否跟业务无关，因此这里需要采用异步的方式发送，防止追踪系统发送延迟与发送失败导致用户系统的延迟与中断</p><p><img src="https://static.bkduck.cn/img/blog/202203242306511648134411_ayVsC9.awebp" alt=""></p><p>可以看出服务A请求服务B时先被追踪器拦截，记录tag信息、时间戳，同时将追踪标识添加进http header中传递给服务B，在服务B响应后，记录持续时间，最终采取异步的方式发送给zipkin收集器。span从被追踪的服务传送到Zipkin收集器有三种主要的传送方式：http、Kafka以及Scribe（Facebook开源的日志收集系统）。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://www.daimajiaoliu.com/series/microservice/479c3a1d51003fc">https://www.daimajiaoliu.com/series/microservice/479c3a1d51003fc</a></p><p><a href="https://juejin.cn/post/6844903761438048269">一文搞懂基于zipkin的分布式追踪系统原理与实现</a></p><p><a href="https://toutiao.io/posts/eaiifaf/preview">10张图就可以搞懂分布式链路追踪系统原理</a></p><p><a href="https://www.daimajiaoliu.com/series/microservice/479c3a1d51003fc">go-kit微服务请求跟踪实现</a></p>]]></content>
    
    <summary type="html">
    
      分布式链路追踪就是将一次分布式请求还原成调用链路，将一次分布式请求的调用情况集中展示，比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等。
    
    </summary>
    
    
      <category term="微服务" scheme="https://www.bkduck.cn/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    
      <category term="微服务" scheme="https://www.bkduck.cn/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
      <category term="zipkin" scheme="https://www.bkduck.cn/tags/zipkin/"/>
    
      <category term="opentracing" scheme="https://www.bkduck.cn/tags/opentracing/"/>
    
      <category term="链路跟踪系统" scheme="https://www.bkduck.cn/tags/%E9%93%BE%E8%B7%AF%E8%B7%9F%E8%B8%AA%E7%B3%BB%E7%BB%9F/"/>
    
  </entry>
  
  <entry>
    <title>腾讯云轻量服务器k3s搭建hexo博客</title>
    <link href="https://www.bkduck.cn/web/k3s-build-hexo/"/>
    <id>https://www.bkduck.cn/web/k3s-build-hexo/</id>
    <published>2022-03-04T16:00:00.000Z</published>
    <updated>2022-03-04T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="腾讯云轻量服务器k3s搭建hexo博客"><a href="#腾讯云轻量服务器k3s搭建hexo博客" class="headerlink" title="腾讯云轻量服务器k3s搭建hexo博客"></a>腾讯云轻量服务器k3s搭建hexo博客</h1><p>最近想入手学习<code>k8s</code>，经网上一顿猛如虎搜索后，奈何钱包不够，只好选择腾讯云轻量服务器 2g4核的<code>k3s</code>集成环境。<code>CI/CD</code>持续集成选择了<code>GitHub Actions</code> ，部署好后，只需提交分支到<code>GitHub</code>就自动构建镜像并且发布到线上服务器。至于镜像仓库，还是秉持白嫖原则，使用腾讯云自带的镜像仓。最后博客的框架是<code>Hexo + Fluid</code>静态博客，评论系统则使用<code>Waline</code>。</p><p>线上博客请移步<a href="https://bkduck.cn">www.bkduck.cn</a></p><p>看官们若想折腾一翻，请移步下方教程，倒腾一翻，反正首年40元即可获得,马上戳下面链接领取呗~</p><p><a href="https://cloud.tencent.com/act/cps/redirect?redirect=1577&cps_key=3f5175eeea52ff27432360605d2c7f81&from=console">【腾讯云】爆款2核2G云服务器首年40元，企业首购最高获赠300元京东卡</a></p><p><img src="https://static.bkduck.cn/img/blog/202203051540191646466019_Rk0cKE.jpg" alt=""></p><h2 id="事前准备"><a href="#事前准备" class="headerlink" title="事前准备"></a>事前准备</h2><ul><li>本地部署好博客环境<code>hexo</code> + <code>fluid</code>,具体方法自行google或后期再出一期教程</li><li>了解<code>GitHub Actions</code> 的基本语法，可以移步看看阮一峰老师的文章<a href="https://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html">《GitHub Actions 入门教程》</a></li><li>缺少点Like的<code>Github</code>账号，收藏三连防丢失 <a href="https://github.com/bkduck/bkblog">github</a></li><li>腾讯云一键初始化的轻量服务器k3s环境，请按照官方文档一顿操作 [文档](使用应用镜像实践 K3s 容器集群管理)</li><li>免费的线上腾讯云镜像仓库，照旧按官方文档走一遍 <a href="https://cloud.tencent.com/document/product/1207/53038">《管理自定义镜像》</a></li></ul><h2 id="实际操作"><a href="#实际操作" class="headerlink" title="实际操作"></a>实际操作</h2><h3 id="k3s环境部署"><a href="#k3s环境部署" class="headerlink" title="k3s环境部署"></a>k3s环境部署</h3><p>登录线上k3s的环境，执行 Ingress，Deployment，Service创建</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ruby"><span class="hljs-meta">&gt;&gt;</span> rz -be  <span class="hljs-comment"># 选择deployment.yaml,上传到服务器</span><br><span class="hljs-meta">&gt;&gt;</span> kubectl apply -f deployment.yaml<span class="hljs-comment"># 创建必须的资源</span><br></code></pre></td></tr></table></figure><h3 id="代码修改"><a href="#代码修改" class="headerlink" title="代码修改"></a>代码修改</h3><p>镜像仓库调整，全局替换<code>ccr.ccs.tencentyun.com/xxx/blog</code> 变成你的镜像仓地址</p><h3 id="GitHub-Action环境参数配置"><a href="#GitHub-Action环境参数配置" class="headerlink" title="GitHub Action环境参数配置"></a>GitHub Action环境参数配置</h3><p>创建<code>action secrets</code>, 然后在<code>.github/workflows/*.yaml</code> 替换成你创建的secrets</p><p><img src="https://static.bkduck.cn/img/blog/202203051632411646469161_CMhSta.png" alt=""></p><p>一切就绪，push分支到<code>Github</code>,剩下构建镜像，推送镜像，重启k3s的deployment的工作就交给<code>Github Actions</code>完成，cafe啜一口，你的博客构建完成呗！</p><h3 id="waline-评论系统的搭建"><a href="#waline-评论系统的搭建" class="headerlink" title="waline 评论系统的搭建"></a>waline 评论系统的搭建</h3><p>方案很多，可以百度搜下，我的方案是k3s部署<code>mysql</code>，当然生产环境不推荐，原因你懂的，但毕竟看荷包的活，毕竟不用钱，你也可以额试试部署<code>mysql-*.yaml</code> + <code>deployment.yaml</code></p>]]></content>
    
    <summary type="html">
    
      腾讯云轻量服务器k3s搭建hexo博客,Gihub Actions持续交付，博客的框架是`Hexo + Fluid`静态博客
    
    </summary>
    
    
      <category term="k8s" scheme="https://www.bkduck.cn/categories/k8s/"/>
    
    
      <category term="hexo" scheme="https://www.bkduck.cn/tags/hexo/"/>
    
      <category term="k8s" scheme="https://www.bkduck.cn/tags/k8s/"/>
    
  </entry>
  
  <entry>
    <title>Golang进阶系列——栈内存管理</title>
    <link href="https://www.bkduck.cn/golang/stack-memory-management/"/>
    <id>https://www.bkduck.cn/golang/stack-memory-management/</id>
    <published>2021-08-14T16:00:00.000Z</published>
    <updated>2021-08-14T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="内存逃逸分析"><a href="#内存逃逸分析" class="headerlink" title="内存逃逸分析"></a>内存逃逸分析</h2><p>Go 语言的逃逸分析遵循以下两个不变性:</p><ol><li>指向栈对象的指针不能存在于堆中；</li><li>指向栈对象的指针不能在栈对象回收后存活；</li></ol><hr><p>栈空间不足导致的扩容会经历以下几个步骤：</p><ol><li>在内存空间中分配更大的栈内存空间；</li><li>将旧栈中的所有内容复制到新栈中；</li><li><strong>将指向旧栈对应变量的指针重新指向新栈</strong>；</li><li>销毁并回收旧栈的内存空间；</li></ol><p>在扩容的过程中，最重要的是调整指针的第三步，这一步能够保证指向栈的指针的正确性，因为栈中的所有变量内存都会发生变化，所以原本指向栈中变量的指针也需要调整。我们在前面提到过经过逃逸分析的 Go 语言程序的遵循以下不变性 —— <strong>指向栈对象的指针不能存在于堆中</strong>，所以指向栈中变量的指针只能在栈上，我们只需要调整栈中的所有变量就可以保证内存的安全了</p><h2 id="栈拓容和缩容"><a href="#栈拓容和缩容" class="headerlink" title="栈拓容和缩容"></a>栈拓容和缩容</h2><h3 id="栈缩容"><a href="#栈缩容" class="headerlink" title="栈缩容"></a>栈缩容</h3><p>运行时只会在栈内存使用不足 1/4 时进行缩容，新栈的大小会是原始栈的一半，不过如果新栈的大小低于程序的最低限制 2KB，那么缩容的过程就会停止。</p>]]></content>
    
    <summary type="html">
    
      Go程序在运行时刻每个协程维护一个栈，这个栈是预申请的内存段，它被作为一个内存池供开辟内存使用。每个协程的初始栈比较小，协程在运行时候将按照需要进行增长或收缩一个协程开辟在栈上的内存只能在此协程内部被引用，其它协程是无法访问的。
    
    </summary>
    
    
      <category term="Golang" scheme="https://www.bkduck.cn/categories/Golang/"/>
    
    
      <category term="golang" scheme="https://www.bkduck.cn/tags/golang/"/>
    
      <category term="go进阶" scheme="https://www.bkduck.cn/tags/go%E8%BF%9B%E9%98%B6/"/>
    
  </entry>
  
  <entry>
    <title>Golang语法系列——string字符串</title>
    <link href="https://www.bkduck.cn/golang/base-string/"/>
    <id>https://www.bkduck.cn/golang/base-string/</id>
    <published>2021-08-09T16:00:00.000Z</published>
    <updated>2021-08-09T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="常用方法"><a href="#常用方法" class="headerlink" title="常用方法"></a>常用方法</h2><h3 id="修改字符串"><a href="#修改字符串" class="headerlink" title="修改字符串"></a>修改字符串</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-number">1.</span> <span class="hljs-keyword">byte</span>类型<br>s := <span class="hljs-string">&quot;hello&quot;</span><br>s2 := <span class="hljs-string">&quot;你好&quot;</span><br>c := []<span class="hljs-keyword">byte</span>(s)  <span class="hljs-comment">// 将字符串 s 转换为 []byte 类型</span><br>c2 := []<span class="hljs-keyword">rune</span>(s2) <span class="hljs-comment">// 转换中文unicode 占4个字节</span><br>c[<span class="hljs-number">0</span>] = <span class="hljs-string">&#x27;c&#x27;</span><br>s2 := <span class="hljs-keyword">string</span>(c)  <span class="hljs-comment">// 再转换回 string 类型</span><br>fmt.Printf(<span class="hljs-string">&quot;%s\n&quot;</span>, s2)<br><br><span class="hljs-number">2.</span>切片<br>s := <span class="hljs-string">&quot;hello&quot;</span><br>s = <span class="hljs-string">&quot;c&quot;</span> + s[<span class="hljs-number">1</span>:] <span class="hljs-comment">// 字符串虽不能更改，但可进行切片操作</span><br>fmt.Printf(<span class="hljs-string">&quot;%s\n&quot;</span>, s)<br><br></code></pre></td></tr></table></figure><h3 id="连接字符串"><a href="#连接字符串" class="headerlink" title="连接字符串"></a>连接字符串</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-comment">// + 号链接 </span><br>s := <span class="hljs-string">&quot;hello,&quot;</span><br>m := <span class="hljs-string">&quot; world&quot;</span><br>a := s + m<br>fmt.Printf(<span class="hljs-string">&quot;%s\n&quot;</span>, a)<br><br></code></pre></td></tr></table></figure><h3 id="多行字符串"><a href="#多行字符串" class="headerlink" title="多行字符串"></a>多行字符串</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs go">m := <span class="hljs-string">`hello</span><br><span class="hljs-string">  world`</span><br></code></pre></td></tr></table></figure><h3 id="判断字符长度"><a href="#判断字符长度" class="headerlink" title="判断字符长度"></a>判断字符长度</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-comment">// 判断中文</span><br>str := <span class="hljs-string">&quot;hello 你好&quot;</span><br><span class="hljs-keyword">if</span> strlen := utf8.RuneCountInString(str); strlen &gt; <span class="hljs-number">500</span> &#123;<br><span class="hljs-keyword">return</span> error.<span class="hljs-built_in">new</span>(<span class="hljs-string">&#x27;wrong len&#x27;</span>)<br>&#125;<br><span class="hljs-comment">// 判断字符 占一个字节</span><br><span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(str)<br></code></pre></td></tr></table></figure><h3 id="字符串比较-忽略大小写"><a href="#字符串比较-忽略大小写" class="headerlink" title="字符串比较 - 忽略大小写"></a>字符串比较 - 忽略大小写</h3><p>如果要忽略大小写来比较包含文字数据的字节切片(byte slice)，<br>不建议使用<code>bytes</code>包和<code>strings</code>包里的<code>ToUpper()</code>、<code>ToLower()</code>这些函数转换后再用<code>==</code>、<code>byte.Equal()</code>、<code>bytes.Compare()</code>等比较，<code>ToUpper()</code>、<code>ToLower()</code>只能处理英文文字，对其它语言无效。因此建议使用<code>strings.EqualFold()</code>和`bytes.EqualFold()</p><p> 如果要比较用于验证用户数据密钥信息的字节切片时，使用<code>reflact.DeepEqual()</code>、<code>bytes.Equal()</code>、 <code>bytes.Compare()</code>会使应用程序遭受计时攻击(Timing Attack)，可使用<code>crypto/subtle.ConstantTimeCompare()</code>避免泄漏时间信息</p><h3 id="struct-类的String-应用"><a href="#struct-类的String-应用" class="headerlink" title="struct 类的String()应用"></a>struct 类的String()应用</h3><ul><li><p>正确用法</p><figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs swift">#note<br>#如果类型定义了 <span class="hljs-type">String</span>() 方法，它会被用在 fmt.<span class="hljs-type">Printf</span>() 中生成默认的输出：等同于使用格式化描述符 <span class="hljs-operator">%</span>v 产生的输出<span class="hljs-operator">。</span>还有 fmt.<span class="hljs-type">Print</span>() 和 fmt.<span class="hljs-type">Println</span>() 也会自动使用 <span class="hljs-type">String</span>() 方法<span class="hljs-operator">。</span><br><br>package main<br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">&quot;fmt&quot;</span><br><span class="hljs-string">&quot;strconv&quot;</span><br>)<br><br>type <span class="hljs-type">TwoInts</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> </span>&#123;<br>a int<br>b int<br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span>()</span> &#123;<br>two1 :<span class="hljs-operator">=</span> new(<span class="hljs-type">TwoInts</span>)<br>two1.a <span class="hljs-operator">=</span> <span class="hljs-number">12</span><br>two1.b <span class="hljs-operator">=</span> <span class="hljs-number">10</span><br>fmt.<span class="hljs-type">Printf</span>(<span class="hljs-string">&quot;two1 is: %v<span class="hljs-subst">\n</span>&quot;</span>, two1)<br>fmt.<span class="hljs-type">Println</span>(<span class="hljs-string">&quot;two1 is:&quot;</span>, two1)<br>fmt.<span class="hljs-type">Printf</span>(<span class="hljs-string">&quot;two1 is: %T<span class="hljs-subst">\n</span>&quot;</span>, two1)<br>fmt.<span class="hljs-type">Printf</span>(<span class="hljs-string">&quot;two1 is: %#v<span class="hljs-subst">\n</span>&quot;</span>, two1)<br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> (tn <span class="hljs-operator">*</span><span class="hljs-type">TwoInts</span>)</span> <span class="hljs-type">String</span>() string &#123;<br><span class="hljs-keyword">return</span> <span class="hljs-string">&quot;(&quot;</span> <span class="hljs-operator">+</span> strconv.<span class="hljs-type">Itoa</span>(tn.a) <span class="hljs-operator">+</span> <span class="hljs-string">&quot;/&quot;</span> <span class="hljs-operator">+</span> strconv.<span class="hljs-type">Itoa</span>(tn.b) <span class="hljs-operator">+</span> <span class="hljs-string">&quot;)&quot;</span><br>&#125;<br><br>输出：<br><br>two1 <span class="hljs-keyword">is</span>: (<span class="hljs-number">12</span><span class="hljs-operator">/</span><span class="hljs-number">10</span>)<br>two1 <span class="hljs-keyword">is</span>: (<span class="hljs-number">12</span><span class="hljs-operator">/</span><span class="hljs-number">10</span>)<br>two1 <span class="hljs-keyword">is</span>: <span class="hljs-operator">*</span>main.<span class="hljs-type">TwoInts</span><br>two1 <span class="hljs-keyword">is</span>: <span class="hljs-operator">&amp;</span>main.<span class="hljs-type">TwoInts</span>&#123;a:<span class="hljs-number">12</span>, b:<span class="hljs-number">10</span>&#125;<br></code></pre></td></tr></table></figure></li><li><p>糟糕写法(内存溢出)</p></li></ul><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">#note<br>#不在 <span class="hljs-constructor">String()</span> 方法里调用涉及 <span class="hljs-constructor">String()</span> 方法的方法，会导致意料之外的错误，它导致了一个无限递归调用（TT.<span class="hljs-constructor">String()</span> 调用 fmt.Sprintf，而 fmt.Sprintf 又会反过来调用 TT.<span class="hljs-constructor">String()</span>...），很快就会导致内存溢出<br><br><span class="hljs-keyword">type</span> TT float64<br><br>func (t TT) <span class="hljs-constructor">String()</span> <span class="hljs-built_in">string</span> &#123;<br>    return fmt.<span class="hljs-constructor">Sprintf(<span class="hljs-string">&quot;%v&quot;</span>, <span class="hljs-params">t</span>)</span><br>&#125;<br>t.<span class="hljs-constructor">String()</span><br></code></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      Go 语言字符串是一种值 类型，且值不可变，即创建某个文本后你无法再次修改这个文本的内容，也可以理解，字符串是字节的定长 数组。
    
    </summary>
    
    
      <category term="Golang" scheme="https://www.bkduck.cn/categories/Golang/"/>
    
    
      <category term="golang" scheme="https://www.bkduck.cn/tags/golang/"/>
    
      <category term="go语法" scheme="https://www.bkduck.cn/tags/go%E8%AF%AD%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>Golang语法系列——闭包函数</title>
    <link href="https://www.bkduck.cn/golang/base-func-closure/"/>
    <id>https://www.bkduck.cn/golang/base-func-closure/</id>
    <published>2021-08-06T16:00:00.000Z</published>
    <updated>2021-08-06T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="常见坑"><a href="#常见坑" class="headerlink" title="常见坑"></a>常见坑</h2><h3 id="for语句的闭包中迭代变量使用不妥"><a href="#for语句的闭包中迭代变量使用不妥" class="headerlink" title="for语句的闭包中迭代变量使用不妥"></a>for语句的闭包中迭代变量使用不妥</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs go">data := []<span class="hljs-keyword">string</span>&#123;<span class="hljs-string">&quot;one&quot;</span>,<span class="hljs-string">&quot;two&quot;</span>,<span class="hljs-string">&quot;three&quot;</span>&#125;<br><br>wg := sync.WaitGroup&#123;&#125;<br>wg.Add(<span class="hljs-number">9</span>)<br><span class="hljs-comment">//goroutines print: three, three, three</span><br><span class="hljs-keyword">for</span> _,v := <span class="hljs-keyword">range</span> data &#123;<br>  <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br>    fmt.Println(v)<br>    wg.Done()<br>  &#125;()<br>&#125;<br><br><span class="hljs-comment">//goroutines print: one, two, three</span><br><span class="hljs-keyword">for</span> _,v := <span class="hljs-keyword">range</span> data &#123;<br>  vcopy := v <span class="hljs-comment">// 使用临时变量</span><br>  <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br>    fmt.Println(vcopy)<br>    wg.Done()<br>  &#125;()<br>&#125;<br><br><span class="hljs-comment">//goroutines print: one, two, three</span><br><span class="hljs-keyword">for</span> _,v := <span class="hljs-keyword">range</span> data &#123;<br>  <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(in <span class="hljs-keyword">string</span>)</span></span> &#123;<br>    fmt.Println(in)<br>    wg.Done()<br>  &#125;(v)<br>&#125;<br><br>wg.Wait()<br></code></pre></td></tr></table></figure><p>在for迭代过程中，迭代变量<code>v</code>会一直保留，只是每次迭代值<code>v</code>不一样,因此在for循环中在闭包里直接引用迭代变量，在执行时直接取迭代变量的值，而不是闭包所在迭代的变量值</p><p>闭包要取所在迭代变量的值，需要for中定义<strong>临时变量来保存所在迭代的值，或者通过闭包函数传参</strong></p><hr><p>定义为指针数组时，输出正常 </p><p>另一个例子:</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs golang">data := []field&#123;&#123;<span class="hljs-string">&quot;one&quot;</span>&#125;,&#123;<span class="hljs-string">&quot;two&quot;</span>&#125;,&#123;<span class="hljs-string">&quot;three&quot;</span>&#125;&#125;<br><span class="hljs-keyword">for</span> _,v := <span class="hljs-keyword">range</span> data &#123;<br>  <span class="hljs-comment">// 解决办法：添加如下语句</span><br>  <span class="hljs-comment">// v := v</span><br>  <span class="hljs-keyword">go</span> v.<span class="hljs-built_in">print</span>()<br>&#125;<br>time.Sleep(<span class="hljs-number">3</span> * time.Second)     <span class="hljs-comment">//goroutines print: three, three, three</span><br><br>data2 := []*StructA&#123;&#123;<span class="hljs-string">&quot;one&quot;</span>&#125;, &#123;<span class="hljs-string">&quot;two&quot;</span>&#125;, &#123;<span class="hljs-string">&quot;three&quot;</span>&#125;&#125;  <span class="hljs-comment">// 注意data2是指针数组 StructA 自定义结构</span><br><span class="hljs-keyword">for</span> _, v := <span class="hljs-keyword">range</span> data2 &#123;<br>  <span class="hljs-keyword">go</span> v.<span class="hljs-built_in">print</span>()                <span class="hljs-comment">// go执行是函数，函数执行之前，函数的接受对象已经传过来</span><br>&#125;<br>time.Sleep(<span class="hljs-number">3</span> * time.Second)     <span class="hljs-comment">//goroutines print: one, two, three</span><br></code></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      Go 语言函数闭包Go 函数Go 语言支持匿名函数，可作为闭包。匿名函数是一个“内联”语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量，不必申明。
    
    </summary>
    
    
      <category term="Golang" scheme="https://www.bkduck.cn/categories/Golang/"/>
    
    
      <category term="golang" scheme="https://www.bkduck.cn/tags/golang/"/>
    
      <category term="go语法" scheme="https://www.bkduck.cn/tags/go%E8%AF%AD%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>Golang语法系列——interface接口</title>
    <link href="https://www.bkduck.cn/golang/base-interface/"/>
    <id>https://www.bkduck.cn/golang/base-interface/</id>
    <published>2021-08-04T16:00:00.000Z</published>
    <updated>2021-08-04T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h3 id="NOTE"><a href="#NOTE" class="headerlink" title="NOTE"></a>NOTE</h3><ul><li>主要概念<figure class="highlight d"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs d"><span class="hljs-number">1.</span> <span class="hljs-keyword">interface</span> 可被 <span class="hljs-keyword">struct</span> 等继承 （animalI = catT, catT 实现所有animalI的方法）<br><span class="hljs-number">2.</span> 了解规则：最好使用<span class="hljs-keyword">new</span>()生成指针类<br></code></pre></td></tr></table></figure></li></ul><ul><li><a href="https://sanyuesha.com/2017/07/22/how-to-understand-go-interface/">参考链接</a></li></ul><h3 id="type-switch-类型选择"><a href="#type-switch-类型选择" class="headerlink" title="type-switch 类型选择"></a>type-switch 类型选择</h3><ul><li><p>参考链接 </p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs awk"><span class="hljs-number">1</span>. 介绍interface的用处<br>https:<span class="hljs-regexp">//gi</span>thub.com<span class="hljs-regexp">/astaxie/</span>build-web-application-with-golang<span class="hljs-regexp">/blob/m</span>aster<span class="hljs-regexp">/zh/</span><span class="hljs-number">02.6</span>.md <br></code></pre></td></tr></table></figure></li><li><p>Comma-ok断言 判断类型  element.(T)</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">&quot;fmt&quot;</span><br><span class="hljs-string">&quot;strconv&quot;</span><br>)<br><br><span class="hljs-keyword">type</span> Element <span class="hljs-keyword">interface</span>&#123;&#125;<br><span class="hljs-keyword">type</span> List []Element<br><br><span class="hljs-keyword">type</span> Person <span class="hljs-keyword">struct</span> &#123;<br>name <span class="hljs-keyword">string</span><br>age <span class="hljs-keyword">int</span><br>&#125;<br><br><span class="hljs-comment">//打印</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(p Person)</span> <span class="hljs-title">String</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> &#123;<br><span class="hljs-keyword">return</span> <span class="hljs-string">&quot;(name: &quot;</span> + p.name + <span class="hljs-string">&quot; - age: &quot;</span>+strconv.Itoa(p.age)+ <span class="hljs-string">&quot; years)&quot;</span><br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br>list := <span class="hljs-built_in">make</span>(List, <span class="hljs-number">3</span>)<br>list[<span class="hljs-number">0</span>] = <span class="hljs-number">1</span> <span class="hljs-comment">//an int</span><br>list[<span class="hljs-number">1</span>] = <span class="hljs-string">&quot;Hello&quot;</span> <span class="hljs-comment">//a string</span><br>list[<span class="hljs-number">2</span>] = Person&#123;<span class="hljs-string">&quot;Dennis&quot;</span>, <span class="hljs-number">70</span>&#125;<br><br><span class="hljs-keyword">for</span> index, element := <span class="hljs-keyword">range</span> list&#123;<br><span class="hljs-keyword">switch</span> value := element.(<span class="hljs-keyword">type</span>) &#123;<br><span class="hljs-keyword">case</span> <span class="hljs-keyword">int</span>:<br>fmt.Printf(<span class="hljs-string">&quot;list[%d] is an int and its value is %d\n&quot;</span>, index, value)<br><span class="hljs-keyword">case</span> <span class="hljs-keyword">string</span>:<br>fmt.Printf(<span class="hljs-string">&quot;list[%d] is a string and its value is %s\n&quot;</span>, index, value)<br><span class="hljs-keyword">case</span> Person:<br>fmt.Printf(<span class="hljs-string">&quot;list[%d] is a Person and its value is %s\n&quot;</span>, index, value)<br><span class="hljs-keyword">default</span>:<br>fmt.Println(<span class="hljs-string">&quot;list[%d] is of a different type&quot;</span>, index)<br>&#125;<br>&#125;<br>&#125;<br></code></pre></td></tr></table></figure></li><li><p>interface 可以嵌套interface</p><figure class="highlight elm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs elm">例如io标准库<br>// io.<span class="hljs-type">ReadWriter</span><br>// <span class="hljs-type">Reader</span>和<span class="hljs-type">Writer</span>两个 interface<br><span class="hljs-keyword">type</span> <span class="hljs-type">ReadWriter</span> interface &#123;<br><span class="hljs-type">Reader</span><br><span class="hljs-type">Writer</span><br>&#125;<br><br></code></pre></td></tr></table></figure></li></ul><h3 id="类型断言"><a href="#类型断言" class="headerlink" title="类型断言"></a>类型断言</h3><ul><li>代码<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><code class="hljs go">#NOTE：如果忽略 areaIntf.(*Square) 中的 * 号，会导致编译错误：impossible <span class="hljs-keyword">type</span> assertion: Square does not implement Shaper (Area method has pointer receiver)。<br><br><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">&quot;fmt&quot;</span><br><span class="hljs-string">&quot;math&quot;</span><br>)<br><br><span class="hljs-keyword">type</span> Square <span class="hljs-keyword">struct</span> &#123;<br>side <span class="hljs-keyword">float32</span><br>&#125;<br><br><span class="hljs-keyword">type</span> Circle <span class="hljs-keyword">struct</span> &#123;<br>radius <span class="hljs-keyword">float32</span><br>&#125;<br><br><span class="hljs-keyword">type</span> Shaper <span class="hljs-keyword">interface</span> &#123;<br>Area() <span class="hljs-keyword">float32</span><br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">var</span> areaIntf Shaper<br>sq1 := <span class="hljs-built_in">new</span>(Square)<br>sq1.side = <span class="hljs-number">5</span><br><br>areaIntf = sq1<br><span class="hljs-comment">// Is Square the type of areaIntf?</span><br><span class="hljs-keyword">if</span> t, ok := areaIntf.(*Square); ok &#123;<br>fmt.Printf(<span class="hljs-string">&quot;The type of areaIntf is: %T\n&quot;</span>, t)<br>&#125;<br><span class="hljs-keyword">if</span> u, ok := areaIntf.(*Circle); ok &#123;<br>fmt.Printf(<span class="hljs-string">&quot;The type of areaIntf is: %T\n&quot;</span>, u)<br>&#125; <span class="hljs-keyword">else</span> &#123;<br>fmt.Println(<span class="hljs-string">&quot;areaIntf does not contain a variable of type Circle&quot;</span>)<br>&#125;<br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(sq *Square)</span> <span class="hljs-title">Area</span><span class="hljs-params">()</span> <span class="hljs-title">float32</span></span> &#123;<br><span class="hljs-keyword">return</span> sq.side * sq.side<br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(ci *Circle)</span> <span class="hljs-title">Area</span><span class="hljs-params">()</span> <span class="hljs-title">float32</span></span> &#123;<br><span class="hljs-keyword">return</span> ci.radius * ci.radius * math.Pi<br>&#125;<br><br>输出<br>The <span class="hljs-keyword">type</span> of areaIntf is: *main.Square<br>areaIntf does not contain a variable of <span class="hljs-keyword">type</span> Circle<br></code></pre></td></tr></table></figure></li></ul><h3 id="调用规则"><a href="#调用规则" class="headerlink" title="调用规则"></a>调用规则</h3><ul><li><p>规则</p><figure class="highlight excel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs excel">Go 语言规范定义了接口方法集的调用规则：<br><br>    类型 *<span class="hljs-built_in">T</span> 的可调用方法集包含接受者为 *<span class="hljs-built_in">T</span> 或 <span class="hljs-built_in">T</span> 的所有方法集<br>    类型 <span class="hljs-built_in">T</span> 的可调用方法集包含接受者为 <span class="hljs-built_in">T</span> 的所有方法<br>    类型 <span class="hljs-built_in">T</span> 的可调用方法集不包含接受者为 *<span class="hljs-built_in">T</span> 的方法<br><br></code></pre></td></tr></table></figure></li><li><p>代码</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><code class="hljs go">#注意<span class="hljs-built_in">new</span>()的用法<br><br><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">&quot;fmt&quot;</span><br>)<br><br><span class="hljs-keyword">type</span> List []<span class="hljs-keyword">int</span><br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(l List)</span> <span class="hljs-title">Len</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> &#123;<br><span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(l)<br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(l *List)</span> <span class="hljs-title">Append</span><span class="hljs-params">(val <span class="hljs-keyword">int</span>)</span></span> &#123;<br>*l = <span class="hljs-built_in">append</span>(*l, val)<br>&#125;<br><br><span class="hljs-keyword">type</span> Appender <span class="hljs-keyword">interface</span> &#123;<br>Append(<span class="hljs-keyword">int</span>)<br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">CountInto</span><span class="hljs-params">(a Appender, start, end <span class="hljs-keyword">int</span>)</span></span> &#123;<br><span class="hljs-keyword">for</span> i := start; i &lt;= end; i++ &#123;<br>a.Append(i)<br>&#125;<br>&#125;<br><br><span class="hljs-keyword">type</span> Lener <span class="hljs-keyword">interface</span> &#123;<br>Len() <span class="hljs-keyword">int</span><br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">LongEnough</span><span class="hljs-params">(l Lener)</span> <span class="hljs-title">bool</span></span> &#123;<br><span class="hljs-keyword">return</span> l.Len()*<span class="hljs-number">10</span> &gt; <span class="hljs-number">42</span><br>&#125;<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-comment">// A bare value</span><br><span class="hljs-keyword">var</span> lst List<br><span class="hljs-comment">// compiler error:</span><br><span class="hljs-comment">// cannot use lst (type List) as type Appender in argument to CountInto:</span><br><span class="hljs-comment">//       List does not implement Appender (Append method has pointer receiver)</span><br><span class="hljs-comment">// CountInto(lst, 1, 10)</span><br><span class="hljs-keyword">if</span> LongEnough(lst) &#123; <span class="hljs-comment">// VALID:Identical receiver type</span><br>fmt.Printf(<span class="hljs-string">&quot;- lst is long enough\n&quot;</span>)<br>&#125;<br><br><span class="hljs-comment">// A pointer value</span><br>plst := <span class="hljs-built_in">new</span>(List)<br>CountInto(plst, <span class="hljs-number">1</span>, <span class="hljs-number">10</span>) <span class="hljs-comment">//VALID:Identical receiver type</span><br><span class="hljs-keyword">if</span> LongEnough(plst) &#123;<br><span class="hljs-comment">// VALID: a *List can be dereferenced for the receiver</span><br>fmt.Printf(<span class="hljs-string">&quot;- plst is long enough\n&quot;</span>)<br>&#125;<br>&#125;<br><br>输出<br>- plst is long enough<br></code></pre></td></tr></table></figure></li></ul><h3 id="空接口复制"><a href="#空接口复制" class="headerlink" title="空接口复制"></a>空接口复制</h3><ul><li>代码<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs go">#需逐个显式复制<br><span class="hljs-keyword">var</span> dataSlice []myType = FuncReturnSlice()<br><span class="hljs-keyword">var</span> interfaceSlice []<span class="hljs-keyword">interface</span>&#123;&#125; = <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">interface</span>&#123;&#125;, <span class="hljs-built_in">len</span>(dataSlice))<br><span class="hljs-keyword">for</span> i, d := <span class="hljs-keyword">range</span> dataSlice &#123;<br>    interfaceSlice[i] = d<br>&#125;<br><br>#错误语法<br><span class="hljs-keyword">var</span> dataSlice []myType = FuncReturnSlice()<br><span class="hljs-keyword">var</span> interfaceSlice []<span class="hljs-keyword">interface</span>&#123;&#125; = dataSlice<br></code></pre></td></tr></table></figure></li></ul>]]></content>
    
    <summary type="html">
    
      如果说goroutine和channel是Go并发的两大基石，那么接口是Go语言编程中数据类型的关键。在Go语言的实际编程中，几乎所有的数据结构都围绕接口展开，接口是Go语言中所有数据结构的核心
    
    </summary>
    
    
      <category term="Golang" scheme="https://www.bkduck.cn/categories/Golang/"/>
    
    
      <category term="golang" scheme="https://www.bkduck.cn/tags/golang/"/>
    
      <category term="go语法" scheme="https://www.bkduck.cn/tags/go%E8%AF%AD%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>Golang语法系列——recover异常处理</title>
    <link href="https://www.bkduck.cn/golang/base-recover/"/>
    <id>https://www.bkduck.cn/golang/base-recover/</id>
    <published>2021-08-03T16:00:00.000Z</published>
    <updated>2021-08-03T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="现象"><a href="#现象" class="headerlink" title="现象"></a>现象</h2><ul><li><code>panic</code> 只会触发当前 Goroutine 的 <code>defer</code>；</li><li><code>recover</code> 只有在 <code>defer</code> 中调用才会生效；</li><li><code>panic</code> 允许在 <code>defer</code> 中嵌套多次调用；</li></ul><h3 id="跨协程失效"><a href="#跨协程失效" class="headerlink" title="跨协程失效"></a>跨协程失效</h3><p><code>panic</code> 只会触发当前 Goroutine 的延迟函数调用</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">defer</span> <span class="hljs-built_in">println</span>(<span class="hljs-string">&quot;in main&quot;</span>)<br><span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">defer</span> <span class="hljs-built_in">println</span>(<span class="hljs-string">&quot;in goroutine&quot;</span>)<br><span class="hljs-built_in">panic</span>(<span class="hljs-string">&quot;&quot;</span>)<br>&#125;()<br><br>time.Sleep(<span class="hljs-number">1</span> * time.Second)<br>&#125;<br><br>$ <span class="hljs-keyword">go</span> run main.<span class="hljs-keyword">go</span><br>in goroutine<br><span class="hljs-built_in">panic</span>:<br>...<br></code></pre></td></tr></table></figure><p>当我们运行这段代码时会发现 <code>main</code> 函数中的 <code>defer</code> 语句并没有执行，执行的只有当前 Goroutine 中的 <code>defer</code></p><h3 id="失效的崩溃恢复"><a href="#失效的崩溃恢复" class="headerlink" title="失效的崩溃恢复"></a>失效的崩溃恢复</h3><p>在主程序中调用 <code>recover</code> 试图中止程序的崩溃，但是从运行的结果中我们也能看出，下面的程序没有正常退出。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">defer</span> fmt.Println(<span class="hljs-string">&quot;in main&quot;</span>)<br><span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>(); err != <span class="hljs-literal">nil</span> &#123;<br>fmt.Println(err)<br>&#125;<br><br><span class="hljs-built_in">panic</span>(<span class="hljs-string">&quot;unknown err&quot;</span>)<br>&#125;<br><br>$ <span class="hljs-keyword">go</span> run main.<span class="hljs-keyword">go</span><br>in main<br><span class="hljs-built_in">panic</span>: unknown err<br><br>goroutine <span class="hljs-number">1</span> [running]:<br>main.main()<br>...<br>exit status <span class="hljs-number">2</span><br></code></pre></td></tr></table></figure><p><code>recover</code> 只有在发生 <code>panic</code> 之后调用才会生效。然而在上面的控制流中，<code>recover</code> 是在 <code>panic</code> 之前调用的，并不满足生效的条件，所以<strong>我们需要在 <code>defer</code> 中使用 <code>recover</code> 关键字</strong>。</p><h3 id="嵌套崩溃"><a href="#嵌套崩溃" class="headerlink" title="嵌套崩溃"></a>嵌套崩溃</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br><span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>&#123;<br>fmt.Println(<span class="hljs-string">&quot;5&quot;</span>)<br>fmt.Println(<span class="hljs-string">&quot;in main&quot;</span>)<br>&#125;()<br><span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br>fmt.Println(<span class="hljs-string">&quot;2&quot;</span>)<br><span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br>fmt.Println(<span class="hljs-string">&quot;4&quot;</span>)<br><span class="hljs-built_in">panic</span>(<span class="hljs-string">&quot;panic again and again and again&quot;</span>)<br>&#125;()<br><span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br>fmt.Println(<span class="hljs-string">&quot;3&quot;</span>)<br><span class="hljs-built_in">panic</span>(<span class="hljs-string">&quot;panic again and again&quot;</span>)<br>&#125;()<br><span class="hljs-built_in">panic</span>(<span class="hljs-string">&quot;panic again&quot;</span>)<br>&#125;()<br>fmt.Println(<span class="hljs-string">&quot;1&quot;</span>)<br><span class="hljs-built_in">panic</span>(<span class="hljs-string">&quot;panic once&quot;</span>) <br>&#125;<br><br>&gt;&gt;&gt;<br><span class="hljs-number">1</span><br><span class="hljs-number">2</span><br><span class="hljs-number">4</span><br><span class="hljs-number">5</span><br><span class="hljs-number">6</span><br>in main<br><br><span class="hljs-built_in">panic</span>: <span class="hljs-built_in">panic</span> once<br><span class="hljs-built_in">panic</span>: <span class="hljs-built_in">panic</span> again<br><span class="hljs-built_in">panic</span>: <span class="hljs-built_in">panic</span> again and again<br><span class="hljs-built_in">panic</span>: <span class="hljs-built_in">panic</span> again and again and again<br>exit status <span class="hljs-number">2</span><br></code></pre></td></tr></table></figure><p><code>panic</code> 先执行同级<code>defer</code>,然后同级<code>defer</code>按FILO顺序执行，若<code>defer</code>内部嵌套<code>defer</code>, 其执行顺序按内部嵌套FILO顺序执行。同时<strong>注意<code>panic</code>位置在<code>defer</code> 前面则同级后面<code>defer</code>不会执行</strong></p><h3 id="panic-和recover-和defer运用"><a href="#panic-和recover-和defer运用" class="headerlink" title="panic()和recover()和defer运用"></a>panic()和recover()和defer运用</h3><ul><li>程序panic，defer中使用recover恢复<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs go">#执行<span class="hljs-built_in">panic</span>，这时<span class="hljs-built_in">recover</span>就不等于<span class="hljs-literal">nil</span>，然后程序先执行<span class="hljs-keyword">defer</span>，然后<span class="hljs-built_in">panic</span>终止<br>#类似于try catch<br><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">&quot;log&quot;</span><br>)<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">protect</span><span class="hljs-params">(g <span class="hljs-keyword">func</span>()</span>)</span> &#123;<br><span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br>log.Println(<span class="hljs-string">&quot;done&quot;</span>)<br><span class="hljs-comment">// Println executes normally even if there is a panic</span><br><span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>(); err != <span class="hljs-literal">nil</span> &#123;<br>log.Printf(<span class="hljs-string">&quot;run time panic: %v&quot;</span>, err)<br>&#125;<br>&#125;()<br>log.Println(<span class="hljs-string">&quot;start&quot;</span>)<br>g() <span class="hljs-comment">//   possible runtime-error</span><br>&#125;<br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> &#123;<br>protect(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> &#123;<br>log.Println(<span class="hljs-string">&quot;in g()&quot;</span>)<br><span class="hljs-built_in">panic</span>(<span class="hljs-string">&quot;dropout g()&quot;</span>)<br>&#125;)<br>&#125;<br><br>输出<br><span class="hljs-number">2018</span>/<span class="hljs-number">07</span>/<span class="hljs-number">25</span> <span class="hljs-number">18</span>:<span class="hljs-number">21</span>:<span class="hljs-number">23</span> start<br><span class="hljs-number">2018</span>/<span class="hljs-number">07</span>/<span class="hljs-number">25</span> <span class="hljs-number">18</span>:<span class="hljs-number">21</span>:<span class="hljs-number">23</span> in g()<br><span class="hljs-number">2018</span>/<span class="hljs-number">07</span>/<span class="hljs-number">25</span> <span class="hljs-number">18</span>:<span class="hljs-number">21</span>:<span class="hljs-number">23</span> done<br><span class="hljs-number">2018</span>/<span class="hljs-number">07</span>/<span class="hljs-number">25</span> <span class="hljs-number">18</span>:<span class="hljs-number">21</span>:<span class="hljs-number">23</span> run time <span class="hljs-built_in">panic</span>: dropout g()<br><br></code></pre></td></tr></table></figure></li></ul>]]></content>
    
    <summary type="html">
    
      Golang 没有结构化异常，使用 panic 抛出错误，recover 捕获错误。 异常的使用场景简单描述：Go中可以抛出一个panic的异常，然后在defer中通过recover捕获这个异常，然后正常处理。
    
    </summary>
    
    
      <category term="Golang" scheme="https://www.bkduck.cn/categories/Golang/"/>
    
    
      <category term="golang" scheme="https://www.bkduck.cn/tags/golang/"/>
    
      <category term="go语法" scheme="https://www.bkduck.cn/tags/go%E8%AF%AD%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>Golang语法系列——slice切片和数组</title>
    <link href="https://www.bkduck.cn/golang/base-slice/"/>
    <id>https://www.bkduck.cn/golang/base-slice/</id>
    <published>2021-08-01T16:00:00.000Z</published>
    <updated>2021-08-01T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="slice切片底层"><a href="#slice切片底层" class="headerlink" title="slice切片底层"></a>slice切片底层</h2><blockquote><p>基于数组实现，可动态拓容，</p></blockquote><h3 id="切片拓容规则"><a href="#切片拓容规则" class="headerlink" title="切片拓容规则"></a>切片拓容规则</h3><p><strong>append()</strong> 在分配内存空间之前需要先确定新的切片容量，运行时根据切片的当前容量选择不同的策略进行扩容</p><ul><li><p>如果期望容量大于当前容量的两倍就会使用期望容量；</p></li><li><p>如果当前切片的长度小于 1024 就会将容量翻倍；</p></li><li><p>如果当前切片的长度大于 1024 就会每次增加 25% 的容量，直到新容量大于期望容量；</p></li></ul><hr><p><strong>copy</strong>拷贝，<a href="https://draveness.me/golang/tree/runtime.memmove"><code>runtime.memmove</code></a> 能够提供更好的性能。需要注意的是，整块拷贝内存仍然会占用非常多的资源，在大切片上执行拷贝操作时一定要注意对性能的影响</p><hr><p><strong>总结</strong> </p><ol><li>append 注意拓容规则，初始化时可规划好长度</li><li>copy 避免大切片拷贝</li></ol><h2 id="slice使用"><a href="#slice使用" class="headerlink" title="slice使用"></a>slice使用</h2><h3 id="slice-cap分析"><a href="#slice-cap分析" class="headerlink" title="slice cap分析"></a>slice cap分析</h3><p><strong>copy() 是值赋值</strong>，不会影响原切片cap，同时基于数组的新切片，修改新切片值，会覆盖数组的值。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs go"> <br> <br> arr := [<span class="hljs-number">10</span>]<span class="hljs-keyword">int64</span>&#123;<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">7</span>,<span class="hljs-number">8</span>,<span class="hljs-number">9</span>,<span class="hljs-number">10</span>&#125;<br>s1 := arr[<span class="hljs-number">2</span>:<span class="hljs-number">4</span>:<span class="hljs-number">8</span>] <span class="hljs-comment">// cap=6,len=2 cap 针对源数组 arr 由2开始</span><br>s2 := arr[<span class="hljs-number">2</span>:<span class="hljs-number">4</span>] <span class="hljs-comment">// cap=8,len=2</span><br> s3 := arrp[:<span class="hljs-number">4</span>] <span class="hljs-comment">// cap=10,len=4 </span><br> <span class="hljs-comment">//s1[0] = 4 // arr: [1,2,4,4,5,6,7,8,9,10] //切片指向arr地址，修改会覆盖</span><br> <span class="hljs-comment">//cp := []int64&#123;22,33&#125;</span><br> <span class="hljs-comment">//copy(s1,cp) //arr: [1,2,22,33,5,6,7,8,9,10] copy是值复制，不影响s1的cap</span><br><br>fmt.Println(s1) <span class="hljs-comment">//[3 4]</span><br>fmt.Println(s2) <span class="hljs-comment">//[3 4]</span><br>fmt.Println(s1[:<span class="hljs-built_in">cap</span>(s1)]) <span class="hljs-comment">//[3 4 5 6 7 8]</span><br>fmt.Println(<span class="hljs-built_in">cap</span>(s1)) <span class="hljs-comment">// 6 8-2</span><br>fmt.Println(<span class="hljs-built_in">len</span>(s1)) <span class="hljs-comment">// 2</span><br>fmt.Println(s2[:<span class="hljs-built_in">cap</span>(s2)])<span class="hljs-comment">//[3 4 5 6 7 8 9 10]</span><br>fmt.Println(<span class="hljs-built_in">cap</span>(s2)) <span class="hljs-comment">// 8  10-2</span><br>fmt.Println(<span class="hljs-built_in">len</span>(s2)) <span class="hljs-comment">// 2</span><br><br> <br></code></pre></td></tr></table></figure><h3 id="Slice增加元素重新分配内存"><a href="#Slice增加元素重新分配内存" class="headerlink" title="Slice增加元素重新分配内存"></a>Slice增加元素重新分配内存</h3><p>slice在添加元素前，与其它切片共享同一数据区域，修改会相互影响；但添加元素导致内存重新分配之后，不再指向原来的数据区域，修改元素，不再影响其它切片。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs go">s1 := []<span class="hljs-keyword">int</span>&#123;<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>&#125;<br>fmt.Println(<span class="hljs-built_in">len</span>(s1),<span class="hljs-built_in">cap</span>(s1),s1) <span class="hljs-comment">//prints 3 3 [1 2 3]</span><br><br>s2 := s1[<span class="hljs-number">1</span>:]<br>fmt.Println(<span class="hljs-built_in">len</span>(s2),<span class="hljs-built_in">cap</span>(s2),s2) <span class="hljs-comment">//prints 2 2 [2 3]</span><br><br><span class="hljs-keyword">for</span> i := <span class="hljs-keyword">range</span> s2 &#123; s2[i] += <span class="hljs-number">20</span> &#125;<br><br><span class="hljs-comment">//still referencing the same array</span><br>fmt.Println(s1) <span class="hljs-comment">//prints [1 22 23]</span><br>fmt.Println(s2) <span class="hljs-comment">//prints [22 23]</span><br><br>s2 = <span class="hljs-built_in">append</span>(s2,<span class="hljs-number">4</span>)<br><br><span class="hljs-keyword">for</span> i := <span class="hljs-keyword">range</span> s2 &#123; s2[i] += <span class="hljs-number">10</span> &#125;<br><br><span class="hljs-comment">//s1 is now &quot;stale&quot;</span><br>fmt.Println(s1) <span class="hljs-comment">//prints [1 22 23]</span><br>fmt.Println(s2) <span class="hljs-comment">//prints [32 33 14]</span><br></code></pre></td></tr></table></figure><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul><li><a href="https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-array-and-slice/">切片实现原理</a></li></ul>]]></content>
    
    <summary type="html">
    
      切片(slice)是 Golang 中一种比较特殊的数据结构，这种数据结构更便于使用和管理数据集合。切片是围绕动态数组的概念构建的，可以按需自动增长和缩小。切片的动态增长是通过内置函数 append() 来实现的，这个函数可以快速且高效地增长切片，也可以通过对切片再次切割，缩小一个切片的大小。因为切片的底层也是在连续的内存块中分配的，所以切片还能获得索引、迭代以及为垃圾回收优化的好处。
    
    </summary>
    
    
      <category term="Golang" scheme="https://www.bkduck.cn/categories/Golang/"/>
    
    
      <category term="golang" scheme="https://www.bkduck.cn/tags/golang/"/>
    
      <category term="go语法" scheme="https://www.bkduck.cn/tags/go%E8%AF%AD%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>SPL语法系列1——有什么用</title>
    <link href="https://www.bkduck.cn/php/section-spl-01-why/"/>
    <id>https://www.bkduck.cn/php/section-spl-01-why/</id>
    <published>2021-03-04T16:00:00.000Z</published>
    <updated>2021-03-04T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="What-SPL简介"><a href="#What-SPL简介" class="headerlink" title="What SPL简介"></a>What SPL简介</h2><p><strong>SPL全称</strong>：Standard PHP Library (标准php类库)。 </p><p>引用phpro.org站点的解析：The aim of SPL is to implement some efficient data access interfaces and classes for PHP. Functionally it is designed to traverse aggregate structures (anything you want to loop over). These may include arrays, database result sets, xml trees, directory listings or any list at all.  </p><p><strong>原文翻译</strong>：SPL的目的是实现一些有效的数据访问接口和类的PHP。功能是用来遍历聚合数据结构(任何你想要遍历)。这些可能包括数组、数据库结果集,xml树、目录或者任何list！</p><p><strong>个人心得</strong>：  </p><ul><li>SPL主要对外提供统一，便捷，快速的遍历方式（++一般不能foreach遍历对象Object++）</li><li>SPL已经编写好了基础的structures（数据结构），如SplDoublyLinkedList（双链表）、SplStack（栈）等 </li><li>SPL核心是Iterrator（迭代器），同时SPL提供很多Iterator，如RecursiveArrayIterator（递归数组迭代器）、ArrayIterator（数组迭代器）DirectoryIterator(目录迭代器) 等   </li><li>SPL 主要组成：数据结构、迭代器、•接口、•异常、SPL 函数、文件处理、各种类及接口</li></ul><h2 id="How-如何学"><a href="#How-如何学" class="headerlink" title="How 如何学"></a>How 如何学</h2><p>也许很多人常听SPL，但大概入门找不到方向，其实学习无外乎‘3W’（what、why、how）bkduck将围绕这3w做一个系列的记录，方便以后自己查阅 </p>]]></content>
    
    <summary type="html">
    
      SPL的目的是实现一些有效的数据访问接口和类的PHP。功能是用来遍历聚合数据结构(任何你想要遍历)。这些可能包括数组、数据库结果集,xml树、目录或者任何list！
    
    </summary>
    
    
      <category term="PHP" scheme="https://www.bkduck.cn/categories/PHP/"/>
    
    
      <category term="php" scheme="https://www.bkduck.cn/tags/php/"/>
    
      <category term="php语法" scheme="https://www.bkduck.cn/tags/php%E8%AF%AD%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>PHP知识点汇总</title>
    <link href="https://www.bkduck.cn/php/php-knowledge/"/>
    <id>https://www.bkduck.cn/php/php-knowledge/</id>
    <published>2020-03-04T16:00:00.000Z</published>
    <updated>2021-03-04T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="phper-knowledge"><a href="#phper-knowledge" class="headerlink" title="phper-knowledge"></a>phper-knowledge</h1><blockquote><p>该仓库主要记录php开发遇到的知识点。<br>针对相应知识点，附上便于理解的链接，若有更好的资源，欢迎提交更正交流。</p></blockquote><h2 id="基础篇"><a href="#基础篇" class="headerlink" title="基础篇"></a>基础篇</h2><ul><li><p><a href="http://php.net/manual/zh/ref.array.php">了解大部分数组处理函数</a></p></li><li><p><a href="http://php.net/manual/zh/ref.strings.php">字符串处理函数</a>  <a href="http://php.net/manual/zh/book.mbstring.php">区别 mb_ 系列函数</a></p></li><li><p><a href="https://secure.php.net/manual/zh/language.references.php">&amp; 引用，结合案例分析</a></p><p>当 unset 一个引用，只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了</p></li><li><p><a href="https://stackoverflow.com/questions/80646/how-do-the-php-equality-double-equals-and-identity-triple-equals-comp">== 与 === 区别</a></p></li><li><p><a href="https://blog.csdn.net/jiaobuchong/article/details/41807011">isset 与 empty 区别</a></p><ul><li>isset=&gt;false: var $x; $x=null;unset($x)</li></ul></li><li><p><a href="http://php.net/manual/zh/language.oop5.magic.php">全部魔术函数理解</a></p></li><li><p><a href="http://www.cnblogs.com/wangxin-king/p/5669336.html">超全局变量 魔术变量 魔术函数总结</a></p></li><li><p><a href="https://juejin.im/post/5af11926f265da0b9a69e80f">static、$this、self 区别</a></p><p>static 未实现时，将其指向了运行时最初调用的类</p><p>self 未实现方法时指向父类，实现后指向当前</p><p>$this 指向对象的方法，不能静态方法</p></li><li><p>private、protected、public、final 区别 <code>public实例能调用 protected实例不能直接调用 final不能继承和重写</code></p></li><li><p>OOP 思想</p></li><li><p><a href="https://blog.csdn.net/qq_40592933/article/details/88813746">抽象类、接口 分别使用场景</a></p></li><li><p><a href="http://php.net/manual/zh/language.oop5.traits.php">Trait 是什么东西</a></p></li><li><p><a href="https://blog.csdn.net/weixin_42545184/article/details/83443720">echo、print、print_r 区别(区分出表达式与语句的区别)</a></p></li><li><p><a href="http://php.net/manual/zh/language.oop5.decon.php">__construct 与 __destruct 区别</a></p></li><li><p>static 作用（区分类与函数内）<a href="http://php.net/manual/zh/language.oop5.static.php">手册</a> 、<a href="https://stackoverflow.com/questions/7508284/static-variables-in-php">SOF</a></p></li><li><p><a href="http://php.net/manual/en/language.oop5.magic.php#object.tostring">__toString() 作用</a></p></li><li><p><a href="https://stackoverflow.com/questions/3446216/what-is-the-difference-between-single-quoted-and-double-quoted-strings-in-php#answer-3446286">单引号<code>&#39;</code>与双引号<code>&quot;</code>区别</a></p></li><li><p><a href="https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81">常见 HTTP 状态码，分别代表什么含义</a></p></li><li><p><a href="https://zh.wikipedia.org/wiki/HTTP_301">301</a> 什么意思 <a href="https://zh.wikipedia.org/wiki/HTTP_404">404</a> 呢?</p></li><li><p>php 499 500 502 504 错误原因 <a href="https://www.jianshu.com/p/7f65a1f53d2c">参考</a></p><p>499 Client Closed Request 客户端超时，nginx&amp;php-fpm正常执行 <code>curl -m 3</code></p><p>500 Server Internetel Error  php-fpm无法正常工作(代码错误)</p><p>502 Bad GateWay php-fpm执行超时，返回给网关无法解析内容(不按套路出牌，fpm数量不够)</p><p>504 GateWay Timeout nginx超时，fastcgi_read_timeout，php-fpm 正常执行</p></li></ul><h2 id="进阶篇"><a href="#进阶篇" class="headerlink" title="进阶篇"></a>进阶篇</h2><ul><li><p>Autoload、Composer 原理 <a href="https://laravel-china.org/topics/2081/psr-specification-psr-4-automatic-loading-specification">PSR-4</a> 、<a href="https://segmentfault.com/a/1190000014948542">原理</a></p><p>必须有顶级命名空间</p><p>必须有最终类名</p><p>类名必须大小写敏感</p><p>必须文件php后缀</p><p>子命名空间 <strong>必须</strong> 与相应的「文件基目录」相匹配</p><p><strong>一定不可</strong> 抛出异常、<strong>一定不可</strong> 触发任一级别的错误信息</p></li><li><p><a href="https://www.cnblogs.com/wenphp/p/4871500.html">Session 共享、存活时间</a></p><ul><li>存活: 默认存活gc_maxlifetime=1440s,默认gc概率1/100 gc_probability/gc_divisor </li><li>共享: 文件同步rsync，redis或mc存储，mysql存储</li></ul></li><li><p>异常处理 <a href="https://juejin.im/entry/5987d2ff6fb9a03c314fe732">详情</a></p><p>php7之前，try catch有部分fatal error无法捕捉需搭配set_exception_handler</p><p>php7之后，Throwable 包含了exception&amp;error </p><p> register_shutdown_function 能捕捉运行中止时错误</p></li><li><p><a href="https://secure.php.net/manual/zh/class.iterator.php">如何 foreach 迭代对象</a> Iterator 接口 或者 IteratorIterator </p></li><li><p><a href="https://secure.php.net/manual/zh/class.arrayaccess.php">如何数组化操作对象 <code>$obj[key];</code></a> ArrayAccess接口 或者 </p></li><li><p><a href="http://php.net/manual/en/language.oop5.magic.php#object.invoke">如何函数化对象 <code>$obj(123);</code></a> __invoke()或者Closure 预定义接口</p></li><li><p>yield 是什么，说个使用场景 <a href="https://www.oschina.net/translate/cooperative-multitasking-using-coroutines-in-php">yield</a> <a href="https://www.laruence.com/2015/05/28/3038.html">鸟哥解析</a></p></li><li><p><a href="http://psr.phphub.org/">PSR 是什么，PSR-1, 2, 4, 7</a></p></li><li><p>如何获取客户端 IP 和服务端 IP 地址</p><ul><li><a href="https://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php">客户端 IP</a>    </li><li><a href="https://stackoverflow.com/questions/5800927/how-to-identify-server-ip-address-in-php">服务端 IP</a></li><li>了解代理透传 实际IP 的概念</li></ul></li><li><p>如何开启 PHP 异常提示</p><ul><li>php.ini 开启 <code>display_errors</code> 设置 <code>error_reporting</code> 等级</li><li>运行时，使用 <code>ini_set(k, v);</code> 动态设置</li></ul></li><li><p><a href="https://stackoverflow.com/questions/7324645/php-header-redirect-301-what-are-the-implications">如何返回一个301重定向</a> </p><ul><li><strong>[WARNING]</strong> 一定当心设置 301 后脚本会继续执行，不要认为下面不会执行，必要时使用 <code>die</code> or <code>exit</code></li></ul></li><li><p>如何获取扩展安装路径</p><ul><li><code>phpinfo();</code> 页面查找 <code>extension_dir</code></li><li>命令行 <code>php -i |grep extension_dir</code></li><li>运行时 <code>echo ini_get(&#39;extension_dir&#39;);</code></li></ul></li><li><p>字符串、数字比较大小的原理，注意 0 开头的8进制、0x 开头16进制</p><ul><li>字符串比较大小，从左(高位)至右，逐个字符 ASCII 比较</li><li>注意字符串和数字组合比较 var_dump(‘10a’ == 10); //true 10a转型10</li></ul></li><li><p>BOM 头是什么，怎么除去</p><ul><li>win记事本保存默认开头<code>0xEF</code>,<code>0xBB</code>,<code>0xBF</code>（UTF8）</li><li>导入含有BOM的html时，页面始终不置顶(php 当成字符串导入)</li><li>trim($str, “\xEF\xBB\xBF”);<a href="https://stackoverflow.com/questions/10290849/how-to-remove-multiple-utf-8-bom-sequences-before-doctype">检测、去除</a></li></ul></li><li><p>什么是 MVC </p></li><li><p><a href="https://segmentfault.com/a/1190000010846788">依赖注入实现原理</a></p></li><li><p><a href="http://www.laruence.com/2008/04/14/318.html">如何异步执行命令</a></p><ul><li><code>&lt;img src=&quot;xxx.php&quot;&gt;</code> </li><li>ajax异步 但需要onload后执行</li><li>异步执行php脚本 如执行shell脚本exec() 但只能本机脚本</li><li>使用curl 但是timeout最小是1s</li><li>使用fsockopen 需要自己拼写发送内容</li></ul></li><li><p><a href="https://www.kancloud.cn/bozoyan/app/212223">模板引擎是什么，解决什么问题、实现原理（Smarty、Twig、Blade）</a></p></li><li><p><a href="https://segmentfault.com/a/1190000008159324">如何实现链式操作 <code>$obj-&gt;w()-&gt;m()-&gt;d();</code></a></p><ul><li>__call() &amp; call_user_func_array() 唤醒调用对象未知方法</li><li>调用的函数return $this</li></ul></li><li><p>Xhprof 、Xdebug 性能调试工具使用</p></li><li><p><a href="https://www.onmpw.com/m/view.php?aid=238">索引数组 <code>[1, 2]</code> 与关联数组 <code>[&#39;k1&#39;=&gt;1, &#39;k2&#39;=&gt;2]</code> 有什么区别</a></p><ul><li>存在覆盖的问题 $arr1 = array(‘one’,’two’,’three’,1=&gt;’four’); // ‘one four three’</li><li>关联数组有界限   $arr1 = array(1=&gt;’four’,’one’,’two’,’three’); // ‘1=&gt;four 2=&gt;one 3=&gt;two’ </li></ul></li><li><p><a href="https://zhuanlan.zhihu.com/p/40091810">缓存的使用方式、场景</a></p></li><li><p><a href="https://www.cnblogs.com/eleven24/p/7422170.html?utm_source=debugrun&utm_medium=referral">foreach 引用问题</a> </p></li></ul><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs stata"><span class="hljs-variable">$a</span> = [1,2,3];<br><span class="hljs-keyword">foreach</span> (<span class="hljs-variable">$a</span> <span class="hljs-keyword">as</span> <span class="hljs-variable">$v</span>)&#123;&#125;<br>var_dump(<span class="hljs-variable">$a</span>); <span class="hljs-comment">// 123</span><br><span class="hljs-keyword">foreach</span> (<span class="hljs-variable">$a</span> <span class="hljs-keyword">as</span> &amp;<span class="hljs-variable">$v</span>)&#123;&#125;<br>var_dump(<span class="hljs-variable">$a</span>); <span class="hljs-comment">//123</span><br># unset(<span class="hljs-variable">$v</span>); 或换了<span class="hljs-variable">$v</span><br><span class="hljs-keyword">foreach</span> (<span class="hljs-variable">$a</span> <span class="hljs-keyword">as</span> <span class="hljs-variable">$v</span>)&#123;&#125;<br>var_dump(<span class="hljs-variable">$a</span>); <span class="hljs-comment">// 122</span><br></code></pre></td></tr></table></figure><h2 id="实践篇"><a href="#实践篇" class="headerlink" title="实践篇"></a>实践篇</h2><ul><li><p>给定二维数组，根据某个字段排序 <a href="https://www.cnblogs.com/Dong-Ge/p/5583753.html">array_multisort() 多维数组排序</a></p><ul><li><code>array_multisort($field1, SORT_DESC, $field2, SORT_ASC, $data);</code></li></ul></li><li><p>如何判断上传文件类型，如：仅允许 jpg 上传 <a href="http://www.111cn.net/phper/21/0742a78eb9237635bd6d2b8e2a7817a9.htm">详情</a><br><code>$ext = $_FILES[&#39;file&#39;][&#39;type&#39;]; 或者 解析文件名(explode())</code></p></li><li><p>不使用临时变量交换两个变量的值 <code>$a=1; $b=2;</code>  =&gt;  <code>$a=2; $b=1;</code> <a href="https://blog.csdn.net/litchi_yang/article/details/78300562">传送门</a></p><figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs lasso">方案一 int 相加后相减  <br>方案二 先拼接$a .= $b; $b=str_replace($b,<span class="hljs-string">&#x27;&#x27;</span>,$a)<span class="hljs-params">...</span><br></code></pre></td></tr></table></figure></li><li><p>strtoupper 在转换中文时存在乱码，你如何解决？<code>php echo strtoupper(&#39;ab你好c&#39;);</code></p><ul><li>[方案传送门] <a href="https://blog.csdn.net/whd526/article/details/77871433">https://blog.csdn.net/whd526/article/details/77871433</a></li></ul><figure class="highlight plaintext"><figcaption><span>mb_convert_case($str,MB_CASE_TITLE,'UTF-8');</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs 方案一">方案二 自定义函数，使用str_split()按一字节切割，然后ord()转asscii 判断字母asscii<br></code></pre></td></tr></table></figure></li><li><p>Websocket、Long-Polling、Server-Sent Events(SSE) 区别 <a href="https://www.jianshu.com/p/4aa085b9984b">传送门</a> </p></li><li><p>“Headers already sent” 错误是什么意思，如何避免 </p><ul><li><a href="https://www.kancloud.cn/iyouki/how_to_interview/566636">避免header(‘xxx’)之前使用echo print等输出</a></li></ul></li></ul><h2 id="算法篇"><a href="#算法篇" class="headerlink" title="算法篇"></a>算法篇</h2><ul><li>快速排序（手写）</li><li>冒泡排序（手写）</li><li>二分查找（了解）</li><li>查找算法 KMP（了解）</li><li>深度、广度优先搜索（了解）</li><li>LRU 缓存淘汰算法（了解，Memcached 采用该算法）</li></ul><h2 id="数据结构篇（了解）"><a href="#数据结构篇（了解）" class="headerlink" title="数据结构篇（了解）"></a>数据结构篇（了解）</h2><ul><li>堆、栈特性 <a href="https://www.cnblogs.com/yanlingyin/archive/2011/12/10/2283303.html">详细</a><ul><li>堆 自行申请和释放空间，存放对象，空间比较大</li><li>栈 系统自动申请释放，执行程序，随函数被调用时分配的空间</li></ul></li><li>队列 </li><li>哈希表</li><li>链表</li></ul><h2 id="对比篇"><a href="#对比篇" class="headerlink" title="对比篇"></a>对比篇</h2><ul><li><p>Cookie 与 Session 区别 (深入分析区别)[<a href="https://juejin.im/entry/5766c29d6be3ff006a31b84e]">https://juejin.im/entry/5766c29d6be3ff006a31b84e]</a></p><ul><li>cookie  存客户端 用户可见 仅支持字符串二进制 缓存时间长 服务器压力少 </li><li>session 存服务端 用户不可见 支持多种数据类型 缓存时间有限 服务器压力大</li></ul></li><li><p><code>GET</code> 与 <code>POST</code> 区别 (传送门)[<a href="https://www.zhihu.com/question/28586791]">https://www.zhihu.com/question/28586791]</a></p></li><li><p><code>include</code> 与 <code>require</code> 区别</p><ul><li>include 使用时加载，require一开始就加载</li><li>include 错误时继续执行代码， require 停止执行</li></ul></li><li><p><code>include_once</code> 与 <code>require_once</code> 区别</p></li><li><p>Memcached 与 Redis 区别 [传送门](PHP include()和require()方法的区别)</p><ul><li>mc   多线程非阻塞IO 只支持K-V类型 key有几率被刷走，非持久化 通过key hashmap分布式</li><li>redis 单线程阻塞复用IO 支持多类型 key不被刷走，持久化存储，redis-cluster分布式</li></ul></li><li><p>MySQL 各个存储引擎、及区别（一定会问 MyISAM 与 Innodb 区别）<a href="https://blog.csdn.net/zgrgfr/article/details/74455547">传送门</a></p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-attribute">MyIsam</span> 写锁优先级更高，因此不适合更新操作，适合读，而且索引结构无须遍历主键索引获得记录<br></code></pre></td></tr></table></figure></li></ul><ul><li><p>HTTP 与 HTTPS 区别 <a href="https://juejin.im/entry/58d7635e5c497d0057fae036">传送门</a></p></li><li><p>Apache 与 Nginx 区别 <a href="https://blog.csdn.net/jcjc918/article/details/46665919">传送门</a></p></li><li><p>define() 与 const 区别 <a href="https://segmentfault.com/a/1190000009559436">传送门</a></p><ul><li>define可用在条件判断中，不成立的条件中，定义的不生效，成功定义后全局可用，可是表达式赋值</li><li>const不可用在条件判断中，不过可定义在类中，不可表达式赋值，必须是标量</li></ul></li><li><p>traits 与 interfaces 区别 及 traits 解决了什么痛点？</p><ul><li>traits 类似插件 use XXX 直接使用</li><li>interfaces 接口协议，不带实现方法</li></ul></li><li><p>Git 与 SVN 区别</p></li></ul><h2 id="数据库篇"><a href="#数据库篇" class="headerlink" title="数据库篇"></a>数据库篇</h2><ul><li><p>MySQL</p></li><li><p><a href="https://blog.csdn.net/qq_28261343/article/details/53044019">CRUD</a></p><ul><li><p><a href="https://blog.csdn.net/fashion2014/article/details/78826299/">50个基础sql</a> </p></li><li><p>JOIN、LEFT JOIN 、RIGHT JOIN、INNER JOIN <a href="https://www.cnblogs.com/blueoverflow/p/4714470.html">传送门</a></p></li><li><p>join 索引等优化 <a href="https://www.jianshu.com/p/6864abb4d885">传送门</a></p></li><li><p>UNION， UNION ALL </p></li><li><p>GROUP BY + COUNT + WHERE 组合案例 <a href="https://www.shuzhiduo.com/A/KE5Q4LWjJL/">详情</a></p></li><li><p><a href="https://www.w3schools.com/sql/sql_ref_mysql.asp">常用 MySQL 函数，如：now()、md5()、concat()、uuid()等</a></p></li><li><p><code>1:1</code>、<code>1:n</code>、<code>n:n</code> 各自适用场景</p></li><li><p>了解触发器是什么，说个使用场景 <a href="https://blog.csdn.net/qq_32575047/article/details/80305027">传送门</a></p></li><li><p>数据库优化手段</p><ul><li>索引、联合索引（命中条件）<a href="https://blog.csdn.net/qq_21987433/article/details/79753551">传送门1</a> <a href="https://juejin.cn/post/6844904073955639304">B+树</a></li><li><a href="https://segmentfault.com/a/1190000039131878">覆盖索引</a>  返回所有字段包含在组合索引当中</li><li>分库分表（<code>水平分表</code>、<code>垂直分表</code>） (水平分表 crc32($str)%5 哈希取模)</li><li><a href="http://mysql.taobao.org/monthly/2017/11/09/">分区</a></li><li><a href="https://www.zhihu.com/question/38418707">分区好处</a>  <a href="https://www.cnblogs.com/GrimMjx/p/10526821.html">分区优缺点</a></li></ul></li><li><p>索引相关</p><ul><li>索引，聚集索引， 非聚集索引</li></ul></li><li><p>会使用 <code>explain</code> 分析 SQL 性能问题，了解各参数含义 <a href="https://blog.csdn.net/wangyy130/article/details/51418880">传送门</a></p><ul><li><p>重点理解 <code>type</code>、<code>rows</code>、<code>key</code></p></li><li><p>Slow Log（有什么用，什么时候需要）<a href="https://www.cnblogs.com/kerrycode/p/5593204.html">传送门</a></p><ul><li>mysqldumoslow 日志汇总</li><li>show variables like ‘%slow_query_log%’;</li></ul></li><li><p>锁了解 <a href="https://juejin.im/post/5b82e0196fb9a019f47d1823">传送门</a></p><ul><li><p>悲观锁，排他锁，共享锁的了解</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">共享锁 共享锁可叠加，排他锁无法加锁<br>加锁方式 <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> tb <span class="hljs-keyword">where</span> id = <span class="hljs-number">1</span> <span class="hljs-keyword">lock</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">share mode</span>； <span class="hljs-keyword">update</span> <span class="hljs-keyword">delete</span> 默认加锁<br>排它锁 排它锁会阻塞所有的排它锁和共享锁<br>加锁方式 <span class="hljs-keyword">select</span> status <span class="hljs-keyword">from</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">where</span> id=<span class="hljs-number">1</span> <span class="hljs-keyword">for</span> <span class="hljs-keyword">update</span>;<br><br>解锁  <span class="hljs-keyword">commit</span>就好<br><br>表锁<br>加锁 <span class="hljs-keyword">lock</span> <span class="hljs-keyword">table</span> xxx <span class="hljs-keyword">read</span>; <span class="hljs-keyword">lock</span> <span class="hljs-keyword">table</span> xxx <span class="hljs-keyword">write</span>;<br>解锁 UNLOCK <span class="hljs-keyword">TABLES</span> <br><br></code></pre></td></tr></table></figure></li><li><p>死锁的出现和解决 </p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">查询是否锁表<br><span class="hljs-keyword">show</span> <span class="hljs-keyword">OPEN</span> <span class="hljs-keyword">TABLES</span> <span class="hljs-keyword">where</span> In_use &gt; <span class="hljs-number">0</span>;<br>查询进程（如果您有SUPER权限，您可以看到所有线程。否则，您只能看到您自己的线程）<br><span class="hljs-keyword">show</span> processlist<br>杀死进程id（就是上面命令的id列）<br>kill id<br><br>查看innodb行锁情况<br><span class="hljs-keyword">show</span> status <span class="hljs-keyword">like</span> <span class="hljs-string">&#x27;innodb_row_lock%&#x27;</span>;<br>查表锁<br><span class="hljs-keyword">show</span> <span class="hljs-keyword">open</span> <span class="hljs-keyword">tables</span> <span class="hljs-keyword">where</span> in_use &gt; <span class="hljs-number">0</span>;<br></code></pre></td></tr></table></figure></li></ul></li></ul></li></ul></li></ul><pre><code>* 行锁 表锁  间隙锁  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs">行锁 InnoDB只有在通过索引条件检索数据时使用行级锁，否则使用表锁！并且是等值过滤条件<br>表锁 InnoDB未通过索引检索<br>间隙锁 当我们用范围条件而不是相等条件检索数据，并请求共享或排他锁时，InnoDB会给符合条件的已有数据记录的索引项加锁；对于键值在条件范围内但并不存在的记录<br><br>行锁优化<br>尽可能让所有数据检索都通过索引来完成，避免无索引行或索引失效导致行锁升级为表锁。<br>尽可能避免间隙锁带来的性能下降，减少或使用合理的检索范围。<br>尽可能减少事务的粒度，比如控制事务大小，而从减少锁定资源量和时间长度，从而减少锁的竞争等，提供性能。<br>尽可能低级别事务隔离，隔离级别越高，并发的处理能力越低。<br><br></code></pre></td></tr></table></figure>* 属性值重复率 (行锁升级表锁)  <figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">当“值重复率”低时，甚至接近主键或者唯一索引的效果，“普通索引”依然是行锁；当“值重复率”高时，MySQL 不会把这个“普通索引”当做索引，即造成了一个没有索引的 <span class="hljs-keyword">SQL</span>，此时引发表锁。<br></code></pre></td></tr></table></figure>* [脏读及隔离级别](https://www.cnblogs.com/fengzheng/p/12557762.html)  [隔离级别实现方式](https://segmentfault.com/a/1190000025156465)* mvcc实现  * mvcc基本版本号实现(事务ID), 隔离级别RU,RC,RR 基于其之上构建</code></pre><ul><li><p>MSSQL(了解)</p><ul><li>查询最新5条数据</li></ul></li><li><p>NOSQL</p><ul><li>Redis、Memcached、MongoDB</li><li>对比、适用场景（可从以下维度进行对比）<ul><li>持久化</li><li>支持多钟数据类型</li><li>可利用 CPU 多核心</li><li>内存淘汰机制</li><li>集群 Cluster</li><li>支持 SQL</li><li>性能对比</li><li>支持事务</li><li>应用场景</li></ul></li><li>你之前为了解决什么问题使用的什么，为什么选它？</li></ul></li></ul><h2 id="服务器篇"><a href="#服务器篇" class="headerlink" title="服务器篇"></a>服务器篇</h2><ul><li><p>查看 CPU、内存、时间、系统版本等信息</p></li><li><p>find 、grep 查找文件</p></li><li><p>awk 处理文本</p></li><li><p>查看命令所在目录</p></li><li><p>自己编译过 PHP 吗？如何打开 readline 功能</p></li><li><p>如何查看 PHP 进程的内存、CPU 占用</p><figure class="highlight coq"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs coq">ps -aux | <span class="hljs-type">grep</span> php-fpm<br><span class="hljs-built_in">top</span> -p pid<br></code></pre></td></tr></table></figure></li><li><p>如何给 PHP 增加一个扩展     </p><ul><li><a href="https://blog.csdn.net/Aaroun/article/details/78225114">phpize安装拓展</a></li></ul></li><li><p>修改 PHP Session 存储位置、修改 INI 配置参数</p><ul><li><a href="https://blog.csdn.net/soonfly/article/details/52175614">配置说明</a></li></ul></li><li><p>负载均衡有哪几种，挑一种你熟悉的说明其原理 <a href="https://www.jianshu.com/p/da6e562fa3a6">4种</a></p></li><li><p>数据库主从复制 M-S 是怎么同步的？是推还是拉？会不会不同步？怎么办 </p><ul><li><a href="https://juejin.im/entry/592287ae128fe1005c2e4f6c">主从同步解析</a></li><li><a href="https://blog.51cto.com/13407306/2067333">不同步解决 重新主从</a></li></ul></li><li><p>如何保障数据的可用性，即使被删库了也能恢复到分钟级别。你会怎么做。</p><ul><li><a href="https://blog.csdn.net/simongeek/article/details/53316980">增量+全量备份+1小时延时从库</a></li></ul></li><li><p>数据库连接过多，超过最大值，如何优化架构。从哪些方便处理？</p><ul><li><a href="https://blog.csdn.net/comeoncomputer/article/details/78649988">连接池+max_connection+程序bug_连接未释放</a></li></ul></li><li><p>502 大概什么什么原因？ 如何排查  504呢？ <a href="https://blog.51cto.com/silencezone/1703413">传送门</a></p><ul><li>502: 请求的fpm由于某些原因没有执行完毕而终止执行(fpm已执行) max_children,request_terminate_timeout，fpm进程数不够，一般数据库有很多慢查询占用进程数</li><li>504：与nginx有关(没有请求到可执行的fpm) fastcgi_connect_timeout fastcgi_send_timeout fastcgi_send_timeout，如nginx的buffer设置太小而挂起504</li></ul></li></ul><h2 id="架构篇"><a href="#架构篇" class="headerlink" title="架构篇"></a>架构篇</h2><ul><li><p>偏运维（了解）：</p><ul><li>负载均衡（Nginx、HAProxy、DNS）</li><li>主从复制（MySQL、Redis）<ul><li><a href="https://www.cnblogs.com/kismetv/p/9236731.html">redis主从复制</a></li></ul></li><li>数据冗余、备份（MySQL增量、全量 原理）</li><li>监控检查（分存活、服务可用两个维度）</li><li>MySQL、Redis、Memcached Proxy 、Cluster 目的、原理</li><li>mysql分片</li><li>高可用集群</li><li>RAID</li><li>源代码编译、内存调优</li></ul></li><li><p>缓存</p><ul><li>工作中遇到哪里需要缓存，分别简述为什么</li></ul></li><li><p>搜索解决方案 <a href="https://www.cnblogs.com/jajian/p/9801154.html">es+solr+lucene</a></p></li><li><p>性能调优</p></li><li><p>各维度监控方案 <a href="https://zhuanlan.zhihu.com/p/33470183">传送门</a></p></li><li><p>日志收集集中处理方案  <a href="https://www.ibm.com/developerworks/cn/opensource/os-cn-elk/index.html">ELK</a></p></li><li><p>国际化(传送门)[]</p></li><li><p>数据库设计 <a href="https://zhuanlan.zhihu.com/p/25960208">传送门</a></p></li><li><p>静态化方案 <a href="https://blog.csdn.net/qq_15096707/article/details/50809197">传送门</a></p></li><li><p>画出常见 PHP 应用架构图 <a href="https://segmentfault.com/a/1190000008016139">传送门</a></p></li><li><p>kafka消息队列</p><ul><li><p><a href="https://segmentfault.com/a/1190000037455372">原理图</a></p></li><li><p><a href="https://juejin.im/post/6844903889003610119">知识点</a></p></li><li><p><a href="https://mp.weixin.qq.com/s/ejZBAGI7qLE_QYSe-AqipA">面试题</a></p></li><li><p><a href="https://zhuanlan.zhihu.com/p/46421050">nsq对比</a></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs">通过nsqlookup注册和发现服务，去中心化<br>nsq通过先写内存，再刷硬盘，而kafka直接刷硬盘<br>nsq更适合二开，和规模小的MQ需求<br></code></pre></td></tr></table></figure></li></ul></li></ul><ul><li>压测工具 <a href="https://www.jianshu.com/p/8c622e304e14">apache的ab，java的jmeter</a></li></ul><h2 id="框架篇"><a href="#框架篇" class="headerlink" title="框架篇"></a>框架篇</h2><ul><li>ThinkPHP（TP）、CodeIgniter（CI）、Zend（非 OOP 系列）</li><li>Yaf、Phalcon（C 扩展系）</li><li>Yii、Laravel、Symfony（纯 OOP 系列）</li><li>Swoole、Workerman （网络编程框架）</li><li>对比框架区别几个方向点<ul><li>是否纯 OOP</li><li>类库加载方式（自己写 autoload 对比 composer 标准）</li><li>易用性方向（CI 基础框架，Laravel 这种就是高开发效率框架以及基础组件多少） </li><li>黑盒（相比 C 扩展系）</li><li>运行速度（如：Laravel 加载一大堆东西）</li><li>内存占用</li></ul></li></ul><h2 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h2><ul><li><p>单例模式（重点）</p><ul><li><a href="https://segmentfault.com/a/1190000006062259">实现方式</a></li><li>注意：<strong>construct&amp;</strong>clone()防止被重写，</li><li><a href="https://www.cnblogs.com/damsoft/p/6105122.html">单例缺点</a>：</li></ul><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs markdown">1.多线程运行，实例未创建导致创建多实例 <br><span class="hljs-bullet">2.</span> 多进程运行，子进程使用父进程实例有问题 <br>3.长时间不使用会被GC回收<br>4.单一拓展困难<br></code></pre></td></tr></table></figure></li><li><p>工厂模式（重点）</p></li><li><p>观察者模式（重点）<a href="https://segmentfault.com/a/1190000007531552">传送门</a></p><ul><li>主题Subject实现attach detach notify 观察者Observer实现update()</li></ul></li><li><p>依赖注入（重点）<a href="https://segmentfault.com/a/1190000010846788">传送门</a></p><ul><li>依赖注入DI: 具有Interface的工厂类，能力实现在外部</li><li>控制反转IOC：DI上的容器container概念，make &amp;bind活用闭包实现</li><li>主要概念：超人由固定多能力转换成外部赋予单一能力</li></ul></li><li><p>装饰器模式 <a href="https://segmentfault.com/a/1190000007544975">传送门</a></p></li><li><p>代理模式 <a href="https://segmentfault.com/a/1190000007547503">传送门</a></p><ul><li>Proxy代理&amp;RealSubject实现Subject的接口方法</li><li>可以理解成代理服务器</li></ul></li><li><p>组合模式</p></li></ul><h2 id="安全篇"><a href="#安全篇" class="headerlink" title="安全篇"></a>安全篇</h2><ul><li>SQL 注入<ul><li><a href="https://segmentfault.com/a/1190000008117968">mysql_real_escape_string mysqli和pdo 预处理</a></li></ul></li><li>XSS 与 CSRF<ul><li><a href="http://jartto.wang/2017/12/15/xss-and-csrf/">htmlspecialchars过滤 get post区分  token 和 referer校验</a></li></ul></li><li>输入过滤 (goto)[<a href="https://laravelacademy.org/post/4610.html]">https://laravelacademy.org/post/4610.html]</a></li><li>Cookie 安全  <ul><li><a href="https://segmentfault.com/q/1010000007347730">https + AES加密 + 加salt + httponly</a></li></ul></li><li>禁用 <code>mysql_</code> 系函数</li><li>数据库存储用户密码时，应该是怎么做才安全 (sha加密 md5+salt)</li><li>验证码 Session 问题 <a href="https://itopic.org/nonsense-captcha.html">验证通过后清空</a></li><li>安全的 Session ID （让即使拦截后，也无法模拟使用）<ul><li><a href="https://www.jianshu.com/p/c4b32eb24894">位数长，固定时间无效,重要业务加独立密码</a></li></ul></li><li>目录权限安全 770 750 640</li><li>包含本地与远程文件<ul><li><a href="https://www.kancloud.cn/a173512/php_note/1460408">allow_url_include 开启</a></li></ul></li><li>文件上传 PHP 脚本 <ul><li><a href="https://www.php.net/manual/zh/features.file-upload.multiple.php">检查文件头部 后缀名，重命名文件</a></li></ul></li><li><code>eval</code> 函数执行脚本</li><li><code>disable_functions</code> 关闭高危函数</li><li>FPM 独立用户与组，给每个目录特定权限</li><li><a href="https://blog.csdn.net/liangxw1/article/details/79480014">了解 Hash 与 Encrypt 区别</a><ul><li>hash不可逆但能被穷举</li><li>encrypt 可逆</li></ul></li></ul><h2 id="高阶篇"><a href="#高阶篇" class="headerlink" title="高阶篇"></a>高阶篇</h2><ul><li>PHP 数组底层实现 （HashTable + Linked list）<a href="https://zhuanlan.zhihu.com/p/97762122">传送</a></li></ul><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-bullet">1.</span> 查找：key先time33算法再中间映射表找到下标然后找到对应bucket，然后遍历bucket当key相当返回数据，索引数组和字符数组的覆盖原因<br><span class="hljs-bullet">2.</span> 插入&amp;删除：影响索引，按2倍拓容，仅当触发阈值才删除<br><span class="hljs-bullet">3.</span> 有序：bucket双向链表存储，避免hash冲突<br></code></pre></td></tr></table></figure><ul><li><p>Copy on write 原理，何时 GC</p><ul><li><p>(手册)[<a href="http://php.net/manual/zh/features.gc.php]">http://php.net/manual/zh/features.gc.php]</a></p></li><li><p>(其余)[<a href="https://www.jianshu.com/p/d73b3ca418b0]">https://www.jianshu.com/p/d73b3ca418b0]</a></p></li><li><p>实例代码</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs php"><span class="hljs-variable">$a</span> = <span class="hljs-keyword">array</span>( <span class="hljs-string">&#x27;one&#x27;</span> );<br><span class="hljs-variable">$a</span>[] =&amp; <span class="hljs-variable">$a</span>;<br><span class="hljs-keyword">unset</span>(<span class="hljs-variable">$a</span>);<br></code></pre></td></tr></table></figure></li></ul></li><li><p>PHP 进程模型，进程通讯方式，进程线程区别</p><ul><li><a href="https://www.jianshu.com/p/065bbbb74565">php 进程模型</a></li><li><a href="https://www.jianshu.com/p/08bcf724196b">php 进程通讯</a></li><li><a href="http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html">进程线程区别</a></li><li><a href="https://blog.csdn.net/deniece1/article/details/102983304">进程的状态</a></li></ul></li><li><p>僵尸进程和孤儿进程和守护进程</p><ul><li>孤儿进程： 一个父进程退出，而它的一个或多个子进程还在运行，那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养，并由init进程对它们完成状态收集工作</li><li>僵尸进程： 一个进程使用fork创建子进程，如果子进程退出，而父进程并没有调用wait或waitpid获取子进程的状态信息，那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程（ps 进程状态’Z’）</li></ul></li><li><p>yield 核心原理是什么 <a href="https://www.laruence.com/2015/05/28/3038.html">传送门</a></p></li><li><p>PDO prepare 原理 (绑定变量 相当于预设输入是字符串)</p></li><li><p>PHP 7 与 PHP 5 <a href="http://www.runoob.com/php/php7-new-features.html">区别</a>  <a href="https://www.zhihu.com/question/353685596">原因</a></p></li></ul><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-bullet">1.</span> JIT:opcache的增强版本<br><span class="hljs-bullet">2.</span> zval的改变<br><span class="hljs-bullet">3.</span> php数组变化<br><span class="hljs-bullet">4.</span> 函数调用机制<br></code></pre></td></tr></table></figure><ul><li>Swoole 适用场景，协程实现方式 <a href="https://wiki.swoole.com/#/">swoole文档</a></li></ul><h2 id="前端篇"><a href="#前端篇" class="headerlink" title="前端篇"></a>前端篇</h2><ul><li>原生获取 DOM 节点，属性</li><li>盒子模型</li><li>CSS 文件、style 标签、行内 style 属性优先级</li><li>HTML 与 JS 运行顺序（页面 JS 从上到下）</li><li>JS 数组操作</li><li>类型判断</li><li>this 作用域</li><li>.map() 与 this 具体使用场景分析</li><li>Cookie 读写</li><li>JQuery 操作</li><li>Ajax 请求（同步、异步区别）随机数禁止缓存</li><li>Bootstrap 有什么好处</li><li>跨域请求 N 种解决方案</li><li>新技术（了解）<ul><li>ES6</li><li>模块化</li><li>打包</li><li>构建工具</li><li>vue、react、webpack、</li><li>前端 mvc </li></ul></li><li>优化<ul><li>浏览器单域名并发数限制</li><li>静态资源缓存 304 （If-Modified-Since 以及 Etag 原理）</li><li>多个小图标合并使用 position 定位技术 减少请求</li><li>静态资源合为单次请求 并压缩</li><li>CDN</li><li>静态资源延迟加载技术、预加载技术</li><li>keep-alive</li><li>CSS 在头部，JS 在尾部的优化（原理）</li></ul></li></ul><h2 id="网络篇"><a href="#网络篇" class="headerlink" title="网络篇"></a>网络篇</h2><ul><li>IP 地址转 INT</li><li>192.168.0.1/16 是什么意思 <a href="https://zhuanlan.zhihu.com/p/93869570">解析</a> <a href="https://tool.chinaz.com/tools/subnetmask">在线转换</a></li><li>DNS 主要作用是什么？</li><li>IPv4 与 v6 区别</li></ul><h2 id="网络编程篇"><a href="#网络编程篇" class="headerlink" title="网络编程篇"></a>网络编程篇</h2><ul><li><p>TCP 三次握手 四次挥手流程 <a href="https://www.cnblogs.com/buxiangxin/p/8336022.html">传送门</a></p><ul><li>三次握手 server半连接，伪造大量syn包造成ddos攻击</li></ul></li><li><p>TCP&amp;UDP区别，分别适用场景 <a href="https://blog.csdn.net/u013777351/article/details/49226101">传送门</a></p></li><li><p><a href="https://blog.csdn.net/yaopeng_2005/article/details/7064869">OSI七层协议</a>  缩写 物链网 传输会话表示应用</p></li><li><p><a href="https://zhuanlan.zhihu.com/p/163569041">有什么办法能保证 UDP 高可用性(了解)</a></p></li><li><p>TCP 粘包如何解决？<a href="https://segmentfault.com/a/1190000039691657">原因及处理</a></p></li><li><p>为什么需要心跳？ <a href="https://www.jianshu.com/p/a1b9bd5b78c6">传送门</a></p><ul><li>TCP并无及时的断线检测</li><li>TCP的keepalive能检查deadLink，但影响全局</li></ul></li><li><p>什么是长连接？</p></li><li><p>HTTPS 是怎么保证安全的？<a href="https://www.jianshu.com/p/b894a7e1c779">解析</a> <a href="https://www.cnblogs.com/kubidemanong/p/9390021.html">流程图解</a></p><ul><li>非对称加密+对称加密SSL: 服务器明文传输公钥-&gt;客服端使用公钥加密得到密文（对称加密的秘钥x）-&gt; 服务器私钥解密得到对称加密的秘钥x-&gt;服务器&amp;客服端使用秘钥x进行对称加密传输数据</li><li>第三方认证机构的CA证书：hash  </li></ul></li><li><p>流与数据报的区别 <a href="https://www.cnblogs.com/zzyoucan/p/6361195.html">解析</a></p><ul><li>tcp流模式协议，可靠</li><li>udp数据报模式，数据可能会丢失</li></ul></li><li><p>http 1.0 1.1 2.0有哪些改变 <a href="https://juejin.im/post/6844903923136856078">参考</a></p><ul><li><p>http 1.0： 增加POST HEADER，支持请求头和相应头，扩充了传输内容格式Content-Type</p></li><li><p>http1.1 ： </p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-bullet">1.</span> 增加PUT DETELE<br><span class="hljs-bullet">2.</span> 支持长连接Connection，可以设置keep-alive保持连接不断开<br><span class="hljs-bullet">3.</span> 支持缓存 cache-control，客户端的静态文件缓存<br><span class="hljs-bullet">4.</span> 支持断点重传，Content-Range: bytes 0-499/22400<br></code></pre></td></tr></table></figure></li><li><p>http2.0</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs makefile"><span class="hljs-section">二进制分帧: HTTP2之后将所有传输的信息分割为更小的消息和帧，并对它们采用二进制格式的编码，提高传输效率</span><br>多路复用：在共享TCP链接的基础上同时发送请求和响应<br><span class="hljs-section">头部压缩: 维护一个头部信息字典，差量进行更新头信息，减少头部信息传输占用的资源</span><br>服务器推送：服务器可以额外的向客户端推送资源，而无需客户端明确的请求<br></code></pre></td></tr></table></figure></li></ul></li><li><p>http 断点重传原理 <a href="https://www.cnblogs.com/findumars/p/5745345.html">goto</a></p></li><li><p>进程间通信几种方式，最快的是哪种？<a href="https://www.cnblogs.com/zgq0/p/8780893.html">解析</a></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs">1.管道：速度慢，容量有限，只有父子进程能通讯    <br>2.FIFO：任何进程间都能通讯，但速度慢    <br>3.消息队列：容量受到系统限制，且要注意第一次读的时候，要考虑上一次没有读完数据的问题    <br>4.信号量：不能传递复杂消息，只能用来同步    <br>5.共享内存区：能够很容易控制容量，速度快，但要保持同步，比如一个进程在写的时候，另一个进程要注意读写的问题，相当于线程中的线程安全，当然，共享内存区同样可以用作线程间通讯，不过没这个必要，线程间本来就已经共享了同一进程内的一块内存<br></code></pre></td></tr></table></figure></li><li><p><code>fork()</code> 会发生什么？</p><ul><li>pid=0:子进程 pid=-1：错误失败 pid=1:父进程</li></ul></li></ul><h2 id="API-篇"><a href="#API-篇" class="headerlink" title="API 篇"></a>API 篇</h2><ul><li><p>RESTful 是什么 <a href="https://www.ruanyifeng.com/blog/2011/09/restful.html">阮一峰</a></p><ul><li>如何设计RESTful 阮一峰</li><li><a href="https://my.oschina.net/crazymus/blog/521523">php 实现RESTful</a></li></ul></li><li><p>如何在不支持 <code>DELETE</code> 请求的浏览器上兼容 <code>DELETE</code> 请求</p><ul><li>form表单添加 <code>&lt; input type = hidden name = _method value = DELETE&gt;</code> </li></ul></li><li><p>常见 API 的 <code>APP_ID</code> <code>APP_SECRET</code> 主要作用是什么？阐述下流程 </p><ul><li>app_id 开发者账号，app_key(账号权限)+app_secret(账号密码)  <a href="https://blog.csdn.net/fuxx__/article/details/68927764">传送门</a></li></ul></li><li><p>API 请求如何保证数据不被篡改？</p><ul><li>https，sign排序签名，数据加密</li></ul></li><li><p>JSON 和 JSONP 的区别 <a href="https://www.jianshu.com/p/c6ac7d344cc6">goto</a></p><ul><li>jsonp支持跨域，附带callback，header支持Access-Control-Allow-Origin:domain</li></ul></li><li><p>数据加密和验签的区别 <a href="https://blog.csdn.net/simonchi/article/details/38531971">传送门</a></p><ul><li>数据加密 公钥加密，私钥解密，保证接收方数据安全</li><li>数据签名 私钥加密，公钥解密，保证发送方数据安全，确保自己发送而不是别人</li></ul></li><li><p>RSA非对称加密 是什么 <a href="http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html">goto</a></p></li><li><p>API 版本兼容怎么处理  <a href="https://juejin.im/post/5977f8ba5188255b9a6ad820">传送门</a></p><ul><li>url参数加版本号?v=2, header头部添加版本号</li><li>通过token参数 服务端确认权限和版本</li><li>token 生成原理 <a href="https://blog.csdn.net/zhu_xiao_yuan/article/details/77017196">传送门</a></li></ul><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">1</span>. api_token = md<span class="hljs-number">5</span> (&#x27;模块名&#x27; + &#x27;控制器名&#x27; + &#x27;方法名&#x27; + &#x27;<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">18</span>&#x27; + &#x27;加密密钥&#x27;) = <span class="hljs-number">770</span>fed<span class="hljs-number">4</span>ca<span class="hljs-number">2</span>aabd<span class="hljs-number">20</span>ae<span class="hljs-number">9</span>a<span class="hljs-number">5</span>dd<span class="hljs-number">774711</span>de<span class="hljs-number">2</span><br><span class="hljs-attribute">2</span>. user_token = user_id + 过期时间 + 加密密钥 <br></code></pre></td></tr></table></figure></li><li><p>限流漏桶、令牌桶 <a href="https://www.jianshu.com/p/a59c13e70582">传送门</a></p><ul><li>漏桶算法：强调流出速率，以恒定速率处理请求，水满则溢出拒绝服务，类似消息队列</li><li>令牌桶： 强调流入，以一定速率往桶里加令牌，处理请求则获取一个令牌，桶满则不加令牌，优点在于能调整令牌加入速率（google Guava的RateLimiter）</li></ul></li><li><p>OAuth 2 主要用在哪些场景下 rbac呢</p><ul><li><a href="http://www.heibaiketang.com/blog/show/21.html">oauth2 流程</a></li><li>接口授权，用户权限</li><li>oauth2：按规则认证，权限粒度更细（查看按钮）。rbac基于节点认证，粒度比较大</li></ul></li><li><p>JWT <a href="http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html">传送门</a></p></li><li><p>PHP 中 <code>json_encode([&#39;key&#39;=&gt;123]);</code> 与 <code>return json_encode([]);</code> 区别，会产生什么问题？如何解决</p><ul><li><p>输出</p><figure class="highlight gml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs gml"><span class="hljs-built_in">json_encode</span>([<span class="hljs-string">&#x27;key&#x27;</span>=&gt;<span class="hljs-number">123</span>]);<br><span class="hljs-keyword">return</span> <span class="hljs-built_in">json_encode</span>([]);<br><br>#输出<br><span class="hljs-built_in">string</span>(<span class="hljs-number">11</span>) <span class="hljs-string">&quot;&#123;&quot;</span>key<span class="hljs-string">&quot;:123&#125;&quot;</span><br><span class="hljs-built_in">string</span>(<span class="hljs-number">2</span>) <span class="hljs-string">&quot;[]&quot;</span><br></code></pre></td></tr></table></figure><ul><li>区别 {}是对象（key是string），[]是数组（key是int） (传送门)[<a href="https://blog.csdn.net/weihuiblog/article/details/79124364]">https://blog.csdn.net/weihuiblog/article/details/79124364]</a></li></ul></li></ul></li></ul><h2 id="拓展项"><a href="#拓展项" class="headerlink" title="拓展项"></a>拓展项</h2><ul><li>了解常用语言特性，及不同场景适用性。<ul><li>PHP VS Golang</li><li>PHP VS Python</li><li>PHP VS JAVA</li></ul></li><li>了解 PHP 扩展开发</li><li>熟练掌握 C</li></ul>]]></content>
    
    <summary type="html">
    
      该仓库主要真是国内 PHP 面试经常被问到的知识点做汇总。 仅是针对性指出知识点，相应还需自己查找相关资料系统学习。 我希望各位能不仅仅了解是什么，还要了解为什么，以及背后的原理。
    
    </summary>
    
    
      <category term="PHP" scheme="https://www.bkduck.cn/categories/PHP/"/>
    
    
      <category term="php" scheme="https://www.bkduck.cn/tags/php/"/>
    
      <category term="php知识点" scheme="https://www.bkduck.cn/tags/php%E7%9F%A5%E8%AF%86%E7%82%B9/"/>
    
  </entry>
  
</feed>
