【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

洞悉微服务:从 PaaS 到 Serverless 的演进

作者:杨华辉

  • 2023-08-08
    北京
  • 本文字数:8947 字

    阅读完需:约 29 分钟

洞悉微服务:从 PaaS 到 Serverless 的演进

本文整理自 QCon 全球软件开发大会北京站 2023,字节跳动基础架构函数计算负责人杨华辉的主题演讲微服务从 PaaS 到 Serverless 的演进。以下为演讲全文实录。


本文将带你洞悉微服务 Serverless 的挑战和实战经验,了解函数计算如何打破固有模式,扩展应用边界和提高领域天花板的思路。本次演讲共分为 4 个部分:


  1. 背景介绍:PaaS/Serverless/ 字节微服务 / 函数计算;

  2. 探索之路:FaaS 尝试 / 改造 / 变数 / 优势;

  3. 生产实战:框架 /RPC/Mesh/ 镜像 / 异步长任务;

  4. 总结展望:应用场景 / 通用 Serverless/ 云边一体。


背景介绍:PaaS/Serverless/ 字节微服务 / 函数计算



对于 PaaS,可以很直观的以汽车行业作为比喻,FaaS 的演进思路类比于汽车行业,就像是打车的形态,是资源效率极度高度的利用,去增加弹性的能力,比起租车更加灵活。对于整体的集团业务来说,资源的利用率进一步提升,可能比较重要的一种计算承载形式就是函数计算。当然这是在传统概念中,今天的 topic 不是纯粹的函数计算,我希望用函数计算去承载一些微服务的 workload。


字节的微服务与函数计算规模



字节跳动的微服务应该是业内比较庞大的体系了。对于字节的私有云租户来说,有 9 万 + 的微服务,日变更有 2 万 +,容器数有 1,000 万 +。字节跳动的在 Mesh 方面已经大规模落地,支持 Golang(Kitex/Hertz)、C++、Python、Java 等高级语言。



字节跳动函数计算跟微服务框架是属于一个大的部门,体量在业界已经做到最大的规模。目前在私有云场景,有 17 万 + 的函数,高峰的 QPS 每秒钟有 1.2 亿的请求数,低峰的时候大概是 3,000 万的这样子。支持的语言生态体系除了前面这些所谓的高级语言,也为了微服务形态支持 native 形态。


PaaS -> Serverless 演进的原因


为什么要做微服务 PaaS -> Serverless 演进。目标很明确,就是要降本。我们从两个思路去看,一是弹性调度,如果能做到整体的弹性调度,对于整个集团规模的机器调度是一种容量托管,平台方就可以做到满足用户 SLA 的弹性,对于高低峰错峰比起混部来说会做到更加的极致。二是因为要上 serverless,服务必须要有个假设,就是扩缩会成为常态,所以服务的整体部署跟研发体系其实是要往弹性方面去做变更的,至少你得有这样的一个心态,为整体架构体系带来一种比较鲁棒的设计,因为你可以随时应对弹性,弹性不会让你的业务受损。


探索之路:FaaS 尝试 / 改造 / 变数 / 优势


既然要做的话我们怎么做?先说一下我们一些错误的示范,大家之后避免走弯路。



首先我们希望去复用 FaaS 这种弹性的能力的,因为既然 FaaS 做到了弹性,为什么不用?因为都在一个组织架构下,所以思路也很简单。早期我们希望 FaaS 改的尽量少,只在现有架构上改,对于 FaaS 体系来看,这张图应该是比较具备代表性的, FaaS 一般都会走 Gateway,后面会打到 function Pod,它只是一个运行时的载体,然后无论在中心的 Gateway 的网关还是 Pod,都会有个我们内部叫 runtime 的组件,不同的地方叫的不一样。我们要支持 KiteX,rpc 的话如果是 HTTP 相对简单,因为大部分 FaaS 本身就是 HTTP 这种 transport 协议的。


