欢迎访问

这是我的个人博客,用于记录学习、技术实践与独立思考。内容涵盖计算机科学、开源软件,以及对历史与人文问题的探索性思考。所有观点仅代表个人理解,欢迎理性讨论与交流。

关于对事物定性判断的一些体会

在现在,生活中充斥着各种信息。我能够从各种消息来源获取各类信息,比如新闻、社交平台、长短视频等等。在这些信息中,包含着对周遭世界的各种变化的概要情况,或者是细节分析。其中不妨也有一些基于现在的情况,对一件事情进行定论和判断。有的分析往左边说,有的分析往右边说,有的分析自称“中立”。总之,众说纷纭,莫衷一是。我也是很好奇,我该如何判断这件事情的性质?哪一些是“对”的,哪一些是“错”的? 很多时候,我发现人们对一件事情的判断,其实是处处受到其自身所处的状态的影响。自身所处的状态又受到其社会地位、人际关系、经济实力、自身经历等影响。而且,对于一件事情,若非当事人,旁观者从各种消息中得到的总是这个事件的某个描述,描述者也将会有意无意在描述中渗入一些倾向性,这是不可避免的。比如说,虽然这个描述者说得都是事实,但是对于事实的描述也可以有侧重。有些他觉得重要的或者有意义的细节,他会多说。而另一些他认为不重要的,当然就会少说,甚至直接忽略。一件事情,除了其发生的时候的过程以外,还涉及到各种背景,包括但不限于参与者各自的背景,还有事情发生之前的各种关联性事件,甚至当事人在某一时刻闪过的想法。如果要在描述中,把这些事情都交代详尽,那是不可能的,更不用说有些细节根本难以探知。 很多时候我注意到,对于一件事情、一个事物或者一种现象,从两个完全不同的立场出发,完全可以各自发展出一套非常有道理且充满正义的说法。然而从这两个不同的立场看对方,往往会轻易给出非常尖锐的批评,双方都可以援引各种实际的例子。有趣的是,这些批评可以互相套用,用在各自身上,从切换一下立场,然后按照这个立场的模式进行一套有逻辑的推理,即可很好对号入座。所以,我尝试对我身边发生的事情、我感兴趣的大事、我忧虑的事情和我日常的小事等等进行这样的正反分析,几乎都能得出两个相反的判断和结论。在这段时间内,我陷入了一种分裂。我经常苦苦思索,到底哪一种说法是对的,按照事情的发展,哪一种立场可以笑到最后。我也渴望,一种中立、理性、客观的立场。然而,每当我认为某个人阐述的这种立场非常客观,非常中立,一段时间后就会有针对持有这种立场的人的批评之声。要么就是,客观到味同嚼蜡,我都不想继续继续听下去。我为什么会这样,我常常反思。 在长达几年的断断续续地思考后,我最终得出我自己的结论,寻找一种”中立“且”客观“的立场观点,或者是对一件事物去苦苦追寻”真相“都毫无意义。这样反而会让自己活得非常累。根本的原因在于,我自己是有自己的立场的,不需要再去强迫自己接受其他人的立场。我的立场来源于个人喜好、社会地位、经济实力、过往经历、经验见识等等,这些也是我上面所提到的影响个人对于事物判断的几个影响点。所以,我会在某些时候,对于一些声音感觉很”客观“,对于另一些声音觉得很反感。那是因为,其实对于这些事情早已经在自己的内心产生的自己的判断。那为什么我不能去乐呵呵地接受所有的声音?我承认,我可以去听取所有的声音,并思考其他的声音中包含的那些道理,然后尝试去加深自己对于一件事情的认识,但其实很难做到去开心地接受。然后,让我去站在我自己的对立面去思考,完全可以,但是对于我自己并无意义,因为我毕竟无法成为对立面的那一些人,很难按照他们的立场来做或者享受这样的立场带来的利益。 我更加倾向于认为,每个人都有自己的基本法,基本法是按照自己的个人情况量身定制,是自己对于周遭世界该怎么样才会“完美”的朴素认识。我自己是立法者,为了对于一件事情进行认识和判断,我可以通过基本法衍生出一些实际的法律。我自己也是法官,根据基本法衍生出的现行法律,对一件事情进行定性和判断。我是否要去参考其他人的定性和判断,可以,但是不是必须,应该更多地秉持着一种认识世界上的思想多样性的态度。别人如何判断,那是基于其他人的基本法,他们的基本法可能与我有相似之处,也可能与我大相径庭,完全相同的可能性非常小。那既然不是同一套基本法,那么各自也会衍生出自己的一套实际的法律。既然他们与我所处的体系都不同,我为什么要完全接受一种立场? 对待日常生活中,那些自称的”中立者“的态度,基于我自己的这套观点,也有了一些清晰的方法应对。这些中立者,其中一类,无非是宣称自己所述的是事情的真相,但是前面已经说过,一件事情的真相是很难原汁原味的还原的,各类描述都带有自己的侧重,而且总有不为人知的细节未曾曝光。既然这样,那空谈真相还有何意义,即使事情有一个绝对的争相,那理论上也只有神知道了。所以我觉得,我大可放下“追求真相”的精神洁癖,基于我自己所知道的,去给出我自己的看法。还有一类,说自己的观点很“中立”和“客观”,这是可笑的,因为他也是一个人,有自己的基本法,如何能抛却七情六欲,成为一个神仙,从而来达到“中立”和“客观”。如果有,那就有一种相对的中立和客观,那就是穷尽世界上的所有的立场与其对应的思想观点,以及他们对于这件事情的看法,然后全部精确记录并阐述。有些“中立者”正在向着这方面努力,这类人,我觉得确实是往正确的方向在走。但是,我作为一个人,听取这么多的立场有什么意义,难道我能随心所遇地切换自己的人生,来去实地体会和践行这些思想观点和判断?类似地,就比如一个问题有即使多种解决的方法,但是最终我还是会用一种方法去解决它,选择哪一种方法完全是个人的事情。所以在这里,我认为某个事情该怎么判断,听从自己基于自己立场的判断即可,在自己的这个参考系下,这就是中立和客观的了。其他的参考系下的看法,又与自己何干?当然,我们也可以多看看其他的参考系,但这绝不能作为一个强制的要求。如果自己想真正独立,那么维护自己的参考系的独立性,捍卫自己的是立场、观点和偏好,是非常重要的。 我也注意到,对于基本法某些部分或者条文有比较多相似性的一些人,是完全可能聚合成一个团体的。一个人独自战斗地久了,找到和自己有相似观点的人,还是一件很欣喜的事情的。对于一些事情,一个人完全可以去发现和寻找自己的团体,不一定要加入和做些什么实际的事情,也可以是一种内心的靠拢。很多时候,人们会为了一件事情的看法,相互攻讦,给不同立场的人扣上一顶帽子。如果这个时候,一个人能够站在一个自己舒服的团体下,那可以减轻很多压力并有可能产生一种安定感。很多情况下,我觉得“站队”是对个人有利的。更多的人如果能通过“站队”,共同努力去维护和传播一种相似的立场,才能让一种“共同立场”持续存在并产生力量去真正推动现实中的一些事情。这样才有可能,在大家去解决某个社会性问题时去考虑这种立场,然后去采取这种立场提供的方法或者部分思路。如果真能这样,个人才能够在这种情况下获得更多的利益,让自己觉得自己的观点偏好与自己所喜爱的有了更多的意义。 很多人说,自己“看开了”,没有太多的想法了或者是总想看看中立的或者什么都不看,我觉得不如说自己“绝望了”或者“放弃了”。既然上面说,出于自己的特性和基本法,个人总有自己的立场,这种情况下,如果“看开了”,想让自己陷入一种“中立”,那其实是抛弃了自己的基本法,把原来属于自己的权利——对一件事情保留自己的观点和看法,让渡给了他人,然后自己甘愿随波逐流。甚至有“看开”的人说,那些没有看开的人,“幼稚”且“不成熟”,他们所汲汲或戚戚于的事情,很多是陷入世俗的不成熟的表现。然而,在所谓“幼稚”的人看来,这些“看开”者无非也就是一个自我精神阉割的可悲个体,由于世俗的压力丧失了属于自己的基本权利,甘愿做一个随波逐流的空壳。看似“廓然无累”,其实任由他人摆布,这种摆布不限于观点立场的通过潜移默化的强制接受,而且也会通过其他观点掌握绝对话语权后,通过提出属于他们解决社会问题的方案来产生个人利益的受损。所以,我们也无须去贩卖“成熟”,或者是感到沾沾自喜。基于世界上思想的多样性,成熟也是一个相对的概念了,社会主流立场所看来的“成熟”,在另一些立场看来,反而是幼稚且不成熟的。关键就在于,那些立场掌握了这个社会的话语权。 想了这么久,终于能够把自己一部分对于这个主题的思考比较具体地阐述出来,这也是一件好事,能够认识到自己的某一条法律条文究竟该怎么写。当然,基于我自己所想的,我自己所写下的也是一家之言,也收到自己所叙述的东西约束。就是说上面这些,完全可以归为一种属于我相对的观点,从读者的立场看,这些可以没有任何作用,完全可以不以为然,甚至可以完全荒谬或者全盘错误。但这种观点或者理论,对我有效,让我对周遭世界有一个我自己认为自洽的阐释,那就够了。而我写出来,对于他人的意义在哪里?用我自己的话来说,就是能够让别人在愿意的时候,去体会一下这个世界上的思想多样性。

