如何归化获取日本国籍

2022年12月的某天,被闹钟吵醒之后习惯性打开手机浏览器看一眼日本官报的归化者许可一栏。
第一眼看到了我的名字。

从2022年3月提交申请,大概经历了8个多月的时间,我顺利地加入了日本国籍。
从2012年10月来日本留学开始,已经经历了10个年头。这一段旅途,在经历留学,工作,买房,转职,结婚之后,终于告一断落。

递交完户籍申请书之后,住民票上的国籍一栏消失了,取而代之的是我日本本籍地的信息。

来说一下日本归化需要准备哪些东西,办理哪些手续吧。

归化资格

归化的资格国籍法里面讲得很清楚。

  一 引き続き五年以上日本に住所を有すること。

    二 二十歳以上で本国法によつて行為能力を有すること。

    三 素行が善良であること。

    四 自己又は生計を一にする配偶者その他の親族の資産又は技能によつて生
     計を営むことができること。

    五 国籍を有せず、又は日本の国籍の取得によつてその国籍を失うべきこと。

    六 日本国憲法施行の日以後において、日本国憲法又はその下に成立した政
      府を暴力で破壊することを企て、若しくは主張し、又はこれを企て、若
      しくは主張する政党その他の団体を結成し、若しくはこれに加入したこ
      とがないこと。

其中最重要的一点是,需要连续在日本居住5年以上。

什么叫连续居住五年以上? 国籍法里面并没有解释。
但是,归化担当的人会明确和你说,一年以内不能有3个月以上的出国时间,同时,最近五年内在国外呆的时间总数不超过180天。
另外比较坑的是,有些担当会对这个有自己的见解,比如一开始我相谈的担当就认为,这个五年不包括留学的时间。因为我本身已经来日很久,所以没去和他计较这些,
但是如果是来日方长的朋友,可能得注意一下了。

当然,国籍法里面也说了,如果你满足下面任何一个条件的话,这个连续居住五年的条件可以适当放宽。

    一 日本国民であつた者の子(養子を除く。)で引き続き三年以上日本に住所又は居所を有するもの

    二 日本で生まれた者で引き続き三年以上日本に住所若しくは居所を有し、
     又はその父若しくは母(養父母を除く。)が日本で生まれたもの

    三 引き続き十年以上日本に居所を有する者

也许你会觉得这三条不是废话么,比如第一个,既然已经是日本国民的孩子,那为什么本来不是日本国籍? 原因有很多,最简单的,这个孩子的父母归化了。

还有其他一些可以适当放缓条件的情况,不过这些情况基本都是你家里有个人是日本人为前提。对于我们其他大多数人,如果你已经满足了居住条件,同时,没违法犯罪(轻微交通违反的话也没事), 有一份能养活自己活着一家人的工作,没有反社会,颠覆国家的倾向的话,你可以网上搜索一下你家所属地区的法务局联系电话,打电话咨询归化事宜了。

电话预约相谈

归化的申请的第一步,都是打电话给你所在地方的法务局。

在电话里直接说明自己想要归化,法务局国籍担当的人会当场问你一些简单的情况,他觉得没问题的话,就会和你约定一个时间去法务局面谈。

资料

归化需要的资料非常非常非常多,有些资料需要国内的公证处出具,所以预约完第一次相谈你就可以开始准备资料了。

因为第一次相谈的内容,无非是和你说明一下需要哪些资料,同时更加了解一下你的基本情况。

中国国内资料

  1. 出生公证书
  2. 父母的结婚公证书/离婚公证书
  3. 自己和父母的亲属关系公证书
  4. 如果父母有去世的,需要死亡公证书
  5. 有一张纸,让你妈填一下,基本内容是她和你爸什么时候结婚,什么时候生下了你,一共生了几个这种。

国内资料的话,其实日本方面并不是要审查什么东西,只是因为日本有户籍这个概念,当入籍成为日本人之后,需要制作你的户籍,户籍上需要写上你家的这些情况。
所以其实你爸妈的名字啥的最后都会写到你的户籍上。

另外这些东西都是要翻译的,基本上多给公证处点钱的话,就能加上翻译,也可以自己翻译就是了。

