2024-04-04 08:00:00
Flyway 是一款开源的,基于 Java 实现的数据库内容变更控制工具。它提供了 CLI、Java API、Maven/Gradle Plugin 等多种方式方便开发人员将数据库的表结构改动、内容变动以可追溯的代码形式进行管理和部署。Flyway 支持包括大多数主流的关系型数据库:MySQL、SQL Server、Oracle Database、PostgreSQL、SQLite、TiDB、MariaDB 等,对于 MongoDB 的支持尚在预览阶段(更推荐 Mongock),同类竞品有 Liquibase,但 Liquibase 的使用相比 Flyway 更复杂,有额外的概念作为学习成本。
遵循 SpringBoot 广为人知的约定大于配置,Spring 官方提供了 Flyway 的自动配置实现。
全限定类名 org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
会在检测到以下几种状态下实现自动配置:
Flyway
类 (即 org.flywaydb.core.Flyway
)FlywayDataSourceCondition
类中的 Bean 或 Properties 条件,其实就是 DataSource
和数据库 URL 等连接配置存在spring.flyway.enabled=true
属性配置对于 MySQL 而言,除了必须的 JDBC 依赖之外,需要引入 Flyway 自身依赖,SpringBoot 已经在 pom 中声明过版本号,因此此处无需额外定义版本字段:
具体使用有两种方式:
Flyway 会基于默认配置的文件夹路径 classpath:db/migration 发现版本变更文件或实现类。
VERSION
可使用包含小数点、下划线的字符串版本,DESCRIPTION
则是简单的描述文本。中间的分割符是两个下划线。可通过 spring.flyway 配置自定义前后缀。
V{VERSION}__{DESCRIPTION}.sql
文件需要存放在 src/main/resources/db/migration 文件夹下; Java 类则需要定义包名 db.migration
并且继承父类 BaseJavaMigration
,重写如下方法:
按照官方的写法,我们就可以在 migrate()
方法中去用 Java 代码实现数据库内容版本变动了,但是这里有一个缺点,那就是我们在 Spring 环境下无法针对 V{VERSION}__{DESCRIPTION}.java
进行依赖注入,只能尝试通过原生 JDBC 的方式、静态方法等去写比较朴素的 SQL 实现,而不能充分成分利用现有的 DAO 接口来做业务数据的更新。因此需要自定义配置 Flyway。
由于 Flyway 的配置基于这种手动配置,因此需要在 SpringBoot 启动类上排除原有的自动配置类,以防止自动配置存在加载冲突。
最后,定义一个 Component Bean 去实现 BaseJavaMigration
:
这样,所有的迁移类都可以方便地使用依赖注入来愉快地做 CRUD 了。经过实践,Flyway 会在数据库连接配置后、HTTP 服务暴露(也就是 Servlet 容器监听端口)前同步地执行完所有迁移,因此无需担心执行时机影响线上服务。
2023-12-03 08:00:00
最近看到很多博主在 App Defaults 中分享了他们的默认应用程序。以下是我自己的:
[email protected]
。2023-06-11 08:00:00
NET:
Cloudflare 是一家在业内比较知名的 CDN 服务商,提供包含 DNS 解析、WAF 防火墙、CDN 加速、DDoS 防护,后续推出了一系列比较方便开发人员的许多功能:Cloudflare Workers、KV、Zero Trust Tunnel、WARP... 一切都是为了提供一个安全、快速的互联网环境。如果说 Vercel 给前端开发人员提供了基础设施,那 Cloudflare 则为数千万网站后端流量提供了基础设施。
今年年初,Cloudflare 缓解了破纪录的 7100 万个请求/秒的 DDoS 攻击。
三年前,我把我的博客从 WordPress 迁移到了 Vercel,老用户们或许都记得,那时候的域名可还都是 *.now.sh
。初次使用 Vercel 的感受可以说是如获至宝,在今天看来可能显得很幼稚了——如果不是自己知道如何从 0 到 1 申请域名去部署一个全栈的 Web 项目的话,很难理解 Vercel 这种平台背后做了哪些复杂工作。这是我所理解的美国公司的一贯作法 —— 他们总是把庞大、精密、复杂的技术或基础设施掩盖在简约、优雅的产品外观之下。而我每次都会保持警惕和观察力,如果换我做,我怎么来实现?后来我便学习起了 Kubernetes。
说回 Cloudflare。从我第一次买域名(2015 年)一直到现在,我全部把解析权安排在 Cloudflare 上。归因于 Vercel 的一次网络问题,国内的网络受某种不可抗力在 2021 年的时候突然无法访问 Vercel 的部分域名了,尽管我的博客每天仅有为数不多的访客,但作为一个以中文为主的博客,还是有必要保持国内网络访问的畅通。根据官方提供的新的 CNAME 值,我在 Cloudflare 上更换了解析记录,也算顺利解决。也就在当时,我才注意到 DNS Records 控制台之前一个一直忽视的选项:
我好奇地把它启用了,即从灰色 DNS only
换成了这个橙色的 Proxied
,那时我还没意识到,其实 Cloudflare 从那刻起已经完全接管了我的网站的全部流量并进行任播 (Anycast);换句话说,我在现有的 Vercel CDN 之上,又套了一层 Cloudflare CDN。是的,这迎来了两个问题:
不可能没人像我这样做吧?事实上,Vercel 并不推荐在其基础上使用另一层 CDN。后续我也依次找寻到了解决方案:对于 CNAME 来说,Vercel 会定时访问网站跟路径下的 .well-known
路径下的资源来识别包括 CNAME、HTTPS 证书这类配置验证网站控制权信息,因此我们可以直接在 Cloudflare 的 WAF 中,把这类路径作为白名单让 WAF 跳过其他安全规则直接放行。对于客户端 IP,可以参考 Available Managed Transforms,将一些客户端原始信息置于请求头中。缓存时间方面,还是要熟悉 MDN 上的一些标准 HTTP 协商协议,细粒度地对不同资源设置不同的 TTL,尽可能发挥 Cloudflare CDN 和浏览器自身缓存的优势 - 一个博客而已,是不是有点大炮打蚊子了?
Cloudflare 的整体防御从 L3 到 L7,遍布了所有能覆盖的防御范围。一个请求进入 Cloudflare 所代理的网站流量会经历顺序由上到下:
Traffic Sequence in Cloudflare |
---|
DDoS |
URL Rewrites |
Page Rules |
Origin Rules |
Cache Rules |
Configuration Rules |
Redirect Rules |
IP Access Rules |
Bots |
WAF |
Header Modification |
Access |
Workers |
这些流量经过内部的层层筛选,以及我们自己定义的一些 Rule,最终反代到源站。因此,在决定使用任何 CDN 产品的时候,有必要将服务端源站 IP 妥善隐藏,尽可能不暴露任何历史解析值,否则一切防御都是徒劳。如果源站 IP 已经暴露,只能及时更换新的地址。在新的规则录入好后,Cloudflare 的全球网络会立刻应用规则并实时生效,这里或多或少要归功于大佬 agentzh 章亦春 开源的高性能网关 OpenResty。
我们可以将一个个「函数」部署在公有云的「边缘计算节点」之上,并暴露 Socket 给这些节点上的函数,来实现无需忽略底层服务器,直接部署可随意伸缩的 HTTP 服务的能力。当然,这要求这些函数尽可能无状态。在没有任何请求,闲置一定时间时,这些函数进程会直接消失以腾出计算资源,直到下次事件驱动它们迅速重新启动并继续提供服务。这便是老生常谈的 Serverless。
初次了解 Serverless 也是非常惊讶。AWS Lambda 竟能将 function 如此商业化 (FaaS),Vercel 在此之上也做到了开箱即用。借助 Cloudflare 现有的数据中心,Cloudflare 也推出他们的 Serverless 解决方案 - Cloudflare Workers。不同的是,Cloudflare Workers 相比原始的 Vercel Serverless Function 而言能够做 Server Sent Event、WebSocket 这类支持长连接的请求。尽管后续 Vercel Edge Function 也能实现,但是它能支持的 Node.js Module 实在太少了。(作者注:后来我才了解到 Vercel Edge Function 其实构建于 Cloudflare Workers 之上)
前不久,Cloudflare 开源了 Workers 运行时 workerd。
Cloudflare Workers 有许多应用场景。比如实现一个简单的短 URL 重定向服务、GitHub Proxy、以及一大堆各自实现的 ChatGPT API Proxy...方便了太多国内用户。
Node.js 作者 Ryan Dahl 这几年给 JavaScript 写的另一个全新运行时 Deno 也有类似的 Serverless 服务,体验也很友好,同样支持 Web Standard API。
为了实现 Serverless 的更多数据持久化功能,他们也各自推出了自家的 KV 存储实现服务,或者说是 Serverless 数据库。
再来谈谈技术方面的一些进展。很多读者都知道 Server Name Indication(服务器名称指示,SNI)的存在,它是 TLS/SSL 协议在最初的 Client Hello 阶段由客户端发往服务端的一个字段,内容是网站的主机名或域名。引用 Cloudflare 的形象解释:
SNI 有点像邮寄包裹到公寓楼而不是独栋房子。将邮件邮寄到某人的独栋房子时,仅街道地址就足以将包裹发送给收件人。但是,当包裹进入公寓楼时,除了街道地址外,还需要公寓号码。否则,包裹可能无法送达收件人或根本无法交付。许多 Web 服务器更像是公寓大楼而不是独栋房子:它们承载多个域名,因此仅 IP 地址不足以指示用户尝试访问哪个域名.....当多个网站托管在一台服务器上并共享一个 IP 地址,并且每个网站都有自己的 SSL 证书,在客户端设备尝试安全地连接到其中一个网站时,服务器可能不知道显示哪个 SSL 证书。这是因为 SSL/TLS 握手发生在客户端设备通过 HTTP 指示连接到某个网站之前。
有点类似于 HTTP 协议中的 Host
请求头(如果在同一台服务器上用 Nginx 配置过多个虚拟主机应该都熟悉),但是 SNI 是作用在 L4,而且在 TCP 握手前完成。起初它并不是 TLS 协议的一部分,最早在 2003 年作为扩展字段增加到 TLS 协议中 (RFC 6066)。现代浏览器等客户端都早已支持这个字段。我们会发现一个细节问题,对基于同一 CDN 的网站的 HTTPS 请求,我们传入的 TLS SNI
和 HTTP Header Host
会有不一致的情况,在不严格校验 SNI 的情况下,这类请求有可能被路由到 Host
所定义的主机上,本质也就无视了 SNI
,因此对于某些防火墙来说,由于它们能通过 SNI 来侦测到用户所请求的 HTTPS 站点,它们无法得到后续 TLS 握手后的 HTTP 报文内容,在客户端更换了 Header Host
后,实际返回的 HTTP 报文内容其实已被调包 —— 这种攻击方式,或者说叫伪装方式被称为域前置(Domain Fronting)技术。Cloudflare、CloudFront 都会校验二者的一致性返回 403,但依然有部分 CDN 对这一做法采取保留,比如 Fastly。
我们可以用 WireShack 抓包获取到 SNI
字段。应用这个过滤条件 ssl.handshake.extensions_server_name
,尝试抓包发送一次 TLS 请求
可以从结果看出,SNI 确实使用了明文进行传输,这就导致了前文提到的一个问题 - 就算经过 TLS/HTTPS 加密的流量,仍然明文地暴露了我们在访问的域名。「这又如何?DNS 不也暴露了嘛?」好问题 - DoH 解决了 DNS 请求的明文风险 (RFC 8484)。因此,实际上 TLS 目前唯一在数据上有泄密风险的就只有这个字段了。Cloudflare 先后搬出了两个解决方案:ESNI 以及 ECH。
我们可以使用 Chrome 的开关 chrome://flags/#encrypted-client-hello
来开启浏览器 ECH 的客户端支持。通过 Chrome DevTool 的 Security Tab 能够查看 HTTPS 流量的安全性信息。我们可以通过这个链接来测试客户端对这个方案的支持情况,当然,这些需要服务端做相应的配置才能完全启用。话题就此结束,我不能再细说了。
Updated:2023 年 9 月底,Cloudflare 宣布向所有基于 TLS 1.3 的代理站点启用 ECH,目前默认全部启用且改选项不可关闭。
利用 Client Hello 来做安全保护的另一个实践是 TLS 客户端指纹: JA3 & JA3S。这一设计灵感来源于信息安全专家 Lee Brotherston 的研究 TLS fingerprinting。
具体的工程实践可以参考 Salesforce 开源的 JA3.
简而言之,TLS 握手过程中客户端发送的字节数组,也就是 Client Hello 阶段的一些字段和扩展名,通过固定方式拼接,基于摘要 MD5 来生成一个唯一的字符串,称为 JA3 指纹。不同的浏览器或 TLS 客户端有不同指纹。在大量的数据采样中,Cloudflare 就能够基于此数据 (JA3 & JA3S,后者包含了 Server Hello 阶段的服务端指纹) 统计出哪些请求来自于僵尸网络、机器人爬虫、Python 库还是正常用户的浏览器、或者手机访问。这也就解释了很多同学写爬虫时,利用 HTTP 协议更换 User-Agent
这一请求头无效的情况,因为 Cloudflare 的防御处在更底层的 L4 TLS 阶段。ChatGPT 的 Web 端也部署了 Cloudflare 的 TLS JA3 指纹鉴定 WAF (仅限 Enterprise 账户);GitHub 上我也找到了相关的代码实现通过更换 TLS Client 的方式来绕过这一防御。对于多数人来说,这已经有很大的防爬门槛了;而且 Cloudflare 可以随时更换 WAF 策略让旧的指纹失效。
JA3 由来自 salesforce 的三位工程师共同实现:John Althouse, Jeff Atkinson & Josh Atkins。看到这里,想必你也知道为什么 JA3 叫 JA3
了。
和 Vercel,Netlify 如出一辙,Cloudflare 采用「免费试用,付费增值」的商业模式。Cloudflare CEO Matthew Prince 曾在 StackOverflow 上回答过这个问题:「How can Cloudflare offer a free CDN with unlimited bandwidth?」:
2019 年,Cloudflare 在纽交所上市,股票代码:NET。发行价 US $15,目前 US $63,上涨了 320%。画外音:现在买它还来得及吗?
在中国目前和京东云合作,仅限企业用户。500 强企业中目前有三分之一使用 Cloudflare,还有很多上升空间。OpenAI 的 ChatGPT 上线后,Cloudflare 获得了大量曝光,防御了大量滥用用户和潜在威胁请求。
Cloudflare 因坚持网络中立原则受到了一些批评。
比较典型的一件事是 Cloudflare 因舆论和法律的压力终止对 8chan 的服务。Cloudflare 声称自己是一家私营公司,并且 Cloudflare 半数营收来自于美国之外的地区,可以不受美国宪法第一修正案的约束,其服务的客户对象是整个互联网市场。由于业务量大,有些包含恐怖主义、仇恨言论的网站也免不了会使用其服务。这也是大多数大型互联网公司所面临的问题。和快播王欣事件类似,他们都不愿扮演内容仲裁者。互联网诞生至今,法律的步伐总是跟不上技术的发展。
OpenAI 的 ChatGPT 对 Cloudflare 作了一次很好的展示,我向读者推荐 Cloudflare。一方面是因为它一直提供永久的个人免费服务,另一方面是它的易用性以及全球视野。我也用过国内某套路厂商的 WAF 产品,界面纷繁错乱,一看账单都不知道为什么收费,套路太深,价格高昂(可能怪我太穷)。
2023-01-21 08:00:00
APPLE:
过年回老家看电视,运营商送的网络电视盒子主屏幕花花绿绿,我一个程序员都费了好久才找到地方卫视的直播频道。
索性去电商平台搜搜看有没有更好的硬件、翻看各种测评文章和视频。清晰了需求定位后,我果断找了一家有现货的店铺下单了一款美版 Apple TV 2022 (4K)。
一直担心年前收不到货,没想到快递很敬业地发过来了。
由于购买之前就已经熟悉了大部分使用细节,所以安装、使用的时候毫不费力;像是把玩过很久的玩具一样自然流畅。
用美区 Apple ID 购买了很多付费应用,主要都是一些国内独立开发者的作品。
很难想象不少开发者会为国内极其小众的平台开发上架了如此小而美的 tvOS App。
搜集一些电视直播源,我就反常地看起了 CCTV。比期待的画质高出不少。遥控器的金属质感像第一次摸到棱角分明的 iPhone 5S 一样爱不释手!还用它和我爸玩了一局桌球游戏。
种种体验让我想起知乎上一个回答:
长这么大,听过最清晰的《义勇军进行曲》是在 Apple Music。
2023-01-11 08:00:00
Aaron Swartz 于十年前的这个时候自杀了。他起草的 RSS (1.0) 协议、和 John Gruber 一起设计、创造的 Markdown 至今一直拥有大量互联网用户。这十年间互联网并没有因他的离世而产生 Open Web 原教旨主义者所期待的愿景。类似「剑桥分析公司」的事情你我都有耳闻。万维网的发明人 Tim Berners-Lee 博士后来提出了 SoLiD 项目 —— 通过将用户数据和应用彻底分离,来实现用户对自身数据的完全掌控。ActivityPub 协议与之类似,但仅面向社交网站。如今,ActivityPub 已经成为了 W3C 的推荐标准;Elon Musk 收购 Twitter 公司之后,由于 "Hardcore Software Engineering" 所展露出的负外部性,Mastodon (长毛象)成为了最火热的分布式/去中心化社交网络平台,而 Mastodon 正是 ActivityPub 的实现之一。这个 Implementation Report 页面展示了一些实现了 ActivityPub 协议的网站列表。
折腾了几天,终于在百忙之中将这个小小的网站基本实现了 ActivityPub 最主要的接口。下面简单梳理一下大致实现的 Server to Server 接口,这些接口对于一个静态博客足矣。注意,这不是一篇严肃教程,仅仅是一些基于个人实现时的简单概括。
本站点实现 ActivityPub 的所有 REST API 均系由 ▲ Vercel Serverless Function (JavaScript) 驱动。
此 API 的定义参考 RFC 7033。这个 WebFinger 协议目的是提供一种针对单个域名的用户发现方式。考虑到此 API 必须使用 Content-Type: application/jrd+json
作为 HTTP 的报文响应类型,因此不推荐直接使用静态文件托管 JSON,请使用 REST API 来构建此实现。
subject
中的 URI 内容后半段和电子邮件非常像 —— ActivityPub 最终的实现效果也和电子邮件类似!
links
中会添加上我们即将要实现的 Actor API。
除此 WebFinger 之外,以下所有 API 都必须设置 Content-Type: application/activity+json
作为响应头。ActivityPub 服务端(比如一个 Mastodon 实例)都会在请求头使用 Accept: application/activity+json
类似的形式来要求我们的实例返回对应的报文格式。
Actor 就是 Activity 的参与者。WebFinger 会暴露此用户信息 (Profile) 接口。通过此 API,可以告知 ActivityPub 所有关于此用户的其他 API Endpoint,比如用户的 Outbox、Inbox、Followers 等等。所以这些 API 的具体 URL 都可以由自己去定义,而非一成不变。
除此之外,需要提供用户的 PublicKey 来验明身份。我们只需要在自己本地生成一对密钥就可以了。服务端通信中,发往不同 ActivityPub 的实例 HTTPS 请求都需要经过密钥加密。
类似 RSS/JSON Feed, 类型为 OrderedCollection
,必须按照时间顺序将最新内容放在 orderedItems
的最前。
OrderedItems 数组中的单个 Item (一般为 Note) 可以是如下形式:
在 ActivityPub 中,所有的对象都必须要提供一个 id
来作为唯一的全局标识符。而且,这个 id 必须是公开可访问的 URI,即可以通过此 id 来访问到此资源对象本身。 例上述如 Outbox 中的一项 Note 可以通过如下 curl 请求得到:
而如果你用浏览器直接打开这个 URL,你将会看到的是一个网页。原因就在于 Accept
这个请求头。
本质是一个必须支持 POST 请求的 WebHook。当联邦宇宙中其他用户对你的内容作出了一些交互(比如关注、回复、收藏、转发、删除等操作),会触发此 WebHook。你需要根据 Activity 的类型去处理这些 Payload。一般来说,我们会使用自己的数据库来配合 Inbox Message 做 CRUD。
数据存在自己的数据库之后,你就可以直接在自己的站点上去展示它们。要保持数据于联邦宇宙中的一致性,你需要处理好所有消息类型,并做到接口的幂等 —— 因为 Mastodon 实例会有重试机制。
关注者列表 API。当 Inbox 接收到来自其他用户的关注请求时,可以获取用户账户后保存到数据库然后通过此 API 展示出来。类型为 OrderedCollection
。也是最简单的一个接口。
需要针对实现 Outbox 中的每一个 orderedItem
的 id
中的 URI 实现一个 JSON 输出。形式可以和 Outbox 中单个 Item 保持一致。
除了 Note
之外,ActivityPub 可以有其他类型的资源,比如长文章的 Article
、视频资源 Video
。不同 ActivityPub 的实现平台对不同资源的展示方式不尽相同。
我的博客页面地址和对应 Activity ID 的 URI 在 URL 形式上保持了一致。因此在实现此 API 后,用户可以在任何 Mastodon 实例的搜索栏中通过搜索我的博客文章页地址来发现它对应的 Mastodon 贴文(由 Outbox 生成);在完全实现 Inbox 后,对贴文的交互数据就能够展示在我的网站上。比如文章页面最下方的 Replies
。
我的站点没有完全实现所有 ActivityPub 协议,比如 Inbox 消息目前仅处理了 Create Note 和 Accept Follower,还有许多消息类型亟待实现;大部分接受 GET 请求的接口也应当适当配置缓存;Inbox 要严格验证发送者的密钥。
很巧合地发现 Cloudflare 也在同一时间段开发了兼容 Mastodon 的 ActivityPub 实现:WildeBeest,有兴趣可以直接用他们的商业化技术栈来部署一个小型实例,或者直接参考他们的代码,用自己擅长的服务端语言实现自己的 ActivityPub。
2022-12-17 08:00:00
技术方面今年依旧学习了很多新的内容。
首先从 0 到 1 使用 Next.js 重构了此 lawrenceli.me 的整个站点。由原先基于 Notion 私有 REST API 的 hacking 方式换成基于纯 Markdown 文本的 SSG(静态页面生成)和 ISR (增量静态再生成)并完成了几乎没有太多压力的 Next.js 13 升级。所有文章内容均和代码共存,这样能在所有页面上自由发挥,甚至可以在纯 Markdown 文件中直接使用 React/JSX 组件(基于 HTML 抽象语法树 - AST)。依赖的版本也基于 GitHub 自动的 PR 保持了最新。 React/JSX 写的也比去年熟练多了。 另外简单研究了一下 TailWind CSS,上手很快,也在此站点上用上了。
Node.js 模版项目 完工,一个快速使用 JavaScript 开发 REST API 的脚手架项目,各种测试、CI/CD、Code Coverage、日志、Open API (Swagger)、JWT 等基础设施都加上了。摒弃了古老的 CommonJS 而基于纯 ES Module。
WebSocket Cluster 项目在 GitHub 上也快到达 100 stars 了,每周都会看到一两个国人参考我那很久没维护但精妙绝伦的代码。
年底 Spring 6 和 Spring Boot 3 的 GA 同样令人欣喜。我们终于可以基于 GraalVM 的 AOT 去做 Spring Native on Cloud Native 了。JDK 8 仍有接近 7 年的寿命。JDK 17 的 ZGC 是最值得研究学习的,另外下个 JDK LTS (JDK 21?) 应该会让协程 (虚拟线程) GA,目前仍在孵化阶段。学 JavaScript / TypeScript 就是玩玩,真正企业级大型项目还得看 Java。为什么这么说,你去看看 Nest.js 就知道了。
另外又用上了一个比较简单可靠的托管服务(关键是免费):fly.io,除常规项目外,它可以部署 Docker 容器,并提供大概 3 GB 额度的免费磁盘挂载。大多静态页面和轻量 Serverless 都依赖 Vercel 或 Cloudflare Workers,一旦遇到需要更加复杂的场景(比如 WebSocket 或 SSE),我会选择使用 fly.io 来部署。
Solidot Robot 已稳定运行将近一千天了,目前依旧基于 Vercel Serverless Function。异常稳定。Solidot 依旧是我每天都会逛的科技新闻源。
公司内的 OpenShift 今年并没有花太多时间研究,权限、开发环境问题无法在本地快速调试容器。当然我依旧对 Kubernetes 保持高昂的学习热情,并用半个工作日的时间完成公司对所有开发人员提供的 immersive training。
我对所在团队 (CVA Trading Desk1 in XVA) 的业务有了一定的认知: CVA Trading Desk 其实是一家内部保险公司。负责保障 Business Line Trading 在 couterparty (交易对手) 有可能失信、违约的风险下,可以得到来自 CVA Trader 专门针对此 couterparty 的风险做 Hedge 交易而得到的利润补偿;当然 Business Line Trader 会向我们 CVA Desk 定期支付保险费用。
每天有数以百计的 Batch Job 去处理这些 Trade 数据。最累的还是作为一个开发去解决各种突如其来的 production issue。
新冠瘟疫给我带来的除了处于长期封控下的 WFH,没有额外影响。我可能是相对幸运的一批了 —— 从未因此损失什么,反而得出了类似「草台班子」的观点。
经历上海的封控后,我现在已经会炒相对好吃一点的蛋炒饭了。还给前不久程序员做饭指南 How to Cook 出圈之前贡献了一份可乐鸡翅的 PR。我发现中国人花在食物烹饪的时间成本上有点高,我依然点了不少的外卖。等各方面条件具备后我会尽可能花少的时间做简单的西餐来替换现有的饮食方式、并持续减少盐分、高油脂的摄入。
2022-12-27 后续更新: 发布此文后一周,我感染了新冠病毒。多次高烧突破 40 度,目前仍有些许低烧、乏力、困倦、多汗,并几乎同时丧失了味觉和嗅觉。我的父母也几乎同步感染,尽管我目前和他们不生活在一起。和多数人一样,可能是有生之年最痛苦的一次发烧经历。
周杰伦的今年新专马马虎虎。力荐的那当然是今年 The 1975 的新专 Being Funny In A Foreign Language。另外附上 Matty 在 Apple Music 的真诚采访:
毕业后的这几年看观影、观剧的时间不那么充裕了。今年相对好了一点,但在豆瓣也仅标记了 20 多部。漫威自从复联四结束后最近这两年口碑有点差了,《雷神 4》拍得稀烂,希望来年有新的精彩故事线。今年看的比较好的商业片里印象深刻的有:《瞬息全宇宙》《壮志凌云 2:独行侠》《投行风云》《西线无战事》以及三季《Barry / 巴瑞》,尤其是这部 HBO 的犯罪片,讲述退役兵逐梦演艺圈的故事,很久都没有两三天一口气刷完好几季的感觉了,现在无比期待 2023 年的第四季。
软件领域,我把我常用的《极光词典》换成了《欧路词典》,导入了 iOS 内置的 mdx 词库文件。理由是《极光词典》不具备单词本功能,复习新单词无任何操作入口,但我仍然保留了它,因为作为词典这种功能型应用,已然完全胜出市面 99% 的同类产品了。
效率工具方面接下来我会重点去使用 Retool 开发一套自己的 Workflow,类似于 IFTTT 的平台但复杂度比 IFTTT 高许多。搜索领域我开始用了 ChatGPT Chrome Extension 搭配 Google。
文本编辑器方面,我开始尝试使用 Obsidian。目前桌面端和移动端都有一些 bug,作为 Markdown 编辑器,它的使用门槛对小白来说很低 —— 单纯的码字工具而已,而它的上限对习惯折腾的玩家来说也很高 —— 丰富多元的社区第三方插件。
最后不得不提的便是 Cloudflare 的优秀网络工具 WARP+。他们开会期间我完全依赖它才能正常上网。使用期间发现 WARP 有流量限制,利用 API 刷到了几十 TB 的额度后发现其下「零信任网络2」是完全免费且不限流量的 —— 我果断切换成此模式,同时仍然续费另外一项网络协议工具互为备选方案来帮我维持突破网络封锁的高可用。
今年 10 月中下旬,我的网易云账号因在某天评论了一首歌被禁言 366 天。微博帐号也只因点赞评论某事件3的微博而被永久封禁。对此我的态度只有:
😅