发布管理 - 向一部分用户发布 - 它如何适用于面向公众的网站

时间:2011-11-14 23:50:42

标签: asp.net .net sql-server release-management

我读到某个地方(抱歉不记得来源)facebook已经发布星期二了。他们首先向内部员工发布新功能,然后向一小部分外部用户发布,然后发布到整个世界。我相信谷歌也会做类似的事情

我主要使用Microsoft堆栈(TFS用于源代码控制,IIS,asp.net,带有大量数据的sql server)。当然,面向公众的网站,所以他们必须24x7x365。虽然我可以设想只在其中一个服务器(在webfarm中)发布我的api / dll并测试它,如果有DB(存储的proc签名,表模式更改),我该怎么做呢?目前我们正在对SP进行版本控制(新的将是mySPNameV2,其中旧的将是mySPNameV1 - 两者都采用不同的参数集,因此重命名)并且新的API将使用SP-V2,因为旧的API将继续使用SP -V1。

我看到一些设计气味,但还有更好的方法吗?

编辑:我们只将新代码发布到一个服务器并对其进行测试,难的是你将如何抽象(可能是抽象的不是正确的词,但你得到了这个想法)db schema更改来自多个并发版本申请

6 个答案:

答案 0 :(得分:5)

在我的公司,我们几乎总是以交错的方式进行任何重大发布。我们通过为users表中的每个新功能添加一个标志来实现此目的。默认情况下,此标志设置为false;当我们向更多用户推出该功能时,我们只需在数据库表中切换标志。

这意味着在应用程序级别,我们必须确保在所有地方检查此标志。代码推送到所有服务器。进行数据库更改;但仍然只有一些用户看到了新功能。

在数据库级别,我们确保对SP的任何更改都是“向后兼容”的。这是通过遵循一些简单的规则来完成的:

  1. 添加到SP的任何新参数都必须转到参数列表的末尾。
  2. 新参数应具有默认值。这样做是因为对SP的现有调用不会中断。
  3. 如果必须更改SP的现有参数(或者参数的顺序必须更改),那么显然所有对SP的调用都会更改。但SP的编码方式使其支持启用了该功能的用户以及未启用该功能的用户。
  4. 大多数表格更改都与添加新列有关。当我们必须修改现有列时,这种情况非常罕见。所有新列都添加了默认值或allow NULLs。
  5. 对于API,我们的大多数参数都作为自定义对象(结构)传递。这样我们就可以向API方法添加一个新参数,并且仍然可以防止对API的现有调用中断。

    此外,对于每个用户,我们都会存储他们正在使用的API版本。根据版本,用户会转到其他API网址。因此,基本上当用户进行第一次API调用进行身份验证时,我们会传递一个新的API URL(基于用户的API版本)。对于所有后续调用,它们应该调用新URL。 Salesforce.com也遵循此API进行API调用。

答案 1 :(得分:3)

去年我在QCon,当时一位来自Facebook网络团队的人正在谈论这种方法。 100%正确,为了实现这一点,会有代码味道。但它需要的不仅仅是一个代码味道(实际上他们称那些代码闻起来像Gate Keepers :)。

首先 - 他们表示和存储数据的方式要复杂得多,他们根本不使用外键或数据库的任何其他“高级”功能:),据我记忆,他们的数据并不是那种“关系”,因为我们都想保留我们的:)。

你可以做的是添加守门人(如果(用户来自新西兰){使用新版本的新版本}其他{粘贴旧版本}}。

您可以想象在结构化模型的情况下,这会导致什么样的代码重复(例如,您需要有2个用户,2个订单,2个订单详细信息)。抛开这一点,我认为如果你不是非常小心,也会发生很多回归。

据我记忆 - 他们比周二更频繁地发布。我认为他们有一个持续的部署和代码每天上线,只是他们清理旧的Gate Keepers /架构更改星期二,因为他们在那时将所有用户切换到新功能。

基本上是这样的:

  1. 您可以在新功能所需的任何地方添加守门员 的地方。
  2. 您维护了两个版本的架构。
  3. 您添加越来越多的用户以切换到新用户。
  4. 你清理旧的。
  5. 你进入第1点(如果另一支球队还没有参加:)。
  6. 我意识到这个答案可能不够充分,不足以获得答案,但我从他们的家伙那里听到了这个,并认为值得分享。