日本国内资料

  • 帰化許可申請書
    这个上面会要写你归化后想要取的姓名。这个想清楚点,因为写上去了就不好改了。如果你不想改名,也没啥问题,但是你的名字必须是常用汉字和假名能够表示的。

  • 親族概要(日本 外国)
    这个可以看网上一些样本填写。 日本亲属和中国亲属各写一张。

  • 履歴書その1
    这个建议看一下网上人家怎么写。要把你从出生到当前所有的东西都写下来。 比如什么时候出生,什么时候上小学,什么时候上中学,高中,大学,什么时候来日本,什么时候换了住址,什么时候上了日本的学校,什么时候开始工作了,结婚了之类的。

  • 履歴書その2
    这个要写的是你最近五年的出国经历。按着护照上的章一个个看过来写吧。

  • 卒業証明書
    最终学历的就行了,比如我是日本的大学研究生毕业,所以只需要复印我这个修士学位证书就行了。如果是中国大学毕业的,还需要翻译。

  • 運転免許証

  • 運転記録証明書(過去5年
    这个直接去邮局说明你要这个东西,会给你个汇款单,按着填写交钱之后会寄送到你家。第一次相谈时候担当也会给你一份汇款单。

  • 帰化の動機書
    这个就是一篇作文,建议看一下网上怎么写的。好好得,表达自己是一个良民。从不逃税漏税。

  • 宣誓書
    这个最终提交时候要朗读并签名的,上面写的不是效忠天皇,是让你保证遵纪守法,不逃税漏税。

  • 生計の概要を記載した書面
    这上面的月收切记是到手的所得,然后需要把支出和收入凑到相等。有房产的写房产,有股票的也写,担当会多问一句你的获益申告没有。

  • 事業の概要を記載した書面
    如果你是个人事业主或者法人代表的话你需要写这个。我也没写过 ,可以上网参考一下。

  • 在勤・給与証明書
    这个不是你公司按自己格式给你开一个,而是需要拿着法务局给你的纸,让你公司去填写。

  • 住民票の写し

  • 在留カード

  • 源泉徴収票
    这个一般要的是去年和前年两年分的。

  • 都道府県・市区町村民税納税証明書
    一般要的是前年度的。

  • 課税(非課税)証明書
    这个和上一个容易搞起来,上面一个是证明你已经交了税,这一个是说明你要交多少税。这个需要的是今年度的。

  • 賃貸借契約書(賃貸の場合)

  • 土地・建物登記事項証明書(所有の場合)
    这个直接去法务局的时候顺便申请一下就行了。

  • 年金定期便 直近1年分
    如果没有了的话可以打印一下网上的电子版,然后复印一下年金手帐首页证明的确是你的号。

  • 自宅、勤務先地図 過去三年

谷歌地图截图就行了,格式网上都有样本。

  • スナップ写真
    如果已经结婚的话得要几张你们夫妻的合照啥的。

  • パスポート
    这个除了有信息的页面,需要把所有出入境章的页面复印下来。

  • 通帳写し、残高証明書など

对了这些材料基本上都要夫妻双方的,就算只有你申请归化,你对象的材料也得一起开具审查。

提交申请

第一次打电话预约面谈之后,担当会在面谈的时候和你解释一些需要的资料,然后你去准备这些资料,当你准备得差不多了,就可以预约第二次面谈。
还是一样打电话预约。由于归化的人总体比较多,一般打电话都的预约到几个星期之后。

第二次面谈如果你的材料准备的都差不多了,这个时候担当会和你约定提交归化申请的时间。在提交申请之前,你还需要一个资料,就是你的国籍证明书。

这个国籍证明书现在好像叫领事证明,去大使馆可以开具。 直接告诉大使馆的人说你要归化需要办理自动丧失国籍的领事证明,交护照即可,不需要钱。

之后这个证明会随护照到付寄到你家。这个证明大体意思是你现在还是中国人,只不过一旦当你的归化获得许可,你自动丧失中国国籍。 这个证明也需要翻译一下,可以自己翻译。

到了预约提交申请的日子,带齐所有的资料去提交申请。 需要当场宣读宣誓书,然后签名。如果日语不够6的,可能还会考你一下日语,反正我是没有碰到。

提交完申请之后,你的担当也换人了,新的担当会告诉你接下来他会研究一下你的资料,然后当他觉得差不多的时候会打电话联系你面谈。

面谈

不同地方可能因为申请人数不同,从提交资料到面谈的时间不一样,不过我的情况,大概在提交申请三个月之后,担当会打电话过来和你预约面谈时间。
面谈需要你和你的配偶一起去,两个人各自分别面谈。基本就是把你从出生到现在所有的细节再问一个遍。看看有没有他觉得可疑的地方。
面谈下来没有问题的话,基本就是回去等最终的归化结果出来吧。期间可能担当还会打电话到你们公司询问一下情况。
要注意的一点是,面谈之后,你的资料就提交去法务省审批了,法务局也是不知道进度的。如果想要尽快获得结果,你唯一可以做的事情就是,少开车,少出国,少有变化。
因为出国需要申告,你出国的期间关于你资料的审查也是中断的。

最终确认电话

一般来说,归化的结果会刊登在官报上。虽然官报上不是每天都刊登归化许可,但是也可以时不时刷一下。

不过根据大多数人的经验,登报之前,担当会给你打一个电话,大体上就是问你最近有没有住所啊,工作的变化,有没有出国的打算。有些担当会直接告诉你,1个月以内出结果。
有些却会假装只是时间比较久了,确认一下近况。不过无论如何,接到这个电话基本9成9,你的归化结果就要下来了。

登报以后

归化结果登报之后,大概过一个星期,法务局担当会联系你去取归化身份证明,有了这个证明就可以去区役所办理户籍相关的手续,正式登记成为日本户籍。但是拿到证明之前,通常会给你一张之前已经提交的国籍证明的复印件,上面盖了法务局的章,证明你已经归化,然后你得拿着这个证明,带着你的护照去中国大使馆领事部办理护照注销手续。

我是直接当天一大早先去法务局拿了国籍证明复印件,赶到五反田的领事部,敲章,剪护照不到10分钟。期待挽留你一下? 没有的,走好不送。
拿着失效的护照和国际证明回到法务局,才能拿到了身份证明,然后就可以到区役所办理户籍相关手续。

之后的手续

太多了,之后有空再写吧。

慎用gorm的preload加载大量关联数据

Prepared Statement contains too many placeholders

这是一个Mysql的Error。
当我们用类似 select * from users where id in (1,2,3,4,5)的时候,我们可能并没有在意这一串id到底能有多长。

实际上,这个in后面的list是有长度限制的,而且在Mysql中,这个限制无法更改,最大支持65535。

上面的query,当我们给的id太多超过这个限制的时候,就会出现
Prepared Statement contains too many placeholders
的报错了。

Why we should not abuse Gorm Preload?

1
2
3
4
5
6
7
8
9
10
11
type User Struct {
Id uint
Name string
Orders []Order `gorm:"many2many:user_orders"`
}

type Order Struct {
Id uint
Amount string
Users []User `gorm:"many2many:user_orders"`
}

上面的定义,如果我们需要找出用户id 1,2,3他们对应的所有order,可以简简单单用一个preload。

1
2
3
4
5
6
7
var users []User
var orders []Order
ids := []uint{1,2,3}
db.Preload("Orders").Find(&users, ids)
for _, user := range users {
orders = append(orders, user.orders)
}

这个乍看没啥问题呀。这个时候我们先看看gorm会去做哪几件事情。

1
2
3
// select * from users;
// select * from user_orders where user_id in (1,2,3);
// select * from orders where id in (xxx,xxx,xxx);

这个时候,如果1,2,3中间某个用户拥有大量的order,那最后的where语句就会抛出Prepared Statement contains too many placeholders的Error。

那咋办?

Work Around

直接用Raw写subquery。

1
2
3
var orders []Order
subquery := db.Raw("select order_id from user_orders where user_id in (1,3,4)")
db.Where("id in (?)", subquery).Find(&orders)

MySQL的自增ID并不一定连续

很多时候当我们给数据库表定义

1
id int auto_increment;

的时候可曾有的期待是这个id会随着插入的数据增多连续递增。

然而,递增是没有问题的,但是连续性其实并不能得到保证。

举个例子,在Gorm中,有相关Upsert的用法如下。

1
2
3
db.Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&users)

