写在前面

前面的文章主要介绍了美团Octo服务治理框架,随着云原生的崛起,大量服务治理体系普遍“云原生”化,而Mesh则是云原生中非常重要的一个流派,今天我们看下美团的Octo是如何一步步的Mesh化的。

Mesh化

经过一整套服务治理能力的升级,原有Octo已经支持了,包括set化、链路级复杂路由、全链路压测、鉴权加密、限流熔断等治理能力。

但整个治理体系仍存在一些痛点及挑战:

  • 多语言支持不友好:每一个语言搞一套治理体系不现实

  • 中间件和业务绑定在一起,彼此制约迭代:原有的一些治理能力是通过入侵业务代码实现的,比如filter、api、sdk集成等,只是做到了逻辑隔离,未做到物理上的隔离。一般来说核心的治理能力主要由通信框架承载,如果没有更进一步的隔离(如物理隔离),那中间件引入的bug就需要所有业务配合升级,对业务研发效率造成伤害

  • 治理决策比较分散:每个节点根据自己的状态进行决策,无法与其他节点协同仲裁

针对于以上问题,Octo升级到2.0架构,引入了Mesh概念。

Mesh模式下,为每个业务实例部署一个Sidecar代理,所有进出应用的业务流量统一由Sidecar承载,同时服务治理的工作也由Sidecar执行,所有的Sidecar由统一的中心化大脑控制面进行全局管控。

做好Mesh化升级,怎么解决上面的痛点的呢:

  • Service Mesh 模式下,各语言的通信框架一般仅负责编解码,而编解码的逻辑往往是不变的。核心的治理功能(如路由、限流等)主要由 Sidecar 代理和控制大脑协同完成,从而实现一套治理体系,所有语言通用。

  • 中间件易变的逻辑尽量下沉到 Sidecar 和控制大脑中,后续升级中间件基本不需要业务配合。SDK 主要包含很轻薄且不易变的逻辑,从而实现了业务和中间件的解耦。

  • 新融入的异构技术体系可以通过轻薄的 SDK 接入美团治理体系(技术体系难兼容,本质是它们各自有独立的运行规范,在 Service Mesh 模式下运行规范核心内容就是控制面和Sidecar),目前美团线上也有这样的案例。

  • 控制大脑集中掌控了所有节点的信息,进而可以做一些全局最优的决策,比如服务预热、根据负载动态调整路由等能力。

总结起来:尽量将治理能力与业务逻辑剥离开来,通过轻量级的SDK与业务逻辑耦合,但这一部分设计需要尽量轻薄,更多治理能力下沉到SideCar与控制大脑。

整体架构

Octo2.0整体架构如下:

协作系统包括服务治理系统、鉴权服务、配置中心、限流服务等,这些原有服务治理能力在Mesh架构下是可复用的,无需重复开发。

Mesh的技术选型

美团的Mesh改造起步于2018年底,当时的一个核心问题是整体方案的考量应该关注于哪几个方面。

启动计划阶段时,有了一些非常明确的关注点:

  • Octo体系经过五年的迭代,形成了一系列的标准与规范,进行Mesh改造治理体系升级范围会非常大,在确保技术方案可以落地的同事,也要屏蔽技术升级对于业务改动

  • 治理能力不能减弱,在保障对齐的基础上逐渐提供更精细、更易用的运营能力

  • 可以应对超大规模的挑战,技术方案需要确保支撑当前两级甚至N倍的增量,系统自身不能成为整个治理体系的瓶颈

  • 尽量与社区保持亲和,一定程度上和社区协同演进

于是产出了如下的技术选型方案:

于是选择了一种数据层面基于Envoy二次开发,控制码自研的整体选型与方案。

数据面方面,当时 Envoy 有机会成为数据面的事实标准,同时 Filter 模式及 xDS 的设计对扩展比较友好,未来功能的丰富、性能优化也与标准关系较弱。

控制面自研为主的决策需要考量的内容就比较复杂,总体而言需要考虑如下几个方面:

  • 美团容器化主要采用富容器的模式,这种模式下强行与 Istio 及 Kubernetes 的数据模型匹配改造成本极高,同时 Istio API也尚未确定。

  • Istio 在集群规模变大时较容易出现性能问题,无法支撑美团数万应用、数十万节点的的体量,同时数十万节点规模的 Kubernetes 集群也需要持续优化探索。

  • Istio 的功能无法满足 OCTO 复杂精细的治理需求,如流量录制回放压测、更复杂的路由策略等。

  • 项目启动时非容器应用占比较高,技术方案需要兼容存量非容器应用。

