求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
Codenvy架构:第一部分
 
作者 Tyler Jewell,火龙果软件 发布于:2014-01-21
 

1.0 概述

Codenvy是一个云IDE,大约有10万开发者在使用该IDE编码、构建和测试应用程序。

本文解释了Codenvy的各种技术和网站的架构。

我们的架构是由打算为用户提供的不同内容驱动的:

Codenvy.com ——一个托管的云IDE,支持服务协议定义(SLA)和硬件。

Codenvy Enterprise——允许组织在他们自己的服务器上编码、构建、测试和部署应用程序。

Codenvy ISV ——使用推动工厂(Promoted Factories)、收费插件(Monetizable Plug-ins)和IDElets驱动并度量已发布的SDK和API的技术约定。一个工厂是一种通过策略启动一个临时的编码、构建、测试和调试工作空间的方法。一个IDElet是一个能够插入到其他产品中的可嵌入的编码、构建、测试、调试工作流。

Codenvy Platform——一个云IDE引擎,为开发者提供了一种方法让其能够开发、测试、运行工具插件和应用程序。

Codenvy Platform是用于交付Codenvy.com、Codenvy Enterprise和Codenvy ISV的引擎。我们还能使用它创建任何其他有品牌化实现需要的IDE。该SDK的结构和Eclipse Platform相似,但是它是针对云环境设计的。该IDE还支持插件开发,开发人员能够为构建、运行、测试和调试工作流开发插件,而这些插件通常在IDE本身之外运维。

架构讨论将会分为两个部分:1)驱动Codenvy Platform的SDK,2)对SDK进行扩展从而创建Codenvy.com的组件。

2.0云IDE和桌面IDE之间的区别

这两种IDE之间的主要技术区别是——典型区别——对于桌面IDE而言,IDE提供商的期望是在工具本身之外安装和管理打包、构建、测试和运行环境。当然并非总是如此,因为确实有一些先进的IDE能够将这些额外的组件安装到主机上,但是这并不是很常见。

对于云IDE而言,它完全存在于云端;所以除了执行IDE之外,云IDE提供商通常还必须在一个托管的位置提供构建和运行时环境。

这个区别带来的不仅仅是希望,还有挑战。云IDE带来的希望是,开发者能够完全在云端开发,云IDE提供商能够提供更多的组件、做的更快并且潜在地降低由配置而引发的错误。挑战是管理一个工作空间的额外成本,现在它不仅包含了一个IDE,还包含了项目、代码、构建器和运行器。

2.1云IDE工作空间的管理方法

管理云端工作空间通常有两种方法可以采用:

为用户分配一个专有的虚拟机,他们在该虚拟机上拥有特权,可以安装那些能够被编辑、构建或者执行的文件以及其他的软件。
用户共享合并的均匀规整的资源、IDE功能(例如重构)命令,构建功能、运行功能分布在不同的集群上并针对每一个功能做了针对性的优化。开发环境越规整,对于操作员而言系统的操作越密集。负面影响便是这些系统不够灵活,也不太可能成为开发者工作上的一个完美的匹配方案。

Codenvy.com为用户提供了这两种配置。

对于资金计划充裕的用户而言,我们会运行第一个配置,为用户分配一个支持SSH选项的专有虚拟机。用户将能够在2013年的第4季度使用该计划。他们可以在虚拟机上安装不同的构建和运行时软件。然后可以重新配置运行在Codenvy系统内部的IDE,调整构建、运行和调试命令的指向让它们执行驻留在专有虚拟机中的进程。

对于想使用免费计划的用户而言,我们运营了一组集中式服务器,它们被分为了三个集群:一个用于IDE,一个用于构建,一个用于运行。每一个集群之间存在一个队列系统,它们控制着活动在集群间的前进和后退。每一个集群能够独立地扩展,并且支持不同用户的高密度操作。IDE集群对它们的I/O和内存瓶颈进行扩展,构建集群对技术能力扩展,运行集群对内存扩展。

3.0 CODENVY 平台的SDK架构

因为不同的开发者使用的开发工作流可能是不同的,所以我们需要设计一个引擎,该引擎允许创建不同的云IDE,它们能够改变系统的行为和体验。有一些良好定义的方式能够组织过去这些年由Eclipse和JetBrains产生的那些插件,同时这些接口能够被扩展到一个多租户的云环境。

SDK包括:

一个云客户端运行时,负责发现、注册、加载和管理插件。该运行时还负责管理一组连接到参与工具工作流系统的多租户入站和出站外部连接。

