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

从一月底到二月初,都属于春节的范畴。期间,负责保障春节阶段的运行,需要随时待命处理线上问题。我一直处于一种担忧的状态,好在线上问题并没有主动找上门。整个春节期间维持整体不动是最好的。 这周我在评估一个重要的需求,所带来的影响。我认为,对于一个新的业务需求,特别是应用于一个复杂的业务系统,需要考虑多方面的影响。如果这个时候,对这个系统并不是特别熟悉,经验不多的话,最好选择做最小改动。这不是保守,而是将影响控制在你可以想象地到的地方。因为你不会知道在什么地方,某个违背直觉的机制在运行重要的业务逻辑。得出这样的结论,并不是出于我的想象。 这篇文章写于半年后,到我写这篇文章的时候,我已经遇到至少两次这样的事情了。当时我对某个服务做出了大刀阔斧的调整,在调整完的时候,一切正常。发布后,也看似正常。直到若干星期之后,我偶然发现了某个串联上下游的机制,它差点受到我的影响。在承接一个业务系统的时候,很大概率它是转接和很多手的,藏有很多你不知道的历史,所以框架、核心逻辑能不动就不动。 然后,本周彻底解决了针对一个安全加密服务加密接口不兼容中文的问题。主要的问题是,它将加密的原文直接作为 Redis 的 Key。当原文中存在中文,会发现虽然 Key 存储了,但是找不到。我的方案是在存储的时候,Key 的内容不要直接包含任何业务原文,先取哈希。这样既能够避免一些编码、兼容的问题,也可以大大提升安全性。

2023年6月21日

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

这一周主要是确保在春节前各类服务的稳定。近期发现某个服务经常在流量高峰时段报超时,我提醒转交服务负责人处理。但是几天过去,服务负责人依然无法说明原因。只好亲自处理这个问题,因为报警已经十分严重,部分节点超时率能够达到 20%。 在这段时间中,应该是由于临近假期,流量大幅度上涨,12 月底相比已经上涨了 100%。所以首先是怀疑服务的承载力不足,所以先进行了一次扩容。但是,扩容后并未解决此问题,告警频率和超时率基本未变化。这种情况下,对服务的源代码进行分析后发现,该服务的接口会首先调用一个下游服务,然后再异步地向数据库中插入数据。因为异步操作并不阻塞工作线程,所以首先应该怀疑是这个下游调用的问题(实际上,我在数据库那边纠结了很久)。 从终端登录进一个节点检查日志,发现所有下游调用走的居然是同一个 IP 和端口,并且走的是稻草人节点。所以我先将该服务做上云操作,排除稻草人转发损失造成的影响。上云完成后,刚切流量的时候发现某个地域(不是原先的地域)下有大量超时产生,做了扩容后也无解,十分疑惑,所以暂时切回来了。我非常奇怪,为什么上云之后,超时率却变得更高了。 从云上监控分析问题,发现在切流量后,下游服务的某个云上节点 CPU 占用非常高,而其他的节点却很低。开始怀疑是负载均衡的问题,所以又回去检查服务的源代码。果然,该服务从注册中心拿到下游服务可用节点列表后,只会调用列表中的第一项。所以某个地域的几乎所有流量都集中在下有服务的某一个节点上了。而原先通过稻草人转发的时候,稻草人调用云上节点的时候会做一次负载均衡。所以,最初的问题应该是流量大量上涨,该服务却没有负载均衡,所以导致某个稻草人过载,进而导致超时。稻草人是代理节点本身没有逻辑,所以能够处理的并发数更大,超时问题不是很明显。而一旦上云,流量会直接集中在某个业务节点上,大量流量一齐涌入,会瞬间导致该业务节点大批量超时。 有了上述的论断后,回去找稻草人的监控,发现某个稻草人确实已经过载了,CPU 占用居然达到了 95 以上。知道原因后,接下来继续上云,先最大限度消除超时告警,然后再改代码添加随机负载均衡算法。这样做是因为节前修改代码,流程上会比较麻烦。 所以首先,我加倍了下游服务的单个节点的核数,这大大增加处理能力。然后,做切流量的操作,一个下游节点的 CPU 占用数突然升高,最终一直保持在比其他节点高很多的比例上,这是意料之中的事情。当整个服务平稳下来后,上云就成功了,这个时候告警已经消除,超时率归零。接着,就是修改改服务的代码,添加负载均衡的机制,在各个流程审批通过后为该服务发布新的代码版本。 在我发布完新版本后,下游服务的各个节点的 CPU 占用就接近了,最终问题解决。