整体Mesh方案如下:

这张图展示了 OCTO Mesh 的整体架构。从下至上来看,逻辑上分为业务进程及通信框架 SDK 层、数据平面层、控制平面层、治理体系协作的所有周边生态层。

先来重点介绍下业务进程及SDK层、数据平面层:

  • OCTO Proxy (数据面Sidecar代理内部叫OCTO Proxy)与业务进程采用1对1的方式部署。

  • OCTO Proxy 与业务进程采用 UNIX Domain Socket 做进程间通信(这里没有选择使用 Istio 默认的 iptables 流量劫持,主要考虑美团内部基本是使用的统一化私有协议通信,富容器模式没有用 Kubernetes 的命名服务模型,iptables 管理起来会很复杂,而 iptables 复杂后性能会出现较高的损耗。);OCTO Proxy 间跨节点采用 TCP 通信,采用和进程间同样的协议,保证了客户端和服务端具备独立升级的能力。

  • 为了提升效率同时减少人为错误,我们独立建设了 OCTO Proxy 管理系统,部署在每个实例上的 LEGO Agent 负责 OCTO Proxy 的保活和热升级,类似于 Istio 的 Pilot Agent,这种方式可以将人工干预降到较低,提升运维效率。

  • 数据面与控制面通过双向流式通信。路由部分交互方式是增强语义的 xDS,增强语义是因为当前的 xDS 无法满足美团更复杂的路由需求;除路由外,该通道承载着众多的治理功能的指令及配置下发,我们设计了一系列的自定义协议。

控制面(美团内部名称为Adcore)自研为主,整体分为:Adcore Pilot、Adcore Dispatcher、集中式健康检查系统、节点管理模块、监控预警模块。此外独立建设了统一元数据管理及 Mesh 体系内的服务注册发现系统 Meta Server 模块。

每个模块的具体职责如下:

  • Adcore Pilot 是个独立集群,模块承载着大部分核心治理功能的管控,相当于整个系统的大脑,也是直接与数据面交互的模块。

  • Adcore Dispatcher 也是独立集群,该模块是供治理体系协作的众多子系统便捷接入 Mesh 体系的接入中心。

  • 不同于 Envoy 的 P2P 节点健康检查模式,OCTO Mesh 体系使用的是集中式健康检查。

  • 控制面节点管理系统负责采集每个节点的运行时信息,并根据节点的状态做全局性的最优治理的决策和执行。

  • 监控预警系统是保障 Mesh 自身稳定性而建设的模块,实现了自身的可观测性,当出现故障时能快速定位,同时也会对整个系统做实时巡检。

  • 与Istio 基于 Kubernetes 来做寻址和元数据管理不同,OCTO Mesh 由独立的 Meta Server 负责 Mesh 自身众多元信息的管理和命名服务。

实现原理

我们看下核心Mesh架构实现原理。

流量劫持

