「如何实现流动式软件发布」线上课堂开课啦,快来报名参与课堂抽奖吧~ 了解详情
写点什么

Cellery:向 Kubernetes 部署应用程序的代码优先方法

2019 年 9 月 13 日

Cellery:向Kubernetes部署应用程序的代码优先方法

本文要点

  • 尽管微服务架构(MSA)有很多好处,但管理数百个松耦合的微服务很快就会变得很麻烦。这就是设计基于单元格的架构(CBA)的原因。

  • CBA 是一种微服务架构模式,它的主要要求是将多个微服务(及其他组件)分组成称为单元格的构建块,以方便管理和重用。

  • 从零开始在容器编排平台上创建 CBA 很费力。在撰写本文时,Kubernetes 是业界广泛采用的容器编排平台;然而,使用 YAML 编写用于此目的的 Kubernetes 工件并不是一项简单的任务。

  • Cellery 遵循代码优先的方法,处理实现 CBA 的底层复杂性。

  • Cellery 包含一个 SDK、一个运行时和一个管理框架。


Cellery 简介

Cellery 到底是什么,它如何帮助我们在 Kubernetes 上部署和管理应用程序?Cellery 是一种在 Kubernetes 上构建、集成、运行和管理复合应用程序的代码优先方法。这种复合应用程序的构建块称为单元格——其名称为 Cellery,而不是 Celery。为了帮你理解单元格和 Cellery,让我们看看如何使用 Cellery 部署、管理和观察一个已有的由谷歌编写的Kubernetes应用程序。但是,在此之前,让我们先了解下单元格是什么以及单元格的工作原理。


单元格是什么?

让我们看一下,为什么需要在微服务架构中使用复合组件。


微服务是构建复杂且不断演化的应用程序的热门选项,它可以缩短上市时间,加快创新速度。每个服务都可以由专门负责该服务的团队独立开发,并且他们可以自由选择任何他们认为合理的技术。最重要的是,微服务是可重用的,每个服务都可以独立伸缩,使团队可以使用最能满足服务资源需求的最佳部署基础设施。开发人员可以对其服务进行本地更改,并在测试完成后立即部署这些更改。那么,这有什么挑战吗?


微服务(包括无服务器函数)的使用正在快速增长,因为组织的目标是提高开发速度和可伸缩性,而且它们还必须调整为面向业务能力的团队。在拥有数十个或数百个应用程序的企业中,管理如此多的松耦合微服务,不仅会成为运营的噩梦,也在团队沟通以及服务发现、版本控制和可观察性等方面提出了挑战。更多的服务、更多的沟通路径、更复杂的网络安排以及更多的潜在故障区。这就有了对高级结构的需求,将多个微服务和无服务器函数聚合到易于管理和重用的构建块中。


基于单元格的架构是一种微服务架构模式,它将系统的微服务、数据和其他功能组件(包括前端应用程序、遗留服务、代理、网关和遗留系统适配器)分组为内聚的、可单独部署的架构单元(称为单元格)。


通常,组件分组的依据是作用范围、所有权和组件之间的相互依赖关系。每个单元格都应该单独设计和开发,并且应该可以独立部署、管理和观察。此外,单元格内的组件可以使用支持的传输协议在单元格内部实现相互通信。然而,所有传入的服务请求必须首先通过单元格网关。该网关使用标准网络协议通过受控的网络端点提供安全 API、事件或流。团队可以通过自组织的方式生成可以持续部署和增量更新的单元格。下面是对单元格架构中单元格的一个简单描述:



图 1:自包含的架构单元:单元格


实现单元格架构的方法

