<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>ZingLix Blog</title>
    <description>这里是 ZingLix 的个人博客，与你一起在计算机的世界探索~</description>
    <link>https://zinglix.xyz/</link>
    <atom:link href="https://zinglix.xyz/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Thu, 07 Aug 2025 11:55:46 +0000</pubDate>
    <lastBuildDate>Thu, 07 Aug 2025 11:55:46 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Immich 反向地理编码原理和汉化思路</title>
        <description>&lt;p&gt;Immich 默认识别出来的照片位置都奇奇怪怪的，不仅仅是英文，还有一些不常见的名字，在照片分类搜索的时候非常麻烦。周末仔细研究了下 Immich 到底是怎么实现反向地理编码的，并想办法对其进行了汉化。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;如果你到这里，是为了实现地名汉化的话，请直接前往 &lt;a href=&quot;https://github.com/ZingLix/immich-geodata-cn&quot;&gt;这个项目&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;immich-反向地理编码工作原理&quot;&gt;Immich 反向地理编码工作原理&lt;/h2&gt;

&lt;p&gt;为了能够实现汉化的目标，首先我们得先明白 Immich 是怎么在本地实现反向地理编码的。&lt;/p&gt;

&lt;h3 id=&quot;反向编码&quot;&gt;反向编码&lt;/h3&gt;

&lt;p&gt;以下以 v1.124.2 为例，Immich 的反向地理编码都实现在 &lt;a href=&quot;https://github.com/immich-app/immich/blob/1311189fab958bea2177a92e1cc1b7ebb1822bd8/server/src/repositories/map.repository.ts#L108&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reverseGeocode&lt;/code&gt;&lt;/a&gt; 这个函数中，传入的是一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GeoPoint&lt;/code&gt; 对象，实际上就是经度和纬度。&lt;/p&gt;

&lt;p&gt;之后，根据经纬度，进行了如下的 SQL 查询&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;geodata_places&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;earth_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ll_to_earth_public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;@&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ll_to_earth_public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;earth_distance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ll_to_earth_public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;ll_to_earth_public&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这其中&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;earth_box&lt;/code&gt; 创建一个以给定点为中心的球体范围&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ll_to_earth_public&lt;/code&gt; 将地理坐标 (纬度和经度) 转换为三维球体上的点&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt; 子句筛选出 &lt;strong&gt;距离输入的目标点 25,000 米（25 公里）范围内&lt;/strong&gt; 的地理点，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ORDER BY&lt;/code&gt; 子句根据距离从近到远排序。换句话说，就是找到了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;geodata_places&lt;/code&gt; 库中，距离输入点最近的地理点。&lt;/p&gt;

&lt;p&gt;找到了最近的点之后，取出这个点的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ countryCode, name: city, admin1Name }&lt;/code&gt;，也就是 &lt;strong&gt;国家码&lt;/strong&gt;、&lt;strong&gt;名称&lt;/strong&gt;、&lt;strong&gt;一级行政区名称&lt;/strong&gt;。整理一下顺序，将国家码转换成国家名，这就对应了我们在 Immich 中看到的照片位置中的 &lt;strong&gt;国&lt;/strong&gt;、&lt;strong&gt;省&lt;/strong&gt;、&lt;strong&gt;市&lt;/strong&gt; 三级。至于这个表是如何构建的，后面我们再单独分析。&lt;/p&gt;

&lt;p&gt;这里名称和一级行政区名称都是直接从数据库表中得到的，而国家名是从国家码转换得到的，这里用到了 &lt;a href=&quot;https://github.com/michaelwittig/node-i18n-iso-countries&quot;&gt;node-i18n-iso-countries&lt;/a&gt; 这个库的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getName&lt;/code&gt; 方法。但在 Immich 中，调用时的代码是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getName(countryCode, 'en')&lt;/code&gt;，将语言用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;'en'&lt;/code&gt; 写死了，所以只能是英文，并没有加上任何 i18n 的机制。&lt;/p&gt;

&lt;p&gt;而如果上面没有找到的话，就会再进行一次 SQL 查询&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;naturalearth_countries&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;coordinates&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段 SQL 就是在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;naturalearth_countries&lt;/code&gt; 表中找到哪些记录的 coordinates 包含输入的坐标，也就是根据自然地球中国家的划分，确定坐标所在的国家。如果走到这一条，则不会再去确定更细粒度的省市两级划分。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简而言之，Immich 就是在数据库里事先准备好了大量地名，然后用照片的坐标去匹配数据库里最近的地名，之后就以该地名作为照片的地名。找不到的话，就退化到只用国家信息，根据国家的区划划分。&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;数据构建&quot;&gt;数据构建&lt;/h3&gt;

&lt;p&gt;接下来的一个大问题就是，数据库里的数据是从哪来的。&lt;/p&gt;

&lt;p&gt;Immich 所有的反向地理编码数据都来的 &lt;a href=&quot;https://www.geonames.org/&quot;&gt;GeoNames&lt;/a&gt;，放在了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/build/geodata&lt;/code&gt; 文件夹下，每次发版都会从 &lt;a href=&quot;https://download.geonames.org/export/dump/&quot;&gt;这里&lt;/a&gt; 获取最新的数据。&lt;/p&gt;

&lt;p&gt;文件夹中有这么几个文件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;admin1CodesASCII.txt：一级行政区划列表（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id | name | name ascii | geoname id&lt;/code&gt;）&lt;/li&gt;
  &lt;li&gt;admin2Codes.txt：二级行政区划列表（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id | name | name ascii | geoname id&lt;/code&gt;）&lt;/li&gt;
  &lt;li&gt;cities500.txt：所有人口大于 500 的城市列表&lt;/li&gt;
  &lt;li&gt;geodata-date.txt：数据更新时间&lt;/li&gt;
  &lt;li&gt;ne_10m_admin_0_countries.geojson：自然地球国家划分，详细介绍可以 &lt;a href=&quot;https://github.com/nvkelso/natural-earth-vector/tree/master&quot;&gt;看这&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Immich 导入的入口在 &lt;a href=&quot;https://github.com/immich-app/immich/blob/1311189fab958bea2177a92e1cc1b7ebb1822bd8/server/src/repositories/map.repository.ts#L41C1-L42C1&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init&lt;/code&gt;&lt;/a&gt; 函数中，这里会首先查看 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system-metadata&lt;/code&gt; 中 key 为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reverse-geocoding-state&lt;/code&gt; 的值，里面记录了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lastUpdate&lt;/code&gt; 的时间，也就是上次导入数据的时间。会将这个时间与 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;geodata-date.txt&lt;/code&gt; 文件中的时间进行比较，如果文件中时间较新则说明有更新的数据则开始导入，否则就跳过避免重复导入。&lt;/p&gt;