三月 6, 2025

二零二三年四月第一周技术周报

看了一下当时的周报,值得说的事情也就是关于日志整理的事情,然后就是关于一个配置下发服务的多地部署。关于多地部署的事情,下一篇技术周报再详细说明。 首先来说说,日志方面的事情。现在的话,线上有很多服务。然后目前DEBUG的日志,是用了腾讯云的CLS。而目前存在一些问题,这些服务的日志有些分散,不太好找。如果一个请求跨越了多个服务,有可能需要研发去跨越几个日志集然后来定位问题。然后就是,这些服务技术栈并不统一,并没有一个统一的日志监控框架来监控这些服务的状态。目前追踪跨服务的请求使用TraceID,TraceID是在请求进入网关服务或者入口服务的时候自动生成的。TraceID有可能是纯数字,也有可能是数字带字母。后续的服务之间的请求基本上会带有TraceID这个字段,用于追踪请求。然而,也还有很多请求内容里不带TraceID,导致服务没办法从请求本身来拿到TraceID,只能索性重新生成一个,所以这些请求在各个服务之间的TraceID都会不同。总而言之,理论上,正常情况下,一个TraceID将伴随一个请求,从进入追踪范围到出追踪范围为止。 另外还有个问题,就是这些个日志集中,日志内带有的字段也不太统一,有写日志集的请求内容的字段叫X,有些又叫Y。有些字段日志的结构上有,实际输出的日志中却没有填写。如此这些,都增加了新的研发上手定位BUG原因的学习成本。上述这些问题,都很让人头疼。 还有一个就是降本增效的问题,在目前的已有日志的基础上需要看看还有哪些日志可以精简优化的。整个部门,每个月的单单存储和整理日志成本都十多万。而我负责的这一块的业务量又占大头,日志量也是非常大的,每天大概有540亿条日志。所以,我需要在这一块多下功夫。 对于当前的的TraceID问题,我采取先把同样技术栈服务的TraceID的生成方式和传递方式都改为统一的。对于Java技术栈的服务,用的无非都是Spring Boot框架。所以我对每个服务都增加了一个统一的拦截器,来拦截请求和响应。对于请求,这些Java服务处理的都是文本类型的请求,基本上都是JSON格式编码的。所以,无外乎就是检查JSON字段中是否有相应的traceId字段,如果有的话,那就拿来用。具体就是把TraceID放置到线程私有化存储中(MDC),伴随着这个线程处理。这个方法也有局限性,有些请求会在某些步骤上进行异步处理,转移到其他线程池的线程上处理了。这样,TraceID的追踪就丢失了。我的办法是写一个包装类,接管所有异步处理的需求,参数与调用方式和原来一模一样,然后在其中检查上一个线程中是否含有TraceID字段,然后把TraceID字段先放到包装类产生的对象中。切换到另一个线程后,这个线程先看看包装类对象中是否有上一个线程存下的TraceID,如果有就提取这个TraceID然后记录到自己的私有化存储中。这样TraceID的追踪就不会中断了。 请求拦截器的TraceID生成过程,也可以提一下。由于很多以前的老服务使用了二进制的请求格式,技术原因导致了这些请求无法灵活变更。一旦更改了请求,那么上下游都需要重新编译生成编解码器,然后重新部署。这对于日志改造来说,工作量不可接受。然后有很多请求中,TraceID是long类型的,而非字符串。所以我就需要照顾这些老服务,大家都统一使用long类型的TraceID。然后,我还希望在TraceID中带有一些额外的信息,比如说这个请求是从哪个地方被赋予TraceID的。如果这个信息能直接从TraceID中看出来,那就再好不过了。所以我不是简单地随机生成一串数字当作TraceID,而是保留前面的4位数字。前两位用99作为开头,表示这个是使用了统一后的TraceID方案,和以前的纯数字的TraceID区分开来。然后,后两位数字按序号表示各个服务。01,表示网关服务A;02,表示入口服务B等等,这个我来手动分配,最后会把分配方案写到文档中。后面的数字,都是随机生成的。 注意,这里采用随机生成的方式,在每天这么大量的日志的情况下,可能产生重复。但我评估了一下,这种重复对我们定位问题这个需求来说,无关痛痒。所以索性重复就重复,再重复15分钟之内重复的概率也非常小,可以不考虑。最后,生成的TraceID都是固定位数,以99开头的数字。 在业务处理请求的过程中,会不断有日志输出,采用异步化的方式输出到日志盘中的目标文件中。这个日志输出的格式和分类需要强制统一,包含下面的内容:时间、日志等级、TraceID、对应线程ID与日志内容。这些都用“|”隔开来。然后,需要把不同等级的日志区分开来存储。后续腾讯云CLS会通过规则来监控这些日志文件的修改,然后采集这些输出的日志,通过上面的统一格式和区分等级,能更简单地统一各个服务的采集配置,并避免把一些低等级的日志采集上去。低等级的日志可以留在日志盘中,如果研发人员需要看,就可以登录到容器中来看。毕竟,相比于被CLS采集后,附带产生的流量、计算和存储的成本,日志盘的存储成本还是能接受。这样也算是通过差异化地方式提供服务来降低成本。后续,对于不是那么高频用到日志,只需要通过降低其日志等级来避免被采集,从而降低成本。值得一提的是,被采集上去的高等级日志,只会按照TraceID索引,而不再索引内容,这样可以非常有效地降低成本。我称这些日志为业务日志,这些日志在CLS上只能通过TraceID来找,而不必通过内容来查找。 在响应拦截器中,我们需要一次性向日志输出这个请求的简要信息,也就是Access日志,包括请求的字段,响应的字段,还有处理时间,容器的IP地址,TraceID,服务名称等等内容。Access日志将单独输出的access.log中,然后这个文件也将被CLS采集。CLS会按照其中最关键的字段(请求字段,响应字段,TraceID,服务名称)索引这些日志,后续我们通过模糊搜索请求字段或者响应字段就能定位到具体的一次业务请求了,我们可以对这个请求有直观地了解,然后也能拿到TraceID。通过TraceID,我们可以用CLS一次性找出与这次请求相关的所有业务日志,摸清楚这次业务请求的来龙去脉。 通过上面对于业务日志和Access日志的方案的设置,我能够避免让CLS建立全文索引(索引日志的所有内容),从而大幅度降低成本。虽然没有了全文索引,但是研发人员通过BUG单中的用户给出的信息,搜索请求或者响应中的对应字段,来查出TraceID,后续也能快速且准确找出所有有关的日志来。这实现了成本和易用性的一种平衡。还有一个没有提到的,就是被CLS采集上去的日志,就没必要在日志盘中长期存在了,可以设置滚动规则,1-2小时后就删除掉。 对于Node.js技术栈的服务,我也基本上采取相同的思路,通过框架提供的context能力,来处理TraceID。日志的输出和采集方案,也严格遵守Java服务的规范。然后还有C++技术栈的服务,那就麻烦很多,这些服务非常老了,但依然处于关键位置上。但我没时间处理这些了,这些服务出问题的概率非常小,先放着。 通过这样的处理,CLS上对每个服务配置日志文件的采集规则都是统一的了,大大简化了配置的繁琐程度。对于每一个服务,我们只需要复制粘贴现有的采集和索引规则即可,因为这些服务的日志格式和输出都是呈现形式都是相同的了。这样处理下来,有一个月的时间。完成后,日志这块确实都规范起来了,查问题也变得方便了。我也能对现有日志进行归类,看看哪些业务日志对查问题真正有效,哪些并没有太大的帮助。后续我再带着另一个同事,削减了一大批以前没发现的无效或者重复的日志,又节省了一大笔费用。具体地,我们两个经过评估分析发现每天可减少121亿条无效与重复的日志,削减现有成本的38%。