2023年2月14日

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

时间进入二零二三年,今年是将一个辛苦的一年。今年将面临几方面的挑战,一个是将原先部署在物理服务器上的数据都迁移上云。然后就是,加快培养几个团队新人,让他们尽快承接目前的主要业务所涉及的服务,并且要求能够独立解决用户问题并对服务做出优化。这样我就能将一些工作转交到他们名下,专注今年预估耗时较长的重要目标。还有就是,个人的在技术和其他方面学习也到了一个攻坚阶段,这些方面决定了我未来 7~8 年的人生方向。 这一周,我的主要的精力在几个服务的日志框架与日志追踪的设计与规范上。首先是解决日志追踪的问题,为了能够跨服务地对调用过程中产生的日志进行统一的追踪,需要现将 TraceId 统一。但是目前这几个服务 TraceId 的类型层次不齐,有使用 Long 类型的也有用字符串的。并且,这些服务所使用的语言和技术栈也不一致。直接使用一些标准的分布式追踪框架中的 TraceId 应该无法支持所有服务目前的情况,只能用在一些技术栈比较新的服务上。 所以,从兼容性与改造的简洁性考虑,准备采用自定义生成的 Long 类型的数值作为 TraceId 并且限定位数为 16 位。前四位以 99 开头,作为统一 TraceId 的特征,然后剩余两位标识服务。接下来 4 位为当前的微秒数,最后的 8 位为两组四位随机数拼接。 虽然说这种 TraceId 并不能保证唯一性,但是在当前这种情况下是足够用的。如果服务为 Java 技术栈,生成 TraceId 需要充分考虑到线程竞争的情况,最好为每个线程分配一个随机数发生器。或者,直接使用 TreadLocal。 Java 服务在处理请求时,会提取请求中的 TraceId。如果 TraceId 是 99 开头的,就不生成新的 TraceId 了。如果不是的话,按照上述方式产生 TracId 并存储于 MDC 中。当遇到异步执行的时候,需要注意拷贝 MDC 中的内容到旁线程,不然会丢失追踪信息。当需要调用下游服务时,需要将存储的 TaceId 传递给下游服务。最后,当请求处理完毕时,需要清空 MDC,防止污染下一个请求的追踪信息。

2023年1月20日

二零二二年十二月第四周技术回顾

本周我感染了新冠病毒,一共在家待了 9 天。在此期间,工作上的最主要的事情是评估一款小程序的推广上线,对我所负责的基础服务体系的影响。这款小程序切中了当时国人的需求,预计有大量流量涌入,可能对基础服务体系的核心服务造成冲击。原先,他们有也一个功能将要上线,流量也很大,我已经评估过并且扩容了。但是,这次还是在他们推送了上亿的量级的通知后,发生了大批量的超时现象。 早上八点钟,躺在床上修养的我就被叫起来,说是登录接口出现大量超时现象。 我马上拿出笔记本,连上内网一看,这个接口流量直接翻了 20 多倍。我都捏了一把汗,首先是怀疑业务容器数量不足,无法承接这么大的流量。然后通过筛选日志,发现并不是这个问题。问题分为两个部分,其一是内部某个接口有限流,当前流量已经超出限流;另一个是缓存数据库挂接的数据库打满。 内部接口限流比较好解决,通过紧急呼叫找到了对应的同事调高了限流的阈值。另一个缓存数据库,由于大量老用户涌入导致不断要从数据库中读取老数据到缓存中,而且频率随着流量的增长不断提升,最终打满了缓存后面挂接的数据库的 I/O。后续针对几个核心的缓存数据库,再次做了一批扩容,提升了缓存容量和节点数量以应对后续更大的流量和并发。 在日常维护上面,主要是将某个网关服务 Pod 的 k8s 调度策略做了调整,将其调度到由 CVM 虚拟化直接支持(一层调度)的计算节点上。原先,这个服务调度的到的计算节点是我们公司采购的物理服务器上虚拟出来的 CVM,用作计算节点。调度到这上面以后,再于这个 CVM 上虚拟出一个执行空间(参考 Docker 技术),然后将 Pod 调度到这个节点开始运行(二层调度)。一个计算节点上,可能存在几个甚至十几个 Pod。这些后台程序都运行于同一台操作系统中,并且只是执行空间不同罢了。这就导致了 CPU、内存、网卡等资源的隔离性不强,常常会互相干扰。并且当这台 CVM 计算节点需要重启或者升级的时候,其上的所有 Pod 都会被驱逐。 计算节点操作系统中的风吹草动,会影响到其上所有 Pod。这对于这种网关这种可靠性和延迟要求苛刻的服务,来说并不合适。 而一层调度计算节点,其实是由云来分配计算资源并虚拟出 CVM 来单独执行这个程序,CVM 资源是从整个云资源池中取得。并且这种方式支持 k8s,因为这种新的调度方式会虚拟成 k8s 上的一种“超级节点”。k8s 可以直接将 Pod 调度到超级节点上,Pod 可以直接基于一层调度运行,也就是直接运行在一个单独 CVM 上。这种方式隔离性强,互相之间不会受到影响。由于每个 CVM 只运行一个节点,所以当单独一个 Pod 出现问题时,可以针对这个 Pod 的问题进行修复,而不会影响到其他 Pod。 通过修改调度策略,将服务的所有 Pod 调度到超级节点后,整个服务运行更加稳定了。在高峰期超时率和 CPU 占用率双高的情况也很少再出现。