如果对于 Thrift 这种框架的支撑,针对客户端的流量,你需要去能认识它的 Thrift,我们前期是让 Thrift 去用 HTTP transport 去做,这样可以用现有的 FaaS gateway 去承载流量。在每个 pod 内部,我们改变了用户的编程习惯,让他既知道了 rpc server,又知道了 FaaS 的 handler,这样确实能去 enable 一些业务上 FaaS,而且获得 rpc 用 FaaS 的体验。但是问题不久就显露出来了,一个是学习成本高,用户不仅需要学习 rpc 框架,也要学习 FaaS 这种框架。第二个是排查错误困难,因为涉及到两个团队的跨团队协同,在上线具体业务的时候,在 trouble shooting 的时候需要去明确错误的边界在哪里。第三个是用户接受度相对来说比较低,因为 RPC 相对在 FaaS 上面有点像二等公民,所以这块的支撑未来都是具备不确定性的。



我们反观看一下 Lambda 怎么做的。Lambda 在支持这种现有的框架的时候,右边 Web APP 为每一个框架去启用 adapter,但是 Lambda 这种模型基本上是用橙色部分去向它的内部的事件中心组件去拿这种消息转化成 HTTP 格式发给现有的框架。这套架构写出来的 container image 放在 paas 平台就是 AWS Fargate 或者放在 AWS Lambda 上面,只要通过一个开关去 enable 这个 adapter,就可以去实现一套的代码或者一套 container image 在不同的平台中做切换。所以给我们带来启发,不能让用户动太多的代码,用户基本上就是写蓝色部分就行了。


我们从以上的这种错误示范中得到了一些经验教训。


开发体验对齐:支持运行原生的框架代码,HTTP/RPC⽤户不⽤额外学习 FaaS 开发规范


RPC 协议⽀持:FaaS 要原生支持 RPC 协议,不用引入代码层⾯的协议转化


性能深度优化:贴近原生的性能,减少流量调度引入的额外性能损耗


但是我们也看到如果 FaaS 要做这个事情的话,有一定的变数:


1、自动扩缩容是一种非稳态的情况


2、服务发现机制和传统微服务的不⼀致,因为 FaaS 是一种毫秒级别的冷启动要求,对于服务发现的这种中心化的架构来说,其实在设计中不太满足这样的需求,基本 FaaS 都会自己去做一套比较快速的毫秒级别的服务发现体系,势必带来两种服务发现体系之间的差异


3、数据链路的管控带来的开销


有劣势当然肯定也会有优势,我们要去规避劣势,发挥 FaaS 的以下优势:


  • 无流量缩零,快速冷启动,自动扩缩,敏捷开发上线,降本增效

  • 结合 FaaS 事件驱动的能力,便捷的触发器接⼊

  • 一套代码,处理在线请求、MQ 请求等


生产实战:框架 /RPC/Mesh/ 镜像 / 异步长任务


FaaS 整体架构



我们先简单看一下 FaaS 的整体架构,各家的 FaaS 的架构基本上都是差不多的,大家基本上可以只关注于机房内部,因为外部的多个机房是为了多机房容灾。我们需要关注 FaaS 做弹性的能力以及做流量管控需要引入的一些组件,在此现出来的是 gateway 组件和 dispatcher,它是一种流量管控的组件,这种组件可以对流量进行强管控,这样才会在平常没有流量而突然有流量进来之后,能兜住流量,让它能启动完之后把流量再打过去。冷启动需要有一些比如说池化技术、预先启动等技术,一般是通过 Worker Manager 来做的。我们内部通过一些快速的消息通道去做服务发现,抽象出来一个 Discovery 服务。我们是在 k8s 这种生态上面去打造的,我们可以把 k8s 当成一个运维平台,让它去承载 FaaS 这样的 workload。但是 k8s 原生的对 pod 的拉起、服务发现其实是不满足 FaaS 需求的,所以我们内部需要在每台机器上去构建一种 Daemonset,就是 HostAengt 去做一些强管控。


当然这只是极致情况下 FaaS 的一个路径,当你做一些大规模部署的时候,一些组件可能会被无意识或有意识的 bypass 掉,去满足大规模情况下的部署需求。


目标:开发原生应用