答案 2 :(得分:1)

如果我理解正确,你想要的是有两种使用相同实时数据但不同API版本的机制。现在,假设您已经在使用双缓冲机制,我猜您的实际问题是在转换过程中使用实时表。

解决方案是让您的表包含V1和V2列(例如,一个用户表将包含来自两个API的字段)。注意:所有非公共字段必须具有默认值。

为了使其无缝工作,您应该为V1和V2创建视图,只显示每个API版本的相关字段,您应该为视图而不是表开发(类似于为接口而不是开发的概念)实现)。

存储过程也是如此 - 所有非公共参数都必须具有默认值等...

答案 3 :(得分:0)

选项1:

(半)(公共?)devsite的(子)域。 基于手动设置cookie(员工等)<私人测试

为某些用户设置cookie的主域(当时间在时间​​X和时间Y之间时设置cookie)<取决于您的流量。 如果您每小时获得1000个(唯一)访问者,并且您希望在开发域中获得10%,则确保增量时间为6分钟。 当您希望将用户重定向到正常站点时,您可以简单地删除cookie。(确保重定向整个传入URL以防止书签损坏。

选项2:

将一定比例的流量负载平衡到运行新应用的服务器。

<强>数据库

1:开发时与实时数据库交互&lt;定期备份+常规技术开发人员=安全

2:查看主从复制以创建数据库的“实时”卷影副本

答案 4 :(得分:0)

最简单的方法IMO将通过使用安全性来确定用户获取哪个页面的页面集来分隔功能。除了安全性和其他方法以确保用户无法访问他们无法访问的页面(站点地图等),您可以在数据库中存储功能列表以及哪些用户或角色可以访问该功能:

Create Table Features
    (
    Code varchar(10) not null Primary Key
    , StartPage nvarchar(max) not null
    , Description nvarchar(max) not null
    )

Create Table UserFeatures 
    ( 
    UserId ... not null
    , FeatureCode varchar(10) References Features ( Code )
    )

首先,我将使用文本代码作为功能主键而不是代理键(如IDENTITY列或guid)的原因是只有系统才会查询功能。用户永远无法随意添加功能。因此,它使您的代码更清晰,更易于查询...Where FeatureCode = 'AdvancedEntry'而不是...Where FeatureId = 13

其次,在这种方法中,页面本身的代码将决定调用哪个过程。但是,如果这些功能只涉及其他信息领域,那么这可能意味着相当多的重复。

因此,如果功能紧密集成到现有的代码库和表示层中(这就是为什么版本控制如此困难),另一种方法是存储应该使用的存储过程的名称。 Features表。您的代码将查询连接上面的表并返回应该使用的存储过程名称。对于参数,您可以在数据库中存储参数化调用(例如exec Schema.Foo @bar, @gamma, @beta),并且在执行查询时只需检查该字符串是否包含给定参数,如果是,则添加该参数值:

if ( ProcTemplate.Contains( "@bar")
    commandInstance.Parameters.AddWithValue( "@bar", barValue );
if ( ProcTemplate.Contains( "@gamma")
    commandInstance.Parameters.AddWithValue( "@gamma", gammaValue );
...

如果将要素映射到用户角色或组,则需要设计“特朗普”规则,以确定在返回多个要素的情况下应使用哪个要素。在这种方法中,您将单独保留现有存储过程,禁止更改模式,这需要更改存储过程(例如,删除列)。这种方法的一个额外结果是您可以在Features表中添加日期,以确定新功能何时上线。

答案 5 :(得分:0)

也许我在这里简化,但为什么不进一步实例化数据库呢?

当你做一部分用户来“测试”它是内部的,外部的子集,还是世界你实际上不是在执行类似beta测试的东西?这不会调用具有存储过程的beta版本与当前版本/实例的数据库的不同实例吗?只是在那时指向Web配置中的连接字符串不是吗?

这里的约束可能是服务器的成本和“巨大的数据”,我承认我正在瞥一眼,或者真的是关于存储过程的版本化等问题?他们不能只是源控制以及架构更改吗?

我可能要求的不仅仅是回答。