这样其实在Mysql中会被映射成on duplicate的语句,这个时候本来预约给新的record的id就被消耗了一个,所以如果这次成了更新没有插入,那么这个id就被跳过了。

更多其他参考

The only guarantees from an auto_increment column (or IDENTITY in MSSQL, and the other names the concept goes by) is that each value will be unique and never smaller than a previous one: so you can rely on the values for ordering but you can not rely on them not to have gaps.
引用

DNS01验证方式手动获取Lets Encrypt 证书

背景

以前用Lets Encrypt获取ssl证书都是用最傻瓜的方式。

1
2
3
4
5
6
7
8
$ sudo letsencrypt certonly
Saving debug log to /var/log/letsencrypt/letsencrypt.log

How would you like to authenticate with the ACME CA?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Nginx Web Server plugin (nginx)
2: Spin up a temporary webserver (standalone)
3: Place files in webroot directory (webroot)

通常我会选2,然后就得把nginx先停了,然后跑一遍,然后再启动nginx。
这个其实也不麻烦,但是无论选择哪个选项,都得有一个前置条件就是本机的80端口是开放的。

这个对于vps或者公有云上的一些主机来说根本没什么问题,但是对于藏在家里路由器后面的主机,不想默认使用80端口的话,就略麻烦了。
更何况有些服务提供商禁用80端口。