开发原生态应用,这是我们的一个目标,围绕这个目标,我们希望做到以下几件事:


  • 支持不同类型,不同语言的原生应用迁移到 FaaS 平台,低成本 Serverless 化

  • 帮助业务便捷的接⼊FaaS 已有事件源(消息队列、定时器、⽹关)

  • 提供完整的冷启动、自动扩缩容能力



用户说:我不想改代码,但是我想 Serverless。


我们提出了 FaaS Native 这样的一个方案。如图左边大概是 FaaS 原业务的单 pod 内部的一种主要组件,它需要用户去写用户 handler,用户 initialize 它(比如用 Golang),我们会提供一个 SDK。这是传统像 FaaS 事件触发类型的模式,其实需要进一步去支撑 HTTP 这种框架的话相对比较简单,因为内部都是 HTTP 协议的,我们只要去约定监听在哪个端口,然后你去监听我的 health checker 请求,我的 HTTP 的请求比较透明的去 proxy,中间可能有些 debug server。


多协议支持


用户说:服务侧的微服务⼀般都是 RPC 框架,HTTP 不够!


我们去支持多协议的时候,看到了几个需求,在讲这个需求之前,我们可能需要对架构进行一定的调整跟梳理。



在 FaaS 这种体系当中,其实是有虚线跟实线两种主要的路径实现部分,就是数据流的转发和数据包的转发。虚线部分其实是什么时候拉起 pod,有点像管控的需求,这部分你在支持多协议方面其实是不需要改动的,这也是为什么用同一套架构可以比较快速地去支撑多协议的这种应用。



具体其实一开始左边这部分我们有一个统一流量端口,因为我们都是 HTTP 协议的,所以大家通过 HTTP 的 header 直接去区分就可以了,没必要去启两个端口,但是在多协议支撑方面,我们意识到这对于工程代码质量方面是一个负担。我们需要去开两个端口,实现单独开数据请求窗口,流量到户。在多协议支持方面,这种数据路径上基本趋向于一致。



一开始我们是 HTTP1.1 的,而 gRPC 是基于 HTTP2 的,然后所以 HTTP2 反正都得做。另外 HTTP1.1 其实对于微服务是不太友好的,原因是 HTTP1.1 不是二进制协议,它本身的传输代价比较高,另外它不能在一条连接中去多路复用,一个连接进行一个多个 connection 的传输。所以 HTTP2 二进制跟多路复用的特性也是让我们有升级的动力。整体上的变化如图,右边部分是代表它是可以做到多路复用,多个 stream 的 request 和 response 可以在同一个连接中去完成,这样对于 pod 多并发模式是更加友好的。



做完 HTTP2 的支持,gRPC 相对来说就比较简单了,因为它本身就有 HTTP2 的。不一样的地方是 gRPC 不看 HTTP 整体的返回码 400、500 或者 200 这种 status code,更多看的是 head 中返回的 response 中的 gRPC status。所以为什么我们要去看 response,但是不会去看它的 body,原因是在 FaaS 中你需要去监听他的错误或者正确,所以我们要针对这种请求的结果做一些支撑。当然 gRPC 做流式传输的时候,在 FaaS 平台上也是支持的,比如说我们一次请求调用支持最长 15 分钟,那 15 分钟之内做一些流式传输是不会被掐断的,如果有更长时间的流式传输,我们会有其他方案。



讲完这种 gRPC 的支持,我们看一下 Thrift 的这种支持,因为 Thrift 在字节跳动内部还是主要的一种协议场景。


我们内部有一个私有协议,叫 TTheader,它开源叫 Theader,关键就是中间用中文写的,可变长度的 Header 内容,可以让你去携带一些原数据信息,为什么要携带原数据信息?是因为在 FaaS 中我们是通过 FaaS 的 function ID 或者其他 ID 做一些传输,但是整体微服务肯定会根据整个公司治理的一种唯一标识,在我们自己内部叫做 psm,这个在函数底层架构要有一些转化,我们不希望去感知这种上层的智力带来的复杂的无意义的东西,所以我们希望把它做一个转换,转换成 FaaS 内部自己可以感知的一种唯一标识。所以说你要在 head 中去感知这个东西,刚好有个地方可以存,当然你在协议设计方面本来就应该有这种灵活度,否则你的协议就是不健康的。