Cellery 设计用于基于单元格架构原则创建 Kubernetes 应用程序。使用 Cellery,我们可以编写代码来定义单元格及其组件,方法是指向现有的容器镜像(其中包含构成单元格的微服务及其他组件),并定义这些组件之间的关系、对其他单元格的依赖和单元格 API(网关)。然后就可以使用单元格定义代码生成单元格镜像。实际上,一旦我们将单元格定义代码提交到版本控制存储库中,就可以触发 CI/CD 管道。像 Jenkins 这样的 CI/CD 系统可以构建单元格镜像,对其进行测试,并将其推入容器存储库。然后,CI/CD 系统可以拉取单元格镜像并将其部署到相应的生产环境。而且,我们还可以像在 Kubernetes 上部署的其他任何应用程序一样更新、扩展和观察部署好的单元格。



图 2:面向 Cellery 的 DevOps 流


使用 Cellery 创建单元格

简言之,Cellery 包含了 SDK、运行时和管理框架。安装Cellery时,可以通过 Cellery CLI运行命令,执行各种任务。


首先,你应该在本地机器上创建一个 Kubernetes 集群,或者使用已有的 Kubernetes 集群作为 Cellery 运行时环境。输入一个简单的命令 command cellery,就会看到一个提示,让你通过交互式 CLI 选择部署首选项,Cellery 将根据你的首选项为你配置 Kubernetes 集群。


设置好 Cellery 运行时环境之后,就可以开始用 Cellery 语言编写单元格。Cellery 语言基于Ballerina编程语言,因此可以使用 VSCode 和 IntelliJIdea 作为 IDE。要自动生成包含标准导入语句和所需函数的单元格定义文件,可以使用 cellery init 命令。接下来,你可以定义组件,完成构建,并使用 Cellery 语言运行逻辑。然后,Cellery 编译器将编译代码并使用一个简单的命令 cellery build 创建相应的 Kubernetes 构件。


要在 Cellery 运行时环境上部署单元格,运行 cellery run 命令并提供必要的参数。你可以使用 cellery push 命令将构建好的镜像推入单元格镜像存储库,使用 cellery pull 命令从存储库中拉取单元格镜像,并将构建和部署流集成到 CI/CD 管道。此外,你还可以通过 cellery view 命令查看单元格的可视化表示。Cellery 还提供了单元格测试功能和可观察性工具,让你可以监控、记录和跟踪单元格。


为什么是 Cellery?为什么不使用 YAML 为单元格配置 Kubernetes 部署?

对于已经采用容器的组织,开发人员不仅要创建微服务,还要理解容器编制系统如 Kubernetes 的细微差别。此外,除了准备 Kubernetes 集群、创建、部署和管理应用程序,从头开始创建 CBA 涉及到配置服务网格、处理服务身份验证和配置符合CBA原则的安全策略等任务。因此,使用标准 Kubernetes 资源配置单元格需要一些 Kubernetes 专业知识。


此外,Kubernetes 资源(如 pod、服务和部署)是通过 YAML 文件声明性地创建的。因此,随着部署规模的增加,面对不断增长的、越来越复杂的 YAML 代码,如果没有成熟的 IDE 帮助他们提高生产效率,DevOps 团队就会陷入挣扎。而且,由于缺乏对函数、抽象和封装等编程概念的支持,YAML 本身就鼓励大量重复代码。因此,在 Kubernetes 上创建复杂的部署意味着 DevOps 团队必须经历一个冗长而令人畏惧的过程,编写并维护可能长达数千行的 YAML 文件。这很容易出错。


Cellery 使用类型安全的、经过验证的代码而不是 YAML 来定义部署,而且,它还负责处理配置部署、单元格连接、服务、自动缩放等底层复杂性。此外,在默认情况下,使用 Cellery 编写的单元格通过单点登录、令牌、基于策略的访问控制和 mTLS 等安全机制来确保安全。Cellery 是围绕 DevOps 实践而设计的,因此,可以使用蓝/绿部署和金丝雀部署无缝地进行构建、推送、拉取、测试、部署和更新。用户还可以通过监控和跟踪功能观察部署。


简言之,Cellery 的目标是简化 Kubernetes 上应用程序的配置、构建、测试和部署。如上所述,该项目试图从不同的角度解决这个问题,包括开发、DevOps、安全性和可观察性。