这个时候,其实Lets Encrypt提供了使用DNS服务器获取证书的方式。

Lets Encrypt 验证主机身份的方式

在获取证书的时候,需要验证想要获取证书的域名的确是本人所有的,这个验证被叫Challenge。
Lets Encrypt提供了好几种验证方式,具体可以查看
Challenge Type

其中,最常见的验证方式就是之前一直用的方式,就是lets Encrypt的客户端(certbot)会在你指定的服务器目录放一个随机生成内容的文件,然后lets Encrypt会通过你指定的域名去访问你放在这个端口的文件,验证里面内容是否正确。
这种验证方式被成为HTTP-01 Challenge。

我们今天使用另外一种方式,DNS-01,手动获取证书,这个方法最大的好处是不需要开放80端口。

DNS-01 方式获取证书

首先,带参数运行certbot。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ sudo certbot certonly --manual \
> --server https://acme-v02.api.letsencrypt.org/directory \
> --preferred-challenges dns \
> -d www.bocchi.tokyo \
> -m gyorou@tjjtds.me \
> --agree-tos \
> --manual-public-ip-logging-ok
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Cert is due for renewal, auto-renewing...
Renewing an existing certificate
Performing the following challenges:
1 ---
dns-01 challenge for www.bocchi.tokyo

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.www.bocchi.tokyo with the following value:

M4v67OXrYU-nXcveB8Oajqavh6ZWY3tLUMqrZTaFjGQ

Before continuing, verify the record is deployed.

按这个指示,我们上dns的服务提供商,新建一个hostname 是_acme-challenge.www.bocchi.tokyo的TXT记录。
里面的内容就是下面这串随机生成的字符串。

过了一会儿,我们验证一下这个DNS记录有没有生效。

1
dig @01.dnsv.jp -t TXT _acme-challenge.www.bocchi.tokyo

其中01.dnsv.jp 是DNS的服务器。

当看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
~ dig @01.dnsv.jp -t TXT _acme-challenge.www.bocchi.tokyo

; <<>> DiG 9.10.6 <<>> @01.dnsv.jp -t TXT _acme-challenge.www.bocchi.tokyo
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52094
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;_acme-challenge.www.bocchi.tokyo. IN TXT

;; ANSWER SECTION:
_acme-challenge.www.bocchi.tokyo. 3600 IN TXT "M4v67OXrYU-nXcveB8Oajqavh6ZWY3tLUMqrZTaFjGQ"

;; AUTHORITY SECTION:
bocchi.tokyo. 86400 IN NS 01.dnsv.jp.
bocchi.tokyo. 86400 IN NS 02.dnsv.jp.
bocchi.tokyo. 86400 IN NS 03.dnsv.jp.
bocchi.tokyo. 86400 IN NS 04.dnsv.jp.