十一月 17, 2024

二零二三年三月第四周技术周报

本周有个插入的事项,就是说我们目前在使用某云服务商的API,对于其中的API鉴权,那边有更安全的方案。原先,我们应用调取云产品的功能时,是通过一个AccessKey和AccessSecret来的。在调用的时候,需要在SDK中提供这两个值,然后就可以在应用中使用SDK提供的云产品API了。原先的安全要求是不能在代码中存储这两个值,公司会有某种自动扫描机制,通过这个机制可以检测代码仓库中的敏感信息,这两个鉴权用的字段也当然囊括在内。所以,我们一般都会把这两个值写在配置文件中,安全性会更好一些,也方便更改。 而现在他们说这两个值是静态的,无法在泄露的情况下快速进行更换。毕竟我们服务一般都有几十上百个容器,而目前来说,启动配置虽然已经集中于各种配置中心里了,但是在技术上是无法实现秒级别的动态更改的,必须在修改配置后重新启动容器。而容器的启动是无法通过一次性重启所有容器来完成,必须进行分批重启以保证服务的平稳,没有十几分钟甚至几十分钟是搞不定的。 所以云提供商的技术团队提出了一个新的方案,通过向我们提供一些必要的而且泄露后不容易造成直接影响的凭据(好像是一个密钥文件),然后通过在线认证的机制下发动态的验证信息。这个过程比我上文提到的验证机制复杂许多,特别是我们需要在容器中纳入这样一个配置文件,或者直接在JAR包中纳入。然后我们需要引入一个SDK,来专门做这个动态验证信息的获取,然后将这个信息通过某种方式传递给云产品的SDK,最后才能实现云产品API的调用。 这个具体的过程并不是我来跟进,我把这些问题交给其他同事了,由他们来具体执行。我只是作为云账号权限的控制者,为他们提供密钥文件并给他们提供正确的方向即可。