刚才一直在讲的是单机层面的多协议的支持,但是对于一整个系统来说,遇到一些中心的流量,可能会经过一些网关。刚才我们一开始讲 FaaS Gateway 只是一个 HTTP 的 Gateway, HTTP 不可能去接受这种 sleep 的协议,他直接就拒绝掉了。所以我们需要在 gRPC 跟 http 共享一个 http Gateway,但是在 SaaS 的这种场景中,我们要独立部署一个新的 Gateway 去支撑所有的协议,因为我们要对一些长尾的流量做强管控。当然要强调一点,我们不是所有的流量都是过网关的,否则每秒就会有 1.2 亿的流量过网关,资源开销也是巨大的。


融入字节微服务治理体系 ByteMesh


用户说:前端流量通过网关进来没问题,微服务流量如果都经过网关引入额外消耗,治理也不够自由!



其实我们的回答也很明确,我们做微服务上 FaaS 是不希望丢弃掉原先在传统的微服务平台上面建设的各种治理能力的,如果你能很好地继承下来,这件事情的推进会相对的更加顺畅。


简单来看,我们去迎合自己内部微服务的一个治理体系,内部的产品名称叫做 ByteMesh。首先要解决几个问题:


  • 上游服务,通过 ByteMesh 出流量代理,访问下游 FaaS

  • FaaS 服务,通过 ByteMesh 出流量代理,访问下游服务

  • FaaS 服务,通过 ByteMesh⼊流量代理,承接上游服务发来的请求


上游的服务要通过 Mesh 去访问到 FaaS 类的服务,要通过 Mesh 达到下游。FaaS 本身的服务可能要开启 Mesh 入流量去做单机 pod 的治理,无论是安全的治理还是限流的治理。这些都可能是一种开关,当然是一种排列组合的关系,不一定所有的服务要把所有都开启,是按需开启的,我们在一定场景中开启的大部分可能都是 FaaS 针对下游的出流量 Mesh。目前跳动内部的 Mesh 体系推广的更多是一种 sidecar 的体系。



然后这边讲一下上游服务要访问 FaaS 的话,我们要解决两个问题,一方面是我们跟 Mesh 之间要有一种服务发现的协商,我们不希望把这种路由信息发布到原始的 Faas 体系中的服务中心里头,因为它太慢了,所以它不能支撑 FaaS 这种需求,所以我们要去打通 Mesh 跟 FaaS 之间的服务发现体系,做到毫秒级别的服务发现。对于小流量跟大流量我们是分开去考虑的,对于小流量场景,完全通过 gateway 的话更加简单一点,因为可能流量就是从 0 个实例变成个位数的实例,网关去承载这些流量完全没有负担,如果是超大流量的话,我们会跟 Mesh 进行协商,其实 Mesh 不太敢做这一点,原因是我们返回给 Mesh 的,有可能是 gateway 的 IP,有可能是后面直接的 IP。所以对于 Mesh 团队来说,他没有这种心智负担,主要问我们有哪些 IP 就可以了。我们可能有时候告诉他是 gateway 的,有可能告诉他不是 gateway 的,取决于我们觉得流量规模有多大,能不能过中心网关。



处理代理做完之后,用户通过 Mesh 就可以访问到 FaaS 了,上游的 FaaS 如果访问下游的话,如果要去享受一些治理的特征,也得去 follow Mesh 这条链路。不同点在于,常规的 Mesh 做这种流量的服务的拉起的时候,更多考虑的是环境变量或者环境是后注入的,原因是在 FaaS 上有一些冷启动的 pod 会给这种合作提供一些更高的要求,我们需要 Mesh 提供 hot reload 的机制,让我们动态可以加载一些服务信息。另一方面对于 Mesh 本身的 sidecar 的拉起速度我们也提了更高的要求。所以其实是 FaaS 团队跟 Mesh 团队一起去达成这样的目标。