就知道DNS Record已经生效,这个时候,再按下会车键,Continue,验证就完成了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Press Enter to Continue
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/www.bocchi.tokyo/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/www.bocchi.tokyo/privkey.pem
Your cert will expire on 2022-07-09. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
- If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

使用BLE Itag控制Gopro的拍摄

背景

gopro可以用语音控制。
在人流攒动的地方,说一句”gopro开始录像”,保不准你成为万众瞩目的焦点。况且,其实在室外亲测gopro有时候很难听到你说了什么,你得凑近了,大声说。

那么可不可以用手机去控制gopro通断呢? 手机上有gopro官方的app,直接可以控制gopro的各种配置以及摄影,但是一手gopro,一手手机,都得单手操作,还是十分不方便。

仔细一想,我们其实只想要一个开关按钮,当我们想要摄影时候,按下开关,gopro开始摄影,当我们想停止,再按一下,gopro停止摄影。

于是我找到了一个叫做Itag的纽扣蓝牙设备,这个设备自带一个按钮,当按下这个按钮,就会向连接这个itag的设备发送通知。

思路

按下itag按钮之后, 我们只需要在接受到这个通知的时候告诉gopro开启摄影或者停止摄影,那么我们的需求就满足了。

gopro官方最近几年推出了BLE的API,所以控制gopro的工作也不是大问题。

我们可以在IOS上用corebluetooth建立两个连接,一个连接itag,一个连接gopro,并且订阅itag上的按钮消息,当接受到按钮消息,我们发送gopro摄影的通断命令给gopro。

官方demo

只需要修改官方demo的一部分地方,增加开始摄影,停止摄影,切换摄影模式,以及监听itag按钮通知的方法。

随便建立一个新的swiftui界面,初始化两个CentralManager,在onAppear中分别扫描gopro设备和itag设备。

在ui界面上建立两个按钮,分别对应按下之后连接gopro和itag设备,同时连接完gopro切换到摄影模式,连接完itag设备开始监听button的事件,同时注册handler,如果有button事件就控制gopro进行摄影和停止摄影。

注意点

注意一定要初始化两个CentralManger,一个CenteralManger是无法同时连接两个设备的。

注意在info.plist中添加background功能,并添加”use ble accessories”,否则可能我们屏幕锁了itag的按钮就失效了。

构建家用kubernetes集群(上)

背景

之前家里仅仅放了一台Qnap的四槽NAS。用来存放一些照片,旅游的视频,绰绰有余。
傻瓜式的NAS一条龙服务的确很便利。但是一旦有了安全的问题就不是自己能够解决的了。

最近针对Qnap的勒索软件闹得沸沸扬扬,我的Qnap也不慎中招,所有小于20M的文件都被强制加密了。

好不容易打上不补丁升级系统清扫完毕,
一波未平一波又起,最近google的新闻又开始给我推送一些Qnap又遭新型勒索软件攻击的新闻。

看开是时候升级一下工具了。
于是打算整合一些二手PC,搭建一个kubetnetes集群,利用一些开源的项目,自己host所需存储服务。

硬件构成

设备 数量 价格(JPY)
intel-nuc7i7bnh 2 52,000
intel-nuc5i3ryh 1 23,000
Toshiba HDD 1T 2.5Inchi 5400rpm 3 6,900
散装 DDR4-2133 8G 5 1,5000

总共花费10万日元上下吧。

预先准备

操作系统

三台机器分别装上ubuntu server 20.04 LTS。

连接上家里的无线热点

因为家里路由器放在了电视机旁边,为了不拉拉扯扯一堆网线,准备让三台NUC通过WIFI连接到路由器。
由于ubuntu server没有图形界面,这一切需要在terminal上进行。
由于安装系统之后需要用apt install安装一些常用的工具,所以不可避免我们先得把机器用有线暂时先连接到路由器上。

1
2
sudo apt update && sudo install net-tools && \
sudo apt install wireless-tools

查看一下无线网卡接口的名称。

1
2
iwconfig
=> wlp1s0