&lt;p&gt;具体导入的逻辑在 &lt;a href=&quot;https://github.com/immich-app/immich/blob/1311189fab958bea2177a92e1cc1b7ebb1822bd8/server/src/repositories/map.repository.ts#L207&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;importGeodata&lt;/code&gt;&lt;/a&gt; 中，其中抛开建立表的逻辑，核心在于 &lt;a href=&quot;https://github.com/immich-app/immich/blob/1311189fab958bea2177a92e1cc1b7ebb1822bd8/server/src/repositories/map.repository.ts#L226C3-L226C11&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loadCities500&lt;/code&gt;&lt;/a&gt; 函数。&lt;/p&gt;

&lt;p&gt;cities500.txt 中格式类似 csv，以 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\t&lt;/code&gt; 作为分隔，通过如下规则转换成数据库中的内容&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;alternateNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;latitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;longitude&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;countryCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;admin1Code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;admin2Code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;modificationDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;admin1Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;admin1Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;admin2Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;admin2Map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineSplit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这其中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin1Map&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin2Map&lt;/code&gt; 就是通过读取 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin1CodesASCII.txt&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin2Codes.txt&lt;/code&gt; 中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; 到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; 的映射关系得到的。&lt;/p&gt;

&lt;p&gt;再结合前面提到的反向编码逻辑，就是根据 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latitude&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;longitude&lt;/code&gt; 找到最近的点，然后拿到他的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;countryCode&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin1Name&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt;，这一信息就作为了照片的地理位置信息。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;没错，admin2Name 根本没用上，admin2Codes.txt 也没用&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;汉化思路&quot;&gt;汉化思路&lt;/h2&gt;

&lt;p&gt;Immich 将照片的地理位置信息分为了 &lt;strong&gt;国&lt;/strong&gt;、&lt;strong&gt;省&lt;/strong&gt;、&lt;strong&gt;市&lt;/strong&gt; 三级。再捋一遍文件的作用，也就是&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;从 cities500.txt 中找到最近的点，拿到他的名称作为 &lt;strong&gt;市&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;根据这个点的 admin1Code 信息，去 admin1CodesASCII.txt 文件中找到 &lt;strong&gt;省&lt;/strong&gt; 级别的名称&lt;/li&gt;
  &lt;li&gt;根据这个点的 countryCode，用 &lt;a href=&quot;https://github.com/michaelwittig/node-i18n-iso-countries&quot;&gt;node-i18n-iso-countries&lt;/a&gt; 转换成 &lt;strong&gt;国&lt;/strong&gt; 级别名称&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;作用搞清楚了，接下来汉化的思路就好搞了&lt;/p&gt;

&lt;h3 id=&quot;国&quot;&gt;国&lt;/h3&gt;

&lt;p&gt;这一步骤主要依赖 &lt;a href=&quot;https://github.com/michaelwittig/node-i18n-iso-countries&quot;&gt;node-i18n-iso-countries&lt;/a&gt; 这个库，而 &lt;a href=&quot;https://github.com/immich-app/immich/blob/1311189fab958bea2177a92e1cc1b7ebb1822bd8/server/src/repositories/map.repository.ts#L131&quot;&gt;代码&lt;/a&gt; 中把转换的目标语言写死为了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;en&lt;/code&gt;，那么没有办法改目标语言，就只能从这个库的数据入手。&lt;/p&gt;

&lt;p&gt;这个库的数据来源也是通过静态文件的形式实现的，具体文件内容可以看 &lt;a href=&quot;https://github.com/michaelwittig/node-i18n-iso-countries/tree/master/langs&quot;&gt;这里&lt;/a&gt;。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;en.json&lt;/code&gt; 就是转换成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;'en'&lt;/code&gt; 时候的数据来源，那我们只需要将其改写成中文即可，而中文的信息就在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zh.json&lt;/code&gt; 里，替换掉即可，就像 &lt;a href=&quot;https://github.com/ZingLix/immich-geodata-cn/blob/main/i18n-iso-countries/langs/en.json&quot;&gt;这样&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;最后，将修改后的文件替换掉 Immich 镜像中的原始文件就可以了。&lt;/p&gt;

&lt;h3 id=&quot;省&quot;&gt;省&lt;/h3&gt;

&lt;p&gt;省的名称都在 admin1CodesASCII.txt 文件中，好在 &lt;a href=&quot;https://download.geonames.org/export/dump/&quot;&gt;GeoNames&lt;/a&gt; 提供了 alternateNamesV2.zip 这一文件，包含了许多地点的不同语言的名称，借助这一信息可以直接进行翻译，替换掉原来的名称即可。代码实现在 &lt;a href=&quot;https://github.com/ZingLix/immich-geodata-cn/blob/432198c58216c1d7de75f8283ae35fd310abd8ae/geodata/translate.py#L119&quot;&gt;这里&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;市&quot;&gt;市&lt;/h3&gt;

&lt;p&gt;cities500.txt 这个文件主要的目标就是翻译 name 字段，但观察这个文件后可以发现，它的粒度非常细，不仅仅到市一级，还可能是区或者县，还是很古老的名字，非常不适合使用。&lt;/p&gt;

