理解 Kubernetes 中的 CPU 资源分配机制

Kubernetes(k8s)是一种流行的容器编排平台,它允许开发人员在云环境中部署、管理和 自动化容器化应用程序。在 Kubernetes 中,CPU 资源的分配是一个关键的问题,它直接影 响着应用程序的性能和可靠性。本文将介绍 Kubernetes 中的 CPU 资源分配机制,包括 CPU 请求和限制、CPU Share 机制以及 CPU 调度器等相关概念,以帮助开发人员更好地控 制容器的 CPU 分配,从而提高应用程序的性能和可靠性。 CPU 分配单位 在 Kubernetes 中,CPU 的分配单位是 millicpu(毫核),一个 CPU 资源等于 1000 毫 核。例如,一个 Pod 请求 0.5 个 CPU 资源,可以表示为 500 毫核。它是基于 Linux 内 核的 CPU 分配机制实现的。在 Linux 内核中,CPU 时间是以时间片的形式分配的,每个时 间片通常为几毫秒。Kubernetes 利用这个特性,将 CPU 时间片分配单位转换为毫核。 CPU 资源分配机制 在 Kubernetes 中,CPU 资源的分配是通过两种方式来实现的:CPU 请求和限制。CPU 请求 用于告诉 Kubernetes 调度器,该 Pod 至少需要多少 CPU 资源才能正常运行;CPU 限制用 于告诉 Kubernetes,该 Pod 最多可以使用多少 CPU 资源。如果节点上可用的 CPU 资源不 足以满足 Pod 的 CPU 请求,该 Pod 就无法调度到该节点上运行。如果节点上可用的 CPU 资源不足以满足 Pod 的 CPU 限制,该 Pod 仍然可以运行,但可能会受到 CPU 资源的限 制,导致运行缓慢或者出现其他问题。 需要注意的是,CPU 请求和限制是 Kubernetes 中 的两个独立的概念。通常情况下,应该根据应用程序的实际需求来设置 CPU 请求和限制。 如果设置的 CPU 请求过低,可能会导致 Pod 无法正常运行或者运行缓慢;如果设置的 CPU 限制过高,可能会导致节点上的其他 Pod 的 CPU 资源不足,影响整个集群的性能和可用 性。 ...

2023年6月20日

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

这一周主要是确保在春节前各类服务的稳定。近期发现某个服务经常在流量高峰时段报超 时,我提醒转交服务负责人处理。但是几天过去,服务负责人依然无法说明原因。只好亲自 处理这个问题,因为报警已经十分严重,部分节点超时率能够达到 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 来跟踪这次请求及其关联响 应。对于同步请求来说,发送请求后会被阻塞,直到响应到来为止。 ...

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日

搭建一个免费开源好用的Docker面板 Portainer

Portainer 是一个好用的 Docker 容器控制面板。它的代码仓库在这 里。它支持单节点和集群部署。 在此之前,我已经将个人服务器完全容器化了。如果能够可视化操作、监控各种容器,对于 日常的运维帮助很大,感觉一切尽在掌握。 可以看看搭建好后的效果,界面还是很美观 的。 可以快速拉取镜像 可以可视化发布容器,并灵活设置环境变量、网络、重启策略等等参数。 可以在镜像更新的时候,快速用新镜像更新容器。 还可以快速进入接入容器命令行,查看容器日志。 可以说,我日常需要的功能都有,感觉很完善了。 搭建 那么对于单点服务器来说,该如何搭建呢,首先你要确认你的服务器目前有安装 Docker。 可以输入docker -v来看看当前环境中是否存在 docker。 如果不存在,需要安装 docker,你可以在 Google 找找教程。对于 AWS 的 EC2,可以输入sudo yum install docker -y来安装。 确认安装好 Docker 后。确认一下 Docker 是否启动。然后看看 /var/run 路径下是否有 docker.sock 文件。 然后,确认 8000 端口和 9443 端口都没有 被占用,因为 Portainer 后续要用到这两个端口。 将应用数据存储在 Docker Volume 如果你想将 Portainer 产生的应用数据存在 Docker Volume 中,你可以直接执行下面的命 令。后续部署完成后,Portainer 所使用的 Docker Volume 也可以在 Portainer 看到并管 理。如果 8000 端口被占用了,可以将-p 8000:8000改成其他端口,类似-p 12345:8000。9443 端口也是类似。 如果你想指定容器名为其他,可以修改–name 参数, 比如说--name myportainer。 ...

2022年11月23日

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

这是我的第一篇有关于工作中遇到的技术问题的周期性回顾,所以本期的技术回顾主要是总 结长期以来的经历,为以后的技术回顾开一个好头。 来公司工作了快半年了,最近我从客 户端开发转成了后台开发。这是我希望的,但其实不是我要求的。因为个人感觉,在中国科 技行业目前的职业生涯规划中,后台开发可以探索的东西会稍微多一些,接触问题的规模也 会大很多。其实客户端也大有可为,我的第一个比较成熟的开源项目 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 技 术栈对于面向生产的后台项目来说,确实是个省心、好找工作的技术栈。 ...

2022年11月21日