四月 15, 2024

二零二三年三月第三周技术周报

本周值得注意的事项就是,测试人员那边反馈在测试环境下,某些账号突然无法正常使用的问题。目前整个账号的体系还是在沿用老的系统。而在当前老系统中,账号信息使用某种特殊的数据存储分成多个模块存储,每个模块相当于一张表。每张表都各有侧重,有些侧重于与其他账号之间的关联关系,有些侧重于查询等等。这些账号无法使用的问题在于,其中最重要的一张表也就是主表,这个用户的记录消失了。在其他表中该用户的相关记录还是存在的。 这就引发了我的各种猜想,是否是测试人员测试的过程中的环境弄混了?或者是某个服务的配置有问题导致了部分应该写在测试环境的数据写到了生产环境去了?说代码写得有问题导致了某些极端情况下,用户的主表信息写入失败?通过调查发现,最终要的查询表中,数据结构还是完整的,而且能够反推出用户注册时候的微信账号。查询表只有在账号创建的时候才会去写,后续基本不会再修改了。而主表中的数据应该是和查询表在用户注册的时候一块写入。 对于这个问题,后续推断应该是数据模块发生了某种问题导致数据丢失。而在我咨询数据模块相关维护人员的时候,我还没说清楚问题,他们就说是测试环境并不保证服务的可用性,可能发生任何问题。那么这个问题可能就无解了,因为如果是数据模块这层问题,我作为使用方基本没有任何能力来自己解决。除非我能替换掉这个数据模块,使用更加稳定可靠的解决方案。那对于这个问题,我只能够然同事手动清理查询表中的索引关系,然后在让用户重新进入应用。当用户重新进入应用的时候,由于没有索引就相当于没有这个用户,此时就会重新创建用户。后续几个月遇到了十几例相同的问题,没有办法,让同事添加了一段自动处理这个问题的业务代码,以免去每次手动处理的麻烦。 这个问题第一次出现将近一年后,由于某个重要客户反馈了这个问题。而又正好这个重要客户当时半个月之内反馈的我们的各种问题有点多,领导对此十分困扰,所以对于我这个问题他特别重视。领导后续找到我让我推动解决这件事,我同意执行,因为我也想知道问题具体是什么。现在能够确定的是,问题出现在数据存储层,而这一层又不是我们直接控制,我们只是使用这样一个产品。这个产品已经很老了,维护人员很少了,而且还有其他事情。他们不愿意来主动寻找问题,也是情有可原的。所以想要推动他们的维护人员来解决,就只能抓住具体且直接的证据来证明他们的问题在哪里。 我以当时的了解,知道数据层分为缓存和落盘数据库,而且经过一年在各种问题的解决过程中学习,我也能很熟练调取分析落盘数据库里的数据了。我调取了落盘数据库中的用户ID的倒序情况,发现从某个用户ID开始,后面就没有了。最近一年,用户报问题的用户ID以及新注册的用户ID是远远大于这个ID的。我又去日志中获取了一个最新注册的用户ID,查询数据层,发现是有的。说明缓存中是有正常数据的,但是由于某种原因数据并没有落到数据库中,而是在缓存满后直接消失了。 这已经能够说明问题了。于是我将这些调查记录截图发给数据层产品的维护人员,并提了一个工单,推动他们来解决这个问题。该问题的脉络我已经提前分析地很清楚明了了,而且又有直接的证据,他们也开始认真对待这个问题。后面确实,他们重启某个模块后,这个问题就好了。而且最终他们承认问题与缓存层落库的机制有关系。

四月 15, 2024

二零二三年三月第二周技术周报