Octo并未采用Istio的原生方案,而是使用iptables对进出POD的流量进行劫持:

  1. iptables自身存在性能损失大、管控性差的问题:

  • iptables在内核对于包的处理过程中定义了五个“hook point”,每个“hook point”各对应到一组规则链,outbond流量将两次穿越协议栈并且经过这5组规则链匹配,在大并发场景下会损失转发性能。

  • iptables全局生效,不能显式地禁止相关规则的修改,没有相关ACL机制,可管控性比较差。

  • 在美团现有的环境下,使用iptables存在以下几个问题:

    • HULK容器为富容器形态,业务进程和其他所有基础组件都处于同一容器中,这些组件使用了各种各样的端口,使用iptables容易造成误拦截。

    • 美团现在存在物理机、虚拟机、容器等多个业务运行场景,基于iptables的流量劫持方案在适配这些场景时复杂度较高。

    鉴于以上两个问题,最终采用了Unix Domain Socket直连方式,实现了业务进程和Octo Proxy进程之间的流量转发。

    服务消费者一方,业务进程通过轻量级的Mesh SDK和Octo Proxy监听的UDS地址建立连接。

    服务提供者一方,Octo Proxy代替业务进程监听在TCP端口上,业务进程则监听在制定的UDF地址上。

    UDS相比于iptable劫持有更好的性能和更低的运维成本,缺点是需要SDK。

    服务订阅

    原生的Envoy的CDS、EDS请求时全量服务发现模式,是将系统中所有的服务列表都请求到数据面来进行处理。

    由于大规模服务集群的服务数量太多,而需要的服务信息是少数的,所以需要改造成按需获取服务的发现模式,只需要请求要访问的后端服务节点列表就可以了。

    流程如下:

    业务进程启动之后,通过http方式向Octo proxy发起服务订阅请求,Octo Proxy将所要请求的后端AppKey更新到Xds中,Xds在向控制面请求具体的服务资源。

    为增加整个过程健壮性,降低后期运维成本,做了一定的优化。比如Octo Proxy的启动速度有可能比业务进程启动慢,所以Mesh SDK中增加了请求重试的逻辑,确保请求真正可以经由Octo Proxy发出去。

    Mesh SDK和Octo Proxy之间的http请求改成了同步请求,防止pilot资源下发延迟带来问题。

    Mesh SDK的订阅信息也会保存在本地文件中,以便在Octo Proxy重启或更新过程中,服务的可用性。

    无损热重启

    由于业务进程和Octo Proxy是独立的进程,确保Proxy进程热更新时可以持续提供服务,对业务无损无感知就非常重要。社区的Envoy自己支持的热重启不够完善,不能做到完全的无损流量。

    我们看下在短连接和长连接两种情况下Octo Proxy重启可能造成的流量损耗问题。

    在短连接场景下,所有的新连接会在Octo Proxy New上创建,Octo Proxy Old上已有的连接会在响应到来后主动断开。Octo Proxy Old的所有短连接逐渐断开,当所有连接断开之后,Octo Proxy Old主动退出,Octo Proxy New继续工作,整个过程中流量是无损的。

    在长连接场景下,SDK和Octo Proxy Old之间维持一个长连接断不开,并持续使用这个连接发送请求。Ocot Proxy Old进程最终退出时,该链接才被迫断开,这时可能有部分请求还未返回,导致Client端请求超时,因此Envoy的热重启对长连接场景支持的不完美。

    为实现基础组件更新过程不对业务流量造成损耗,业界的主要方式是滚动发布。也就是,不是直接全部更新,而是一部分一部分的更新,滚动的承接流量+主动断开连接。

    服务节点分批停止服务,执行更新,然后重启,投入使用,直到集群中所有实例都更新为最新版本。这个过程中会主动摘到业务流量,保证升级过程中业务流量不丢失。

    美团的方案是进行角色划分,将业务服务分为两个角色:

    • 对外提供服务的server端;

    • 发起请求调用的client端;

    client端octo proxy热更新:

    octo proxy old进入重启状态,对后续的请求直接返回“热更新”标志的响应协议,client sdk在收到“热更新”的协议标识之后,主动切换连接进行重试。然后断开sdk上和octo proxy old之间的长连接。

    通过client sdk和octo proxy之间的交互配合,可以实现client在octo proxy升级过程中的流量安全。

    server端coto proxy热更新:

    server端的octo proxy在热更新开始后,主动向client侧的octo proxy发送proxy restart消息,也就是要求client侧的octo proxy主动切换新连接,避免当前client侧octo proxy持有的旧链接被强制关闭,导致请求失败。

    client侧octo proxy收到“主动切换新连接”的请求后,应及时从可用连接池中清除老的长连接。

    数据面运维

    在云原生环境下,Envoy运行在标准的K8s Pod中,通常会独立出一个Sidecar容器,这样可以借助K8s的能力实现对Envoy Sidecar容器的管理,比如容器注入、健康检查、滚动升级、资源限制等。

    美团内部的容器运行时模式为:单容器模式。就是在一个pod内只包含一个容器。

    由于业务进程和所有基础组件都运行在一个容器中,所以只能采用进程粒度的管理措施,无法做到容器粒度的管理。

    Lego Agent支持了对Octo Proxy热更新的感知,还负责对Octo Proxy进行健康检查、故障状态重启、监控信息上报和版本发布等。相对于原生k8s的容器重启方式,进程粒度重启会更快。

    扩展性及完善的运维体系

    关键设计

    大规模治理体系 Mesh 化建设成功落地的关键点有:

    • 系统水平扩展能力方面,可以支撑数万应用/百万级节点的治理。

    • 功能扩展性方面,可以支持各类异构治理子系统融合打通。

    • 能应对 Mesh 化改造后链路复杂的可用性、可靠性要求。

    • 具备成熟完善的 Mesh 运维体系。

    围绕这四点,便可以在系统能力、治理能力、稳定性、运营效率方面支撑美团当前多倍体量的新架构落地。

    对于社区 Istio 方案,要想实现超大规模应用集群落地,需要完成较多的技术改造。

    因为 Istio 水平扩展能力相对薄弱,内部冗余操作多,整体稳定性较为薄弱。

    解决思路如下:

    • 控制面每个节点并不承载所有治理数据,系统整体做水平扩展。

    • 在此基础上提升每个实例的整体吞吐量和性能。

    • 当出现机房断网等异常情况时,可以应对瞬时流量骤增的能力。

    • 只做必要的 P2P 模式健康检查,配合集中式健康检查进行百万级节点管理。

    按需加载和数据分片主要由 Adcore Pilot、Meta Server 实现。

    Pilot 的逻辑是管理每个数据面会话的全生命周期、会话的创建、交互及销毁等一系列动作及流程;

    维护数据最新的一致性快照,对下将资源更新同步处理,对上响应各平台的数据变更通知,将存在关联关系的一组数据做快照缓存。

    控制面每个 Pilot 节点并不会把整个注册中心及其他数据都加载进来,而是按需加载自己管控的 Sidecar 所需要的相关治理数据。

    同一个应用的所有 OCTO Proxy 由同一个Pilot 实例管控,Meta Server,自己实现控制面机器服务发现和精细化控制路由规则,从而在应用层面实现了数据分片。

    Meta Server 管控每个Pilot节点和OCTO Proxy的归属关系。

    当 Pilot 实例启动后会注册到 Meta Server,此后定时发送心跳进行续租,长时间心跳异常会自动剔除。

    Meta Server 内部有一致性哈希策略,会综合节点的应用、机房、负载等信息进行分组。当一个 Pilot 节点异常或发布时,该 Pilot 的 OCTO Proxy 都会有规律的连接到接替节点,而不会全局随机连接对后端注册中心造成风暴。

    当异常或发布后的节点恢复后,划分出去的 OCTO Proxy 又会有规则的重新归属当前 Pilot 实例管理。

    对于关注节点特别多的应用 OCTO Proxy,也可以独立部署 Pilot,通过 Meta Server 统一进行路由管理。

    稳定性保障设计

    围绕控制故障影响范围、异常实时自愈、可实时回滚、柔性可用、提升自身可观测性及回归能力进行建设。

    命名服务与注册中心打通

    Mesh体系的命名服务需要 Pilot 与注册中心打通。

    采用ZK实现的方式是每个 OCTO Proxy 与 Pilot 建立会话时,作为客户端角色会向注册中心订阅自身所关注的服务端变更监听器,如果这个服务需要访问100个应用,则至少需要注册100个 Watcher 。

    如果存在1000个实例同时运行,就会注册 100 x 1000 = 100000 个 Watcher。还有很多应用有相同的关注的对端节点,造成大量的冗余监听。

    规模较大后,网络抖动或业务集中发布时,很容易引发风暴效应把控制面和后端的注册中心打挂。

    针对这个问题,可以采用分层订阅方式。

    就是每个 OCTO Proxy 的会话并不直接和注册中心或其他的发布订阅系统交互,而是将变更的通知全部由 Snapshot 快照层管理。

    Snapshot 内部又划分为3层:

    • Data Cache 层对接并缓存注册中心及其他系统的原始数据,粒度是应用;

    • Node Snapshot 层则是保留经过计算的节点粒度的数据;

    • Ability Manager 层内部会做索引和映射的管理,当注册中心存在节点状态变更时,会通过索引将变更推送给关注变更的 OCTO Proxy;

    回到刚才的场景,隔离一层后1000个节点仅需注册100个 Watcher,一个 Watcher 变更后仅会有一条变更信息到 Data Cache 层,再根据索引向1000个 OCTO Proxy 通知,从而极大的降低了注册中心及 Pilot 的负载。

    Snapshot 层除了减少不必要交互提升性能外,还会将计算后的数据格式化缓存下来,这样瞬时大量的请求会在快照层被缓存挡住。

    预加载的主要目的是提升服务冷启动性能。

    在 Pilot 节点中加载好最新的数据,当业务进程启动时,Proxy 就可以立即从 Snapshot 中获取到数据,避免了首次访问慢的问题。

    Istio 默认每个 Envoy 代理对整个集群中所有其余 Envoy 进行 P2P 健康检测。

    当集群有N个节点时,一个检测周期内就需要做N的平方次检测,另外当集群规模变大时,所有节点的负载就会相应提高,这都将成为扩展部署的极大障碍。

    美团采用了集中式的健康检查方式,同时配合必要的P2P检测:

    • 由中心服务 Scanner 监测所有节点的状态,当 Scanner 主动检测到节点异常或 Pilot 感知连接变化通知 Scanner 扫描确认节点异常时, Pilot 立刻通过 eDS 更新节点状态给 Proxy,这种模式下检测周期内仅需要检测 N 次。(Google 的Traffic Director 也采用了类似的设计,但大规模使用需要一些技巧:第一个是为了避免机房自治的影响而选择了同机房检测方式,第二个是为了减少中心检测机器因自己 GC 或网络异常造成误判,而采用了Double Check 的机制)。

    • 除了集中健康检查,还会对频繁失败的对端进行心跳探测,根据探测结果进行摘除操作,提升成功率。

    异构治理系统融合设计

    Istio 和 Kubernetes 将所有的数据存储、发布订阅机制都依赖 Etcd 统一实现,但美团的10余个治理子系统功能各异、存储各异、发布订阅模式各异,呈现出明显的异构特征,如果接入一个功能就需要平台进行存储或其他大规模改造,这样是完全不可行的。

    一个思路是由一个模块来解耦治理子系统与 Pilot ,这个模块承载所有的变更并将这个变更下发给 Pilot。

    • 独立的统一接入中心,屏蔽所有异构系统的存储、发布订阅机制;

    • Meta Server 承担实时分片规则的元数据管理;

    执行机制如上图:

    • 各系统变更时使用客户端将变更通知推送到消息队列,只推送变更但不包含具体值(当Pilot接收到变更通知后,会主动Fetch全量数据,这种方式一方面确保Mafka的消息足够小,另一方面多个变更不需要在队列中保序解决版本冲突问题。);

    • Adcore Dispatcher 消费信息,并根据索引将变更推送到关注的 Pilot 机器,当 Pilot 管控的 Proxy 变更时会同步给 Meta Server,Meta Server 实时将索引关系更新并同步给Dispatcher;

    • 为了解决 Pilot 与应用的映射变更间隙出现消息丢失,Dispatcher 使用回溯检验变更丢失的模式进行补偿,以提升系统的可靠性;

    运维体系设计

    操作流程如下:

    • 运维人员在 LEGO 平台发版,确定发版版本;

    • 新版本资源内容上传至资源仓库,并更新规则及发版范围至 DB;

    • 升级指令下发至所要发布的范围;

    • 收到发版命令机器的 LEGO Agent 去资源仓库拉取要更新的版本(如有失败,会有主动 Poll 机制保证升级成功);

    • 新版本下载成功后,由 LEGO Agent 启动新版的 OCTO Proxy;

    总结

    美团的Mesh方案已经看不懂了,看不懂只能后续再熟悉Mesh一些回头再看。

    整体看来,美团这套Mesh演进方案对大家还是非常有借鉴意义的,因为上Mesh势必是已经到了一定的治理规模,这里会遇到一个很重要的问题是,如何将Mesh的治理能力有机的集成到已经成熟的某套治理能力下,比如微服务治理体系。

    所以这种面向未来新的治理体系来了之后,更多的问题是如何借鉴新模式方案方式去优化我们的系统中,简单说是要个“神”,而不必是“形”,比如你可以按需替换掉Mesh里面某些模块、组件等,以我们成熟的能力去承接。