对入流量接入代理,其实跟出流量差不多。入流量也是流量先先过 Runtime Agent,当然在实际情况下可能经过 Mesh,可能经过 sidecar,当然这是内部架构的调整,我们预期的这种模式应该是先过 FaaS Runtime Agent,然后再过流量代理,然后再到用户的代码,然后 Mesh 可以去做一些流量治理,可能还会有一些 sidecar 策略。其实这些大部分都是一些传统微服务的内容,我们只是把它在 FaaS 的场景中做适配,然后让整体的 FaaS 体系跟原先的微服务体系尽量趋近,做到这种流量治理的继承。


自定义镜像支持


用户说:我有好多依赖,默认的镜像环境没有我要的依赖, 我的依赖打成 static library 很麻烦!



FaaS 逻辑是基础镜像从整体的产品角度管控,如果有太多的基础镜像的话,做基础镜像的分发对于冷启动来说是一个很大的压力,所以我们要去管控基础镜像是有限个的。但是基础镜像中可能没有某些客户要的东西,我们要支撑这部分用户的话,就得去做自定义镜像的支持。在 k8s 上面有一种 Init 这样的模式。Init 跟应用的 business content,share 一个 volume,volume 里头可以扔一些二进制,应用容器可以看到里头的二进制,我们控制启动命令,用户提供的就是应用容器的镜像,我们不需要对镜像进行二次的打包修改,但是可以直接享受 FaaS 能力,因为我们会注入一个 sidecar 运行它。Init 容器在跑完之后就会消失不见,所以它本质不占用运行时的资源开销,所以整体上对于资源开销也是没有问题的。


异步长任务支持



用户说:15min 调用时长不够,我的业务需要是⼀个长任务


不支持太长时间的原因是传统的 FaaS 平台更多是一种同步调用的逻辑,这种逻辑去支撑更长时间的调用对于平台来说是一个很大的负担。但是有时候用户说没办法,你就得支持异步任务。在异步任务方面,我们抽象出来几个组件做为边界,在单机 Pod 层面可以看右边的两条线,运行时它落在同样的一个 Pod 内部,同样一个 Pod 内部的网络空间,基本上是可以支撑超长时间连接的建立跟维护。你要做的是要在你的中心调度系统跟 Runtime Agent 的之间做异步调用的解耦。这种解耦通过异步的轮巡的机制,用户调用你的时候,一次返回一个 request ID 然后可以慢慢拿到结果。通过这样的方式,其实只是用户调用的方式有一定的改变,剩下的仍然是在一套系统中做的支撑,只是可能在调度系统这边会引入一个 Gateway。


微服务方面的 building block



以上说的那些部分,基本上解决完之后,原生的服务框架基本上可以不改代码的情况下运行在 FaaS 平台上,但要落到实际生产中仍然会遇到一些问题。


一是在 FaaS 平台中我们基本上会强调一种并发的强控制,无论是单 Pod 的单次并发,还是你单 Pod 同一时间支持 100 个并发。在支持这种并发的时候,其实更多考虑的 workload 是比较单一的,但在微服务场景中不一样,不同的 endpoint 都可能是不同的 workload,无法用归一化的 workload 去简单描述一个 Pod 在同一时间能接受多少并发,所以就只能改变,就是我们要弱化并发这种模式。我们希望通过过载反馈的机制把扩容的需求做出来,因为之前你要去控制并发,无非就是防止 Pod 过载。我们弱化并发,不再在微服务场景中去算并发,仅把它作为一个兜底扩容的策略,本质上我们会用一些服务的表现,去做过载因子的判断,判断反馈服务 Pod 是否过载,可以在 Pod 内部快速感知到这种情况,反馈到 FaaS 一些组件中,在毫秒级别内去拉起一个实例,做到快速扩容。快速扩容完之后,这个服务需要被上游发现才能用得起来。所以又涉及到需要做一些 p2p 的转发,才能让这个新的 Pod 可以尽快加入到生产系统中。


二是对于弹性实例 / 预留实例的思考。传统云厂商是不一样的策略,如左边这张图,绿色部分是整个业务需要的计算资源的情况,蓝色部分是 FaaS 的 autoscaling,在一些突发场景,比如标红的 cold start 场景,是不够满足的,这就会造成一种尖峰尖刺,在业务场景里面就会出现一种抖动。传统情况下,如果在 FaaS 上面遇到这样问题,传统的 FaaS 平台告诉你说如果非得让尖峰不存在,要么就是提前知道波峰,提前扩容,要么就是直接预留最大实例,退化成 PaaS 平台。我们不希望那么大浪费,不希望用户为低峰的时候去买单,但是我又不希望自动扩充引入这种毛刺的效应。