然后打开 /etc/netplan/00-installer-config-wifi.yaml, 编辑文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
network:
version: 2
renderer: networkd
wifis:
wlp1s0:
dhcp4: no
dhcp6: no
addresses: [192.168.0.87/24]
gateway4: 192.168.0.1
nameservers:
addresses: [192.168.0.1,8.8.8.8]
access-points:
"your wifi ssid":
password: "you password"

其实已经很明明白白了,还是多解释一下,这里不使用DHCP,直接给机器绑定固定地址。因为作为K8s的node,我们不希望node的ip发生变化。
同时,得指明dns 服务器的ip,一般默认用路由器的ip就好,实在不放心,把谷歌的8.8.8.8 也放上去。

保存之后,执行sudo netplan apply, 查看一下ifconfig,默默等待无线网卡连上wifi,获取到指定的ip地址。

然后把网线拔了,继续准备下一台机器。

使用RKE安装k8s

ssh免密

ssh到每一台机器中。以下称为node。

使用RKE安装K8s的时候,需要一个用户能够ssh进入到每个节点中。假设我们已经有了这么一个用户,叫做kubeuser。

我们把mac本地的ssh公钥放入到每个node的用户home下面,以实现免密码ssh进入node。

1
2
# mac 本地
cat ~/.ssh/id_rsa.pub| pbcopy
1
2
3
4
5
# 各node
ssh-keygen
vi ~/ssh/authorized_keys
#粘贴mac本地的公钥
chmod 600 ./ssh/authorized_keys

安装docker

每个node安装docker,并把之前提到的rke需要用到的用户加到Docker用户组中。

为什么呢?因为rke需要到每个node里面执行docker,所以rke操作的用户必须不用sudo就能使用docker。

1
sudo useradd -aG docker kubeuser

创建k8s

在mac上下载rke。brew install rke

rke config 会展开一个对话模式,询问每个节点信息和你所想要的配置。

我的配置是

node ip role
k8s-1 192.168.0.87 ectd, contropanel, worker, ingress
k8s-2 192.168.0.88 worker
k8s-3 192.168.0.89 worker, ingress

配置完成之后会生成一个cluster.yml, 这个时候就可以执行rke up了。

执行完成会生成一个kube_config_cluster.yml文件,这个就是你的kubeconfig文件了。把它放到 ~/kube/config, 试着看一下是否k8s已经起来。

这里推荐k9s这个工具,我们可以直接 brew install k9s, 然后执行k9s,就可以看到我们的cluster全貌啦。

接下来

我们需要ingress-nginx实现七层LB,同时使用MentalLB实现四层LB,同时使用Longhorn快速创建Persist volumn的。

更重要一点,我们还需要将去往NAS的request转发给NAS。由于篇幅过长,就在下一篇介绍了。

这里留一点提示,三台node,只有两台配置了ingress controller, 想必有一台是要用来直接承接外部所有request的了。

Lets Encrypt是如何颁发证书的

TL;NR

分成两个步骤。第一步验证agent对domain的所有权。
第二部才是给agent的domain颁发证书。

下面假设我们的服务器(上面跑着lets encrypt的agent)的域名是example.com.

验证agent拥有domain example.com

回忆一下我们在服务器上运行certbot certonly的时候,会弹出来几个选项,其中一个是spin up a temp server。

这个就是用来完成Lets encrypt的CA服务器对我们服务器拥有example.com的验证。

具体来说,agent会按照CA服务器的吩咐,放一个文件在example.com的目录下,然后CA服务器会亲自通过example.com来获取这个文件。能够获取则证明ok,我们的agent拥有example.com这个doamin。

接下来,因为要颁发证书,所以我们的agent需要提供一对公私钥。其中公钥我们称作A。CA服务器会产生一个随机数,然后我们的agent用私钥加密了之后,和公钥A一起丢给CA服务器,CA服务器验证ok,就用这个公钥A和你进行下面一个步骤。

看这个图,钥匙指的是我们约定好的公钥。为什么是A呢?因为表示是我们agent用到的。

颁发证书

验证搞定之后,agent会发起一个CRS。这个里面包含了我们实际要给服务器签名的公钥S。除了这个公钥,还包含了一个签名,这个签名是用公钥S对应的私钥对公钥S进行了签名的产物。