2023年1月19日

二零二二年十二月第三周技术回顾

这一周,主要的工作是针对某个 Java 服务进行优化。该服务一直存在 CPU 占用率无法提升上来的问题。首先考虑的问题是,该服务是否存在工作线程不足的问题。后面发现,并非 CPU 占用无法提升,而是 CPU 占用提升后会导致较多的超时问题。该服务很久以前就有反馈性能不足,不建议继续使用。 所以我感觉,这个问题来自于框架,而非业务代码。 经过对框架代码进行阅读和梳理,该框架使用 netty 作为 NIO 服务器框架,并且会在执行业务逻辑的时候分发业务处理任务到工作线程。然后工作线程来处理业务逻辑。之前框架有个问题就是,工作线程数太少,难以应对高 IO 的情况。而现在的情况下,推断并非上次一样的问题,从日志来看工作线程数是足够的。会不会是客户端的问题。在整个微服务架构下,该服务会作为客户端去调用其他服务的接口。 这可能是一个切入点。通过阅读代码,发现该服务框架通过实现 Java 的 InvocationHandler 接口来生成代理类 ObjectProxy,该 ObjectProxy 接管对其他服务的 RPC 调用。业务代码中,通过 RPC 的方式发起对其他服务接口的调用时,ObjectProxy 会通过它关联的 ProtocolInvoker 来获取目标服务对应有效节点列表(这个有效节点列表每隔 30s 刷新一次)。 而后,通过将列表传入负载均衡器 LoadBalancer 获得本次调用的目标节点。然后,就通过具体协议对应的 Invoker 类向目标节点发起调用。Invoker 负责管理与目标服务之间的长连接,在调用的时候会选取一个连接来发送请求并接收响应。具体的请求方式与此次调用类型是异步还是同步有关。 客户端需要调用的每个目标服务由多个节点组成。对于每个节点,该框架都会默认创建两个 I/O 线程负责网络 I/O 传输(NIO 模式)。另外,该框架会为每个节点分别默认创建和处理器个数相当的 TCP 连接。每个 I/O 线程包含有一个 Selector 对连接的相关的事件进行轮询监听。而在每个 TCP 连接上会建立一个 TCPSession,每当需要发送一次请求时,会建立一个 Ticket 来跟踪这次请求及其关联响应。对于同步请求来说,发送请求后会被阻塞,直到响应到来为止。 对异步请求来说,会在 Ticket 中填入 callback 函数,当响应到来时,会通过 TicketNumber(Ticket 的唯一索引)找到对应的 Ticket 并调用其中事先填入的 callback 函数,进行后续的处理。 该框架对 NIO 的操作,在底层用到了 Java 提供的 NIO 库的能力。上面所提到的 TCP 链接,其实就是 NIO 库中的 SocketChannel。那么该框架如何分割各个数据包呢,该框架通过 Buffer 来存储目前已经读到的数据,而我们常用的 RPC 协议,会将该包的字节数存在包的首部,只需要比较 Buffer 与字节数的大小即可知道包是否读全了。如果包没有读全,就继续等待后续的数据到来。如果包读全了,则按照字节数规定的大小从 Buffer 中分割出相应的数据来处理即可。在该框架中,会额外从一个线程池中取出工作线程来进行有关于 Ticket 的后续处理。目前从现有的代码来看,这个工作线程池只用来做这件事情,它的默认工作线程个数与核心数相同,最大线程数是核心数的两倍。 根据 Java NIO 库的定义,Channel 中规定了几个 IO 操作,Selector 会轮询检查这些操作是否就绪,如果就绪就会返回 SelectionKey。SelectionKey 中包含了从就绪通道中进行正确操作的必要参数。 ...