&lt;p&gt;为了解决这个问题，可以通过地图提供商的逆向地理编码 API 对这些地方进行重新识别，获得标准的一级、二级行政区划名称，这里分别实现了适用于 &lt;a href=&quot;https://github.com/ZingLix/immich-geodata-cn/blob/main/geodata/generate_geodata_amap.py&quot;&gt;国内采用高德的版本&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/ZingLix/immich-geodata-cn/blob/main/geodata/generate_geodata_locationiq.py&quot;&gt;国外使用 LocationIQ 的版本&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;另外，默认的 cities500.txt 文件由于数据量有限，部分地区数据点较少，就会导致 Immich 在反向地理编码的时候出错。而实际上，&lt;a href=&quot;https://download.geonames.org/export/dump/&quot;&gt;GeoNames&lt;/a&gt; 还提供了不同国家的完整地理点信息，比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CN.zip&lt;/code&gt;，可以作为补充添加进 cities500.txt 以提升效果，实现在 &lt;a href=&quot;https://github.com/ZingLix/immich-geodata-cn/blob/main/geodata/enhance_data.py&quot;&gt;这里&lt;/a&gt;。但考虑到数据量庞大，所以只默认增加了直辖市，有需要的再增加。&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;以上总结了 Immich 逆向地理编码的原理，以及分享了如何实现汉化的，代码都放在了这个 &lt;a href=&quot;https://github.com/ZingLix/immich-geodata-cn&quot;&gt;仓库&lt;/a&gt; 中，也有现成的东西可以用。&lt;/p&gt;

</description>
        <pubDate>Thu, 23 Jan 2025 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2025/01/23/immich-reverse-geocoding/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2025/01/23/immich-reverse-geocoding/</guid>
        
        <category>Immich</category>
        
        <category>开源</category>
        
        
      </item>
    
      <item>
        <title>Go 在使用泛型时无法与 Pointer Receiver 共存的解决方法</title>
        <description>&lt;h2 id=&quot;问题描述&quot;&gt;问题描述&lt;/h2&gt;

&lt;p&gt;在使用 Go 的泛型时，如果泛型类型存在 constraint，而传入的类型在实现这个 constraint 时使用的是 pointer receiver，那么就会遇到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XXX does not satisfy XXX (method XXX has pointer receiver)&lt;/code&gt; 的报错，就比如下面这个例子希望用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create&lt;/code&gt; 函数完成所有创建 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; 的操作&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;SetID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Student&lt;/code&gt; 用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(p *Student)&lt;/code&gt; 实现了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt;，然而如果用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create[Student](id)&lt;/code&gt; 这种方式调用时，编译会遇到这个报错&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;Student does not satisfy Person (method SetID has pointer receiver)
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;问题解释&quot;&gt;问题解释&lt;/h2&gt;

&lt;p&gt;问题就在于这段代码中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(p *Student)&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 Go 中会认为是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*Student&lt;/code&gt; 实现了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SetID&lt;/code&gt; 方法，或者说实现了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; interface，而不是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Student&lt;/code&gt;，因此提示 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Student&lt;/code&gt; 并不满足 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;那一个办法是把实现 interface 传入的改成 value receiver&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SetID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;// 传入的 p 类型去掉了 *&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样可以通过编译且正常运行，但问题是变成了值传递后，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SetID&lt;/code&gt; 并不会作用于传入的那个变量，这个函数也形同虚设。&lt;/p&gt;

&lt;p&gt;另一个解决方案是可以把调用函数时改成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create[*Student](1)&lt;/code&gt;，加上这个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt;，报错也会随之消除。但问题就解决了吗？&lt;/p&gt;

&lt;p&gt;再仔细看这个函数在传入类型后会变成什么样&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;// T -&amp;gt; *Student&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// var person *Student&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里暂且不论原本的返回类型 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*T&lt;/code&gt; 会变成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;**Student&lt;/code&gt; 的问题，这个很容易通过调整返回值类型解决。&lt;/p&gt;

&lt;p&gt;核心问题在于第二行我们声明了一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*Student&lt;/code&gt; 类型的指针，但实例化在哪？我们创建了一个空指针，所以在运行时会遇到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runtime error: invalid memory address or nil pointer dereference&lt;/code&gt;。同时由于语言限制，我们手上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T*&lt;/code&gt; 并不能转成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; 然后让我们完成实例化。&lt;/p&gt;

&lt;p&gt;那么能不能传入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;，然后转成指针再调用 interface 的方法呢？&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
	&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// 报错&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然而编译器又给了一个错误 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person.SetID undefined (type *T is pointer to type parameter, not type parameter)&lt;/code&gt;，这个问题在于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SetID&lt;/code&gt; 是定义给 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Student&lt;/code&gt; 的，不是给 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Student*&lt;/code&gt; 用的。&lt;/p&gt;

&lt;p&gt;很遗憾，由于 Go 语言层面的缺陷，在仅使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; 这一个参数时并不能完成我们想要的东西，如果有办法，请通过网页最下方的邮件告诉我，不甚感激。&lt;/p&gt;

&lt;h2 id=&quot;解决方案&quot;&gt;解决方案&lt;/h2&gt;

&lt;p&gt;问题在于用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; 编译器不认 constraint，用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T*&lt;/code&gt; 又拿不到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; 进行实例化，那么只能去掉 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; 的限制，同时再传入带有限制的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T*&lt;/code&gt;。思路如此，具体实现来说需要定义这么一个 interface&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PersonPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Person&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个定义了一个指针 interface，第一行这里暂时先去掉了 constraint，允许传入任意类型 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;，然后通过第二行使得这个 interface 允许的类型是且只能是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*T&lt;/code&gt;，让我们能从 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; 拿到指针，再通过第三行去保证实现了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; 这个 interface。&lt;/p&gt;