本周相关的一些事项,我认为最值得讲得就是某个数据库接入层服务的给每一条数据加上键过期时间。这个数据库接入层是一个Java服务,本质上做的工作就是将Redis与数据库结合起来。对上的接口遵循某一个公司内部的特定协议,而对下则使用开源且很多人使用的Java包来访问Redis或者数据库。访问Redis使用的是lettuce,而访问数据库用的就是mybatis。原来访问Redis其实用的是Jedis,后面之所以改用lettuce,是因为它在高并发下的性能/资源表现更好一些。我认为具体地主要体现在多路复用连接,使得不需要在连接池中维护很多连接,总之就是在连接上的开销不会很大。 然后对于数据库来说,并非直接写入,而是在消息队列中写入一条插入指令并附上一些上下文。这样消息中包含了写入数据库需要的所有信息了。然后这条消息队列也是自己在消费,消费的时候将数据写入数据库,如果成功就ACK消息,失败了就让这条消息5分钟后再回来。 这个服务总的读写策略就是,如果一条数据查询来了,Redis中没有数据时就将数据库中的数据拉到Redis。然后返回Redis中的数据。Redis中有数据则直接返回。对于写入数据来说,这条数据会双写Redis和数据库。修改数据的时候,操作复杂得多。因为这个Redis中数据是使用HSET的方式进行存储的,然后一条修改指令可能只是修改HSET中的一部分字段。这个就需要对一些具体的情况进行讨论了,数据库指令也是根据具体情况来构造。 值得一提的事写数据库的时候,目前看前人升级了策略,不直接拿消息中的数据来写,而是消费到消息时,直接从Redis中读数据(这个时候Redis是早就被双写了的),然后直接将Redis中读区到的数据写入数据库。我认为这样做的好处是,让数据库中的信息尽快更新。因为从消息投递到消息队列再到消费的过程中,这一条数据可能已经修改了好几次了。 在这几周我发现这个服务在Redis中的缓存并未设置过期时间,而是依赖Redis实例中设置的统一过期策略来进行数据淘汰。这个策略是在Redis数据存满的时候才会执行的,这就会引入一个风险。根据我们云上分布式Redis的设计,如果Redis存储一直都是满的然后这个时候有大量的数据写入操作(某种特殊的业务高峰期),这样云上分布式Redis为了一次性腾出足够的空间会集中进行淘汰操作。这个时候无法处理任何请求,这种情况将持续几分钟。这对于业务来说,存在较大影响,因为这个时候上游业务服务将无法读或者写入数据了。而现实情况就是,这个服务的Redis一直处于满的状态。 为了降低风险,最好就是为每一条数据设置过期时间。使得Redis中存储的始终是较热的数据。而设置过期时间,采取的策略是取一个固定的时间Y,然后再生成一个均匀分布的浮点随机数因子a,从0-1变化。然后我们对于某一条数据的过期时间就是Y+aY。这样设置能尽可能避免某些情况下大量数据集中过期造成的某段时间内数据库压力过大问题。 当时Redis中有64GB的数据,且整个Redis实例是满的。我评估了一下,如果只是增量进行数据更新,确保新的数据是设置了过期时间的,这样应该能让存储空间缓慢降下来。最终应该是会剩下少量的数据没有更新,占比应该不大,因为我们的业务不是存量业务,大部分用户应该会继续使用。 2024年4月 将近一年多时间观察下来,Redis中的数据只剩下了10GB左右。而Redis实例的规格我也在半年前降下来了,成本有所削减。

四月 14, 2024

关于个人对AWS服务的使用感受