2023年1月19日

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

这一周的工作主要是对我负责的这一方面的工作的一个梳理,目前发现了许多的问题。这些问题主要集中在数据上云方面,目前的问题主要是如何安全上云、怎么改造目前的单地域部署方案、如何修复云下数据和云上数据之间的不一致。另外,发现还是有一些服务在使用云下的数据库,这些云下数据库按道理是要废弃的。但是,这些服务都是一些老服务,代码改动会带来一些风险,这就需要在行动前调查清楚。 调查的方面包括,现有的数据上云辅助服务的基本原理和相关的代码逻辑细节,最好能够尽早发现其中存在的问题并及时修复。 另外一方面,是数据上云过程需要实时监控,尽量全面地对服务接口调用质量、超时率、写失败率、不一致率等等有个清晰的把握。这一方面最好能从上报监控和日志监控两方面来做。而多地部署方案,目前打算采用一主多从,主从之间单方面复制,只写主库,从库只读这几条原则入手。采用多地部署方案主要是提升服务稳定性,降低大多数请求的延迟,提升服务质量,消除跨地域的链路不稳定带来的影响。多地部署所带来的数据同步延迟不容忽视,必须有一个可以接受的延迟,这一块要从理论和监控两个方面来把握。 再就是,掌握一门脚本语言很重要。特别是当有大量重复的事情需要处理,或者需要分析一些数据来获得一个结论的时候。能够较为熟练地掌握类似 Python 这样的脚本语言,有很大的优势。但是,如果说拿 Python 来写一个大型程序的话,我觉得是不明智的。每种编程语言就像不同的刀,都可以拿来切菜,但是有些刀更适合用于切肉或切骨头。

2022年12月14日

二零二二年十二月第一周技术周报

这一周的工作,总结说来,主要就是将一个核心服务上云,然后不断将云下的节点切换成流量转发节点。上云的第一步就是,在云上环境部署该服务的节点:迁移配置文件、环境,然后根据稳定版本的代码编译适用于云上环境的镜像,然后让服务在云上环境跑起来。服务跑起来后并且测试完成后,云上节点目前是没有任何流量的,这个时候需要将云下的部分流量转发到云上,首先是要将云下的部分节点替换成转发节点,转发节点的作用是将主调打过来的流量转发到云上的节点。后续可以利用这部分流量来观察云上节点的工作状态,检查异常,可以称之为“流量灰度”。流量灰度一般控制在整体流量的 1%-5%作用,要看服务的关键性来调整比例,测试环境下可以适当多一些,25%-50%都可以。流量灰度是发生问题时,或者接到问题单时,只需要关闭云下的流量转发节点即可。当然,上面的一切必须先要在测试环境操作,测试妥当了再按照同样的方案谨慎操作生产环境。 有一些后台体系错综复杂,一次调用涉及多个服务,业务逻辑扑朔迷离,这时候尽量控制变量一段时间内仅仅做出一次改动,等观察一段时间后,情况稳定下来了再做下一步操作。或者可以把大的步骤拆分成几个小的步骤,一段时间内只做一小步,观察一段时间后再继续推进下一小步。这样不容易出错。 接下来,如果灰度验证通过了(一般持续一个星期)。 接下来就是切换路由了。切换路由就是将云上的服务节点的路由直接切换到云上节点,在这以后其他云上的主调服务将直接访问该服务的云上节点,而不会再去访问云下节点。在切换路由的时候必须特别小心,因为大量的线上流量会直接打在云上节点上。在操作之前首先,需要根据往常数据计算云上各个地域下需要的节点数量,一般是按照高峰期流量水平来计算,如果不够要扩容,以免由于容量不够造成线上大量超时。这个时候,云上节点数宁愿多一些,因为多了后续还能够慢慢缩回来,成本不高。但是如果少了造成大量超时的话,节点扩容是需要时间的(主要是资源调度与服务启动耗时),这个时候容易造成用户投诉。如果由于超时引发客户端大量重试,就有可能带垮整个服务,引发线上事故。当时,我是挑了一个流量少的时候操作,这样能够减少由于切换抖动造成的影响。 切换了路由后,云下节点就可以逐渐全部换成转发节点,将云下的流量都转发到云上来处理。然后通知主调服务的负责人,尽量把服务上云。因为,转发节点转发到云上是有开销的,会增加延迟。

