如果用一句话总结去年的工作,那就是:平淡无奇,甚至还变卷了一些。看着曾经并肩作战的同事,一个个都离开了,曾经开会的时候,默认的语言还是英语,如今开会都是默认直接中文了。年初由于组织架构的再次调整,我又被合并到了另外一个组,开始用C++开发线上的实时推荐系统。于是,我的技术栈也因此扩展成了React, Go, C++。在业余时间,我还顺便学习了一下现代C++,毕竟上次用传统的C++已经是N年前了。至于工作本身嘛,我只能说,走一步看一步,我还是先刷着LeetCode热热身吧。
2023年这一整年,最值得令人高兴的事情是:我终于又毕业了!
从疫情开始的线上课程,到后疫情时代的线下课程,总共历时两年的时间,我又毕业了。回顾起当初去学院提交资料,领取学生卡的场景还仿佛就停留在昨天。这两年的时间里,做得比较有规律的事情,就是遇到有课程的时候,早上起来一大早去公司上班,下班后又马不停蹄地飞奔去学校接着上课。晚上回家还得卷一下当天课程的quiz,周末也得继续和小组的同学一起讨论项目,写report。除了假期的周末,平时是几乎没有周末的。这种忙碌而又充实的生活,让我觉得仿佛又回到了多年前的那个学生时代。
这种忙碌的生活一直重复了四个学期,最后一个学期伴随着最后两门的考试,最终到了2023年4月底。6月的一天早上,我跟往常一样在公司上班的时候,收到了学院的邮件,通知可以顺利毕业了。
7月,学校的毕业典礼,早上一场突如其来的大雨,导致路上堵车了40多分钟,本来以为是赶不上最后的毕业典礼了,没想到赶在毕业典礼开始的前几分钟顺利入场了。
有同事问我:本科毕业多年后,还有必要再去读一个master吗?对我而言,我觉得是有必要的。
当年因为家庭经济的原因,没有能出国深造的机会。本科毕业之后,我就开始在国内工作了,直到后来到了新加坡。没能出国体验留学,对我来说难免是一件比较遗憾的事情。后来从NUS毕业来公司的新同事那里了解到,原来国外的master是没有入学考试的,直接拿着本科成绩单和英语考试成绩申请就可以了。而且part-time的证书和full-time是一样的,学费也一样,就连平时上课都是一起上的。
于是,我就疯狂DIY走完了接下来的所有流程:考雅思,翻译本科成绩,准备各种资料,提交申请,直到拿到offer入学。
或许多年以后,我还能回忆起这段毕业N年后迟来的海外留学经历,回忆起考试前的忙碌,作业和项目deadline前的紧张。毕竟,我也为之努力过了,这个曾经的缺憾也终于被填补上了。
当然,在各种不同课程的project中,我还有幸认识到了一群同样在新加坡一边上班一边攻读master的队友们,甚至还有跟我在同一个公司的同事,也算是很新奇的体验了。
趁着毕业之际,把之前在NUS学习时候的各种照片和视频整理了一下,制作了一份毕业纪念视频:
Master的课程完结之后,今年倒是看了很多场的演唱会。首先是4月Eason演唱会的新加坡场:
还有不老歌神张学友:
有我最喜欢的爱尔兰乐队: Kodaline
最后还有年底张信哲巡演的新加坡场:
现场的体验确实比平时听Spotify上的音乐更震撼,另外,舞台效果还是挺棒的。
年底的年假还剩了十来天,所以计划了一场跨越几个城市的“毕业旅行”,先后去了吉隆坡,香港,台北和东京。
十一月的吉隆坡和新加坡差不多,仍然是盛夏。双子塔的夜景不错,Bukit Bintang的夜市人潮汹涌,颇有赛博朋克的风格。从新加坡飞吉隆坡是我遇到最短途的飞行了,飞机起飞不到20分钟,就开始降落了……
十二月从新加坡飞去了香港,两天的时间匆匆打卡了小红书上各个比较有名的景点。怪兽大厦是曾经变形金刚电影的取景地,密集的房屋给人很有压抑的感觉。
中途还去打卡了香港大学,正好遇到他们的毕业季
当然,必打卡的地方还有太平山上的夜景
年底得知入台证放开申请之后,立马提交了资料,一周左右就到手了。正好从香港飞去台湾也很近,这一段行程算是无缝衔接上了。
位于台北市附近的野柳地址公园风景很不错
十份的瀑布
九分老街
如果年底的台北还是深秋,东京就算是彻彻底底的冬天了。从台北乘坐虎航直飞东京,其实也挺近的,正好也用上了刚申请的日本五年签。
打卡了地标建筑晴空塔
徒步上野公园
“混进” 了东京大学
东京都厅展望室的夜景
明治神宫
代代木公园
镰仓,灌篮高手打卡地
涩谷SKY观景台
六本木的圣诞点灯
御茶之水站
也许看腻了热带国家的风景,这次旅行台北和东京整体体验还挺不错,值得再去。
回顾了一下去年对今年的展望:顺利毕业,回国,策划一次年底的旅行,好像都实现了。
今年终于可以腾出一点心思,重新思考一下工作以及后续的发展。希望在新的一年,能重新参与到开源项目中,并阅读一些其他感兴趣的书籍。
]]>A heap is a specialized binary tree data structure that satisfies the heap property. In a heap, each node has at most two children, and the key (the value) of each node is greater than or equal to (in a max heap) or less than or equal to (in a min heap) the keys of its children. The binary tree representation of a heap is a complete binary tree, meaning that all levels of the tree are completely filled except possibly for the last level, which is filled from left to right.
Heap sort is a comparison-based sorting algorithm that uses a max heap to sort elements in ascending order. The algorithm works by first building a max heap from the input array. It then repeatedly extracts the maximum element from the heap and places it at the end of the array. This process is repeated until all elements have been extracted and the array is sorted in ascending order.
Heap sort has a time complexity of O(nlogn)
and is an in-place sorting algorithm, meaning it does not require any additional memory beyond the input array.
An array can be used to represent the heap structure by using the following indexing scheme:
2*i+1
and its right child is at index 2*i+2
.Using this indexing scheme, we can represent a heap as an array and perform heap operations on it efficiently.
For example, to build a max heap from an array, we can start at the last non-leaf node (which is at index n/2-1, where n is the size of the array) and perform the heapify operation on each node from this index down to the root. The heapify operation ensures that the node is the largest of the three by swapping it with the largest child if necessary. By calling heapify on each node from the last non-leaf node down to the root, we ensure that the entire array represents a max heap.
Here is an C++ example that builds a max heap based on the input vector:
1 | void heapify(std::vector<int>& vec, int n, int i) { |
The build_max_heap
function constructs a max heap from a given vector of integers. A max heap is a binary tree where the value of each node is greater than or equal to the values of its children. The build_max_heap
function starts by finding the index of the last non-leaf node in the binary tree, which is n/2 - 1
, where n
is the size of the vector.
It then iterates from this index down to the root of the tree, calling the heapify function on each node.
The heapify
function takes a node and its two children and ensures that the node is the largest of the three by swapping it with the largest child if necessary. By calling heapify
on each node from the last non-leaf node down to the root, the build_max_heap
function ensures that the entire binary tree is a max heap.
Now we call this function and construct a max heap:
1 | int main() { |
The output is:
1 | Maximum element: 9 |
If we append a new number 12
to the end of the vector and re-construct the max heap:
1 | vec.push_back(12); |
The output is:
1 | The heap: 12 6 9 5 5 4 2 1 1 3 5 3 |
We will see that, after re-construct the max heap, the newly appended element 12
becomes to the top element of this max heap.
std::make_heap
and std::priority_queue
are both C++ standard library containers that are used to implement heaps. However, they have some differences in their functionality and usage:
std::make_heap
is a function that takes a range of elements and rearranges them into a heap. It does not provide any additional functionality beyond constructing the heap. Once the heap is constructed, you can use other algorithms like std::sort_heap
or std::pop_heap
to manipulate the heap.
std::priority_queue
is a container adapter that provides a priority queue data structure. It is implemented using a heap and provides additional functionality beyond just constructing the heap. It allows you to insert elements into the priority queue, remove the highest-priority element, and access the highest-priority element without removing it. It also provides a way to customize the comparison function used to determine the priority of elements.
左值和右值是Modern C++中引入的新概念。简而言之:
1 | int x = 999; // x 是左值, 999是右值 |
我们,可以把左值大致想象为一个容器,而容器中存放的就是右值,如果没有这个容器,其中的右值将会失效。对于以下的程序,我们在编译的时候将会得到类似的错误:
1 | error: lvalue required as left operand of assignment |
1 | int x; |
很显然,等号左边需要的是一个左值,而123作为一个 literal constant 类型,是没有办法充当左值的。同样,我们也没法对一个右值进行取地址的操作:
1 | int *x; |
编译器报错:
1 | error: lvalue required as unary '&' operand |
左值在很多情况下有可能被转换为右值,比如在C++中的 -
运算符,它将两个右值作为参数,并将计算结果作为右值返回。
1 | int x = 10; |
在上面的程序片段中,我们明显看到x, y本身是左值,但是却以右值的身份参与了减法运算。这是如何做到的呢?答案是编译器对左值做了隐式的转换,将x和y转换成为了右值。C++中其他的乘法,除法和加法运算也是同样如此。
如果左值能被转换成右值,那么右值本身能被转换成左值吗?答案是 否定 的.
C++中引入引用的概念,是为了在程序中方便的通过引用修改原变量的值,并且,在调用方法传参的过程中,传递引用可以避免拷贝。在通常情况下,左值引用只能指向左值,而右值引用只能指向右值。听起来比较废话,但是也有特殊的情况。
1 | int x = 10; |
在上面的示例程序中,我们定义了一个左值x
,然后赋值10。随后定义了一个引用,指向x
。因此,ref_x
成为x
的引用,它就叫做左值引用。通过操作ref_x
,我们就可以改变x
的值。
如果我们把上面的程序简化为:
1 | int& ref_x = 10; |
在编译的时候,我们会得到类似的错误:
1 | cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int' |
显然,左值引用只能指向一个左值,而不能指向一个右值。不错从错误信息中,我们方法可以得出另外一种写法:
1 | const int& ref_x = 10; |
根据编译器的规则,我们被允许通过定义一个const类型的左值来指向右值。不过既然这个左值被定义成了const
,没有办法修改指向的值。
C++中的右值引用以&&
表示。通过右值引用,可以修改其指向的右值。
1 | int&& ref_x = 10; //定义右值引用 |
如果我们尝试将右值引用指向一个左值:
1 | int x = 10; |
编译器也会抛出类似的错误,告诉我们不能把一个右值引用指向左值。
1 | error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int' |
通过一个简单的示例程序,我们就能知道左值引用和有值引用的本质。
1 | void increase(int&& input) { |
从上面的代码示例中,我们可以看出右值引用 ref_b
本身也是一个左值,在调用 increase
的时候,需要通过 std::move
转换为右值,编译器才不会报错。
通过以上的例子,我们可以总结出如下的规律:
const
关键字约束也能指向右值,不过无法对右值进行修改。std::move
等形式也可以指向左值。在前面的例子中,我们已经涉及到了 std::move
这样的操作。右值引用配合 std::move
通常能实现移动语义,从而实现避免拷贝,提升程序性能。
1 |
|
如果运行上面的示例程序,我们会得到这样的程序输出:
1 | str_a: Hello |
很明显,在str_a
被添加到vector的时候,并没有涉及到移动语义,所以str_a
的值被拷贝到了vector中。而在把str_b
添加到vector的过程中,由于用到了std::move
,所以str_b
的值被移动
到了vector中。之后再输出vector的值的时候,可以看到其中已经包含了str_a
和str_b
的值。但是str_b
本身的值已经被偷走
了。
需要注意的是,std::move
本身的名字比较有迷惑性,其实它在这里的工作只是把str_b
从左值转换成右值,而不会做实际上移动资源的操作。如果我们把添加str_b
的代码替换成:
1 | list.push_back(static_cast<std::string&&>(str_b)); |
会达到一样的效果。而真正的秘密在于 std::vector
提供的两种不同的重载方法:
1 | void push_back( const T& value ); |
第一个重载方法接受的是左值引用,当传入 str_a
的时候,由于 const
关键字的限制,它的值会被拷贝一份,并放入到vector中,而 str_a
本身的值并不受影响。而第二个重载方法接受的是一个右值引用,push_back方法会把其值放入vector中,并转移 str_b
对字符串值 World
的所有权。这样,当我们再输出它的值的时候,发现已经为空了。
完美转发(Perfect Forwarding),转发的意义在于当一个方法把其参数传递给另外一个方法的时候,不光转发参数本身,还包括其属性(左值引用保持左值引用,右值引用保持右值引用)。
std::forward
实际上也是做类型的转换,不同的是 std::move
只把左值转换为右值,std::forward
能转换为左值或右值。
std::forward<T>(arg)
中,如果类型 T
是左值,参数 arg
将被转换为左值,否则 arg
将被转换为右值。
1 |
|
在以上的示例程序中,forward
使用一个Universal Reference类型接受一个参数,并且通过 std::forward
讲参数转发给 target_func
。由于此方法有两个重载,分别接受左值引用和右值引用。在我们分别传入右值 5
和左值 x
的时候,我们发现 forward
这个方法都能准确无误的把参数转发给对应的重载方法。因此,程序的输出分别是:
1 | rvalue reference |
随着疫情的反复,今年又是没能回去的一年,回想起年初的时候,去摩天轮观了夜景,也去金沙酒店的无边泳池游了一回,一年的时间总是飞逝而过。
新加坡的“复工复产”,比起国内来得更早一些。上半年还都在家里办公,公司员工可以自由选择在家上班或者去公司,从下半年开始,就陆续都回公司上班了。再到年底,几乎全员都被要求回公司上班了。
然而,今年的经济形式并不好,再加上美联储加息,公司各种相关的负面新闻层出不穷,股价连续暴跌。经历了年中和年末的几波裁员,身边有不少同事幸运的领到了大礼包。说是大礼包一点也不过分,都是N+2起,但是我命中仿佛一直和大礼包没有太大的缘分。当年Moto关闭成都研发中心,传闻N+7的大礼包我也没领到,在领大礼包之前竟然主动离职了,眼睁睁错过一个亿。这次也一样,一波又一波的裁员也没轮到我,除了老实搬砖也干不了啥。公司的这波裁员也上了国内的新闻,脉眿上都被刷屏了,希望明年的经济能尽快好转起来。
之前因为疫情,我的master课程有两个学期都是完全在线上完成的。从今年8月开始,也就是我读master课程的第三个学期,NUS也要求所有的课程和考试都转成线下形式了。好在Shopee离校园很近,下班后可以走到街对面直接乘坐NUS的校车去上课,时间刚刚好。前两个学期都是上网课,也没有太多机会去熟悉校园,这次趁着去学校上课的机会正好把校园逛了个遍:
UTown的风景
UTown的大草坪
校园礼品店
校园内的李光前自然历史博物馆
参加线下课程,虽然每次得往学校里跑,不过整体体验还是不错的,那种N年前在大学上课的感觉又回来了,哥的青春也跟着回来了:
自从第三学期开始,明显感觉比之前两个学期轻松了许多,因为前两个学期都是选满了三门课程,周末的时间几乎全用来做project和肝作业了。后两个学期分别只有两门课程,workload自然也小了许多。正值年底,第三个学期也结束了,来年开学后还有最后一个学期,不出意外的话,我就可以毕业了。希望来年最后两门课能卷出个高分,顺利毕业。
今年投入开源项目的精力并不多,开学上课过后,也没有太多精力维护开源项目。当然,其间也投入了小部分精力继续维护已有的项目,更新了几个版本。另外,也花了一点时间给GitHub配置了项目赞助功能,还意外得到了一些赞助。 除此之外,LunarVim 是我今年开始玩的Vim配置,用起来挺顺手,于是我也给这个开源项目的作者赞助了一点美刀,算是聊表心意。
今年开源项目的总赞助收入是: 9.99新币
今年的股票和期权账户只能用惨不忍睹来形容。去年底开始,公司股票开启暴跌模式,年初的时候,特斯拉的期权又亏了2W多刀。快到年底的时候,特斯拉和公司股票又开始狂跌。掐指一算,哥的身价亏了几百个W的软妹币。不过虽然亏,税还得照缴,生活也还得继续。今年的投资,就这样了吧。账户也不想看了,也许再等两三年再看看会不会好一点。
说是划水实践其实一点也不错,年底的时候约上NUS的校友(其实都是程序员),实践了一下程序员应该如何正确的划水。从巴西立公园租了几个皮艇,一路划到乌敏岛,中午在一个海上的渔家餐馆吃饭,然后再划回公园。单程4公里,来回大概8公里。
年底第三学期刚结束,去电影院看了Coldplay的一场演唱会,然后开始准备旅行攻略。
之前在Youtube上有看到西澳Perth的各种旅行视频,被种了草。趁着周末的空余时间,提交了澳洲的旅行签证申请。看官网本来以为审批的过程需要3个月以上,如果真是这样的话,年底旅行得换个地方了,当时的Plan B是准备去新加坡旁边的马来西亚走一圈。没想到的是提交完材料之后一周多的时间就收到审批通过的邮件。然后,也就顺理成章的有了这次的澳洲之行。
旅行前好好的准备了一下行程,还顺便把新加坡的驾照转换成了国际驾照,按照行程安排,在网上选酒店,租车,其间也参考了不少的旅行攻略。
这旅行的大致路线是:
行程计划:
万事已俱备,在十二月初的时候,跟公司请了一周的年假,换了一些澳币现金,开启了这次的澳洲之旅。掐指一算,这也是我在疫情快三年之后,第一次乘飞机出去的长途旅行。
新加坡是一年四季都是夏天,Perth正好在南半球,也是夏季。所以出行的衣服可以无缝衔接,都是夏装就行了。长袖长裤也需要备两件,因为跟新加坡不一样的是,澳洲白天和晚上温差更大一些,晚上的风能让你感觉一秒入秋。从新加坡飞Perth只需要大概5小时左右的时间,更神奇的是,西澳的Perth和新加坡,以及和国内都一样,都是东八区,无时差。
这次租的车是个日产的SUV,在国内的同款叫东风日产奇骏。澳洲的车跟新加坡的也一样,驾驶台在右侧,车辆靠左行驶。从西澳Perth沿着海岸线一路北上了几百公里,感觉风景绝佳,一旁是公路,另一侧就是大海。随手一拍也能出不错的照片。
Geraldton的海滩
HMAS Sydney II Memorial
The Pinnacles Desert
Cairn Beach
Cervantes
Phillip Island
实事证明,4天的时间游Perth还是不够的,粉红湖泊因为季节不对,关闭了,Perth的野生动物园也没时间去,Rottnest Island值得花上一整天的时间去,可惜也因为时间关系没包含在此次行程中。这次只是一路北上自驾,从Perth一直往南又是另外一条不错的线路,看来下次有机会还得再去深度体验一下。
这次用Dji的Action 3和手机拍了很多4K的视频,所有的素材整理出来有接近15GB。回新加坡之后,我把之前买的Final Cut Pro翻了出来,第一次学视频剪辑,剪出了一个3分多钟的vlog,供同学们欣赏:
时间穿越回2019年,自从我把我的 HHKB Type-S 改装成了蓝牙版本 [1],这个键盘就成为工作中的主力键盘,一直用到了现在。无线连接很方便,充电一次可以用两三周,这是因为电池寿命衰退的缘故,其实之前充电一次大概能用一个月。另外,最方便的是完全可定制的键位和firmware支持的8层可自定义按键,相当的强大。我一直以为,这将是我所折腾的最后一个键盘了。可惜人算不如天算,还没有玩过客制化键盘的我怎么能停止折腾的步伐呢?
偶然有一次,在公司看到了另外一个喜欢折腾键盘的同事的客制化键盘,彻底坚决了我再折腾一个客制化键盘的心。由于用HHKB多年,既然要折腾一个客制化键盘,当然还是希望选择HHKB的layout。其实支持这种HHKB布局的客制化键盘可选择的范围并不是很多,之前参考过的有Drop上的Tokyo 60系列,还有一个国人自己折腾的HTKB客制化套件。再后来无意中关注到了RAMA WORKS的KARA系列,看了一下广告[2],觉得这键盘还真不错,做工不赖,虽然是个塑料壳子,但是颜色很骚气。金属壳子的客制化键盘虽然逼格更高,但是日常通勤背着估计有点重。
KARA系列提供了多种颜色可以选择,图片中后三种颜色 ICED, AZUR, HAZE 都是我喜欢的颜色。好吧,就它了!
满怀欣喜的去官网看了一圈,原来这款KARA是去年推出的系列了,当时只是官网组织团购了一波,后面再也没补货了。难道这公司也是跟小米学的饥饿营销?幸运的是,在新加坡本地的一个键盘玩家群,刚好碰到有个出二手未拆封的HAZE。官网去年价格160美刀发售,折合新币222.9,这个卖家出价是260新币,后来跟他砍价到了250新币。比起官网发售的价格也没多太多。就这样,HAZE被我纳入囊中了。
虽然KARA的官方套件中已经包含了组装客制化键盘的大部分零件:键盘外壳,静音隔层和放置键帽的Plate,还送一根USB Type-C的线。键帽和轴是没有默认提供的,这也是键盘客制化中可玩性很高的部分。市面上可以买到的轴有很多种,颜色也多种多样,再也不限于当年Cherry原厂的那几种MX轴了。多方面搜索了一下之后,决定在Drop上下单键帽和开轴器。在新加坡本地的一个键盘玩家网站买好了润滑剂和轴,还去Shopee买了润滑轴需要的工具。下面列了一个整体的清单:
Drop是一个美国的网站,下单后快递直接从美帝邮寄到新加坡。不知道是疫情的缘故还是其他,这个包裹等了快一个月。清单上,我把USD部分的金额转换成了新币。所以,最后的总价是640.85新币,合人民币大概3000+了。我去!这是我目前折腾过最贵的键盘了!
键帽我选择了GMK LASTER系列的Synthwave M170,这种深紫色的键帽加上蓝色的印刷字母,跟键盘壳子比较搭配。之前用了太多的青轴和茶轴,这次换个不一样的,买了Gazzew的紫色线性轴。
等所有的东西准备齐,就可以开始着手组装键盘了。
打开盒子,PCB板,各种配件和说明书都有了。
原厂出产的轴除了特别说明是润滑过的之外,默认是不带润滑的。其实这也是组装键盘里面最耗时的部分。如果不想折腾,建议直接买那种已经润滑过的轴,能省不少时间。组装的第一步就是把所有的轴用开轴器拆开,然后分别把轴里面经常摩擦的部分手动刷上润滑剂。根据润滑过后的测试,润过的轴和没润过的还是差别挺大的,主要是声音方面要更沉稳好听一些。
关于如何润滑轴,Youtube上有两个很不错的视频,手把手教你如何润滑轴和Stabilizer [3] ,看完就可以开始上手了。
按照新手入门开始润第一个轴,大概需要5分钟时间。后续熟练的话大概需要2-3分钟左右。如果按照平均润一个轴需要话3分钟左右计算,60个键的键盘就需要3个小时了。总之,这是个很耗时的过程,润轴的时间我大概听了2-3个podcast,不然实在太无聊了。
等所有的轴都润好,再直往PCB板上装上Stabilizer,组装算是正式进入正题了。拿出PCB,把之前拆开润好的轴,一个一个按在板子上固定,然后通电测试。RAMA WORKS的板子做工还真不错。
等所有的轴和键帽安装完毕,把键盘背面再固定一下,盖上盖子,就算大工告成了。
接上笔记本测试,这灯光效果还真不错,骚气袭人,亮瞎眼。
最后接上VIA Configurator,把这个键盘按照HHKB蓝牙的使用习惯,配置好各种自定义按键,就能正式开始使用了。
客制化的选择实在是太多了,比起各种键盘官网发售的标准配置,能替换和定制的东西也很多,包括但不限于:各种轴,各种键帽,配重模块,外壳的材质,静音处理,等等。也许这也是客制化的吸引力所在吧。而热拔插轴的应用,也让玩客制化键盘的门槛低了一些,再也不需要电烙铁了。
或许是HHKB玩太久的缘故,这种线性轴的声音更好听一些,力道更轻盈一些。我打算把这个键盘带到公司,在工作中用一段时间再更深入体会一下。
被动收入(Passive Income) 是不需要花费多少时间和精力,也不需要照看,就可以自动获得的收入。在很多年前,我就从网上看到这样的说法,作为一个普通打工上班族,工资收入大概就是我们最单一的收入来源了。被动收入的终极目标,就是建立一些可以自动运营和自我维护的业务,即使在后期不需要投入太多精力的情况下,这些业务照样能为我们带来源源不断的收入。被动收入能在一定程度上带给我们财务上的安全感,即使面临失业和财务上的风险,被动收入的收益足以帮你减轻或者解决掉财务上的危机。
自从开始写Blog以来,我也尝试过如何利用互联网来产生一些被动收入,比如在Blog中植入Google Adsense的广告,通过流量和点击来赚取广告费。无奈广告的点击量不高,而Google Adsense的最低付款额度是100刀,广告挂了一两年也只赚了50多刀,达不到提取的最低门槛,这赚钱的效率实在是有点低,后来干脆放弃了。一直到现在,账户里面还留着那50多刀,就当是个纪念了。
说到真正的被动收入实践,我是从2017年开始的。那个时候,还不流行抖音带货,视频自媒体和直播还不如现在这么火。而互联网上比较传统的带货方式还是: Affiliate Marketing,又叫做 营销联盟 。就是通过URL链接带推荐码的方式,进行营销和宣传,当有订单成交的时候,商家就会给推荐者返利。
作为一个还算 比较早期的VPS玩家,我的第一台VPS是从2010年开始折腾的。在2016,2017年的时候,当时的VPS服务商有名的也就只有那几家。其中,搬瓦工也算是挺有知名度了。之前搬瓦工提供的线路也算比较一般,其产品都是以低价获取口碑和用户。从2017年开始,他们家相继推出了CN2线路和CN2 GIA线路的VPS,从此开启了国内VPS玩家的竞相购买,销售一度火爆。
从那个时候开始,我决定做VPS主机的推广来赚取佣金。本着折腾VPS是一种爱好,另外也对这方面比较熟悉,推广起来比较有信心。搬瓦工当时也是处于扩大业务的阶段,他们家的推广佣金比例也很不错,一个订单可以提取高达22%的佣金,一直到现在都没变过。而且这22%的佣金返利还是循环的,只要购买过的用户继续续费,那下个月续费的费用里面,也有22%的佣金直接归你。更惊为天人的是,他们家的佣金可以闪电提现,基本上分分钟提取到Paypal账户上。直到现在,我也还没见过有其他VPS商家能做到这种程度的。
从2017年8月下旬开始,我的 第一篇推广搬瓦工VPS的文章 在Blog上发布了,这也是第一篇为我的个人blog带来收入的文章。由于我的blog建立较早,有从Google自带来的流量,也有通过RSS订阅和友链交换来的流量。发布的当天,我还把文章的URL发去了一些技术社区推广。翻看了一下最早的佣金记录,这篇文章从发布的当天开始,就有4个人订购了VPS,总共带来了24.8美刀的收入,这可真比Google Adsense来钱快多了。从那天开始,后续的几天也一直有了佣金收入。
第一次尝到了甜头,我决定再把推广业务好好发展一下,后续又多发了几篇VPS和线路的评测文章,每篇都加上推广链接。凭着Blog本身在Google的权重还不错,新的文章几乎每篇都很快就被Google收录,再优化一点关键词,很多关键词搜索都能直接排在首页第一条,或者前三页。这自然能带来更多的流量,更多的佣金也随之而来。
当然,光靠Google的自然流量还是不够的。为了引入更多的流量,提高订单的成交率,我还尝试了多种方式来引流。为了把写好的评测文章发去国内各大网站和论坛进行推广,我在掘金,知乎和简书都注册了帐号,也会把推广的文章发到程序员聚集较多的论坛V2EX以及Twitter。得益于这些网站在搜索引擎的权重都比较高,在上面发表的评测文章也能在搜索结果页中出现在较为靠前的页面。这一波操作也带来了不错的收入。不过后来,由于“同行”间的恶行竞争,有几篇文章在知乎和简书都遭到了举报并被删除了。再后来,知乎的号也因为多次举报被永久封号了,我也放弃了这样的推广方式。
由于搬瓦工的VPS销售一度火爆,刚出炉的一批VPS会在短时间内被一抢而光,想要购买的玩家只能等下一批机器上线。而这些消息之前都是靠Blog,论坛和QQ群口口相传。后续也有人开发了VPS的库存监控网站,主要就是通过监控服务商页面的变化,有新机器可以订购的时候就发出邮件或者其他方式的通知。后来我也跟着做了一个专门监控VPS库存的网站: https://vpsdalao.com,除了监控搬瓦工的VPS库存,还监控其他一些VPS商家的库存情况,也顺便嵌入了推广链接。
除此之外,我还尝试过搭建了一个专门的VPS评测网站,推广包括搬瓦工VPS在内以及各种商家的VPS。也有从Google购买广告,购买关键词引入流量。
在躺赚收入越来越多之后,每个月从搬瓦工所赚取的佣金已经够覆盖所有推广网站和域名的支出,还大大有剩余。从2017年开始,我几乎可以不花钱的玩着一堆VPS,顺便还躺赚着美元。细算了一下从17年到现在,大概5年的时间,从搬瓦工总共提取的佣金大概有 36332.99 美元,按照当前汇率,合人民币 24.5万 左右。平均一年躺赚 4.9 万人民币。嗯,除此之外,我还推广其他商家的VPS,大概算了一下,收入也有几千刀左右。搬瓦工这边算是赚得最多的了。这些副业所带来的躺赚收入,也算是业余给自己挣了一些零花钱,虽然数量不多,但是躺赚的过程还是比较享受的。
从2017年到现在,明显感觉到VPS主机推广已经不如过去那么火了。随着某qiang的不断扩展和升级,搬瓦工每次都成为“重灾区”,有很多VPS玩家的机器莫名被block。另外,搬瓦工现在已经不走性价比路线了,新出的机器都不如以往那么便宜,也间接导致了他们家推广的业务越来越不好做。虽然现在每个月仍然有搬瓦工的佣金可以提取,但是我也没把太多的精力放在主机推广这个副业上了。
总结一下主机推广这个副业的运作方式,不难发现跟其他业务推广的套路相似。我也有几条经验在这里分享一下:
]]>
- 任何一个副业或者躺赚的途径,都跟软件开发一样,有它自己的生命周期。要看准时机,在初期进入,当这个行当被更多人都知道而变得火爆的时候,你可以在竞争中获得先机,并充分享受到这个蓝海市场带来的更高和更多的利益。
- 流量才是王道,正所谓酒好也怕巷子深,如何把你的推广文章让更多的人看到,提高订单成交率,就能带来更多的佣金和利润。各种推广方式都可以尝试一下,去各种论坛,群和社区,找到目标用户进行有目的的推广。
- 对于VPS推广,除了写评测文章,也可以考虑到这方面玩家的需求,对他们的需求进行深挖,作出对应的产品,这也很有利于推广。比如VPS比价网站,VPS库存监控网站,VPS二手交易网站,等等。只不过我没精力继续搞了……
本驾照转换攻略适用于持有新加坡长期准证,并且在来新加坡之前已经拥有驾照的同学。
去驾校注册是开始的第一步,其实也挺方便的。新加坡有三个驾驶培训学校,分散在不同的区域,建议找一家离家近的就行。带上你的护照和工作准证,去驾校咨询处咨询一下就行。工作人员在了解你的来意之后,会给你一份表格填写,收集一些基本信息。后续还会在驾校的系统上开设你的账户,有了账户一切就好办了,你可以往账户中充值,然后预约后续的考试。考试的费用会直接在你的驾校账户中扣除掉。
账户注册完成之后,接下来就是备考了。基础理论考试的参考资料可以在驾校买到,也可以去大众书局购买。有两种参考书可以购买,我只买了左边的那一本。
理论考试的题型,都是选择题,一共50道,由系统随机生成,能保证答对45题及以上,就能通过考试了。基础理论考试的书,本身内容没有多少,备考的时候建议先快速刷一遍,大多内容就是介绍交通标志和信号灯的含义,以及各种交通规则。速度快的话,2-3天就可以过完一遍,然后就是刷题了。我在网上找到一个不错的理论考试题库,直接购买付费会员,所有的题都是在这个网站刷的: https://drivingtheory.sg/。
看完第一遍书过后,就可以开始刷题,中途有遇到不理解的问题,再对照着书加强一遍记忆。在看书备考的时候,就可以开始预约去驾校参加理论考试了。通常备考需要一到两周的时间,因个人看书的速度和刷题的快慢而异。预约考试可以提前开始,因为有时候报考的人太多,不一定有合适的时间段。
预约考试都是在驾校各自的官网进行的,用开的驾校账户登录和充值过后,直接在网上预约。预约的时候需要选择具体的考试日期和时间段。预约成功后,会从你的会员帐户中扣除6.5新币的考试费用。需要注意的是,预约考试的时候不用关心是中文考试还是英文考试,考试当天,系统界面上可以自由选择切换语言为中文或者英文。
参加考试当天,记得带上你的身份ID,提前到达考试场地。之前因为COVID-19,在进入驾校之前会检查Trace Together和是否有预约考试,现在估计不用了。考试的场地是每人按照考官规定的座位入座,每人面前一台电脑。听考官介绍完之后,用指定的账户和密码登录过后开始答题。整个考试时长50分钟,50道选择题。其实准备充分的话,很快就能搞定,一般时间是在5-10分钟左右。答完所有题目之后,还有时间可以快速把不确定的题目再过一遍。
考完之后,系统会立即显示你的成绩和是否通过。这时候可以提前立场,并在考官那里领取一张驾照转换的申请表。拿到申请表,就可以从驾校闪人了。另外,你的考试成绩会记录在系统中,去交警局进行后续流程的时候,对方可以直接从系统中查到你的成绩,不需要任何的纸质证明。
通过了考试,填写完申请表,还需要找一家本地认可的翻译公司,把你的中国驾照翻译成英文版本。拿到翻译件之后,就可以直接去交警局转换驾照了。去交警局不用提前预约,在大厅排队系统拿到一个号码就可以等待办理业务。在办理窗口提交驾照原件和翻译件,身份ID,驾照转换申请表和一张照片,留下你的居住地址,付款,搞定。等待大约4-5个工作日之后,驾照卡片会邮寄到你的邮箱。
Traffic Police Headquarters 10 Ubi Ave 3, Singapore 408865
TEL: 6547 0000
用我经常维护的一个项目 GoDNS 举个栗子,每次发布的时候,需要手动执行的两个任务:
在使用 GitHub Actions 之前,我的做法就是在本机使用 Makefile 分别构建二进制文件和 Docker 镜像。这一系列操作在 Mac OS 下的结果就是:无法避免的风扇狂转,以及CPU温度的陡然上升。为了减轻笔记本的负担,后来我把构建 Docker 镜像的流程迁移到了VPS上。不过,这也使发布流程略显繁琐。
有了 GitHub Actions,事情就变得简单了,通过设置一个触发流程的事件,就可以用两个并行的流程分别构建二进制文件和 Docker 镜像了:
在 GoDNS 的构建流程中,触发条件是当每次有新的 tag
被创建的时候。通过把新创建的 tag
push 到GitHub,两个 actions 就会并行执行。其中一个 action 负责构建跨平台二进制文件,完成后自动 release 一个相应的新版本。另外一个 action 负责构建 Docker 镜像,并 push 到 Docker Hub。
不得不说,自从用上了 GitHub Actions,我的笔记本被彻底解放了。附上两个自动构建发布流程,供参考。
1 | name: Auto Release |
1 | name: Docker Image CI |
还记得去年大致这个时候,去Google Photos整理照片的情形,一晃又过完了一年。2021,又是被疫情困住没能回去的一年。
从Spotify的年度大数据来看,这首Kodaline的《Say Something》是我在2021听得最多的一首歌。喜欢这个来自爱尔兰的小清新乐队很多年了,主唱的声音清新干净明朗温柔,很治愈。
因为疫情的反复,今年只回公司上了几个月的班,大部分时间还是远程工作。尤其是快年底的时候,疫情反扑,每天确诊人数3000以上,年底最后几个月都一直在家工作,实在是将远程工作体验到了极致。今年由于公司项目组架构进行了很大的调整,结果是我分别待了三个不同的项目组,其间又做前端开发,也做后端开发,项目技术栈涉及到了Go, Java, C++, React, TypeScript, 感觉自己快真正成为一个全栈工程师了。年初的时候,EP工作签证快到期,公司HR主动联系我renew工作签证,结果一切顺利,又能继续在坡再躁两年了。
根据新加坡的交通法规,如果在国内已经持有驾照,到新加坡这边只需要参加一个理论考试,并且熟悉了新加坡这边的道路交通规则,就能直接把国内的驾照,转换成新加坡的驾照了。这件事情要从去年说起,其实我本来已经去驾校注册完毕,买了理论考试的书备考,还预约了2020年的6月去驾校的理论考试。可后来疫情突然爆发,新加坡全面封国,鼓励所有人尽量减少外出,那次的理论考试直接被驾校给取消了。再后来因为搬家,住得离那个报名的驾校更远了,搬家过后就一直搁置了这个事情。直到今年,才又想起去年挖的坑,找出理论考试的书,拂去灰尘,重新准备。延迟了近一年,在六月中旬的时候,顺利预约到了六月底的考试。还记得考试当天,打车从家到驾校,半小时的车程,几乎跨越了半个新加坡,考试却只花了5分钟就搞定了。
剩下的流程就是带着翻译好的驾照翻译件和原件,以及填好驾照转换的申请表,去交警局申请转换驾照。几天后,驾照邮寄到家,新加坡驾照就这样到手了。
毕业N年,在国内的时候也纠结和考虑过要不要读研究生的事情,可我一想到国内的研究生考试都离不开考政治,我就顿时没了兴趣。自从来了新加坡,才了解到原来只有国内的研究生才有入学考试一说,国外并不需要,只要提交一门规定的英语考试成绩和大学本科成绩就可以申请。另外,跟国内不同的是,国外的master,不管是全职脱产,还是在职,学的课程一样,学费也一样,认可度也一样,毕业证书也一样。考虑了一下平日休息的时间除了刷剧和徒步,要不再顺便折腾一下part-time master?其实自从来2019年来新加坡过后,这个想法就一直在脑海中扎根了。去年底,趁着工作后下班的时间准备了A类雅思考试,虽然口语成绩不是很理想,但是一看NUS (National University of Singapore) 对于计算机master的申请要求,雅思6分的总分应该是达到了 (正好考了6.5) ,索性抱着试一试的心态,在年初1月底的时候,提交了Computer Science part-time master的申请。
NUS计算机学院的研究生课程主要分为四个方向:
申请的时间窗口只有一年两次:
为了准备资料,提取大学时候各科成绩,还专门托我老爸跑了一趟本科学校的档案馆,提取出成绩并找翻译公司翻译成英文版,再邮寄到新加坡来。正好赶在年初的时候,准备好所有的材料,并提交了申请。至于专业方向,我还是选择了本科的老本行:计算机科学。
我本科毕业的学校是国内的不知名非985/211大学,其实申请的时候并没有报太大希望。在提交个人陈述的时候,把自己对计算机科学的兴趣和当前工作的相关性都详细叙述了一下,还把之前获得的一些业界认可的证书也都提交上去了,总之,本科学校一般,成绩也不出彩,就拿其他的来凑吧。1月底提交申请,5月早上的某一天,突然收到了一封email,竟然是offer letter来了。
在NUS的学院网站上accept了offer,然后预约入学体检,所有的手续搞定之后,去计算机学院领了张学生卡。
还是因为疫情的原因,大部分课还是线上进行的,中途去学校了两次,围观了一下学校,顺便去学校的食堂吃了两顿。不得不感叹,还是学校里吃饭便宜,比公司旁边吃饭便宜多了。
从8月入学到年底,已经过完第一个学期了。好家伙,一学期三门课一起修,累并快乐着,周末和平时休息时间几乎都用来肝作业了 -_-# 第一学期算是掌握了课程的大致情况和节奏,后面的学期估计会更适应一点。希望能不出意外两年后顺利毕业吧……
PR也就是新加坡的永久居民,俗称绿卡。来坡大概一年半的时候,也就是跟提交申请NUS master一起的时候,我也顺便替全家提交了PR申请。
提起新加坡的PR,那可真是一个让人又爱又恨的东西。每年PR申请者那么多,批准人数只有3万。当然,就算申请被拒了,移民局也不会告诉你任何原因,只能半年后再重新申请。而申请PR的条件,也是未公开的谜,官方并没有公布一个PR评估的条件和细则,民间都只是根据申请通过者的条件来猜测。因为从学历水平和薪资水平来看,每年各个阶段的都有批准的。有学历低、薪水低的批准,也有学历高、薪水高的被拒。所以,申请者的年龄、来新加坡的年份、学历、所从事行业、薪水、纳税情况、家庭稳定情况等等,都会影响到你申请PR成功与否。
其实提交PR申请之后,我心里也没底,毕竟这是来坡之后第一次申请。可这申请提交过后,申请状态就从2月底开始,一直都是pending,超过10个月了。在年底12月的时候,状态终于发生了一点变化,登录去ICA上看了一下,PR大概率是有戏了。
在2021的最后一天,PR申请状态变为 Approved
,这下PR是真的稳了。掐指一算,从2019年5月来坡搬砖,到PR到手,大概两年零七个月左右。参考了一下各种网上的案例,有多次申请仍然被拒的,也有在坡7、8年仍然没能申请到PR的,我这第一次申请就通过,大概是被幸运之神眷顾了吧!
作为一个在新加坡搬砖的外国人,有了PR,身份上的焦虑顿时消失了:不用再担心工作的不稳定性,也不用担心工作签证续签出什么乱子,在坡买房能立省30%的外国人印花税。另外,小孩读书的费用也能省掉一大笔。接下来,可以安心的搬砖和读书了。
周末和假期的徒步依然是在新加坡生活的人们的保留节目。因为疫情不方便去周边其他国家,户外徒步依旧是最受欢迎的运动之一。今年刷了两次麦里芝蓄水池,也再刷了乌敏岛,还去了新加坡最边缘上的两个地方:地图右上角的 Coney Island 和地图左上角的 Sungei Buloh Wetland Reserve (双溪布洛湿地保护区)。
自从2020年底开始接触到期权投资,然后在网上自学了期权相关的知识。从2021年开始,正式开始期权投资的各种骚操作,总的说来,2021年期权投资还是盈利的。从IBKR的年度报表来看,2021年期权盈利为接近8万刀。
由于是初次接触期权的小白,中途也有一些误操作,导致了一部分损失。另外,由于预估价格错误,期权挪仓也导致账面亏损,看来期权知识还得加深一下。从去年开始,也顺带在业余时间写了期权入门笔记系列,由于master课程和作业实在繁忙,没能继续,希望2022能继续填完这个坑。
开源项目今年也没多少contribution,主要还是由于学业繁忙。快年底的时候休假,把之前一直维护的两个项目 SKM 和 GoDNS 重构了一下代码结构,方便后续维护。另外,今年因为工作的原因,还重新拾起了多年未用的C++,学习了一些现代C++的新特性。业余时间还继续学习了一下Rust,不过确实因为精力有限,进展比较慢。希望2022能用Rust搞个开源软件的新坑……
因为疫情大致两年没回家了,希望来年疫情能消停一下。安心搬砖、读书、回家一次应该是明年的关键词吧……
]]>看了一下手上的吃灰的几个小鸡,发现竟然还有一台之前买的搬瓦工的小鸡,登录上去一看,好家伙,uptime
都659天了,稳啊! 好吧,就它了!接下来又是一波骚操作,从德国Contabo的主机rsync直接拷贝所有静态页面过来,速度还挺快。之前用的Nginx,这次打算再次换成Caddy Server,毕竟Caddy都出第二个版本了,自动更新SSL证书是比较能吸引我的地方。去官网研究了一下文档,还是决定用Docker来跑Caddy2,方便!
由于这个VPS只是用来放我的静态blog,所以也不用考虑多站点的问题,Caddyfile
的配置简单到发指:
1 | https://xiaozhou.net |
配置里面有两个请求转发,把所有非HTTPS的请求都转发到HTTPS。然后,再准备一个shell脚本,用来运行Caddy2:
1 | docker run -d --restart=always --name caddy -p 80:80 -p 443:443 \ |
容器直接暴露80端口和443端口到宿主机器即可。万事具备,只欠东风!去Cloudflare把域名解析过来,等生效,然后直接启动Caddy server。一切顺利,Caddy Server会在第一次启动的时候,自动申请LetsEncrypt的证书,后续也能自动renew。我懒啊,要的就是省心!
后续的流程,去GitHub改掉blog的自动部署流程,把部署的机器更新成现在的小鸡,打完收工!就这样,Blog又又又搬家了一次,从德国搬去了美国西海岸…… 唉……
]]>自从去年接触美股以及期权到现在,已经有小半年时间了。期权是一种非常有吸引力的金融衍生品,也是一种能通过时间给你带来不错回报的投资工具。在没有接触期权之前,我一直以为期权是一种风险很高的投资行为,其实不然。在金融市场中,风险总是伴随着对等的收益,只要你善于利用期权,不但能为你规避一些风险,还能带来收入。相比股票,期权更像是一个功能齐全的工具箱,合理的运用各种期权策略和组合,就能构建出丰富多样的投资组合,这一过程本身也极其具有创造性和趣味性,这也许就是期权更吸引我的地方。
最近读麦克米伦的《期权投资策略》,略有心得,准备写一系列的学习笔记,方便自己以后查阅,也方便对期权感兴趣的同学参考。
股票期权(Stock Option)是一种买卖双方在未来某一个时间以某种特定价格买入或者卖出某种特定标的资产(Underlying Asset)的权利。简单的说,股票期权就是一种权利。
期权是一种在期货基础上产生的金融衍生品,对于期货而言,标的资产是某种商品或者实物。而对于美股期权而言,这种买入或者卖出的标的资产,就是股票。
对于期权交易的买卖双方来说,期权更是一张合约,这张合约规定了买方的权利以及卖方所应当实现的义务。
以 InteracriveBrokers 上显示的期权为例,我们先来看看一张期权合约到底长什么样的:
1 | TSLA 16JUL21 585.0 C |
一张期权合约,在各大交易商的系统上显示的格式或许略有差别,但是任何一张期权合约都具有以下四个特征:
C
字母,彻底暴露了它是一张看涨期权。通常我们把看涨期权叫做Call Options,把看跌期权叫做Put Options。把以上四个要素综合起来,我们就能够理解一张期权合约所要表达的意义:TSLA 16JUL21 585.0 P
是指一张在到期日2021年7月16日,期权的买方能够以585.0美元一股的价格,买入100股标的股票为特斯拉的合约。在这里我们还要了解的一点就是:一张期权合约正好对应了100股的标的股票。
期权作为一张合约,是具有其自身价值的。当期权买家从期权卖家手中买入一张期权合约的时候,需要支付给期权的卖家一笔费用。这笔费用,就叫做权利金(Premium)。
按照几种不同的划分方式,我们可以把期权分为几种不同的类型:
而针对这两种类型,我们能有四种对应的操作:
在期权交易市场中,买方和卖方是互为对手方,对于同一张到期后的期权合约,盈利的要么是买方,要么是卖方。期权合约也规定了期权买家和卖家双方的 权利 和 义务。
和欧式期权不同,美式期权的买家可以不必等到期权到期的时候才行权,而是可以在期权的存续期内的任何时候行权。期权的行权与指派是由专门的 期权清算公司(Options Clearing Corporation, OCC)来完成的。在整个期权交易的生命周期当中,期权清算公司控制着所有期权的行权和指派。在期权指派的时候,期权的卖家会随机被选中。作为期权的卖家,必须服从指派并且必须交割股票,这也是前面提到的在期权交易过程中,卖家必须履行的义务。
]]>既然忘不了,那就不要去忘记,让时间去解决一切。
2020过得如此之快,以至于年底整理手机相册,才发现已经进入2021了。新加坡是一个四季如夏,一年四季气温都变化不大的地方,没有了四季明显的变换,更容易让人模糊了对时间的感知。
从年底网易云音乐生成的年度报告来看,黄老板的这首《Photograph》竟然是我本年度听的次数最多的一首歌……
听着黄老板的歌,整理着照片,顺便用照片回顾一下飞逝而去的2020。
00
年初临近春节的时候,还换了一把马币,跟同事一起去马来西亚的新山玩了一圈。大家一边畅玩,一边感叹:仅仅跟新加坡一桥之隔的城市,为何消费如此便宜? 谁知道,一场突如其来的疫情,改变了全世界人们的生活。
春节临近,请了年假回国,直飞的往返机票略贵,临时选择了去HK转机的路线(新加坡->HK->成都)。由于2019年在HK发生的各种众所周知的事情,订完机票过后突然有点后悔,甚至有点担心去HK转机的安全,后来又觉得退票麻烦,还是壮着胆子去了……
在HK中途转机大概有6小时左右的空余时间,正好持中国护照在HK转机,可以免签注入境HK。在跟机场工作人员反复确认入境是否安全后,得到的答复是:”Until now, it’s safe…”。好吧! 二话不说,兑换了点港币,直接入境HK去走一圈。
路过中环,去了佐敦道,经过重庆大厦,还去维港旁边走了一圈。
维港边上,还听一个说着四川话的大妈正在拍照,感觉一切风平浪静,好像什么都没发生过一样……
找了家路边的餐馆,吃了顿味道不错的烧鹅饭,微信上得知国内口罩已经卖断货了,顺便还在一家屈臣氏买了袋口罩,然后顺利登上了回国的航班。
01
回成都后,简单收拾了一下,又直接开车回到了老家。在老家待了一周,见到了从各地回老家的初中同学。
然后,终于又吃到了向往已久的老家的各种米粉……
后来疫情越发严重,提前改变计划,从老家绵竹回到了成都。回成都的高速路上,已经有各种检查站开始逐个给乘客量体温和登记了。
返回成都后,最关心的是航班信息。因为疫情,各种航班都有不同程度的改动或者是取消,很庆幸之前订的2.1号回坡的机票并没有受到太大影响。
回到新加坡之后,才知道因为疫情,各国入境政策不断收紧,2.2号过后很多从国内来新加坡的航班都取消了。
02
2月初刚回新加坡,大街上都没几个人戴口罩,网上也看到各类文章,开始质疑新加坡佛系抗疫。直到后来客工宿舍疫情爆发,新加坡开始了漫长的阻断措施。
回新加坡需要居家隔离两周,其间不断收到人力部打来的电话和发来的短信,要求每天间隔一定的时间,上报实时位置,其实这边并没有佛系抗疫,对于居家隔离人员的监督还是挺到位的。居家隔离结束后,回公司上班没多久,随着疫情不断严重,4月7号到6月1号,整个新加坡进入了接近两个月漫长的阻断阶段。
所有公司的员工,都被要求把电脑带回家,在家里远程工作。
作为一个IT搬砖码农,之前在国内的时候也读过DHH的那本《Remote》,幻想着有一天也能体验一下远程工作这种方式。但是没想到,这个愿望竟然在2020这一年,以这样的方式实现了。
阻断期间,各种商铺一律要求停止营业,所有人也被要求尽量减少外出,除非是出去购买食物和生活用品。要去餐馆吃大餐是不可能了,不过在家里也可以把火锅、烤肉、钵钵鸡、腊肉给安排上。
03
6月开始,终于又可以回公司上班了,公司的所有同事分为A/B两组,每周轮流回公司上班,这样可以随时保证只有一半的人员在公司,也避免了人员过于密集。这样的工作方式一直持续到年底,随着新加坡的抗疫宣布进入第三阶段,也总算取得了阶段性的胜利。最近的大新闻也要算疫苗开始正式注射了,真希望这波疫情能尽快过去。
PS: 五月算是在坡工作满一年,六月回公司领了个杯具:
来新加坡一年半有余,已经搬家过两次了。刚来的时候短租了两个月的HDB,后来因为接家里人过来,在HillView租了一个公寓,6月阻断措施结束过后,合同到期,7月再次搬家到Serangoon。对比住过的三个地方,印象最深刻的还是HillView了。HillView译为“山景”,是一个名副其实的接近大自然风景的地方,因为旁边就是新加坡号称第一高峰(海拔163.63米) 的自然保护区: Bukit Timah Nature Reserve。
我租的公寓,大概就在Bukit Timah山脚下的街对面。当初看上这套公寓,是因为这种老式的公寓空间宽敞,阳台外的风景很吸引人,经常还有各种不知名的鸟飞来阳台,朝客厅内打量,偶尔一两只大胆的鸟踱步进客厅也是常事。
公寓所在的小区也很原生态,满眼可见的绿色,去小区里走走,半路上碰上个蜥蜴也不要太惊讶!
自从来坡以后,喜欢上了徒步,2020一整年,也去徒步了很多次。次数最多的,要算之前HillView旁边的Bukit Timah,在疫情阻断期间,几乎每周都去。虽然号称海拔163.63米,但是20多分钟就能从山脚到山顶,40分钟能走个来回。
其次是麦里芝蓄水池 (MacRitchie Reservoir),在坡县徒步必去的一个地方,徒步的线路可以自由DIY,我每次去大概是走一圈13KM的线路。
10月跟同事组织了一次海岛游,因为疫情的缘故,新加坡旁边的马来和印尼都封锁了边境,要来一次跨国海岛游是不大可能了。这个时候,坐船去乌敏岛(Pulau Ubin)是个不错的选择,因为这个岛是新加坡的第二大外岛,不用出入境,直接坐船可登岛。去乌敏岛,可以欣赏岛上的多处历史古迹、椰子橡胶种植园、养鱼场,以及新加坡硕果仅存的甘榜 (kampong,源自马来语,意指 “传统村落”)。去岛上租个自行车四处骑着撒欢是个不错的选择!
又或者,在东部的海边栈道漫步,看看海景……
岛上猴子成群,野猪也随处可见,不过它们已经对游客司空见惯。
整个上半年保持了一定的更新和迭代速度,可是到了下半年,就开始忙了,大部分经历耗在了工作和其他事情上,今年整体的contributions只是是去年的一半。
很幸运的是,JetBrains的开源项目支持计划,又给续了一年的Free Subscription,感恩!
四月,阻断期间在家,受邀参加了一期《小人物》PodCast的录制,跟播主聊了一期在新加坡的生活和长期一直维护的开源项目。
另外,上半年还自学了一下React,同时用Next.js把之前做的一个网站重写了一遍,没想到到了年底,日访问量涨了不少。今年打算继续对网站进行一波优化,然后看看如何将流量变为收入。
上半年阻断期间,一直在家工作,继续维护从入职以来做的项目。下半年由于组织架构调整,之前做的项目被并入到其他组了,我也跟着换了项目组,随即开始忙起来。年底的四次大促:(9.9 / 10.10 / 11.11 / 12.12) 依旧很忙。
嗯,在2020下半年开始忙的时候,我还参加了了两场考试,都是为了填之前挖的坑。
CKAD考试
之前国内的工作一直跟Kubernetes打交道,想到要是能拿个相关认证也是极好的。2019年底圣诞的时候,剁手入了CKAD考试,结果到了2020才想起还有这么个考试。于是,从8月开始准备,9月参加在线考试,中途还买了个在线的课程便于复习。考试过程中有惊无险,总算是幸运的通过了,写了篇经验总结,方便要备考的同学准备:《CKAD认证备考经验分享》
雅思考试
9月考完CKAD,又马不停蹄的开始准备雅思。其实雅思也是之前来坡之后一直想准备的考试,从19年10月开始有了这个打算,后来从淘宝海淘了几本剑桥雅思真题,中途断断续续开始准备,其间一直不得要领,几乎放弃。转眼间就来到了2020,上半年也就断断续续做了一下阅读,听力,看了一遍雅思写作的书。直到准备完CKAD考试,狠心报了个国内在线的雅思强化班。
刚好年底又是公司的四次大促活动,工作上本来很忙,雅思课程的直播时间大部分是在白天上班时间,要不就是下午还没下班的时间。要看雅思课程的直播是没办法了,只能下班后连忙赶回家吃饭,然后看在线课程回放,一期期的跟完强化课程,然后跟着指导进行练习。我也不知道我哪来的精力,感觉上大学考英语四级的时候,我都没这么认真过……
雅思考试确实是有一些技巧和方法的,跟着强化班训练下来,感觉效果不错。终于在11月的时候,预约了年底圣诞节前一周的雅思机考,终于在圣诞节过后,拿到了成绩。
考试总结:
另外,选择机考雅思还是不错的,出分快,而且写作可以直接键盘输入,效率比手写高,完美规避了书写不好看影响分数的问题,最重要的是:码农打字速度有先天优势……
所以,雅思首考,口语不尽人意,2021,要不再刷一次雅思A类?
2020年底准备考试,阅读的时间减少了,如果新年有什么计划的话,大概是希望能继续保持阅读和持续学习的习惯。之前从国内海淘的大屏幕电子阅读器,也吃灰了一段时间,得重新用起来。
另外,希望这场疫情能尽快结束,真想再回一次老家……
]]>最近通过了CKAD认证考试,也算是填了一个去年底挖的坑。这一切要源于去年底圣诞的时候,Linux Fundation的认证考试打折,原价300刀的CKAD考试,打折下来只用花255刀,忍不住剁手了。
此认证费用,包括一年有效期内任意时间预约考试的机会,以及一次免费重考的机会。
本来想着有一年的时间备考和准备,买了之后拖延症又犯了,就一直没管它。直到最近,突然收到了Linux Fundation的邮件,提醒认证考试年底就要过期了,才想起来之前竟然还买了个这个认证,突然开始慌起来……
二话不说,立马着手开始备考,由于之前工作中也算用过Kubernetes,对其核心概念也有一个大致了解,从8月到现在,大概花了一个多月的时间来准备。最后,准备总算没白费,通过了这个认证考试。
本篇blog就介绍和分享一下CKAD认证备考的一些经验。
总的来说,CKA和CKAD是CNCF和Linux基金会联合推出的两个Kubernetes考试认证:
CKA: Kubernetes管理员认证(CKA)旨在确保认证持有者具备履行Kubernetes管理员职责的技能,知识和能力。 CKA认证可帮助经过认证的管理员在就业市场中快速建立自己的信誉和价值,并能帮助公司更快地雇用高质量的团队来支持他们的发展。
CKAD: Kubernetes应用程序开发人员认证(CKAD)旨在确保CKAD具备履行Kubernetes应用程序开发人员职责的技能,知识和能力。 经过认证的Kubernetes Application Developer可以定义应用程序资源并使用核心原语来构建,监视和排除Kubernetes中可伸缩应用程序和工具的故障。
就两种考试的定位而言,CKA更偏运维一些,CKAD更面向开发人员一些,所以我选择了CKAD认证。
CKA和CKAD的考试范围和比重,是直接公布在认证官网的。CKAD的考试范围和比重如下:
跟CKA的考试时间不一样,相比CKA的3小时时间,CKAD只有2小时。CKAD考试题目总共19道题,总分100分,66以上就算是通过了认证。
虽然之前工作中也有用到Kubernetes,为了让知识点覆盖更全面,我还订阅了KodeKloud的两个课程:
他们家的课程是订阅制的,最近疫情期间也打折,所以学完之后可以取消订阅。两个课程都带了在线的动手实验室,学过一个知识点过后,就可以立马去动手实验室操作,用以对知识的巩固加深,还不错哦!
最近还发现他们也把这个课程放到了Udemy,可以一次性购买: https://www.udemy.com/course/certified-kubernetes-application-developer/ 相对于订阅更划算一些。
和其他的一些认证考试不一样的地方在于:CKA和CKAD非常注重动手操作。考试题目并不是常规考试的判断题,选择题,问答题。所谓考试实际上就是在他们官方提供的Kubernetes环境中进行实际操作。所以备考的时候,需要对 kubectl
的一系列命令了如指掌。总的来说,多动手操作实验才是通过这门认证的最佳途径。
在GitHub上,有人为这个考试专门准备了一个动手实验题库: https://github.com/dgkanatsios/CKAD-exercises
在考试之前,我大概把里面每一个题目动手操作了5-6遍。最后要达到的目标,就是看到题目之后,能想到应该如何在Kubernetes上操作并实现题目要求,能做到了然于心才是最佳状态。
关于实验环境,推荐在本机安装运行 minikube 或者 k3s ,他们都是轻量级的Kubernetes实现,用来动手操作实验题目还是不错的。如果你实在懒得搭建本机环境,用网上现成的也有:
在操作一些比较复杂题目的时候,你甚至可以用上面那个”Play with Kubernetes”提供的免费资源,自己搭建一个Kubernetes集群进行演练。
在完成备考过后,可以真正的预约考试了。听说最近Linux Fundation还专门为中国准备了国内现场考点,国内考试可以去专门的考点上机操作完成。另外的方式就是在家或者在一个安静的地方在线参加考试。
对于在线考试,有考前检查和一系列的规定,可以去官网逐一阅读一下。大致说来,规定如下:
考试形式: 在线监控,需要共享桌面和摄像头。如果你的电脑外接了显示器,两个屏幕都得共享。另外,考试中只允许你的浏览器开两个窗口,一个是考试的界面,另外一个就是Kubernetes的官方文档界面。在考试中,遇到不会的配置项,是允许你去官方文档中查询的。
考试环境: 在一个密闭空间,例如书房、卧室、会议室等,电脑屏幕不能对着窗户,房间里除了考生不能存在第二个人,考试的桌面不能放其它东西,水杯也不行(透明的玻璃杯是可以的)。
考试时间及题目: CKA-3小时-24道题、CKAD-2小时-19道题,均为动手操作题。
选择考试时间: 由于监考官在美国,所以考试的时候别忘了选择一个跟你所在的时区最匹配的时间。我选择在了我所在时区的周五晚上9:30,考完大概晚上11:30。
电脑要求: Windows的电脑和Mac OS的电脑都可以。在考试前可以在这里 WebDelivery Compatibility Check 对你的电脑进行兼容性检测。
关于时间管理
前面有介绍,CKAD的题目是19道,时间2小时,平均一道题能花的时间是6分钟左右,所以时间管理是非常重要的。19道题目的难度不一,有的简单有的复杂。对于一道题目,先看题,如果觉得没头绪,可以先标记这道题,直接跳过去做后面的题目。等19道题大致做过一遍之后,再回来看标记过的不会的题目。
题目权重与优先级
每一道考试题目上会标注这道题目所占的分数比重,总分100分的题目,达到66分可以通过考试。所以,结合与时间管理的策略,整个考试可以并不按照题目的顺序来做题。我就是在考试开始的时候,直接先快速把19道题过一遍,把分值权重较高的题目先标记出来,优先做这些分值较高的题目,然后再做剩余的题目。
注意考试场景的切换
所有的19道题目并不是在一个Kubernetes环境中设立的,这就涉及到需要在做题之前,先切换到对应Kubernetes的Context。每道题目前都有对Context进行说明和切换的要求,在做题前特别留意一下,确保是在正确的Kubernetes环境中操作即可
考试界面语言的选择
由于是一个针对全世界开发者的考试,官方的考试界面也提供了多语言支持。为了避免翻译的偏差对题目的影响,我还是没选中文,选了英文。
监考官的互动
考试的整个过程,除了共享你所有的桌面,还得开摄像头,也就是你只能被监考官通过视频进行监督,而你是看不到监考官长啥样的。整个过程与监考官的互动,是在一个Web弹出的聊天窗口中进行的。包括考前注意事项说明,以及考试过程中遇到问题,都可以通过这个聊天窗口跟考官互动。考官除了回答你跟考试过程相关的问题,还会在考试过程中进行随机抽查,比如要求你在考试过程中,把双手或者桌面通过摄像头给他看看,确保你没有作弊……
为 kubectl
设置 alias
考试的整个过程都是在Kubernetes中进行操作,所以 kubectl
这个命令输入的频率那是相当高的。反复输入这么长的命令,实在是有点浪费时间,要知道在这2小时的考试过程中,时间就是一切。所以在开始考试之初,我就在考试环境中为 kubectl
设置了alias。比如 alias kc=kubectl
或者 alias k=kubectl
。后面所有输入 kubectl
的地方,都可以用 kc
或者 k
替代,能为你节省不少时间。
熟练操作kubectl命令kubectl
能操作和创建的资源有很多,在考试过程中,能不用YAML来创建资源就尽量不用。因为编辑YAML是比较花时间的,还容易出错。一般做法是用kubectl命令创建资源,通过dry-run的方式,先生成YAML文件模版,再根据题目对这个YAML进行改动。这里有一份 kubectl Cheat Sheet 你当然不能错过。
熟悉各种资源的简化名称
Kubernetes本身也有提供一些简化的资源名称,比如namespace
可以简化为 ns
, deployment
可以简化为 deploy
,pod
可以简化为 po
。了解这些简化的资源名称,也能为你省掉不少的时间,在备考的过程中,可以记忆下来这些简化的命令。Kubernetes的官方文档,列出了所有资源的简化名称,可以参考: https://kubernetes.io/docs/reference/kubectl/overview/#resource-types
由于我的网络问题,在考试中竟然断网了两三次 (具体表现就是终端卡住不动了,无法输入任何命令),不得不重新连接进入考试界面,浪费掉了一些时间。当时的心情,真有种万念俱灰的感觉。所以,考前最好再检查一下你的网络环境。最后一道题本来打算做,临交卷还有几分钟的时候,又卡住了。我索性放弃了,直接交卷……
考试结束之后,官方保证会在36小时之后出考试结果,结果会Email到你考试时候注册的邮箱。虽然考试过程中掉线了两三次,不过还是比较幸运,周五晚上的考试,周日就有了结果,通过了。我在想,要是不断网,再给我多来几分钟,我应该能上90分吧……
希望此篇blog对备考CKAD的同学有所帮助。
]]>恰巧最近又买了个德国的高配置VPS,访问速度还不错。于是心血来潮,在上面折腾了开发环境,还装了Doom Emacs,打算实现用任何一台低配置的瘦客户端SSH登录到云端VPS, (或者在Chrome中用SSH扩展连接过去) 达成用Emacs在 “云端” 写代码的一个小目标……
其实,这样的目标已经实现了,美中不足的地方在于: 由于是SSH连接过去,每次退出SSH会话的时候还得退出Emacs,下次启动Emacs又得花掉人生中宝贵的四秒钟时间,实在是不甘心。正好,Emacs从26.1开始,已经支持使用systemd管理Emacs服务端Daemon的功能。这样一来,我们只需要在VPS上后台使用Systemd启动一个Emacs的服务端,每次SSH登录的时候,用Emacs客户端启动,就能实现秒开Emacs的效果了。
在Emacs安装完成后,会在 /usr/lib/systemd/user
中创建一个Systemd的配置文件,名为: emacs.service
。跟其他的Systemd服务一样,我们只需启动这个服务即可。
启动并运行Emacs服务端程序:
1 | systemctl --user start emacs.service |
为了下次随机器启动时,自动启动Emacs服务端程序,我们还可以运行:
1 | systemctl --user enable emacs.service |
随后,查看Emacs服务运行情况:
服务端启动妥当,最后,只需要在SSH登录后,运行Emacs客户端程序,即可秒开Emacs了,又为人生中省下了不少个4秒钟:
1 | emacsclient -t |
支持的操作系统包括:
正好,我基于Hexo的blog仓库也是放在GitHub,这样的虚拟环境配置用来持续构建和自动部署blog再合适不过了,还可以避免每次本地部署重复输入命令和CPU风扇狂转的困扰。
我的blog是基于Hexo的,所有的Markdown文件托管在GitHub的私有仓库中,另外,构建生成的静态页面是托管在另外一台阿里云国际版的VPS主机上。
利用GitHub Actions需要达成的目标就是:每次提交或者更改新的blog文章,触发GitHub Actions进行构建,构建出静态页面后,直接发布到VPS上。
首先需要生成一对新的SSH密钥,注意这对密钥不能有密码保护,因为整个构建过程应该是自动的,有密码保护的密钥对会中断构建过程。
将这对密钥的public key添加到托管blog的VPS对应账户的~/.ssh/authorized_keys
中。然后在Blog仓库的Settings->Secrets
里添加刚刚生成的私钥,名称为BLOG_DEPLOY_KEY
。这样做的目地是保护私钥不被泄漏,这样在GitHub Actions中不用以明文的方式填入私钥。
以我的blog为例,在blog仓库的Actions选项卡下点击新建workflow,填入如下配置:
1 | name: HEXO CI |
构建过程中有几个要点:
webfactory/ssh-agent
用来缓存部署blog所用到的私钥~/.ssh/known_hosts
中,这样可以避免部署时候要求用户输入确认的步骤。一切准备就绪,为了测试blog的自动构建和发布,可以尝试新建或者修改一篇blog post,然后在仓库的Actions中,就能看到自动触发的构建流程了。
]]>Contabo提供的VPS套餐分为两种:SSD和HDD混合系统,另外一种就是纯SSD系统。在硬件配置上,Contabo一点都不吝啬,光是配置表看下来,你就能心动一回了。
需要注意的是: Contabo的VPS第一次购买,需要安装费,安装费只需要付一次,后面续费就没有安装费了。安装费用为月付4.99欧,季付3.99欧,半年付的话就是2.49欧。一次性购买年付套餐,没有安装费。
整体来说,SSD和HDD混合VPS,性价比最高的一款机型是:
机型: VPS 300
CPU: 2核
内存: 4GB
硬盘: 300GB
带宽: 100Mbps
流量:无限流量
IPv4: 1个
价格/月: 3.99欧元
点击购买
接着是100%纯SSD配置:
CPU | 内存 | 硬盘 | 带宽 | 流量 | IPv4 | 价格/月 | 购买链接 |
---|---|---|---|---|---|---|---|
4核 | 8GB | 200GB | 200Mbps | 无限流量 | 1个 | 4.99欧 | 购买链接 |
6核 | 16GB | 400GB | 400Mbps | 无限流量 | 1个 | 8.99欧 | 购买链接 |
8核 | 30GB | 800GB | 600Mbps | 无限流量 | 1个 | 14.99欧 | 购买链接 |
10核 | 60GB | 1600GB | 1Gbps | 无限流量 | 1个 | 26.99欧 | 购买链接 |
这种配置和价格,实话说,在其他国外的服务商那里还很少看到,多核CPU+大内存+大硬盘,绝对是亮点!
本次测试款为纯SSD的机型,月付4.99欧,4核8G内存,200GB SSD硬盘。
这里看来速度并不是很高,但是可以给客服发邮件,要求提速SSD的读写性能,经过邮件沟通后,SSD读写速度顺利提速到500MB+/s,不错不错!
此机型位于德国纽伦堡机房
Contabo的VPS性价比不错,用少于其他主机商套餐的价格,就能买到相当不错的配置。无限流量和大硬盘都是亮点。不过机器位于德国,国内访问并不是很快,当然比不上CN2和CN2 GIA线路。用来做站的话可以搭配CloudFlare CDN使用。另外,用来作为自己的云端网盘和下载机器也是很不错的。站长已经把这台机器配置为我的云端开发环境,多核CPU和大内存,用来远程编译程序和构建Docker镜像,很是方便,能让我本机减少不少的运行负担。
另外,Contabo并不支持国内支付宝和微信,但是支持Paypal支付也是很方便的。
]]>Verified
的标记,它表示这次提交确实是出自你自己,而不是冒用你帐号的人。目前GitHub和GitLab均已经支持此功能。通过GPG签名的Git Commit更加可信,就算你的SSH private key泄漏,别人也没办法仿冒你的身份对Git Commit进行签名,或者更改你已经签名的Git Commit。
Mac OS下安装GPG,可以直接用 homebrew
:
1 | brew install gpg |
跟SSH的Key一样,GPG也是采用非对称加密方式。生成一对GPG密钥,我们需要使用命令:
1 | gpg --full-generate-key |
生成类似SSH Key,需要你输入用户名,邮箱,或者是密钥的保护密码。生成完成后,我们可以通过如下命令,查看系统中已经生成的GPG Key:
1 | gpg -K |
系统中的每对GPG key,均有一串类似十六进制的Key ID。由于我的系统中已经有两对GPG Key,所以看起来是这样的:
GitHub的帐号设置中,有专门添加SSH Key和GPG Key的地方,GitLab也类似。首先,我们应当使用命令把GPG Key的公钥导出和显示出来:
1 | gpg --armor --export <Key ID> |
接着,我们需要把 BEGIN
和 END
之间的内容复制,添加到GitHub,成功之后,GitHub就能显示我们的GPG Key了:
接下来,我们需要设置本地的Git:
1 | git config --global user.signingkey <Key ID> |
上面命令的目地,是设置在Git Commit签名的时候所用到的Key,并全局开启GPG签名,然后还指定了签名使用用到的程序 pgp
。如果你使用SKM,你需要在对应SSH Key的目录中,创建一个HOOK 文件,然后复制上面的命令,这样,在切换SSH Key的时候,也能自动生效设置了。
最后,就是验证提交和测试了,如果一切进展顺利,你就能在GitHub或者GitLab的提交上,看到右边绿色的 Verified
标记了。
很幸运的是,Emacs的Git插件magit 也完美支持GPG签名功能,这样在Emacs中提交的时候,也可以直接用GPG签名了,nice!
]]>通常能想到的办法,就是在国内购买一个云服务器,搭个代理连接回去,这样,能得到一个国内的IP,什么问题都迎刃而解了。不过,附加成本就是还得月供一个国内的云服务器。和国外的服务器相比,国内的服务器带宽小太多,跟国外的完全无法比拟,而且价格也不菲,性价比巨低。
最近,在网上看到一个通过自签名证书实现解锁网易云音乐海外限制的骚操作,手动操作了一波,完美绕过了网易云音乐的限制,我的黑胶VIP又可以派上用场了。下面简单介绍一下通过自签发证书解锁网易云音乐限制的操作。
大致的原理,就是通过自签发证书,并劫持本地的DNS解析,强行将网易云音乐的客户端请求导向你自己搭的服务器,服务器上通过Nginx反向代理,并设置为请求增加X-Real-IP
参数,设置一个国内的IP地址,再把请求反向代理到真正的网易云音乐官网,从而达到解锁海外限制的目地。总的说来,你的服务器作为中间人,劫持网易云客户端的请求,并把请求伪装成从国内发起,从而达到欺骗服务端地域验证的效果。
解锁的大致操作如下:
我们需要先自签发一个根证书,再用这个根证书签发一个有效期为500天的服务端证书。如果本机装有openssl
,可以直接开始操作:
1 | # 创建CA私钥 |
接着,再生成一个文本文件: v3.ext
,内容如下:
1 | authorityKeyIdentifier=keyid,issuer |
最后,使用根证书签发服务端证书:
1 | # 生成并签名服务端证书 |
以MacOS为例,接着,我们打开Mac OS上的 Keychain Access
,导入根证书 rootCA.pem
,并设置证书的信任设置:
导入成功过后,用浏览器访问网易云音乐官网,会发现跟平常无异,不过点开证书信息后,会发现证书是我们自己签发的证书,嗯,我们成功劫持了DNS解析,并骗过了本机浏览器。
这一步,是通过 hosts
文件,强行将 music.163.com
解析到我们自己的服务器去,这一招俗称”DNS劫持”。使用管理员权限,在本机 /etc/hosts
文件添加如下内容:
1 | 11.22.33.44 music.163.com |
其中 11.22.33.44
也就是我们自己的服务器的公网IP地址了。
最后,把第一步用根证书签发的服务端证书,拷贝到我们自己的服务器上,并用Nginx配置反向代理:
1 | server { |
配置中将所有请求直接反向代理到真正的 music.163.com
,这里的 X-Real-IP
和 X-Forwarded-For
用到了一个搜到的国内IP,其实只要任意一个国内的IP就行,这里我找了一个成都电信的IP:
万事具备,打开网易云音乐Mac OS客户端,发现之前因为限制被灰掉的歌曲现在可以播放了…… 另外,只要打开过一下Mac OS客户端,估计网易的服务端验证之后缓存了状态,发现手机端APP打开过后,灰掉的歌曲也能播放了。其实手机端也能通过劫持DNS的方式来实现解锁,不过我没有具体试过。
嗯,此方法完美拯救了我的黑胶VIP……
]]>最近我的开源项目GoDNS就有用户提了个issue,GoDNS的Docker镜像在树莓派平台下运行失败,从错误提示来看,就知道是镜像的问题。之前只考虑了镜像在Linux或者Mac OS下运行,完全没考虑兼容树莓派平台。解决的办法,最直接的就是在树莓派中去打包和构建镜像。不过,我手头又没有树莓派,难道要去买一个?这成本也有点太高了点。
幸好,新版的Docker从版本19.03后已经开始支持一个新的命令行工具,叫做buildx
,目地就是为了解决我们在一个平台下,一次性构建出多个平台可用镜像的需求。buildx
是一个基于命令行的Docker扩展插件。在MacOS下,如果你使用桌面版的Docker,非常容易启用这个插件,只需要打开Experimental features
这个选项即可。
启用这个新特性之后,需要重启Docker,然后在命令行进行验证:
1 | > docker buildx version |
看到显示的版本号,表示buildx已经启用成功。默认情况下,Docker不会启用多平台架构的构建器,需要我们自己创建一个新的构建器,并激活和启动这个新的构建器:
1 | > docker buildx create --name mybuilder |
创建完成后,启用新的构建器:
1 | > docker buildx inspect mybuilder --bootstrap |
最后,再次验证构建器是否正常运行:
1 | docker buildx ls |
在此可以看到,新的构建器已经支持其他多种平台。接下来,我们需要实战操作一下,用新的构建器,构建支持多系统平台的多个镜像。以我的项目GoDNS为例,Dockerfile如下:
1 | FROM golang:alpine AS builder |
Dockerfile使用多步构建并打包镜像。随后,我们使用docker buildx一次性构建出多CPU平台的镜像,并push到DockerHub:
1 | docker buildx build --platform linux/amd64,linux/386,linux/arm64,linux/arm/v7 -t timothyye/godns:latest . --push |
在DockerHub上,我们能看到同一个版本的镜像下,用digest区分出了基于不同平台版本的镜像:
而当不同系统的用户,通过docker pull命令去拉取镜像的时候,docker会自动pull跟当前系统所匹配的镜像。这样一来,我们再也不用为构建不同CPU架构的镜像而犯愁了。
]]>自从上次从Vim切换到Spacemacs之后, Spacemacs已经能满足我日常开发需求。不过,现在我找到了更加适合我的Emacs配置: Doom Emacs。
Spacemacs的配置已经日益复杂,默认配置加载的插件较多,启动Spacemacs往往会耗费更 多的时间。对于我现在的配置,启动花上20多30秒是常有的事情,毕竟Emacs是一个伪装 成编辑器的操作系统。
Spacemacs的版本更新频率变慢。截至目前,v0.200.13版本的发布还是在2018年1月。
虽然提到Spacemacs面临的一些不足,但Spacemacs仍然是从Vim切换到Emacs的一个非常不错 的选择。
Doom Emacs跟Spacemacs类似,依然是一个基于Emacs+Evil的配置,相比Spacemacs,Doom Emacs吸引我的的理由在于:
切换到Doom Emacs一个多月有余,目前整体使用效果非常满意,顺便分享一下个人Doom Emacs配置,供参考:https://github.com/TimothyYe/doom-emacs
]]>