Cellery 实例

让我们看一个真实的微服务示例,你可以亲自尝试一下。(要了解如何使用 Cellery 编写单元格代码,可以查看Cellery语法并尝试一些示例。)


为此,我们使用了谷歌的“Hipster Shop”演示应用程序。这里有原始 Hipster Shop 演示程序的详细信息、源代码、Docker 文件等。这个示例适用于 Cellery 0.3.0 版本。


Hipster Shop 应用程序是一个多层次、多语言的微服务应用程序,它是基于 Web 的电子商务应用程序。用户可以浏览商品,将它们添加到购物车中,使用应用程序购买它们。Hipster Shop 由前端和多个微服务组成,通过 gRPC 相互通信。服务架构如图 3 所示,表 1 是 Hipster Shop 微服务的说明。



图 3:Hipster Shop 应用的服务架构


服务语言说明
frontendGo暴露一个HTTP服务器来为网站提供服务。不需要注册/登录,会自动为所有用户生成会话ID。
cartserviceC#把用户购物车中的物品保存在Redis中并检索它。
productcatalogserviceGo从一个JSON文件提供产品列表以及搜索产品和获取单个产品的功能。
currencyserviceNode.js将一种货币转换成另一种货币。使用从欧洲央行获取的真实值。这是QPS最高的服务。
paymentserviceNode.js从给定的信用卡(mock)扣除给定的金额,并返回一个交易ID。
shippingserviceGo根据购物车给出运输成本估计。将物品发送到给定的地址(mock)。
emailservicePython向用户发送一封订单确认邮件(mock)。
checkoutserviceGo检索用户购物车,准备订单,并安排付款、发货和电子邮件通知。
recommendationservicePython根据购物车中的物品推荐其他产品。
adserviceJava基于给定上下文单词提供文本广告。
loadgeneratorPython/Locust不断向前端发送模拟真实用户购物流的请求。


表 1:Hipster Shop 应用程序的现有服务


为了将 Hipster Shop 的微服务映射到基于单元格的架构,我们将这些微服务分组为五个单元格:ads、products、cart、checkout 和 front-end。我们根据每个微服务单独执行的任务以及它与单元格中其他微服务的关系密切程度设计了这种分类。一个单元格由一个团队拥有,但是一个团队可以拥有一个或多个单元格。定义单元格边界可以基于其他标准,例如组件到组件的连接数量,而不仅限于功能和所有权。要了解更多关于单元格粒度的信息,请点击这里


还有一点非常重要,为了使用 Cellery,我们没有对 Hipster Shop 原来的微服务做任何更改;Cellery 仅引用微服务的现有容器镜像。表 2 列出了单元格及各自的组件,图 4 进行了说明。


单元格组件
adsadservice
productsproductcatalogservice、recommendationservice
cartcartservice、cacheservice
checkoutcheckoutservice、emailservice、paymentservice、shippingservice、currencyservice
front-endfrontendservice


表 2:Hipster Shop 服务到单元格的映射



图 4:Hipster Shop 应用程序基于单元格的架构


单元格 front-end 包含前端应用程序,这是其唯一组件,HTTP 流量通过其网关访问单元格,而 front-end 通过 gRPC 与其他单元格交互。


单元格 checkout 是该架构中除 front-end 之外惟一与外部单元格(products 和 cart)通信的单元格,剩下的单元格 products、ads 和 cart 是独立的单元格,在这些单元格中,只有其内部组件之间发生通信。可以点击这里查看所有完整的单元格定义文件(扩展名为.bal 的文件)以及运行和部署 Hipster Shop 单元格的说明。请注意,这个示例已经在 Cellery 0.3.0 版本上进行了测试。


创建单元格

让我们看下 ads 的代码,它包含一个组件:adservice。


ads.bal