2022年12月9日

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

这一周,我主要在优化一个服务。这个服务是用 Java 编写的。生产环境流量不大的时候,也会出现调用批量超时的现象。而且发送超时的时候,CPU 占用率很低。经过观察,CPU 占用一直就上不去。这个时候就推测是不是线程都阻塞在了某个操作上,导致这个问题。我所接触到的大多数服务,包括这个服务,都是 IO 密集型的服务。 这类服务涉及大量的 RPC 调用,当 RPC 调用的时候,工作线程会阻塞,导致无法处理其他请求。所以这类服务的工作线程数都会设置得很大,确保有多余线程来处理 IO 请求,防止由于大部分线程阻塞导致后续请求得不到处理,最终导致大批量超时。 这其实是有问题的,虽然说目前 Java 在处理 Socket 和解析 Request 的时候已经采取了 NIO 的模式,但是对于请求的逻辑处理依然采用工作线程池的方式。当遇到工作线程阻塞在 IO 请求上的时候,这个工作线程只能等待 IO 请求完成或者超时。如果说,IO 请求所返回的结果并不是该请求最终结果的依赖,这种情况会将请求提交到其他线程池处理。这个时候它的处理对于该工作线程来说是异步的。 最后经过排查,这个服务由于框架问题,有关于工作线程数量的配置并未被读取。这导致了该服务启动时采取了默认的工作线程的数量设置,也就是工作线程数和 CPU 核心数相同。所以,工作线程在处理下游 RPC 调用时,只要下游接口的耗时稍微有波动,就会导致大量请求超时。这个时候,由于工作线程大部分时间阻塞在等待 IO 操作返回上,所以 CPU 占用也是很少的。 如何排查的呢,首先需要在日志框架中设置打印当前线程名称,对于 logback 日志框架,就是在日志格式设置中加上%t。然后,对单个节点在测试环境进行压测,查看在一定 QPS 下,该服务对于请求的处理情况。最好能给这个请求的逻辑代码加上 StopWatch,辅助进行分析。最后发现,输出日志的线程只有几个,不对劲。然后,使用 jstack 过滤出线程数量。发现确实只有四个。 可以解释一下,这个 Java 服务使用的框架采用了 netty 框架进行请求的处理,最后会转交到 netty 的工作线程池对请求的逻辑进行处理。在这里,netty 工作线程池对应的前缀就是 nioEventLoopGroup-5。另外可以从 cpu 累计时间来辅助证明,确实只有这 4 个线程在处理所有的请求的主要逻辑。对于这种服务来说,生产环境下,工作线程数量一般设置为 800~1000,并且会严格设置 RPC 调用的超时时间防止大量工作线程被阻塞,最终导致节点的吞吐量急剧下降。 在更新框架后,该问题得到了解决。另外,我还发发现了该服务的一个主要接口涉及基于 http 协议的下游调用。该调用通过操作 OkHttp 库来进行,我发现调用并未设置超时时间。这是错误的情况,如果遇到极端情况,下游迟迟不返回,那么工作线程就会阻塞很久。所以,必须对下游调用设置一个合理的超时时间来保护上下游调用之间的通畅,防止雪崩现象的发生。超时时间一般分为三种,连接超时、读超时、写超时,都需要设置。另外,对于可能存在大量的 Http 调用情况,我开启了 OkHttp 的 ConnectionPool。 ...

2022年11月28日

二零二二年十一月第三周技术回顾