原文经过AI润色,非本人文风。 从2022年11月开始,我开始使用AWS服务,已经接近1年的时间了,而且不是免费套餐。从一开始对它的使用感到新奇和陌生,到现在对它的了解更加深入,期间经历了很多有趣的事情。那么,为什么我选择使用AWS呢?因为我个人有一些需要在AWS上运行的服务,比如博客网站(Wordpress)、Git仓库、RSS订阅等等。当时,我觉得AWS在云服务市场占有最大的份额,所以它的解决方案应该已经非常成熟了。对于AWS,我还是有一定的信任,愿意花钱购买适合的服务,以确保我的个人托管业务能够长期稳定运行。 在开始使用AWS之前,我也尝试过免费套餐,但是一直没有找到合适的需求场景,所以用了一段时间后就不再使用了。这样,我的免费套餐周期就白白浪费了。 当我第一次接触AWS的时候,我首先尝试了EC2产品,并试用了1核心1G内存的T2.micro实例,但后来发现内存不够用,导致机器卡死。因此,我决定换成T2.small型机器,它拥有1核心2G内存,并且我还增加了SWAP分区,每个月大概需要花费17美元。虽然这个价格包括了带宽费用,但我仍然觉得有些贵。然后,我开始考虑是否可以使用AWS的保留实例,听说可以减少60%-70%的费用,看起来非常划算。于是,我下定决心购买了三年的保留实例,先支付了170美元,然后每个月承诺支付4.75美元。这样一来,每个月的费用大约是8.84美元,这个价格看起来相对合理。 然后遇到了一个坑,当时我配置了30G的系统盘和10G的数据盘,以为30G的系统盘是免费的。然而,我又考虑到数据的安全性,因此还增加了定期快照备份,以为快照的费用不会很多。最后,月度账单一出来,竟然要20美元!我很惊讶钱都花在哪里了? 我在控制台上查找,又在Cost Explorer上进行了进一步的查询,才发现系统盘和数据盘的费用都按照我申请的规格计算了!没想到,AWS竟然是按照服务器和云硬盘分别计算价格的。而且,我还不知道如何配置第二代SSD,虽然性能很好,但我用不到这么高的性能,而且价格比第三代SSD贵20%!还有,几个快照居然每个月费用超过了7美元。而且,费用查找界面非常复杂,一个选项下可能有很多项目,需要一个一个取消勾选,慢慢查找到底是哪一项花费了多少钱。 因此,从2022年12月开始,我开始不断削减成本。我逐渐取消了快照备份,然后取消了数据盘,最后还设法减小了系统盘的容量。系统盘容量扩容很方便,可以一键操作,但缩减容量非常麻烦,需要手动操作。在网上找了一圈,发现AWS有个名为Lightsail的个人用户友好产品,我感到后悔了。 我还发现DNS托管每个域名每个月要0.5美元!而且,根据DNS访问次数还会额外计费。特别是域名内有多个CNAME跳转的DNS配置风格,一次访问会被计算多次解析次数。如果不想被计费,就需要设置Alias。虽然DNS功能很强大,但我这种个人用户用不上,而且好像Cloudflare的DNS更快而且免费。当时,我还把两个域名迁移到了AWS,并且都托管了。因此,这块DNS每个月花费大约1.5美元左右。最后,我想了想,还是把DNS解析迁移到了Cloudflare。值得一提的是,AWS的域名续费很贵,每个月com域名要花费12+美元,而Cloudflare大约是7美元左右。这也是一笔支出,几乎相当于一个月的EC2费用。对于个人用户来说,实在不划算。 总结一下,AWS提供了很多功能和选项,但对于个人用户来说,有些功能可能用不上,而且费用也会让人感到意外。因此,在使用之前,一定要仔细了解每个选项的费用计算方式,以免产生不必要的额外费用。 从2024年2月开始,AWS开始对公网IPv4地址收费,无论是弹性IP还是绑定到EC2上。根据计算,每月大约需要花费约3美元。考虑到这并不划算,我决定移除公网IPv4地址,将服务器改为纯IPv6。这样一来,我的网站基本上无法直接访问了,因为在很多情况下,对IPv6的支持并不完善。我尝试了AWS CloudFront的解决方案,但发现CloudFront的源站访问不支持IPv6,也不支持内网的IPv4地址。我思前想后,好像没有解决方案了?(实际上,可以将域名托管到Cloudflare,然后开启代理) 综上所述,作为个人用户,我不推荐使用EC2等产品。如果必须使用,建议选择类似Lightsail这样的产品,计费较为简单,而且不会在不知不觉中开启"增值服务",账单无法控制。而且最好使用用AWS托管比较轻量的静态网站,用Lambda、Amplify、S3这类产品,按量付费才是划算的。对于域名和DNS方面,个人用户推荐选择像Cloudflare这样的服务提供商,因为它们免费且性能很好,而且域名续费也不贵。我认为AWS的产品更适合企业等大型用户,这类用户可以协商价格,相比我们按照官网给出的标价会更划算。最后需要提醒的是,AWS的技术服务需要额外付费! 当然,AWS也有它的优势。我的1核心(平均性能为20%)的机器能够运行很多服务,并且响应速度也很快。一年下来也非常稳定,几乎没有出现过什么问题。另外,像CloudFront这样的加速服务,每个月的免费额度居然有1TB,而且还是永久的。因此,我将我一些速度敏感的站点都配置在AWS的CloudFront上,一年来发现速度稳定且响应快速。只是配置有些复杂,需要一些基础的WEB技术知识。 现状 目前,我将我的博客网站和RSS订阅等动态网站迁移到了专业的网站托管服务商。我选择了一个可靠的服务商,他们提供了稳定的运行环境和安全性保障。不仅如此,我还趁着最近的黑色星期五促销,以非常优惠的价格购买了几年的服务,这样我可以放心地运行我的网站。这家网站托管服务商还提供了许多附加服务,比如邮件服务,让我可以方便地与读者进行沟通。他们还提供了缓存加速功能,可以提升网站的加载速度,让访问者有更好的体验。另外,他们还提供了域名注册服务,让我可以方便地管理我的域名。管理界面也非常简单易用,让我可以轻松地进行网站的管理和维护。对于个人用户来说,这样的服务就足够了。 除了博客网站,我还有一些个人开源项目使用的静态网站需要托管。为了简化操作,我选择了Netifly作为静态网站托管平台。Netifly提供了简单易用的界面,让我可以快速部署和管理我的静态网站。他们的服务非常可靠,我可以放心地将我的项目托管在他们的平台上。 另外,为了进行后端开发和测试,我计划使用AWS的EC2来部署一些个人开发的后端程序。EC2是一个强大的云计算服务,可以提供稳定的运行环境和高性能的计算资源。我还在EC2上配置了公网IPv6地址,为将来使用Cloudflare代理做准备。使用Cloudflare代理可以避开IPV4公网收费的问题。 总的来说,我对我目前的网站的迁移和托管选择感到非常满意。我相信在接下来的几年里,我的网站将能够稳定运行,并为读者提供更好的体验。 总结 在使用AWS服务一年后,我有以下几点个人感受和总结: 我选择使用AWS是因为我个人有一些需要在AWS上运行的服务,如博客网站、Git仓库等。 我尝试了EC2产品,并先试用了T2.micro实例,但发现内存不够用导致机器卡死,后来换成了T2.small型机器,并购买了三年的保留实例以降低费用。 我发现系统盘和数据盘的费用都按照我申请的规格计算,费用查找界面也很复杂,需要一个一个取消勾选来查找具体费用。 为了削减成本,我取消了快照备份、数据盘,并减小了系统盘的容量。 我发现DNS托管每个域名每个月要0.5美元,并且根据DNS访问次数还会额外计费。我最终将DNS解析迁移到了Cloudflare。 从2024年开始,我移除了公网IPv4地址,将服务器改为纯IPv6。然而,由于IPv6支持不完善,导致我的网站无法直接访问,尝试了AWS CloudFront的解决方案但遇到了限制。 作为个人用户,我不推荐使用EC2等专业的产品,而是建议选择类似Lightsail这样的产品,并使用像Cloudflare这样的服务提供商来处理域名和DNS需求。 总的来说,我认为AWS对于个人用户来说费用较高且某些功能可能用不上。在使用之前,一定要仔细了解每个选项的费用计算方式,以免产生不必要的额外费用。

十一月 24, 2023

关于主流道路的一些思考