一个云客户端SDK,能够开发多租户、云客户端插件。该SDK为资源、事件和用户界面的使用提供了一个通用的模型。插件能够按照一种可扩展的、良好定义的格式进行集成或分层。

一组标准的插件,它们提供与核心开发工具相关的功能,并且在必要的时候可以从运行时中分离出来。目前的插件集合包括:git、Java、CSS、Python、HTML、Ruby、XML、PHP、JavaScript、Browser Shell和maven。

一个默认的IDE,它包含了一个结构良好的工作台用于组织代码仓库、项目、文件、构建集成、运行时/调试器集成以及部署/发布工作流。该IDE是一个插件集成包,它交付了一组默认的工具。用户能够通过浏览器或者一组REST风格的Web服务(表示每个工具的功能)访问该IDE。

3.1 插件架构

构建一个IDE的基础是能够创建、打包、部署并更新一个插件。插件能够按照适合自己的方式进行扩展,并且能够以下面的方式进行分层:一个插件能够调用并扩展另一个插件。

SDK是一个两层应用程序,它包含Web应用程序和服务器端应用程序服务。这两层都是可扩展的,能够由第三方修改。

插件使用Java、GWT和CDI实现。接口系统自始至终都使用了注入和面向方面的编程,它们是插件扩展、将插件连接到一起并将插件链接进IDE本身所使用的技术。通过注入和CDI接口系统能够在插件前面创建一个非常干净简单的接口。同时还使用了GWT,因为它对生成能够在多种浏览器上运行的高性能的标准JavaScript代码功能做了一些优化。

下面是一个空白插件,它向IDE中添加了一个菜单项,该菜单项的状态在被选中时会发生变化。该插件能够在它自己的工作流中进行编译、测试和验证。为了扩展Java类的构造函数使用了注入技术,这些注入能够添加额外的会被传入扩展本身的参数,

package com.codenvy.ide.extension.demo;