&lt;p&gt;那我们就可以进一步修改函数，将传入的类型改为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PersonPtr&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ptr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PersonPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]](&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ptr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但这仍然不够，编译器会提示 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined: T&lt;/code&gt;，因为我们没有定义 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;，所以必须在函数的泛型列表中加上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;，这个函数只能变为&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ptr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PersonPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]](&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ptr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;调用时就变成了&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;stu&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样调用真的很丑，但好在 Go 这回终于做了个人，通过类型的自动推导可以自动推导出第二个参数，所以调用时可以简化为&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;stu&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样调用看起来就和谐了许多（虽然背后的实现需要用些难懂的 trick，但我们至少终于实现了 Go 中的泛型与 pointer receiver 的共存…&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;珍爱生命，远离 Go 的泛型！&lt;/p&gt;
</description>
        <pubDate>Wed, 24 Jan 2024 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2024/01/24/go-generic-pointer-receiver/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2024/01/24/go-generic-pointer-receiver/</guid>
        
        <category>Go</category>
        
        
      </item>
    
      <item>
        <title>N5105 PVE 虚拟机随机死机/重启解决方案</title>
        <description>&lt;p&gt;N5105 运行虚拟机会随机死机/重启的问题很常见，之前我采取过如下办法&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;爱快降级至3.6.1&lt;/li&gt;
  &lt;li&gt;OpenWRT 换用 LXC 模式安装&lt;/li&gt;
  &lt;li&gt;关闭各种直通&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;只能说降低了死机概率，一般能撑到一天以上，所以我选择在半夜自动重启，勉强可以正常使用，但日常使用还是不可避免的会断网。&lt;/p&gt;

&lt;p&gt;不过现在似乎有了一个终极解决方案，可以彻底解决 N5105 的死机问题，根据这个&lt;a href=&quot;https://forum.proxmox.com/threads/vm-freezes-irregularly.111494/page-28&quot;&gt;链接&lt;/a&gt;反馈，已经可以超过 10 天稳定运行，我目前也暂时未遇到死机问题。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;UPDATE: 我已经几十天都没有死机过了&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;解决方案就是更新 microcode 至 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x24000024&lt;/code&gt; 版本。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;# 安装 microcode
apt update
apt install intel-microcode
reboot
# 查看 microcode 版本
dmesg -T | grep microcode
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;重启完成后，microcode 应该就已经更新到不会死机的版本了，你应该可以看到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x24000024&lt;/code&gt; 字样。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;root@pve:~# dmesg -T | grep microcode
[Wed Mar 22 22:23:26 2023] microcode: microcode updated early to revision 0x24000024, date = 2022-09-02
[Wed Mar 22 22:23:26 2023] SRBDS: Vulnerable: No microcode
[Wed Mar 22 22:23:30 2023] microcode: sig=0x906c0, pf=0x1, revision=0x24000024
[Wed Mar 22 22:23:30 2023] microcode: Microcode Update Driver: v2.2.
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;或者 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep 'stepping\|model\|microcode' /proc/cpuinfo&lt;/code&gt; 查看 microcode 版本。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;root@pve:~# grep 'stepping\|model\|microcode' /proc/cpuinfo
model           : 156
model name      : Intel(R) Celeron(R) N5105 @ 2.00GHz
stepping        : 0
microcode       : 0x24000024
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但如果源版本比较老的话，更新的版本还是例如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x24000023&lt;/code&gt; 的话，就请继续后续步骤&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;# 接下来继续更新
wget https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files/archive/main.zip
unzip main.zip -d MCU
cp -r /root/MCU/Intel-Linux-Processor-Microcode-Data-Files-main/intel-ucode/. /lib/firmware/intel-ucode/
update-initramfs -u
reboot
# 重启后应当可以更新至 0x24000024
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Wed, 22 Mar 2023 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2023/03/22/pve-n5105-freeze/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2023/03/22/pve-n5105-freeze/</guid>
        
        <category>PVE_kb</category>
        
        
      </item>
    
      <item>
        <title>PVE 下 LXC 启动 Docker 失败解决方案</title>
        <description>&lt;p&gt;PVE 下通过 LXC 安装的 Ubuntu 启动 Docker 镜像时候提示&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;docker: Error response from daemon: AppArmor enabled on system but the docker-default profile could not be loaded: 
running `/usr/sbin/apparmor_parser apparmor_parser -Kr /var/lib/docker/tmp/docker-default6944525` 
failed with output: apparmor_parser: Unable to replace &quot;docker-default&quot;.  
Permission denied; attempted to load a profile while confined?
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解决方式是在调整启动配置&lt;/p&gt;

&lt;p&gt;PVE 设置中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;选项-功能&lt;/code&gt; 中选中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;嵌套&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;然后在宿主机中找到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/pve/lxc/100.conf&lt;/code&gt;（注意把 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;100&lt;/code&gt; 替换成你的 LXC 容器 id），增加如下几句话，之后重启&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;lxc.apparmor.profile: unconfined
lxc.cgroup.devices.allow: a
lxc.cap.drop:
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 09 Mar 2023 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2023/03/09/pve-lxc-docker/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2023/03/09/pve-lxc-docker/</guid>
        
        <category>PVE_kb</category>
        
        <category>LXC_kb</category>
        
        <category>Docker_kb</category>
        
        
      </item>
    
      <item>
        <title>Encode Email</title>
        <description>&lt;script&gt;
function encodeEmail(email, key) {
    // Hex encode the key
    var encodedKey = key.toString(16);

    // ensure it is two digits long
    var encodedString = make2DigitsLong(encodedKey);

    // loop through every character in the email
    for(var n=0; n &lt; email.length; n++) {

        // Get the code (in decimal) for the nth character
        var charCode = email.charCodeAt(n);
        
        // XOR the character with the key
        var encoded = charCode ^ key;

        // Hex encode the result, and append to the output string
        var value = encoded.toString(16);
        encodedString += make2DigitsLong(value);
    }
    return encodedString;
}

function make2DigitsLong(value){
    return value.length === 1 
        ? '0' + value
        : value;
}

function go(){
    var text = document.getElementById('input').value;
    console.log(text)
    console.log(encodeEmail(text, 156));
    document.getElementById('output').value= encodeEmail(text, 156);
}
&lt;/script&gt;

&lt;p&gt;&lt;input id=&quot;input&quot; /&gt;
&lt;br /&gt;
&lt;button onclick=&quot;go()&quot;&gt;Encode&lt;/button&gt;
&lt;br /&gt;
&lt;input id=&quot;output&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 25 Aug 2022 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2022/08/25/encode-email/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2022/08/25/encode-email/</guid>
        
        <category>Tools</category>
        
        
      </item>
    
      <item>
        <title>快速查看显卡使用情况和占用用户</title>
        <description>&lt;p&gt;使用方法： &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python gpu.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;需要的依赖： &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xmltodict&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subprocess&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xmltodict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;UID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EUID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Return username of UID of process pid&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/proc/{}/status&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ln&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;startswith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Uid:&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;uid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ln&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getpwuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pw_name&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;simplify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;product_name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;fan_speed&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;fb_memory_usage&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;utilization&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;temperature&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;processes&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_gpu_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subprocess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Popen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;nvidia-smi&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-q&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-x&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subprocess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PIPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subprocess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PIPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;communicate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;out_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;utf-8&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xmltodict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;nvidia_smi_log&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gpu_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;processes&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;processes&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;processes&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;process_info&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;processes&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;process_info&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;processes&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;process_info&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;process&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;simplify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gpu_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpu_list&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_gpu_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    {: &amp;lt;13}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{: &amp;lt;8}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{: &amp;lt;20}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;used_memory&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;process_name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;---------------------------------------------------------&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gpu&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]):&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{} {} ({}):&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;product_name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;utilization&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gpu_util&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;fb_memory_usage&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;processes&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;process_info&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;used&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;used_memory&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    {: &amp;lt;13}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{: &amp;lt;8}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{: &amp;lt;20}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{: &amp;lt;10} ({:5.2f}%)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;used_memory&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;used&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;process_name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;---------------------------------------------------------&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用效果：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/in-post/gpu_user/1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 17 Nov 2021 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2021/11/17/gpu-user/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2021/11/17/gpu-user/</guid>
        
        <category>GPU</category>
        
        
      </item>
    
      <item>
        <title>NGINX 配置避免 IP 访问时证书暴露域名</title>
        <description>&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;利用 NGINX 1.19.4 后的新特性 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl_reject_handshake on;&lt;/code&gt;，将其置于默认访问时配置中，IP 访问时会终止 TLS 握手，也就不会暴露域名了。&lt;/p&gt;