一般而言,我们总是倾向于捡着他人走过的路来走。一旦偏离这个所谓的“大众路线”,我们很可能就会觉得自己处于悬空状态,从而陷入一种循环地自我怀疑中,总认为自己处于一种脚不着地的危险的状态。我感觉这是我们倾向于认为,大众的某种“主流”的观念是一种无可辩驳地正确,从而不会去思考其中正确性来源的几方观念的出发点,以及这些观念背后的逻辑。 具体来说在我们的生活中,常常会有一种压力,让我们按照主流的方式去行动。这种压力直接来自社会与文化的期望、他人的看法以及我们自身对所谓“成功”的刻板印象。我们害怕偏离主流,也害怕在便利主流后失败,从而被周围的人认为是典型异类或失败者的活生生的例子。因此,我们倾向于随着周围的人选择安全、确定的道路,即那些大家已经验证过的方法。顺从主流方式可以当然可以带来一些实实在在的成果,但这种方式并不一定最适合作为单独个体的某个人,你可能并未从自身的具体情况的细节出发来选择,而可能带有一定的盲目性。我们不断仰视其他人的成功,给自己制造一个个“神像”,时时刻刻去回忆去参拜,在谈论中夹杂这感叹、羡慕或者是丝丝的嫉妒。但我们强调要像这些“神像”看齐,要成为他们,但却始终忽视了自己的特点。我能不能成为另一种定义下的“神像”?这个是很多人都很少去思考的问题,很多时候大多是人可能认为这种思考无意义。 这个时候想象一下,如果别人向你寻求有关他们当下困难的决定或者说是某段时期的人生道路,你可能会毫无思考地说出这些“主流”方式,但当别人进一步问你为什么这些方式是正确的?是否可以列出实际的步骤并从自身出发去论证这些步骤的合理性?你可能第一时间就不知道如何回答了。 虽然说,当你跳出固有的“大众”思维去思考的时候,想着我可否在这个主干道的这个岔路口上走拐进另一条路。这可能会产生很多困惑,因为这对于你思维的惯性来说,何尝不是一次急刹车?也许你第一次考虑一种未曾设想的可能性。并且有可能突然发现原来这件事的逻辑是非常复杂的并陷入一种思维错乱中。在你尝试你认为是正确的新方法时,也有可能发现你其实是走了弯路。最后你可能会得出结论,主流方法也是有一定道理的,是基本上正确的。然而,只有意识到这些,有意识地去了解这些,在拥有可行性的基础上去试着尝试这些,你才能在不断尝试和试错中更清晰地把握事情的本质,最终正确认识“主流”方式的合理性以及它为什么会存在。这个时候你的脚或许真正开始走在实地上,这大概会逐渐在内心深处建立处一种内在的自信,不断引导你尝试其他的可能性,从而有可能找到最适合你个人特质和当下实际情况的道路。 所以,我个人认为不要因为害怕事情的复杂性而放弃寻找其他解决方法的可能性。这个时候要相信自己的现有的能力和基于大量有效思考而对事物判断产生的直觉,不要过分依赖他人的意见和观点,从而自己的一套思想理论和解决问题的宏观的体系。最重要的是,要始终坚持自己的价值观和目标,顺着这个内心的最终指引找到真正适合道路,才能有效避免在大量的思考和尝试汇总迷失方向。

十一月 21, 2023

自建图床服务并配置免费CDN全球加速 过程记录

个人有一些图片托管的需求,在很多场景下比如说在博客文章中插入图片,在社交网站上分享图片,抑或是个人开源项目中的图片等等,都需要在文档(特别是MarkDown)中嵌入一个URL直链来指向图片。对于一些免费的图床服务商,个人感觉并不是很信任。数据安全是一方面,另一方面如果服务商跑路了,那么很多原先我创建的链接都会失效。据我所知,这样的例子比比皆是。 网上还有一些教程将GitHub当成免费图床来用。这样做弊端也很明显,GitHub在国内的访问速度非常慢,图片加载很慢。另外,缺少很多图床该有的功能,比如说自动压缩,自动转换成Webp,自动重命名等等。这样做也是不太可行的。下面就是一个我将GitHub用作图床的例子。每次我还需要将图片移动到代码仓库里,然后commit,push,我个人感觉很麻烦。 还有一段时间我试过把WordPress的Midea库当作图床来使用,但是我发现图片链接中带有wp-content/upload这样的路径。然后链接中还会出现图片的名称。我感觉并不是很雅观,而且在隐私的保护中有一种说不好的感觉。就像是别人一看这个URL链接,就能获知一些图片以外的信息。下面就是用WordPress的Media库当图床例子。可以看到,产生的直链不是很美观,而且还能暴露一些信息。 所以,我这边产生了自建图床服务的想法。这种图床最好能自动转换jpg、png格式的图片为webp。这样图片就能比较高质量、快速地在用户浏览器中加载出来。有关WebP的简要介绍如下: WebP is a raster graphics file format developed by Google intended as a replacement for JPEG, PNG, and GIF file formats. It supports both lossy and lossless compression,[8] as well as animation and alpha transparency. Google announced the WebP format in September 2010, and released the first stable version of its supporting library in April 2018. 然后图床还要能够自动压缩图片,然后产生带有MD5或者SHA256的URL链接,能够不暴露我的图片原始信息。然后还能够配置CDN加速,毕竟对于图床来说CDN非常重要,载入速度很大程度上影响用户的终端体验。还有最好能够用PHP写成,因为这样我能够上传到我的网站托管服务商进行托管,不需要另外去单独为这个买VPS来部署。 搭建EasyImages2.0自建图床 经过查找,我发现一款叫做EasyImages2.0的图床项目比较符合我的上述需要。这款图床用PHP写成,理论上能够在我的服务商托管的规则容许下正常运行。而且不需要数据库,免去了额外的数据库的配置,节约时间。 话不多说,先进入项目仓库下载最新发布版本。 先解压检查目录,不出意外的话应该是这样。项目根路径下有index.php,这种结构就可以直接FTP上传到托管服务商FTP上。不需要额外的配置,应该就可以使用了。然后,我就将压缩包上传到托管服务商的FTP上,解压到网站根目录public_html下(有些托管服务商是根目录www目录) 上述步骤完成后,FTP目录应该是下面这样。这个时候我们记得提前去app目录下,将upload.php权限改为0755。 修改权限的操作详情如下。可以按照我这样来。 还需要注意,根据该项目的兼容性描述,PHP环境需要满足一定的要求。 最低PHP 5.6,推荐PHP≥7.0及以上版本,需要PHP支持fileinfo,iconv,zip,mbstring,openssl扩展,如果缺失会导致无法上传/删除图片 文件上传视图提供文件列表管理和文件批量上传功能,允许拖拽(需要HTML5支持)来添加上传文件,支持上传大图片,优先使用HTML5旧得浏览器自动使用Flash和Silverlight的方式兼容 请确保fileinfo,iconv,zip,mbstring,openssl这几个扩展已经安装妥当。找到下面这种界面,然后用浏览器搜索检查是否都安装了。 ...