然后整个CRS再用之前约定好的公钥A的私钥进行签名,交给了CA服务器。

图中钥匙S就是代表实际上我们想要颁发证书的公钥。圆圈表示签名。然后CA先验证一下用公钥A的签名,再验证一下用公钥S的私钥对公钥S的签名,都没有问题的话,颁发证书,用公钥S对证书进行加密送回给agent。

理解上面三个图需要排除一个误区,就是上面除了最后加密颁发证书之外,都是明文+签名。使用明文中的公钥就能验证签名是否是又对应的私钥签发。

如果觉得抽象就想象一下jwt,jwt谁都可以看里面的内容,至于如何验证jwt有没有被篡改,就得看jwt后面带的一串签名,大概就是同样的意思。

配图理解KMP的NEXT数组

KMP真是记一次忘一次。归根到底,还是没有彻底理解。

为了彻底理解,我们这次来画个图。

理解KMP,最关键的是理解next数组。

next数组是基于需要匹配的字符串的一个数组。

如有字符串target

我们定义next(i) 为,字符串target[0:i+1]的最长的相同前缀与后缀的长度。

什么叫字符串的前缀?

字符串的前缀就是下标从0开始,连续的不包括最后一个下标的子串。
比如abcd的前缀有a,ab,abc
同理,字符串的后缀就是包括最后一个下标,不包括第一个下标的连续子串。
所以abcd的后缀有b,bc,bcd

如何判断最长的相同前缀后缀?

举个例子, target='aabaaba',
i=0的时候,因为一个字符不存在所谓前缀后缀,我们规定next[0] = 0
i=1的时候,前缀有a,后缀有a有前缀a等于后缀a。所以next[1]=1
i=2的时候,前缀有a,aa,后缀有ab,b,但是没有一个是相同的,所以next[2]=0
i=5的时候,有前缀aab等于后缀aab。所以next[5]=3
i=6的时候,有前缀aaba等于后缀aaba。所以next[6]=4

我们这里写一个算法。

1
2
3
4
5
6
7
8
9
10
# 假设字符串为niddle, 这里用dp表示next数组。

dp = [0] * len(needle)
j = 0
for i in range(1, len(needle)):
while (j > 0 and needle[j] != needle[i]):
j = dp[j-1]
if needle[i] == needle[j]:
j+=1
dp[i] = j

这个怎么理解?

我们把j放在niddle的0下标处,开始遍历niddle。当前下标为i。 这里的j就可以看成当前的最长前缀后缀长度。

niddle[i] == niddle[j], 最长前后缀长度加1,继续比较下一个i。

niddle[j] != niddle[i], 我们得把j回溯到一个位置k,使得niddle[0:k] 和从niddle[i-1-k:i]相同。这样我们可以直接继续比较j和i。

那么这个k等于多少呢? 这个k是dp[j-1]

为什么? 因为我们知道niddle[:j]niddle[i-1-j:i] 相同, 而 niddle[j-1-k:j]niddle[0:k] 相同。那么必然,niddle[0:k] 也和niddle[i-1-k:i]相同。

这里说得太抽象了,画个图解释一下。

蓝色部分的长度就是k,红色部分的长度就是一开始的j了。

当我们把j移动到k处,继续比较新的j和i,如果还是不一样,自然我们得继续缩小k了。

注意有个细节是不匹配的时候我们把j移动到dp[j-1],这个时候其实相同的前后缀部分是niddle[:dp[j-1]],因为dp的定义是长度!

至此,kmp的next数组的生成方式应该是理解了。

LeetCode 800题突破

上次更新不知道是猴年马月了。

这期间,做了唯一一件事情就是刷题。

说到刷题本来我是没有多大兴趣的。这么多年来尽管心里知道现在互联网公司跳槽基本就是得刷题,心里还是存着十分的抵触。第一是觉得刷题根本就是偏离实际应用,浪费时间。其次觉得要是一个公司面试看中考数据结构算法,那估计也没什么去的必要。