这是我的第一篇有关于工作中遇到的技术问题的周期性回顾,所以本期的技术回顾主要是总结长期以来的经历,为以后的技术回顾开一个好头。 来公司工作了快半年了,最近我从客户端开发转成了后台开发。这是我希望的,但其实不是我要求的。因为个人感觉,在中国科技行业目前的职业生涯规划中,后台开发可以探索的东西会稍微多一些,接触问题的规模也会大很多。其实客户端也大有可为,我的第一个比较成熟的开源项目 GpgFrontend 就是一个客户端项目。我在其中投入了大量的时间,解决了大量的问题。尤其是编译问题、平台兼容性问题、稳定性问题。其探索的深度,问题的难度,都是不小的。 我目前觉得,为什么客户端和后端开发会被分出高下来,那是因为心没静下来,总想着往上爬。另外,对于技术的追求本来无所谓客户端、前端或者后台,他们背后的技术思想本质上是通用的,但是目前将职业看得太重,反而以职业分工来将技术划分地分明。我觉得这是不好的,所以虽然我是目前是一个后台开发,但我绝不能认为我仅仅是个后台开发,其他技术看都不看,学都不学,自己把自己的思维框起来。 现在职位转成了后台开发,我反而不知道该怎么做了。后台开发,我目前记得,我自己写过的最早的后台项目,是我 15 岁的时候用 Nodejs 写的一个天文论坛 Stelescope 。用了当时流行的 Express 框架。现在我还记得,当时第一次接触 MongoDB、登录登出的状态保存、异步回调等等技术概念。我印象最深的是异步回调,当时用了很久才理解。因为我当时连进程、线程的基本概念都搞得不是很清楚,更别说什么异步回调和闭包了。 那时,网上还在争论 Nodejs 和 PHP 孰优孰劣,Nodejs 到底适合什么场景,然后就是异步回调和多进程并行孰优孰劣等等。当时记得异步回调不能被阻塞,如果遇到阻塞操作会使用一种非阻塞的 API,这种 API 能够立即返回,将阻塞操作放到一边处理,主线程依然能够继续处理后续的代码。等阻塞操作处理完了,就会通知主线程执行回调函数来处理阻塞操作的结果。 后续,我还接触了 Python 的后台开发,写了一个简单的班级学业考勤管理系统(SP)。在这里,我接触了 MVC 的思想,我理解为模型、视图和控制器。这是一种重要的思想,那时候前端的概念还不是很明细,前后端分离还不是主流思想。在当时,服务器负责动态页面的生成,由请求触发控制器做出响应。控制器基于模型进行计算,并最终通过模板引擎将模型渲染成页面,而后页面被返回到用户浏览器。就这么一个过程。记得当时,会在服务端定义很多模板,模板里选一些地方镂空,待会存放数据。或者是定义一个小的卡片控件,放在 for 语句中,待会用模板引擎生成出很多卡片出来。在当时,我作为后台开发要考虑方方面面,包括页面漂不漂亮,数据安全,响应快不快等等。 然后大一下开始,我就接触了 Spring 框架,具体用的就是 SpringBoot。这个时候,才真正了解到了关系型数据库。原来我对于关系型数据库的理解,仅仅是会安装,配置。记得很深刻,当时接触到了前后端分离的思想,觉得这是个好东西。拿出来和王老师讨论,说我们的全员育人管理系统用前后端分离最好。王老师非常开明,和我聊了很多,同意了我的方案。前后端分离,顾名思义,就是将动态页面的渲染放到用户那边,服务器仅仅负责数据的处理和存储。这样有助于分工和解耦,虽然当时网上也有很多质疑的声音,我还是觉得这是趋势。当时害痴迷于一种 RestfulAPI 的接口规范,觉得如果这样做我们连写项目文档也可以免了。但是现实很骨感,在实践中对于一些复杂的情况就难以保持 RestfulAPI 的风格。 通过与 SpringBoot 打交道,我学会了很多有关后台的东西,目前我也在用这些原来的经验。我在大学本科生涯中,写了很多的 SpringBoot 项目。到现在,我进了公司发现部门的技术栈就是 Java,新的项目用的框架都是 SpringBoot,反倒用上了。虽然说我当时很厌恶 Java,觉得它臃肿与繁琐,包括现在也没有什么好感,但是 Java 的生态确实是很强大的,想找个什么组件很容易,而且 Java 组件都是成熟的、良好维护的并且文档齐全。Java 技术栈对于面向生产的后台项目来说,确实是个省心、好找工作的技术栈。 在公司工作半年,其实算上实习都快一年了。总的来说,对于后台这块,我一直在捣鼓优化、缓存、线程这些东西。每天都在分析各种告警,有些是业务上的,有些是技术上问题。对于业务上的问题,只能好好地了解背景。对于技术上的问题,需要扩展自己的知识面,静下心来研究。包括一些目前自己感觉难以解决的问题,比如说在容器化部署过程中,总有一些容器存在偶发的超时问题,一直不能确定到底是容器的问题还是后台应用的问题。我现在思考,这一块需要一些更加深入的知识,比如说 CPU、内存、网络的虚拟化问题。 今天先这样吧,还有点其他事情要做。