美团分布式服务治理框架OCTO之二:Mesh化相关推荐

  1. 美团分布式服务治理框架OCTO之一:服务治理

    写在前面 之前的文章介绍过美团的中间件leaf-code,今天介绍另一款非常强的中间件系统Octo,其实Octo算不上是中间件系统,应该是一整套分布式服务治理平台,美团的很多中间件能力都是通过Octo ...

  2. 分布式服务治理框架Dubbo

    前言 Dubbo是一个被国内很多互联网公司广泛使用的开源分布式服务治理框架,是一个非常全面的SOA基础框架,当当网在Dubbo基础上新增了一些功能,并将其命名为Dubbox(Dubbo eXtensi ...

  3. 分布式服务治理框架Dubbo的前世今生及应用实战

    Dubbo的出现背景# Dubbo从开源到现在,已经出现了接近10年时间,在国内各大企业被广泛应用. 它到底有什么魔力值得大家去追捧呢?本篇文章给大家做一个详细的说明. 大规模服务化对于服务治理的要求 ...

  4. 美团下一代服务治理系统 OCTO 2.0 的探索与实践

    本文根据美团基础架构部服务治理团队工程师郭继东在2019 QCon(全球软件开发大会)上的演讲内容整理而成,主要阐述美团大规模治理体系结合 Service Mesh 演进的探索实践,希望对从事此领域的 ...

  5. 谈谈分布式服务治理框架 Dubbo 重大升级

    为什么80%的码农都做不了架构师?>>>    前言 本文首发公众号 [一名打字员] Dubbo 今年重启维护,并且进行了重大的升级.在 JAVA 社区很多人都知道并且使用过 Dub ...

  6. 阿里云开发者学院电子书《Dubbo分布式服务治理实战》重磅来袭!

    简介:Dubbo 是阿里巴巴开源的高性能分布式 RPC 服务治理框架,已经捐献给 Apache 开源组织,最新的版本是 3.0.在阿里巴巴.工行.电信.银联.中国人寿.网易.滴滴.当当等互联网公司中大 ...

  7. webservice框架_聊聊从RPC到服务治理框架

    与一般的HTTP REST框架不同,一个可用的RPC架构不仅解决了远程调用问题,也提供了用于服务注册和服务发现的基础设施,比如RMI(Java语言的RPC)里的RMI Registry,如下图所示. ...

  8. 微服务治理框架的选择:对比Spring Cloud和Istio

    导读:目前主流的微服务治理框架主要是Spring Cloud.而Istio作为新一代微服务框架,越来越受到关注.在本文中,我们分享如何选择这两种微服务框架. 作者:魏新宇 宋志麒 杨金锋 来源:大数据 ...

  9. 微服务治理框架(C++版)详细设计

    @TOC 微服务治理框架(C++版)详细设计 概述 gRPC 是一款高性能.开源的 RPC 框架,产自 Google,基于 ProtoBuf 序列化协议进行开发,支持多种语言(C++.Golang.P ...

最新文章

  1. Excel 数据有效性,怎么提示指定的命名区域不存在?
  2. ECSHOP如何增加红包序列号字符
  3. 使用 jQuery Mobile 与 HTML5 开发 Web App (十八) —— HTML5 Web Workers
  4. java获取焦点_[Java教程]dialog获取焦点
  5. Sql 列转行 三种方法对比
  6. 【POJ3461】Oulipo(字符串Hash)
  7. 【emWin】例程十:bmp图片显示
  8. 阿里云祝顺民:云网络的十年“修路”历程
  9. ad域推送软件_ManageEngine ADManager Plus(AD域管理工具) V7.0.1 官方中文版
  10. 微信小程序-获取微信用户手机号码
  11. 【渝粤题库】陕西师范大学180102 广告策划 作业(高起专)
  12. 2019秋期第二次机考总结
  13. Mac/Linux/Ubuntu下 视频 显示双语/中英字幕 的方法
  14. 线性变换的不变子空间和特征子空间的关系
  15. java dog cat animal,理解Java的多态
  16. C 宏定义实现字符大小写转换
  17. 程序员自我修养-总结 (1)
  18. 三分钟入门大数据之用户画像标签的分类
  19. 获诱人的实习offer、在Kaggle推AI大赛,大二学生如何做到?
  20. itext7学习笔记——第2章实践example

热门文章

  1. 【网络流24题】D、魔术球问题(有向无环图的最小路径覆盖、思维)
  2. USACO07DEC道路建设Building Roads(prim算法+堆优化与Kruskal+路径压缩对比)
  3. xp用光盘安装linux系统安装教程视频,光盘重装xp系统图文教程
  4. 柱形图无数据可选中_在Excel工作表中插入一个三维柱形图
  5. 从两个bug来看Javascript的装载
  6. java 中的 serialVersionUID
  7. tomcat重启后session没有清除的解决办法
  8. Flutter 实现根据环境加载不同配置
  9. jdk是什么?jdk1.8安装配置方法
  10. Load和Initialize往死了问是一种怎样的体验?