import com.codenvy.ide.api.editor.EditorAgent;
import com.codenvy.ide.api.extension.Extension;
import com.codenvy.ide.api.ui.action.ActionManager;
import com.codenvy.ide.api.ui.workspace.WorkspaceAgent;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
* Extension used to demonstrate the IDE 2.0 SDK fetures
*
* @author <a href="mailto:nzamosenchuk@exoplatform.com">Nikolay Zamosenchuk</a>
*/
@Singleton
@Extension(title = "Demo extension", version = "3.0.0")
public class DemoExtension {

@Inject
public DemoExtension(final WorkspaceAgent workspace,
ActionManager actionManager,
EditorAgent editorAgent) {

menu.addMenuItem("Project/Some Project Operation", new ExtendedCommand() {
@Override
public Expression inContext() {
return projectOpenedExpression;
}

@Override
public ImageResource getIcon() {
return null;
}

@Override
public void execute() {
Window.alert("This is test item. The item changes enable/disable
state when something happend.");
}

@Override
public Expression canExecute() {
return null;
}

@Override
public String getToolTip() {
return null;
}
});
}
}

3.2 插件服务和API

插件有各种各样的服务和API可以使用。它提供的服务分为三类。

3.2.1 IDE 客户端API

这些API是开发者期望一个平台具有的典型API,开发者能够借助它们在IDE的客户端部分创建可视化扩展。该API包括用于首选项(preferences)、菜单(menus)、视图(views)、帮助(helpers)、向导(wizards)、工具栏(toolbars)、选择(selections)、编辑器(editors)、热键(hotkeys)等内容的包。还包括包含UI组件和其他实用功能的通用类库的集合。

默认IDE还提供了一组顶层的、标准化的视图。这些视图安置了各种视角和面板,能够直接通过插件访问。它包含的视图有console、editor、project explorer、menus、forms、shell和wizards。

3.2.2 IDE 服务器端服务

为了让插件能够访问运行在云环境中的工作空间,有一个标准的REST风格的API集合,插件可以通过这些API与云开发环境交互。云负责管理构建集群、运行集群、存储所有文件的虚拟文件系统、Codenvy和外部/第三方服务之间的连接池、管理用户证书的身份管理服务器、账单/订阅的接口以及其他最好在云端而不是在浏览器中进行处理的高计算量的功能。

Codenvy平台和大部分云IDE不同。开发者的工作空间是虚拟化的,它横跨了多种为不同的IDE功能服务的物理资源。依赖管理、构建、运行和代码助手能够在不同的物理节点集群上执行。为了在所有的这些资源上进行合适的虚拟化,我们需要实现一个不仅能够支撑服务和物理资源,还能够理解原生IDE行为的虚拟文件系统(VFS)。

卸载服务的一个例子便是重构。因为一个重构命令可能需要修改许多文件的内容,包括重命名某些文件、删除某些文件,所以将重构作为一个云服务而不是在浏览器端进行处理将更加有效。这些能力是作为一组REST风格的服务暴露的,插件能够直接访问它们。

3.3插件的生命周期

插件是命令服务器端和客户端按照某种方式运行的代码的组合。因为客户端是GWT,所以核心IDE也是使用GWT实现的,加载到系统中的插件每次都必须和IDE的剩余部分进行编译从而生成一个新的IDE。核心IDE GWT代码和GWT扩展(插件提供商实现)一起创建了一个已编译的二进制代码集,该代码集会生成一套集成的、优化的JavaScript UI。插件本身是作为一个单独的JAR文件打包的,该JAR在创建期间会和IDE WAR合二为一。

SDK运行时能够和一个加载的插件集一起运行,然后能够通过配置文件激活或者取消激活应该向用户积极显示的插件。用户能够借助于启动时的插件配置项从整体出发对IDE的内存和CPU印记进行一些适当的微调控制。

ISV和其他第三方开发者需要构建并测试他们开发的插件。有三种插件部署模型:

独立部署。在一个专用的机器上创建、编译插件,然后使用一个自定义的Codenvy运行时(是一个WAR文件)启动。这通常是在桌面上完成的,但是也能够作为托管服务实现。在可下载版本的Codenvy SDK中有一个标准的插件项目模板和一组相关的maven构建命令支持这种打包。

寄宿在SDK中。位于codenvy-sdk.com,这是运行Codenvy平台的一个影子网站。它提供了一个基础的IDE,插件能够在这里创建、编译和执行。执行的插件会被加载到一个实例化的IDE中。所有这一切都发生在浏览器中,实例化的IDE运行在一个不同的Tomcat服务器进程中。本质上,开发者是通过一个影子Codenvy去创建更多的加载了一个插件的Codenvy实例进而进行行为和功能的测试。每一个插件的“运行”都会启动另外一个使用不同IDE加载配置的JVM。

Codenvy.com。我们将被认可的插件合并进Codenvy.com的产品构建中,构建一组Selenium测试自动化接口的测试,然后激活它们从而让社区能够使用。

目前,所有加载的插件都被激活了。我们最终将会创建一种机制,允许Codenvy.com任命的用户确定哪些插件需要在他们命名的工作空间中活动。

4.0 CODENVY.COM 架构

我们使用让平台具有创建Codenvy.com能力的引擎,一个弹性的、托管的、支持的环境。我们将Codenvy.com想象为一个持续生存的实体,它由各种各样的不同角色访问。因为这个愿景,Codenvy.com首先是一个拥有不同接口的系统。开发者使用浏览器利用该系统。Devops和ISV能够通过编程接口访问工作流,按需创建IDE;同时内部的观众能够通过配置接口查看分析数据,管理用户/工作空间,并指示在无人值守的时候系统该如何运行。

4.1客户端和服务器之间的通信

浏览器通过WebSocket和HTTP REST连接与Codenvy交互。这些连接在工作空间会话创建的时候建立。浏览器和服务器之间的通信是有限的,并且仅限于需要服务器访问的功能。服务器访问功能指访问新文件、定期自动保存、构建服务、事件日志和运行服务。没有心跳,协议也已经被优化为最小化网络流量。

当我们需要一个持续的协作讨论集合的时候我们会使用WebSocket通信。在多个用户同时进行编辑和聊天的协作会话期间这是非常常见的。因为WebSockets会消耗服务器上的大量资源,所以对于单向的或者即发即弃(fire-and-forget)的所有命令我们会使用HTTP REST连接,例如一个用户开始一个构建请求、一个重构请求、提交一个PAAS部署或者提交一个日志事件。

4.2 临时工作空间 VS 命名工作空间

Codenvy有临时工作空间的概念。临时工作空间指的是拥有一个项目、代码库以及一些代码、构建、测试、调试服务,但是却构建在一个隔离的区域中、相关工作无法长久持久化的地方。另外,如果一个临时工作空间在一段时间内保持闲置或者关闭了浏览器,那么它将会被销毁。在一个临时工作空间中有一些所有操作都必须重复的行为,例如对一个外部提供者进行身份验证,这是因为系统并不会持久化任何证书。一个永久的工作空间会和一个用户或者一个组织绑定,同时它里面的项目会持久化。此外,系统会持久化账号信息,外部服务的证书(例如连接GitHub的SSH密钥),同时允许公共/私有项目功能。

对于未认证和已认证的用户而言临时工作空间的表现是相似的。对于仅想使用临时工作空间的开发者来说,所有可分解的项目都开始于一个临时工作空间。对于拥有注册账户的开发者来说,他们能够将项目复制到自己的持久化工作空间中。而那些没有注册账号的开发者则能够执行身份认证流程,在身份认证确认之后就能够复制项目了。

临时工作空间和命名工作空间之间的架构区别在于:是否会使用持久性存储(LDAP)保存相关的账号信息。临时工作空间拥有完整的用户概要,但是因为信息是全部保存在内存中的,所以租户自己的任何破坏都将影响整个项目空间。我们还将所有的临时租户放入了虚拟文件系统中的一个隔离区域从而能够对这些文件运行批量清除和报表算法。

4.3用户管理和认证服务

用户能够通过基于表单的登录或者基于OAuth进行身份验证。我们使用Java身份验证和授权服务作为处理身份验证的核心技术。无论何时,如果一个用户试图访问受保护的资源(URL引用)那么就会进行身份验证。有一些URL引用是不受保护的(公共的),另外一些是受保护的(私有的)。在身份验证发生之后,系统会尝试着决定用户应该访问哪些工作空间和项目。如果被引用的URL并没有引用一个明确的工作空间,那么用户就会被重新路由到一个不同的工作空间和租户选择算法。

4.4 匿名用户VS. 命名用户

命名用户指的是拥有关联账号同时被授予了配置的权限能够访问受保护资源的用户。匿名用户指的是那些能够访问系统但是没有可识别的凭证能够关联到账号的用户。匿名用户能够在下面两个场景中出现:

没有cookie的用户启动一个工厂操作,并将它们放入一个临时工作空间。

用户访问一个公共的项目URL并且通过该URL在只读模式下进入了该产品。

匿名用户和命名用户之间的系统行为是不同的。对于匿名用户而言,管理员能够通过配置文件指定一系列的配置参数从而决定匿名用户可以访问产品的哪些功能。为了重新激活这些功能匿名用户必须创建一个账号或者进行认证(或者在产品外部或者在其内部)。匿名用户还必须将他们的所有流量路由到一个IDE集群,该集群与其他处理命名用户流量的IDE集群隔离。该路由和隔离使用HAProxy和我们已经创建的云管理IP。

从体系结构上看,匿名用户有一个自动生成的用户名,但不会持久化进LDAP,仅会存在于内存中。匿名用户和命名用户都有一个关联的权限集合,都是组成员。匿名用户的关联组和权限是预定义的并且不能修改。

4.5 公共项目VS. 私有项目

Codenvy支持公共项目和私有项目的概念。对于公共项目,任何能够访问相关URL的人都可以访问该项目的项目空间和所有文件。我们最终会在Web网站上通过一个导航目录暴露所有的这些公共项目,但是现在需要显式地指定URL才能访问。

我们使用ACL控制针对工作空间、项目和文件的行为。工作空间、每个项目和子目录中关联的文件集合的ACL设置在虚拟文件系统上。我们能够通过根的任意子目录传播ACL属性。对于被指定为完全公共的工作空间(例如我们的社区计划),我们在虚拟文件系统的根目录上创建了一个标准的ACL集合,并且将其设置为不可变的。对于私有项目,我们会让这些ACL可修改。组和个人都能够拥有ACL集合和修改。

4.6 邀请VS.工厂(Factory)

当一个用户被邀请进入另一个用户的工作空间时,他们必须有一个合适的密钥才能访问。对于已有的被邀请进入工作空间的Codenvy用户,我们会自动地将邀请密钥和进入邀请者工作空间的受邀用户进行关联。对于没有关联Codenvy账号的受邀email地址,我们会生成一个临时密钥,并将它和用户的email存储到我们的LDAP仓库中,然后向用户发送一封带有链接和密钥的email。当用户单击链接的时候会应用临时密钥,然后用户就能够访问他们被邀请进入的账号。

对于访问公共工作空间的用户而言,密钥并不是必须的。我们会创建一个自动的访问密钥授予匿名用户或者命名用户访问工作空间的某些权限。这些一般是读文件权限,以及数量有限的项目构建和运行功能。

对于单击工厂的用户,我们会创建一个单独的临时工作空间。用户并不会被邀请使用一个工厂。任何可访问工厂URL的用户都能够利用该工厂。如果访问一个工厂的用户已经认证通过,那么我们就能够识别出他们并允许他们将内容复制到他们已有的工作空间。如果用户未认证,那么我们会在临时工作空间会话期间创建一个匿名用户账号。

4.7协作模式

当一个文件被打开的时候,我们既可以通过标准编辑器又可以通过协作编辑器处理。

一个标准的编辑器仅在一个单独的JVM中运行,它对其他JVM中发生的事情毫不知情。这意味着该编辑器仅对明确打开它的用户可用,并且它不适合真正的协作模式。在有弱连接或者网络ping时间很长以致于会影响WebSocket性能的情况下会创建标准编辑器。使用标准编辑器消耗的内存空间是受控制的,用户的保存完全是人工的,有保证。

协作编辑器是Codenvy平台上的默认编辑器类型。为了让多个用户能够同时编辑文件我们扩展了Google的collide开源项目。协作编辑器控制虚拟文件系统中文件的锁,然后实时地调整每一个用户对文件做出的修改。Collide编辑器能够定期地异步保存文件的变化,同时保存事件会被传播回最终用户的屏幕让他们知道持久性改变。另外,协作编辑器会同步同时在同一个编辑器中处理相同文件的各个用户之间的事件。

4.8多租户(Multi-Tenancy)

IDE集群、构建器和运行器有不同的租户模型。

IDE 集群。在该集群中,每一个节点运行一个单独的JVM,它会占用节点上的所有可用内存和计算资源。在JVM中我们会使用一些内部IP从而在JVM内启用多租户。每一个工作空间有它自己的线程、内存和虚拟文件空间。当一个工作空间和它自己的IDE在一个单独的JVM中实例化的时候,我们会配置路由器将HTTP请求和WebSocket连接映射到正确的IDE。同时IDE本身会被映射到合适的目录。一般来说,在HAProxy必须创建另一个IDE服务器之前,在AWS中等实例上运行的一个单独的JVM能够运行大约300个IDE租户。

构建集群。在构建集群的前面有一个队列集合。在队列的前面有一个BuildManager服务,你能够通过REST风格的Web服务访问它。BuildManager处理消息集合、排序和处理。构建消息会基于进入的客户端上下文被路由到一个队列,例如付费/免费账号。还有一个管理控制台,它指定了应该为一个队列分配多少个节点。我们为社区层(免费)运行了一个处理队列,然后每一个Premium订阅(付费)都有一个专用的队列。使用这个模型我们能够为一个单独的工作空间分配多个硬件节点,同时队列管理者能够在不同的构建节点之间负载平衡工作空间请求。客户端IDE定期调查分配给它的流程的构建器从而收集输出和日志文件并在浏览器中显示。
运行器集群。类似于构建集群,在构建集群和运行集群之间也有一个队列系统。不仅构建集群可以将订单放到运行集群上,IDE也可以直接将命令发送到运行集群。运行集群上的每一个节点都运行了一个多租户部署的CloudFoundry。CloudFoundry黑盒用于决定启动服务器的环境配置。

在本文的第二部分我们将会介绍下面的Codenvy主题:虚拟文件系统的使用,日志和分析是如何实现的,托管API,集群管理、整体情况、发布模型和开发使用的SCRUM流程。

相关文章

企业架构、TOGAF与ArchiMate概览
架构师之路-如何做好业务建模?
大型网站电商网站架构案例和技术架构的示例
完整的Archimate视点指南(包括示例)
相关文档

数据中台技术架构方法论与实践
适用ArchiMate、EA 和 iSpace进行企业架构建模
Zachman企业架构框架简介
企业架构让SOA落地
相关课程

云平台与微服务架构设计
中台战略、中台建设与数字商业
亿级用户高并发、高可用系统架构
高可用分布式架构设计与实践
 
分享到
 
 


专家视角看IT与架构
软件架构设计
面向服务体系架构和业务组件
人人网移动开发架构
架构腐化之谜
谈平台即服务PaaS


面向应用的架构设计实践
单元测试+重构+设计模式
软件架构师—高级实践
软件架构设计方法、案例与实践
嵌入式软件架构设计—高级实践
SOA体系结构实践


锐安科技 软件架构设计方法
成都 嵌入式软件架构设计
上海汽车 嵌入式软件架构设计
北京 软件架构设计
上海 软件架构设计案例与实践
北京 架构设计方法案例与实践
深圳 架构设计方法案例与实践
嵌入式软件架构设计—高级实践
更多...