import ballerina/config;import celleryio/cellery;
public function build(cellery:ImageName iName) returns error? { int adsContainerPort = 9555; // Ad服务组件 // 基于给定上下文单词提供文本广告。 cellery:Component adsServiceComponent = { name: "ads", source: { image: "gcr.io/google-samples/microservices-demo/adservice:v0.1.1" }, ingresses: { grpcIngress: <cellery:GRPCIngress>{ backendPort: adsContainerPort, gatewayPort: 31406 } }, envVars: { PORT: { value: adsContainerPort } } };
// 单元格初始化 cellery:CellImage adsCell = { components: { adsServiceComponent: adsServiceComponent } }; return cellery:createImage(adsCell, untaint iName);}public function run(cellery:ImageName iName, map<cellery:ImageName> instances) returns error? { cellery:CellImage adsCell = check cellery:constructCellImage(untaint iName); return cellery:createInstance(adsCell, iName, instances);}
复制代码


首先,单元格定义以标准 import 语句开始,并包含两个函数:build 和 run(如果你使用 cellery init 命令,那么这些函数将自动生成)。当用户分别执行 cellery build 和 cellery run 命令时,build 函数和 run 函数将被调用。


在 build 函数中,通过指向公共 Docker 镜像 URL(source)并定义网络访问入口点(ingresses)和环境变量(envVars),定义一个名为 adServiceComponent 的组件来表示 adservice。然后,单元格被初始化并使用名称 adsCell 定义,之前定义的 adServiceComponent 被添加到它的组件列表中。然后,使用 cellery:createImage 方法创建单元格镜像。


最后,run 函数将获取构建的单元格镜像(cellery:ImageName iName),其中包含单元格镜像和相应的实例名,并使用 cellery:createInstance 方法从单元格镜像创建一个正在运行的实例。


单元格内组件间的通信

现在,我们已经看了基本单元格文件的代码结构,让我们看一下有两个或多个组件的单元格的代码,以及如何配置这些组件实现彼此通信。


单元格 products 有两个组件:productcatalogservice 和 recommendationservice。如图 4 所示,recommendationservice 需要与 productcatalogservice 交互,因为它根据购物车中的物品来推荐产品。单元格中组件之间的通信是通过环境变量实现的。


如下面的代码片段所示,recommendationServiceComponent 需要通过环境变量(envVars)PRODUCT_CATALOG_SERVICE_ADDR 获得 productCatalogServiceComponent 的地址。此外,productCatalogServiceComponent 被标记为 dependencies 字段下的一个依赖项,这可以确保 productCatalogServiceComponent 启动并运行,并且可以用于解析依赖项。


products.bal


.. // 推荐服务组件 // 根据购物车中的物品推荐其他产品 cellery:Component recommendationServiceComponent = {     name: "recommendations",     source: {        image: "gcr.io/google-samples/microservices-demo/recommendationservice:v0.1.1"     },     ingresses: {        grpcIngress: <cellery:GRPCIngress>{        backendPort: recommendationsContainerPort,        gatewayPort: 31407       }     },     envVars: {        PORT: {            value: recommendationsContainerPort        },        PRODUCT_CATALOG_SERVICE_ADDR: {            value: cellery:getHost(productCatalogServiceComponent) + ":" + productCatalogContainerPort        },        ENABLE_PROFILER: {            value: 0        }     },     dependencies: {        components: [productCatalogServiceComponent]     }   }; ..
复制代码


单元格以名称 productsCell 定义并初始化,productCatalogServiceComponent 和 preferationservicecomponentare 均被添加到其组件列表中,如下面的代码片段所示。


..   // 单元格初始化   cellery:CellImage productsCell = {       components: {           productCatalogServiceComponent: productCatalogServiceComponent,           recommendationServiceComponent: recommendationServiceComponent       }   };..
复制代码


单元格间通信

讨论完组件间通信,下面介绍一个单元格中的组件如何与另一个单元格中的组件通信。由于 CBA 要求所有外部传入的通信必须通过单元格网关进行,因此单元格 front-end 的代码的唯一组件是 Web 前端应用程序,并且必须与位于不同单元格中的各种组件通信。


front-end.bal


..   cellery:Component frontEndComponent = {       name: "front-end",       source: {           image: "gcr.io/google-samples/microservices-demo/frontend:v0.1.1"       },       ingresses: {           portal: <cellery:WebIngress> { // Web ingress is exposed globally.           port: frontEndPort,           gatewayConfig: {               vhost: "my-hipstershop.com",               context: "/"               }           }       },..
复制代码


上面的代码显示了 frontEndComponent 如何暴露一个 HTTP 服务器为 Hipster Shop 网站提供服务。为了与相关的内外部微服务通信,同一个组件需要几个环境变量的值。让我们看一下让 frontendcomponent 可以与单元格 products 的组件进行交互的代码。


envVars: {    ..    PRODUCT_CATALOG_SERVICE_ADDR: {      value: ""    },    RECOMMENDATION_SERVICE_ADDR: {      value: ""    },    ..},
复制代码


如上面的代码片段所示,frontEndServiceComponent 需要通过环境变量(envVars)PRODUCT_CATALOG_SERVICE_ADDR 和 RECOMMENDATION_SERVICE_ADDR 分别获得 productCatalogServiceComponent 和 recommendationServiceComponent 的地址。


dependencies: {    cells: {productsCellDep: <cellery:ImageName>{ org: "wso2cellery", name: "products-cell", ver: "latest"},.. }      }
复制代码


单元格 front-end 依赖于单元格 products,这种依赖关系是通过上面介绍的 frontEndComponent 中的 dependencies 字段定义的。


cellery:Reference productReference = cellery:getReference(frontEndComponent, "productsCellDep");
frontEndComponent.envVars.PRODUCT_CATALOG_SERVICE_ADDR.value = <string>productReference.gateway_host + ":" +<string>productReference.products_grpc_port;
frontEndComponent.envVars.RECOMMENDATION_SERVICE_ADDR.value = <string>productReference.gateway_host + ":" +<string>productReference.recommendations_grpc_port;
复制代码


方法 cellery: getReference (frontEndComponent productsCellDep)会提供一个指向已部署的 products 单元格实例的引用,借助这个引用,我们可以解析出上述代码中环境变量 PRODUCT_CATALOG_SERVICE_ADDR 和 RECOMMENDATION_SERVICE_ADDR 的值。类似地,front-end 单元格通过上述方法与其他单元格通信。


剩下的两个单元格定义文件遵循相同的原则,可以从 GitHub 库中获取。


cart.bal


单元格 cart 是一个包含两个组件的独立单元格。


checkout.bal


单元格 checkout 包含五个组件,为了从 checkoutservice 调用 cartservice 和 productcatalogservice,它需要分别与单元格 cart 和 products 通信。


在完成了所有单元格定义的编码后,接下来可以构建和部署这些单元格了。你还可以将部署 Hipster Shop 微服务所需的完整 Kubernetes YAML 文件与 Hipstershop Cellery 代码进行比较,后者不仅在 Kubernetes 上部署微服务,而且还围绕这些微服务创建了基于单元格的架构。


构建和部署单元格

请按照这里的说明构建和运行所有的 Hipster Shop 单元格。


运行独立单元格


现在看一下如何构建和运行 ads 单元格,它是一个独立的单元格。


打开终端,定位到 ads.bal 文件所在的位置,运行以下命令构建 ads 单元格:


$ cellery build ads.bal wso2cellery/ads-cell:latest
复制代码


我们在 Docker Hub 中的组织名称是 wso2cellery,使用 ads-cell 作为单元格镜像的名称,使用 latest 作为标签。执行 build 命令后可以看到如下输出:


✔ Building image wso2cellery/ads-cell:latest✔ Removing old Image✔ Saving new Image to the Local Repository

✔ Successfully built cell image: wso2cellery/ads-cell:latest
What's next?--------------------------------------------------------Execute the following command to run the image: $ cellery run wso2cellery/ads-cell:latest--------------------------------------------------------
复制代码


要以实例名 ads-cell 运行单元格镜像 wso2cellery/ads-cell:latest,运行以下命令:


$ cellery run wso2cellery/ads-cell:latest -n ads-cell
复制代码


可以看到以下输出:


✔ Extracting Cell Image wso2cellery/ads-cell:latest
Main Instance: ads-cell
✔ Reading Cell Image wso2cellery/ads-cell:latest✔ Validating dependencies
Instances to be Used:

INSTANCE NAME CELL IMAGE USED INSTANCE SHARED --------------- ----------------------------- --------------- -------- ads-cell wso2cellery/ads-cell:latest To be Created -
Dependency Tree to be Used:
No Dependencies
? Do you wish to continue with starting above Cell instances (Y/n)? y
✔ Starting main instance ads-cell

✔ Successfully deployed cell image: wso2cellery/ads-cell:latest
What's next?--------------------------------------------------------Execute the following command to list running cells: $ cellery list instances--------------------------------------------------------
复制代码


运行依赖单元格


现在,以单元格 front-end 为例,看看如何构建和运行依赖于其他单元格的单元格。build 命令与执行单元格 ads 的命令类似。


$ cellery build front-end.bal wso2cellery/front-end-cell:latest
复制代码


然而,在运行有依赖项的单元格镜像时,还必须列出单元格所依赖的其他单元格的运行实例的名称。这从单元格 front-end 的 run 命令可以看出来,如下所示。


$ cellery run wso2cellery/front-end-cell:latest -n front-end-cell -l cartCellDep:cart-cell -l productsCellDep:products-cell -l adsCellDep:ads-cell -l checkoutCellDep:checkout-cell -d
复制代码


输出如下:


✔ Extracting Cell Image wso2cellery/front-end-cell:latest
Main Instance: front-end-cell
✔ Reading Cell Image wso2cellery/front-end-cell:latest⚠ Using a shared instance cart-cell for duplicated alias cartCellDep⚠ Using a shared instance products-cell for duplicated alias productsCellDep✔ Validating dependency links✔ Generating dependency tree✔ Validating dependency tree
Instances to be Used:
INSTANCE NAME CELL IMAGE USED INSTANCE SHARED ---------------- ----------------------------------- ---------------------- -------- checkout-cell wso2cellery/checkout-cell:latest Available in Runtime - products-cell wso2cellery/products-cell:latest Available in Runtime Shared ads-cell wso2cellery/ads-cell:latest Available in Runtime - cart-cell wso2cellery/cart-cell:latest Available in Runtime Shared front-end-cell wso2cellery/front-end-cell:latest To be Created -
Dependency Tree to be Used:
front-end-cell ├── checkoutCellDep: checkout-cell ├── productsCellDep: products-cell ├── adsCellDep: ads-cell └── cartCellDep: cart-cell
? Do you wish to continue with starting above Cell instances (Y/n)? y
✔ Starting dependencies✔ Starting main instance front-end-cell

✔ Successfully deployed cell image: wso2cellery/front-end-cell:latest
What's next?--------------------------------------------------------Execute the following command to list running cells: $ cellery list instances--------------------------------------------------------
复制代码


还可以使用 view 命令查看单个单元格的图形化表示及其依赖关系。例如,要查看单元格 front-end,请键入以下命令:


cellery view wso2cellery/front-end-cell:latest
复制代码


这会打开一个描述单元格 front-end 的 Web 页面,如图 5 所示。



图 5:单元格 front-end 的图形化表示


可观察性

为了监控和排除已部署单元格的故障,Cellery 提供了可观察性工具,包括仪表板。Cellery 仪表板显示了许多单元格视图,其中显示了依赖关系图、单元格的运行时指标、对通过网关的请求的端到端分布式跟踪以及单元格组件。所有指标都是从组件和网关收集的,其中包括与 Kubernetes pod 和节点(包括 CPU、内存、网络和文件系统使用情况)相关的系统指标和请求/响应指标(应用程序指标)。




图 6:Cellery 可观察性仪表板


真得需要 Cellery 吗?

如果你正在寻找这个问题的答案,那么你还需要问问自己,微服务项目是否将是云原生的,以及该项目是否会随着时间的推移而增长和进化。如果答案是肯定的,那么你必须记住,管理数百个松耦合的微服务可能很快就会成为一场噩梦。这就是为什么要设计基于单元格的架构,但是在容器编排平台(如 Kubernetes)上使用 YAML 文件从零开始创建 CBA 绝非易事。这就是 Cellery 的作用所在,它使开发人员能够遵循代码优先的方法,并处理实现 CBA 的潜在复杂性,从而真正利用云原生微服务的优势,避免其中的陷阱。


Cellery网站GitHub库中提供了更多有趣的内容和学习材料。


关于作者

Dakshitha Ratnayake 是 WSO2 的企业架构师,他在软件开发、解决方案架构和中间件技术方面有超过 10 年的经验。这是作者为 InfoQ 撰写的第一篇文章。


原文链接:


Cellery: A Code-First Approach to Deploy Applications on Kubernetes


2019 年 9 月 13 日 08:001415
用户头像

发布了 376 篇内容, 共 165.5 次阅读, 收获喜欢 883 次。

关注

评论

发布
暂无评论
发现更多内容

通过女朋友来通俗易懂讲解“接口回调”,一不小心就被绿

小松漫步

Java 编程 接口 代码

面向对象设计原则----依赖倒置原则(DIP)

张荣召

面向对象设计原则----接口分离原则(ISP)

张荣召

架构师训练营 - 第二周总结

一个节点

极客大学架构师训练营

第二周

等燕归

第二周总结

等燕归

Java中的遍历(遍历集合或数组的几种方式)

keaper

Java List 遍历

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

成长者

极客大学架构师训练营

揭秘开源项目 Apache Pulsar 如何挑战 Kafka

Apache Pulsar

kafka 开源 云原生 Apache Pulsar 消息中间件

作业-2020-09-27

芝麻酱

架构训练营 - 第 2周课后作业 - 学习总结

Pudding

优化Banner广告收入的7种策略

易观大数据

2.框架设计-依赖倒置原则,接口隔离原则

博古通今小虾米

使用Spring Cloud Stream玩转RabbitMQ,RocketMQ和Kafka

Barry的异想世界

kafka RocketMQ RabbitMQ 消息队列 spring cloud stream

面向对象设计原则----单一职责原则(SRP)

张荣召

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

一个节点

极客大学架构师训练营

第二周

scorpion

面向对象设计原则----里氏替换原则(LSP)

张荣召

graylog日志分析系统上手教程

MySQL从删库到跑路

Apache Linux 运维 日志分析 实时 Web 日志分析器

OOA-OOD:面向对象分析/设计练习

张荣召

TensorFlow 篇 | TensorFlow 2.x 基于 Keras 的多节点分布式训练

Alex

tensorflow keras 分布式训练 AllReduce

依赖倒置原则和接口隔离原则练习

知行合一

看动画学算法之:排序-基数排序

程序那些事

算法 数据结构和算法 看动画学算法 算法和数据结构

基于 iOS14 系统的游戏卡顿问题解决方案

白开水

typescript 游戏开发 iOS14 游戏卡顿 ios开发

编程语言的本质

张荣召

架构训练营-week2-作业

于成龙

作业 架构训练营

案例分析--反应式编程框架Flower的设计

张荣召

第二周 框架学习-作业

刘希文

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

张荣召

面向对象设计原则--开放关闭原则(OCP)

张荣召

架构师训练营第二周作业

文智

极客大学架构师训练营

Cellery:向Kubernetes部署应用程序的代码优先方法-InfoQ