然而那么多年应试教育的教训还是那么深刻,在互联网这个内卷的行业,刷题已经是转职加薪的最简单方法。不需要天赋异才,聪慧过人,只需要静下心来,抽出时间,反复推敲琢磨,甚至死记硬背。自然而然,冲破了那个阈值,新世界的大门也就打开了。

RTO是一个契机

RTO==Return to Office。

对于疫情开始之后长期在宅工作的人来说,回公司上班简直是一个噩耗。当然,这个噩耗来得有些突然。
在今年三月伴随着一系列线上的trouble,公司上层开始把原因归结为在家工作。作为Developer,首当其冲,紧急事态宣言时间一结束,首先出台了DEV部门必须每周五天去公司出勤的规定。

于是在每天确诊人数还在增多的四月中旬,我开始了所谓的RTO。开会还是Zoom。毕竟还有一部分人因为各种原因依旧在家工作。于是一到开会时间,你可以听到耳机里传来你摘下耳机也能听得见的,不远处的同组同学的声音,亦或者是你想集中的时候,周围此起彼伏的开会的声音把你打断。

当然中午的免费的便当还是依旧,菜一点点饭管饱。

想着自己还在领着可怜巴巴的工资,看着同期要么离职要么升职,顿时负面情绪就上来了。

动机和契机都有了,接下来就是刷题了。

刷题,刷题,刷题

其实这次不是第一次想要刷题。以前也陆陆续续刷了将近100题,不过时隔太久,回顾一下刷过的题,又得从0开始了。

为了不半途而废,我给自己定了刷题的原则。

  • 不粘贴复制

  • 不跳过不会的题目

  • 不会做的题目看过题解理解之后自己再做

  • 错过的题目过一阵继续做

刚开始印象比较深刻的题目有利用马拉车算法解回文字符串,KMP模版这些。然而其实这些真正面试时候应该不会考到。
不过按着原则,这些题目以及类似题,都多了好几遍。

一开始在国际版的leetcode上刷,每次都得去google找答案,看得也是一知半解,十分痛苦。等刷到大概250题的时候,
干脆买了国内站的会员,一年折合也就500不到,最关键的是可以看到题解以及讨论。

这个时候,刷题的模式固定成,上班有间隙的时间,刷几题,下班运动一会儿,吃饭,大概7点以后,集中刷题,到了晚上21点半左右,模拟一次3题一小时半的面试。
凌晨一点睡觉。

大概刷到400题左右的时候,平时只能过个两题的模拟,竟然也能三题全过了。大部分中等题也不是那么束手无策了。

大概从500题开始,尝试参加周赛。第一次只做出来两题。不过第三题太难了,也没多少人做出来。500题左右还有一种现象,就是觉得很多题目似曾相识,但是又什么都不会了。
不过我知道大概这是黎明前最后一片黑暗吧。

600题左右的时候,正好有一天猎头联系了我。一开始想给我介绍一些其实和当前工作相比没多大优势的职位,于是统统拒绝了,后来我就提了条件。工资大概得到现在的160%,然后必须是top level的公司。

尽管这些公司不多但是我还是如愿进行了面试。这期间继续坚持刷题。

当然刷题的成果在面试中也反映得淋漓精致。真正我做过原题的题目其实也就1/4,但是思路如泉涌,挡也挡不住。最终包括因为已经拿到了offer终止面试的公司,所有的面试都迎刃而解了。

700题左右的时候第一次38分钟全Pass了周赛,第一次拿到了leetcode的骑士徽章。一切都是那么顺利成章。

今天,交完离职信,提交了第800题。百感交集,感觉得写些什么东西。

当然,新的大门才刚打开,新得旅程,才即将开启。

用Python3的Dict作为函数参数的一个陷阱

今天遇到了个大坑。所以要记录一下。
在python3中,如果给函数参数添加默认值,在用到Dict时候一定要小心。

1
2
3
4
5
6
def f(value, key, hash={}):
hash[value] = key
return hash

print(f('a', 1))
print(f('b', 2))

你觉得是

1
2
{'a': 1}
{'b': 2}

但结果确是

1
2
{'a': 1}
{'a': 1, 'b': 2}

因为python会直接把你更改过的default的值保存到下一次call。

使用None可以避免这个问题。

参考