我们做法是区分了弹性实例跟预留实例,然后在预留实例场景中关键时预留实例,比如说你要预留三个,日常尽量的不把请求打到预留实例上,只有当迫不得已的情况下才打过去,跟传统的其他的 FaaS 的做法都不一样。


总结展望:应用场景 / 通用 Serverless/ 云边一体


总结展望一下我们应用场景。


微服务 serverless


微服务 serverless 支持和应用情况如下:


  • FaaS 支持原生 HTTP 框架,Hertz、Koa、Flask、Django

  • FaaS 支持 RPC 框架,gRPC、Thrift based Kitex(Golang)、Python、C++、 Node.js、Rust

  • 自定义镜像、异步任务

  • Mesh <-> FaaS QPS: 15 million qps(5 million online service)


通用 Serverless



非微服务场景怎么办呢?有人提出一种通用 Serverless 的想法,如图右边有点像目前 FaaS 的形态,包含了 FaaS 加上 BaaS,然后把各种非 FaaS 的计算形态或者存储形态提供出来的服务归类成 BaaS。其实这种归类有一个问题,就是不同的 BaaS,假设哪一天说要做 Serverless 的话,可能需要把 Serverless 的各种东西重新做一遍。有没有可能把大家的层次提一级,变成左边这个部分,把最核心的弹性需要的能力、FaaS 的计算能力、存储的能力去下沉。你把单机想象成一个分布式环境的简化版本,在一个分布式环境中,你无非是需要计算跟存储资源,还有网络资源,然后主要考虑计算跟存储能不能抽象成一些 building block,让 FaaS 专注于做弹性计算,让 Object storage 去做存储方面的 Serverless 支撑,统一去构建基础架构的统一的 Serverless 平台,让上面的各种各样的服务可以在这种平台上面去建设,然后获得这种弹性的能力。


Multi-runtime Architecture


我们刚才说的更多是一种渐进式的演进,为什么说是渐进式?因为我们一种理念是希望用户不怎么改变,我们去渐进的去迎合用户去做 Serverless,有没有一些其他的思路?



这 4 张图从单体到微服务到 FaaS,这是我们现在都知道的模式,有没有最右边这种模式,它比起第三种模式,中间又切了一层,把用户的 Business domain 跟基础的一些能力去做分割,为什么要做这个分割?


这么分割的话,灰色部分会专注于所有底层引擎跟远端的交互,有点像 Dapr 那样的一个思路,就是把所有跟架构其他层面上的一些交互全部收敛到下层,因为这是大家都需要建设的,不如让一个技术架构团队去做建设,让它的封装程度进一步的增加。好处是 business domain 会更加收敛,它的产物也会比较小。因为在上面的这种微服务的体系中,其实冷启动是很难解决的问题。能不能让你的业务的代码足够的小,小到基本上可以去做一些比较全量的预加载?



对于基础架构来说,提供的是一种 Runtime 更加聚合收敛的系统接口,这种系统接口可能不像暴露 Linux 上面的各种 syscall,你不能随意的去读写文件,去跟外头进行交互,你可能更多的是通过 Runtime 提供出来的语义,给你提供系统界面,你在这个基础上做一些编程,这种系统界面就可以高度的去做抽象跟收敛。Business code 上面就会变得比较小,它的二进制产物也比较小。我们把整体的 Runtime 进行聚合,WebAssembly Runtime 会去收敛 Hostcalls 对外部的 HTTP、KV、日志的所有东西。


精简架构



其实很多 gateway、dispatcher、Worker manager 等等这些 FaaS 上面为了弹性做出来的组件都可以一概不要,这就变成一个很精简化的架构。你要流量的话它瞬间去拉起,然后瞬间做冷启动,待会可以 share 下数据,这个数据是我们在 Wasm 让他们做的冷启动,是带业务逻辑的,这个冷启动可以做到 0.5 毫秒。无论是微服务内部之间的,还是前场的应用这种冷启动开销都是无感知的。这基本上可以做到充分的弹性,这种充分的弹性其实可以应用到一些云边场景。