2022年11月21日

自己总结出养成习惯的一些实用经验

很多人都会想养成一个好习惯,因为一个好习惯能够然仍长期做一件好的事情。这件好的事情,每做一次都会带来一定的好的影响。而一个好的影响的一次次叠加,经过比较长的一段时间,就能使得一个人的某个方面产生巨大的良性改变。而很多人想养成习惯却难以坚持下来。反而求助于很多外力来“监督”自己,这样反倒让自己变得更加痛苦。 我觉得想养成习惯而不得根本原因是缺乏内在的强大的动机。比如说,你想学习一门外语。你知道,学一门外语能够有种种好处。但是,你其实内心清楚,这些好处只不过是锦上添花,并不会影响你的前途。自己是骗不了自己的,我这里不是要说要不断暗示自己学习一门外语很重要。 我这里想建议,你要让学习一门外语真的对你来说很重要,这才是有效的。比如说,你报了一个出国留学的计划,是你人生规划的一部分,花了很多钱签订了合同。你现在是不学也不行了,不学,你的人生计划就完不成,为了签订合同所花费的钱也打了水漂。这样,才会产生强大的内驱力。这种内驱力一旦产生,它会在你的内心不断提醒你,不断鞭策你,让你自动去拿起外语书。除非,你有什么拒绝它的理由,不然你的怠惰在你自己这里就过不了关。 另一个我发现的重要一点是,习惯的订立要顺应自然,最主要的是要顺应自己的内心。不能说,给自己定一个自己不喜欢的习惯。你不喜欢某件事情,你非要去强迫自己做,那最终是做不成的。喜不喜欢一件事情,主要看你先做这件事情前会不会有一种愉悦感,你能想到你做这件事后,你能收获什么,这让你感到愉悦。有一种愉悦感,能够时不时地提醒你去做一件事。 第三点,长期坚持而非一直坚持。不能说要强迫自己一直坚持某种习惯,因为随着自己生活的继续,习惯是会被慢慢改变的。长期坚持,这意味着在一段很长的时间内,你是会经常做这个事情的。 偶尔的一两次没有做,不代表习惯没有了或者是习惯被打破了。因为事务的发展不是一帆风顺的,总有反复。所以给自己一点做不一样事情或者不做这件事情的时间,让自己处于一种比较放松的状态。记住,盈满则亏,让自己总是游刃有余一些,容许一些小的偷懒,养成习惯的过程中就不会出现大的报复性波动。在养成习惯的过程中,你可能会发现这个习惯并不是一个好习惯,或者并不适合你。这个时候请果断放弃这个习惯。因为养成习惯是一个正向反馈,是自己不断激励自己,不断有所收获,而不是一个自己和自己内心拉扯的过程。 还有一点就是,习惯的养成不能影响自己的正常作息。不能说我今天要背单词,由于一些突发情况没有做成,然后在深夜也要背完,挤占了自己的睡觉时间。这是得不偿失的,反而会因为自己的生活周期的失调,陷入一种疲于奔命的情况,导致习惯越来越难以养成。 最后,如何知道一个习惯是否已经养成?当你发现,你今天不做这件事情的时候,好像缺了点什么的时候。或者,某个时间会自动地想起去做这件事的时候。这个时候就可以认为习惯养成了,你只要顺着来,享受习惯的惯性带给你的快乐,在这个过程中,你就能不断收获自己在某一方面的成长和快乐。

2022年4月23日