十一月 18, 2023

二零二三年三月第一周周报

最近,合规的要求逐渐渗透到技术层面。最近的一段时间,总有产品来找,说要实现这样那样的合规需求。或者是,做一些什么合规的调查问卷。我感觉,合规也主要就是说对于用户信息的存储、访问需要规范化,然后用户能够逐渐开始掌控自己的数据。然后还有一些人事上、组织架构上的变动对于技术层面造成的冲击,比如说一个业务被调度到其他部门去了,然后我们还在一直和这个业务共享数据库等资源。这个时候费用问题就突出了,就是说钱该算在那边。虽然是一个公司、一个事业群,但感觉可能是由于公司内部的核算机制问题,对于这些成本问题还是比较较真的,总会掰扯什么:他们用了我们的数据库,钱还在我们这边什么的。有时候感觉,这些都是属于内部消耗,并不是出于发展的眼光看待问题。另外,别的业务可能由于什么需求调整,需要修改一些公用的数据库结构或者接口,就比较麻烦了。因为这个时候,我们为他们花费工时来做这些事情,并不能得到任何肯定。但是,又会被那边的人催着,天天轰炸。自己夹在中间,感觉就比较难受。 然后,我上个周报提到的PHP网关迁移的问题,其实前一两个月就开始在做了。其实也早就写好了,但是在后续切换流量到新服务的时候,总是会遇到这样那样的问题。具体表现就是,测试环境流量切换很久了没有人来找,一切正式环境这样那样的人就来找了。所这个接口突然用不了,那个接口突然报错什么的。这种情况下,由于问题发生在线上,只能先把流量切回去再分析问题。一来二回,一个星期就过去了。有时候,切换了4、5天才会有人来找。这样一个流程下来久更久了。leader一直说这件事情做了很久如何,但实际的情况就是这样。虽然这个服务的后续具体的代码编写和维护工作并非我来做了,但是我也知道一个新的服务要能够完全替代就有的服务必须有一个这样的流程。因为在缺乏文档和测试用例的情况下,你不知道一个接口有什么暗藏的机制,也无法完全清楚我写的代码是否和原来的等效。所以执行层面,很多事情没有明确汇报,但是我感觉管理上也要能体会到。虽然过程比较反复、艰难,但最终这个服务的重构工作在本周还是完成了,所有的流量都迁移到新的服务上了。也就是代表,这个老PHP网关的生命周期终结了。 我接手主要的后台业务也快半年了,现在发现刚接手的时候由于缺乏经验,总感觉别人的技术高深莫测。但是,自己接触久了才知道里面有这样那样不完善的地方。还存在一些设计缺陷,非常严重的缺陷都有。比如说把Redis当作数据库使用,说白了就是没有设置缓存过期时间,然后Redis里面的数据又很重要不能丢失。这对服务里面还存在着一些缺乏文档、维护人员早已离职的老旧服务,这些老旧服务基本上平时无法来仔细研究。但这些服务中总有那么一两个依然在被访问,或者早就不能访问了,客户这几天突然发现然后急着催我们解决。很多安全问题也需要整改,某几个API账号有一些高位的权限需要收敛,但是我并不知道这个几个账号具体是做什么的。这些问题,就是日常频繁遇到的,很难避免。

十一月 15, 2023

二零二三年二月第四周技术周报

本周我发现有些服务框架写得并不是很好,特别是某些Java框架。当CPU占用达到40%左右的时候,就会出现大量的超时现象。这些服务,CPU核心和内存容量不是不多,工作线程数目也不是不够。但是,就是跑不满CPU。用Java性能工具分析发现,其实发现大部分工作线程处于Idel或者Waiting状态。目前综合所有情况分析,还是百思不得其解。NIO也用上了,还用的是Netty框架,但是吞吐量就是上不去。通过对于线程的分析,发现并没有特别繁忙的业务线程。推断应该是IO或者某种等待机制导致了这种低处理效率。 这次我是准备通过削减节点数量来降本,在执行前并没有考虑特别多的因素。所以我在削减节点容量的时候,只是通过查看CPU的负载来判断节点是否能够承受。当我将北京地域的workload平均CPU占用率提升到35%-40%的时候,整个服务出现了大量的超时现象。当时把我吓了一跳。后面从监控上分析,几乎是整个背景地域的节点都超时了,也就是处于一种”休克“状态。 本周也继续重构某一个老的PHP网关服务,新的网关用Java写成。但是我其实不是很赞成网关用Java,毕竟Java这种语言的执行特性很大程度上决定了它不适合特别高的并发。并且,我们目前所用的都是JDK 8,并没有引入轻量级的线程,每个4c8g容器内线程最多也就800个,多了线程切换开销就会特别大。所以单个容器的吞吐量有限,承载同样的流量需要更多的容器。刚开始我是用Go重写的,这个框架很不错而且也有专门的团队维护,但是leader还是让我用部门自研的那个Java框架。可能人事上的考虑居多吧,无奈,我还是先写吧。

十一月 15, 2023