&lt;h2 id=&quot;细说&quot;&gt;细说&lt;/h2&gt;

&lt;p&gt;CDN 是建站时常用的工具，在自己的主机外面套一层 CDN 是常见操作，一般这样认为自己的主机就安全了，有人来攻击也会先到 CDN 服务器，攻击者根本无法获取到自己主机的 IP，但事实真的是这样吗？&lt;/p&gt;

&lt;p&gt;我们先来看看一般配置后会出现什么问题。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;server {
    listen 80 default_server;

    # Redirect all HTTP requests to HTTPS.
    return 301 https://$host$request_uri;
}

server {
    listen 443 default_server;
    server_name _;
    include conf.d/ssl.config;
    return 444;
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面是一个很常用的 NGINX 配置，HTTP 访问全部重定向到 HTTPS 的 443 端口上，没有配置过的域名返回 444 终止连接。&lt;/p&gt;

&lt;p&gt;好了，现在尝试用 IP 和 HTTPS 访问你的网站，你应该能够看到预想中访问失败、证书无效等连接失败的提示。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;但是！&lt;/strong&gt;注意下浏览器左上角提示的不安全，点开查看证书信息，你就会发现你的域名其实随着证书发送了过来。此时如果你是攻击者，那么其实就可以知道该域名背后的源主机 IP 就是这个。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/in-post/nginx-ssl/1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图即为用 IP 访问后，依旧能看到证书内容。这是因为返回 444 是 HTTP 层面的事情，意味着到达这一步下层的 TLS 握手已经完成。证书不被信任是一回事，但说明已经拿到了服务器的证书。&lt;/p&gt;

&lt;p&gt;CDN 确实避免了直接 DNS 查询暴露 IP 的问题，但攻击者通过扫描全网 IP，用上述方式依旧可以知道每个 IP 对应的域名是什么，这也是为什么很多站长用了 CDN 后并且反复更换 IP 却依旧被攻击者迅速找到 IP 的原因。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://search.censys.io/&quot;&gt;Censys&lt;/a&gt; 就一直在干这件事，全网扫描 IP 并找到其对应的域名&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;那该怎么办呢&quot;&gt;那该怎么办呢？&lt;/h2&gt;

&lt;p&gt;问题根源出在 client 在 TLS 握手时发送了 ClientHello 后，NGINX 在 ServerHello 中带着含有域名的默认证书返回了，因为 NGINX 期望可以完成握手，这可能可以算是 NGINX 的一个缺陷。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;如果你不熟悉 TLS 握手流程，那么可以看看 &lt;a href=&quot;/2019/05/07/tls-handshake/&quot;&gt;这篇文章&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;笨办法&quot;&gt;笨办法&lt;/h3&gt;

&lt;p&gt;既然 NGINX 默认提供了带有域名的证书，那么想不暴露也很简单，提供一个不含有正确域名的证书即可。&lt;/p&gt;

&lt;p&gt;NGINX 设置中 HTTPS 访问如果没有设置证书，那么就会报错。但反正 IP 访问也不需要提供服务，那么直接自签一个 IP 证书，或者随便一个域名的证书都可。当然，如果能搞定合法的 IP 证书也不是不行。&lt;/p&gt;

&lt;p&gt;搞定证书后，添加一个配置，让 IP 访问返回错误证书就完事了。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;server {
    listen 443 ssl default_server;
    server_name your_ip;

    ssl_certificate    xxxx.pem;
    // and more ssl config ...

    return 444;
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;好方法&quot;&gt;好方法&lt;/h3&gt;

&lt;p&gt;这种方法还得自己搞个证书，如果服务器多每个都得这么搞也挺麻烦的，好在这个问题 NGINX 这已经有了很完美的解决方案。&lt;/p&gt;

&lt;p&gt;ClientHello 中是带着 SNI 的，所以其实握手阶段是可以知道访问的域名是否合法的，NGINX 1.19.4 中添加了一个新的配置项 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl_reject_handshake&lt;/code&gt; 用于拒绝握手，也就不会提供证书。&lt;/p&gt;

&lt;p&gt;使用方法也很简单，将原本默认配置中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return 444&lt;/code&gt; 替换成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl_reject_handshake on&lt;/code&gt; 即可。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;server {
    listen 443 default_server;
    server_name _;
    include conf.d/ssl.config;
    
    # 不用返回 444 了，直接拒绝握手
    ssl_reject_handshake on;
    # return 444;
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;配置后，再尝试 IP 访问，会发现浏览器报了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERR_SSL_UNRECOGNIZED_NAME_ALERT&lt;/code&gt; 的错误，也看不到证书信息，目标达成！&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/in-post/nginx-ssl/2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;其实还没完&quot;&gt;其实还没完&lt;/h2&gt;

&lt;p&gt;上述方法是通过 ClientHello 中的 SNI 确定访问是否合法的，那如果 SNI 就是正确的域名呢？&lt;/p&gt;

&lt;p&gt;这种场景发生于攻击者已经确定要攻击某个域名，那么他就可以将带着该域名的握手信息遍历所有 IP，握手成功就找到，这样访问其实与正常访问并无区别，唯一解决方法就是白名单只允许 CDN 服务器访问。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;例如 hosts 直接将硬写 IP，将域名强行指向某个 IP&lt;/p&gt;

  &lt;p&gt;或者用这种方式 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl https://example.com --resolve 'example.com:443:172.17.54.18'&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;location / {
    allow   172.1.2.0/24;
    allow   1.2.3.4/32;
    deny    all;
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上述 IP 段只能向 CDN 提供商询问，一般文档中都是有相关信息的。&lt;/p&gt;
</description>
        <pubDate>Mon, 04 Oct 2021 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2021/10/04/nginx-ssl-reject-handshake/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2021/10/04/nginx-ssl-reject-handshake/</guid>
        
        <category>NGINX</category>
        
        <category>TLS</category>
        
        
      </item>
    
      <item>
        <title>NGINX 配置 HTTPS 最佳实践</title>
        <description>&lt;p&gt;1202 年了，不会还有网站不支持 HTTPS 吧？不过 HTTPS 的配置还是有很多讲究的。本文以 NGINX 的配置为例，嫌麻烦的可以直接跳到 &lt;a href=&quot;#完整配置&quot;&gt;最后&lt;/a&gt; 抄配置。&lt;/p&gt;

&lt;p&gt;如果你不清楚 HTTPS 与 TLS 的工作原理，可以先阅读 &lt;a href=&quot;/2019/05/07/tls-handshake/&quot;&gt;这篇文章&lt;/a&gt;，可以帮助你理解下述配置。&lt;/p&gt;

&lt;h2 id=&quot;获取证书&quot;&gt;获取证书&lt;/h2&gt;

&lt;p&gt;证书是实现 HTTPS 的基础，现在各个云服务商都提供了免费的证书申请，可以直接去申请。这里我以 acme.sh 为例说明下申请证书时的注意事项。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;acme.sh --issue -d &quot;*.zinglix.xyz&quot; --keylength ec-256 --ocsp
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面是一个简单的用 acme 申请证书的命令，其中关键的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keylength&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ocsp&lt;/code&gt; 两个参数，OCSP 的作用我们 &lt;a href=&quot;#ocsp&quot;&gt;后面&lt;/a&gt; 再说，建议能开启则开启，先来谈谈密钥长度的问题。&lt;/p&gt;

&lt;p&gt;证书加密的算法分为 RSA 和 ECDSA 两类，这对应到证书也就分为两类。acme 中 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keylength&lt;/code&gt; 支持的参数有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2048&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3072&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4096&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8192&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ec-256&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ec-384&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ec-521&lt;/code&gt;(参数支持，但暂不支持申请)。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ec-&lt;/code&gt; 开头的对应着 ECDSA 证书，其他的为 RSA 证书，长度越长安全性也就更高，但对性能的消耗也就越高。RSA 有更好的兼容性，ECDSA 可以提供更好的前向安全，具体差异可以看 &lt;a href=&quot;/2021/04/05/tls-13/#rsa-与-dh&quot;&gt;这里&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;根据 &lt;a href=&quot;https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#31-avoid-too-much-security&quot;&gt;SSL Labs 的推荐&lt;/a&gt;，长于 2048 的 RSA 密钥和 256 bits 的 ECDSA 密钥对于 CPU 性能是一种浪费，从中获得安全性的提升有限，导致过度加密。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;因此推荐密钥长度为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2048&lt;/code&gt; 与 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ec-256&lt;/code&gt;！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;那么两种类型证书选择哪一个呢？那当然是全都要啦~ NGINX 支持同时使用两个证书，只需要都写上就行了&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;# RSA 证书
ssl_certificate  /cert/*.zinglix.xyz/fullchain.cer;
ssl_certificate_key  /cert/*.zinglix.xyz/*.zinglix.xyz.key;
# ECDSA 证书
ssl_certificate  /cert/*.zinglix.xyz_ecc/fullchain.cer;
ssl_certificate_key  /cert/*.zinglix.xyz_ecc/*.zinglix.xyz.key;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl_certificate&lt;/code&gt; 最好是使用完整的证书链，如果没有提供必要的中间证书可能会导致证书链不可信。&lt;/p&gt;

&lt;h2 id=&quot;http2-与会话恢复&quot;&gt;HTTP/2 与会话恢复&lt;/h2&gt;

&lt;p&gt;HTTP/2 可以有效提升对网络的利用效率，会话恢复可以复用曾经协商过的数据，两者都可以帮助减少 RTT，所以建议开启，可以有效减少建立连接时的耗时。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;listen 443 ssl http2;

ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;加密协议与套件&quot;&gt;加密协议与套件&lt;/h2&gt;

&lt;p&gt;SSL 已经是不安全的了，绝不要使用。TLSv1.0 与 TLSv1.1 虽然没有被证明不安全，但作为老旧的协议即将过时，除非你的客户真的需要，也不要开启。&lt;/p&gt;

&lt;p&gt;TLSv1.2 可以说是目前被最广泛使用的协议，应当被开启。&lt;a href=&quot;/2021/04/05/tls-13/&quot;&gt;TLSv1.3&lt;/a&gt; 作为最新的协议，在性能和安全性上都有提升，支持的话也应当开启。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;如果为了极致的安全，只开启 TLSv1.3 也是没有问题的，现代的浏览器都已经支持 TLSv1.3，只要你相信你的客户不会使用略微老旧的软件&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;至于加密套件，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RC4&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DES&lt;/code&gt; 等等都不安全，但说那么多套件头也晕了，下面已经整理了一份支持绝大多数客户端且安全的配置&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;

ssl_prefer_server_ciphers on;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl_prefer_server_ciphers&lt;/code&gt; 用于指定服务器是否有推荐的套件，为了能够根据服务器配置用上更安全的套件，防止 BEAST 攻击，建议开启。&lt;/p&gt;

&lt;h2 id=&quot;hsts&quot;&gt;HSTS&lt;/h2&gt;

&lt;p&gt;HSTS (HTTP Strict Transport Security) 能够告诉浏览器，该网站只应该通过 HTTPS 访问，避免使用 HTTP。开启方式如下，只需要添加一个 HTTP 头&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中三个参数&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max-age=&amp;lt;expire-time&amp;gt;&lt;/code&gt;：指明 HSTS 的有效期，最佳实践是一年时间即 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;31536000&lt;/code&gt;。注意，开启后如果关闭 HTTPS 将在有效期内导致网站无法访问。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;includeSubDomains&lt;/code&gt;：是一个可选参数，指明是否同时适用于子域名。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preload&lt;/code&gt;：可选参数，指明是否为预加载。&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;HSTS 虽然可以实现强制浏览器使用 HTTPS，但是第一次访问时依旧不知道目标网站是否采用了 HSTS。&lt;/p&gt;

  &lt;p&gt;为了解决这一问题，Google 维护了一个名单，里面是采用了 HSTS 的网站列表，名单会随着浏览器分发。这样浏览器第一次访问时，会先查看网站是否位于该名单里，从而决定是否采用 HTTPS。这一机制即为 &lt;strong&gt;预加载机制&lt;/strong&gt;。&lt;/p&gt;

  &lt;p&gt;可以在 &lt;a href=&quot;https://hstspreload.org/&quot;&gt;该网站&lt;/a&gt; 上提交申请，注意必须加上与上述配置相同或更严格的配置，同时重定向所有 HTTP 请求。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;dhe-参数&quot;&gt;DHE 参数&lt;/h2&gt;

&lt;p&gt;密钥交换时常采用 Diffie-Hellman 密钥交换，低强度（768 及以下）的参数容易被破解，以及一些常见的 1024 位参数。&lt;/p&gt;

&lt;p&gt;出于性能与安全性的考虑，2048 位即可，如果你不担心性能可以选择更高的。&lt;/p&gt;

&lt;p&gt;可以用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl dhparam -out dhparams.pem 2048&lt;/code&gt; 生成一个（通常很慢）或者获取一个现成的，例如 &lt;a href=&quot;https://ssl-config.mozilla.org/ffdhe2048.txt&quot;&gt;2048位&lt;/a&gt; 和 &lt;a href=&quot;https://ssl-config.mozilla.org/ffdhe4096.txt&quot;&gt;4096位&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;ssl_dhparam /path/to/dhparam.pem;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;ocsp&quot;&gt;OCSP&lt;/h2&gt;

&lt;p&gt;OCSP 是一种从证书颁发者处验证证书是否被撤回的机制，可以让浏览器验证证书有效性。&lt;/p&gt;

&lt;p&gt;最佳实践是证书中强制要求 OCSP，但客户到颁发者处的连接质量可能并不好，例如国内访问 Let’s Encrypt，这会导致网站访问速度下降。&lt;/p&gt;

&lt;p&gt;OCSP Stapling 是一种让服务器在握手过程中同时传递 OCSP 相应的技术，向颁发者验证的过程由服务器代劳，避免用户直接去访问。开启也很简单，如下&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;ssl_stapling on;
ssl_stapling_verify on;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;0-rtt&quot;&gt;0-RTT&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/2021/04/05/tls-13/#0-rtt&quot;&gt;0-RTT&lt;/a&gt; 是 TLSv1.3 很棒的一个特性，有效消除握手时间，但会导致重放攻击，且放弃了前向安全性。&lt;/p&gt;

&lt;p&gt;在足够了解后果的前提下可以开启，但如果不确定那就算了，建议是 GET 等无副作用的操作可以开启。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;ssl_early_data on;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;其他一些细节&quot;&gt;其他一些细节&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;网站所有资源开启 HTTPS，例如一些公用 CSS、JS&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2021/10/04/nginx-ssl-reject-handshake/&quot;&gt;非法域名（尤其是 IP 访问）禁止握手&lt;/a&gt;，避免暴露证书&lt;/li&gt;
  &lt;li&gt;重定向所有 HTTP 请求至 HTTPS&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;server {
    listen 80 default_server;
    listen [::]:80 default_server;

    return 301 https://$host$request_uri;
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;完整配置&quot;&gt;完整配置&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;# HTTP/2
listen 443 ssl http2;
server_name your_domain;

# RSA 证书（推荐2048位）
ssl_certificate  /cert/*.zinglix.xyz/fullchain.cer;
ssl_certificate_key  /cert/*.zinglix.xyz/*.zinglix.xyz.key;
# ECDSA 证书（推荐256位）
ssl_certificate  /cert/*.zinglix.xyz_ecc/fullchain.cer;
ssl_certificate_key  /cert/*.zinglix.xyz_ecc/*.zinglix.xyz.key;

# 会话恢复
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;


# 加密协议与套件
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;

# HSTS 
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;

# DHE 参数（推荐2048位）
ssl_dhparam /cert/ffdhe2048.txt;

# OCSP Stapling 
ssl_stapling on;
ssl_stapling_verify on;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;验证&quot;&gt;验证&lt;/h2&gt;

&lt;p&gt;配置后，可以验证自己的配置是否正确，下面推荐两个网站&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ssllabs.com/ssltest/&quot;&gt;SSL Labs&lt;/a&gt;：除了配置外，可以检查与各类客户端的兼容性&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.immuniweb.com/ssl/&quot;&gt;ImmuniWeb&lt;/a&gt;：有更完整的配置测试&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;上述配置后应该都能取得 A+ 的成绩。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/in-post/nginx-ssl/3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/in-post/nginx-ssl/4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最后提一嘴关于 SSL Labs 中有两项 90 分，虽然没有必要，但可以继续冲击 100。&lt;/p&gt;

&lt;p&gt;关于 Key Exchange，其要求 DH 参数和证书密钥长度均大于等于 4096，替换这两个文件即可。当然之前提到过这会导致过度加密，带来的性能损失并不能带来足够的安全性提升。&lt;/p&gt;

&lt;p&gt;而 Cipher Strength，其要求所有加密套件均大于等于 256 bits，而这里存在一个 &lt;a href=&quot;https://github.com/ssllabs/ssllabs-scan/issues/636&quot;&gt;bug&lt;/a&gt; 是 TLSv1.3 有一个算法为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TLS13-AES-128-GCM-SHA256&lt;/code&gt;，虽然 128 位但其实配上 TLSv1.3 足够安全，但这导致了开启 TLSv1.3 就无法得到 100 分。&lt;/p&gt;

&lt;p&gt;不过除了关闭 TLSv1.3 之外，还是有解决方法的，那就是移除 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TLS13-AES-128-GCM-SHA256&lt;/code&gt; 套件，NGINX 1.19.4 开始支持调整 TLSv1.3 的加密套件，只需加上下述配置，但会导致 0-RTT 失效&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;ssl_conf_command    Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
ssl_ecdh_curve      secp384r1;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sun, 03 Oct 2021 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2021/10/03/nginx-ssl-best-practices/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2021/10/03/nginx-ssl-best-practices/</guid>
        
        <category>NGINX</category>
        
        <category>TLS</category>
        
        
      </item>
    
      <item>
        <title>「论文笔记」A Unified Generative Framework for Various NER Subtasks</title>
        <description>&lt;p&gt;NER 任务分为三类，目前来说，各个子任务目前都有方法可以一定程度上解决，但缺少一个方法可以同时解决三个任务。下图分别介绍了三个子任务的常见解决方案&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Flat NER（扁平实体抽取）：通常采用序列标注的方法&lt;/li&gt;
  &lt;li&gt;Nested NER（嵌套实体抽取）：将输入文本采用类似 n-gram 的方法进行拆分，拆成一个个小的 span 后进行分类&lt;/li&gt;
  &lt;li&gt;Discontinous NER（非连续实体抽取）：用一个堆栈，为每个 token 选择移入移出规约等操作，类似于编译器生成 AST 的过程，&lt;a href=&quot;/2020/07/29/papernote-acl-2020-520/&quot;&gt;详见另一篇论文&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/img/papernote/arxiv/2106.01223/1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这篇文章提出使用 seq2seq 的框架以生成的方式来同时完成三个任务，也就是标题中的那个可用于各种 NER 子任务的通用框架。输出形式则为上图中的 (d)，通过生成的方式输出实体的内容，最后以一个指示实体类型的特殊标签作为该实体的结束，并以同样的方式生成下一个实体。&lt;/p&gt;

&lt;h2 id=&quot;模型结构&quot;&gt;模型结构&lt;/h2&gt;

&lt;p&gt;模型整体结构较为简单，为了让模型更好的完成生成任务，文中采用预训练过的 seq2seq 模型 BART 作为基础。输入为一段文本（首尾加上了特殊 token），输出时采用指针网络的方式，输出在输入中的位置。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/papernote/arxiv/2106.01223/2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;具体来说，模型沿用了 BART 的 encoder-decoder 的架构（下半部分），主要工作在如何生成字符（上半部分）。&lt;/p&gt;

&lt;p&gt;模型通过 encoder 和 decoder 后，分别拿到了两个隐状态 \(H^e\) 和 \(h_t^d\)。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;实际中，index 并不能直接输入 decoder，所以解码器中还进行了一步 index2token，以 token 的方式输入&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;之后作者将 \(H^e\) 通过一层 MLP 后与输入的 Token Embedding 加权相加（公式 7）得到 \(\overline{H}^e\)。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/papernote/arxiv/2106.01223/3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后模型会计算一个 Pointer Distribution 和 Tag Distribution，即文中每个位置（指针）的概率分布和标签的概率分布，两者合在一起就是图中的 Final Prediction Prob.，从中选择概率最高的也就是这一步输出的结果。&lt;/p&gt;

&lt;p&gt;实际上，就是将 \(\overline{H}^e\) 和 tag 的 embedding 后的输出 \(G^d\) 分别与 decoder 的输出 \(h_t^d\) 相乘后拼接（公式 9），通过 softmax 找到概率最大的，作为输出。&lt;/p&gt;

&lt;p&gt;然后就以自回归的方式不断重复上述步骤，依次输出下一个 token，从而得到最后的结果。&lt;/p&gt;

&lt;h2 id=&quot;结果&quot;&gt;结果&lt;/h2&gt;

&lt;p&gt;实验结果基本上都处于 SOTA 的水平，不过文中有许多分析的部分，例如实体表示方式之类的。&lt;/p&gt;

&lt;p&gt;实验表明长度更短、更类似 BPE 序列的实体会有更好的效果。此外，文中出现越靠后的实体，在 Flat NER 和 Discontinous NER 上召回率都会随之增高，而嵌套实体并没有，作者认为是因为实体间有关联，导致错误传播。&lt;/p&gt;

&lt;p&gt;文章最大的贡献点在于提出了一种方式，能够用生成的方式完成各类 NER 任务。&lt;/p&gt;
</description>
        <pubDate>Wed, 16 Jun 2021 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2021/06/16/papernote-arxiv-2106-01223/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2021/06/16/papernote-arxiv-2106-01223/</guid>
        
        <category>NER_pn</category>
        
        <category>NLG_pn</category>
        
        <category>2021_pn</category>
        
        
      </item>
    
      <item>
        <title>Linux 更换时区</title>
        <description>&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timedatectl&lt;/code&gt; 指令可以查看当前的时间信息，如下&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;      Local time: Sun 2021-05-09 23:00:17 CST
  Universal time: Sun 2021-05-09 15:00:17 UTC
        RTC time: Sun 2021-05-09 15:00:17
       Time zone: Asia/Shanghai (CST, +0800)
 Network time on: yes
NTP synchronized: yes
 RTC in local TZ: no
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果时区不对，可以用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo timedatectl set-timezone Asia/Shanghai&lt;/code&gt; 这条指令切换到国内的时区，最后一个参数就是你要切换的时区，这条指令需要 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt; 权限。&lt;/p&gt;

&lt;p&gt;具体的时区列表可以用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timedatectl list-timezones&lt;/code&gt; 获得或者去 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/share/zoneinfo&lt;/code&gt; 路径下查看。&lt;/p&gt;
</description>
        <pubDate>Sun, 09 May 2021 00:00:00 +0000</pubDate>
        <link>https://zinglix.xyz/2021/05/09/linux-timezone/</link>
        <guid isPermaLink="true">https://zinglix.xyz/2021/05/09/linux-timezone/</guid>
        
        <category>Linux_kb</category>
        
        
      </item>
    
  </channel>
</rss>