以上基本就是我们针对字节微服务 Serverless 场景的技术分享和一些展望,希望可以和大家充分交流,共同推动 Serverless 领域的基础能力建设和业务应用。

相关阅读:


如何用 7 分钟击破 Serverless 落地难点?

Serverless Devs 重大更新,基于 Serverless 架构的 CI/CD 框架:Serverless-cd

应用 Serverless 化,让业务开发心无旁骛

Serverless 的前世今生

2023-08-08 14:3224342

评论 1 条评论

发布
用户头像
很好的实践文章!很好奇17 万 +的 Fn 如何维护和管理?这种规模的基建得有多坚固。
2023-09-12 09:04 · 广东
回复
没有更多了
发现更多内容

【大厂面试05期】说一说你对MySQL中锁的理解?

NotFound9

Java MySQL 后端

CDN百科第四讲 | 如何优雅地在云上“摆摊”——做直播带货,你不得不关注的技术

阿里云Edge Plus

CDN 边缘计算 直播 直播带货

一文讲透SpringMVC

知春秋

spring springmvc Servlet

面试官:线程池如何按照core、max、queue的执行循序去执行?(内附详细解析)

一枝花算不算浪漫

面试 jdk源码 线程池

别教我女儿该怎么穿,教你儿子别去强奸

小天同学

教育 日常思考 个人感悟 自我保护

为什么你的简历石沉大海,offer 了无音讯?

非著名程序员

程序员 程序人生 提升认知 简历优化 简历

漫画 | 啊哈,给我一碗孟婆汤

码农神说

程序员 测试 互联网人 设计师

618 将至,融云通信云技术如何助力电商销售

Geek_116789

以太坊颠覆了以太坊:引入密码学实现2.0性能突破

安比实验室SECBIT

以太坊 分布式系统 节点 密码学

小师妹学JVM之:JVM的架构和执行过程

程序那些事

Java JVM 小师妹 性能调优 签约计划第二季

ARTS-Week Four

shepherd

Java algorithm

Spring-AliasRegistry

CoderLi

Java spring 程序员 源码分析 后端

Spring-资源加载

CoderLi

Java spring 程序员 后端 Java 25 周年

架构师训练营作业(二)

Glowry

极客大学架构师训练营

架构师训练营第二周总结

一剑

架构师训练营-课后作业-Week-2

Chasedreamer

架构师训练营第 2 周——学习总结

在野

极客大学架构师训练营

Websocket直播间聊天室教程 - GoEasy快速实现聊天室

GoEasy消息推送

直播 websocket 即时通讯 聊天室 弹幕

LinkedList竟然比ArrayList慢了1000多倍?(动图+性能评测)

王磊

Java 数据结构 性能优化 性能 链表

数字产品开发那些事

涛哥 数字产品和业务架构

产品开发 数字化

编译Spring5.2.0源码

CoderLi

Java spring 程序员 后端 Java 25 周年

Spring 获取单例流程(三)

CoderLi

Java spring 程序员 源码分析 后端

Flink on Zeppelin (1)入门篇

章剑锋_Jeff

大数据 flink 流计算 Zeppelin

架构师训练营第二周作业

一剑

软件开发:软件设计的基本原则

SkyeDance

极客大学架构师训练营

Spring 获取单例流程(一)

CoderLi

Java spring 程序员 源码分析 后端

Spring 获取单例流程(二)

CoderLi

Java spring 程序员 源码分析 后端

架构师训练营第二周 - 作业

Eric

极客大学架构师训练营

作为CEO你比员工厉害吗?

Neco.W

创业 创业者 CEO

重学 Java 设计模式:实战享元模式「基于Redis秒杀,提供活动与库存信息查询场景」

小傅哥

设计模式 小傅哥 重构 代码坏味道 代码优化

Spring 容器的初始化

CoderLi

Java spring 程序员 源码分析 后端

洞悉微服务:从 PaaS 到 Serverless 的演进_微服务_InfoQ精选文章