拼吾爱程序人生编程应用应用系统 Web Farm中异步、高效的用户登录解决方案

1  /  1  页   1 跳转 查看:925

Web Farm中异步、高效的用户登录解决方案

Web Farm中异步、高效的用户登录解决方案

文/Udi Dahan  译/罗小平  出处/InfoQ

在我的咨询工作中,常常会碰到一些持如下观点的人:“有些东西并不适合使用异步模式”——尽管他们自己也认可异步通讯 模式与生俱来的稳定性。一个常常被引用的例子就是用户验证——将用户名和密码对提交给后端系统验证。为了讨论方便,我假设后端系统使用了用户数据库。

问题假设

为保证基本的安全性,我们假设密码在被存储前以某种散列算法编码。同时假设网络结构设计合理,Web服务器在 DMZ区域中得到隔离,它与应用服务器交互,应用服务器再与数据库服务器通讯。当然,再在Web服务器 间(尤其是对用户登录等功能)应用轮循式(round- robin)负载均衡,也是一个很好的想法。

在深入讨论这个问题前,先来段开场白。我发现人们不感冒异步模式,大多是因为没有考虑应用的实际发布环境,或者解决方案不需要以多服务器、 Web Farm或多数据中心的分布式模式部署。

同步解决方案

在同步解决方案中,每个Web服务器对于每个用户的登录请求,都必须与应用服务器通讯。换句话说,应用服务器上的负载(数据库服务器也是类似的)将随用户登录数正比上升。

我不想在这里对同步解决方案多加纠缠,因为以前已经分析 得太多了。在这种系统中,数据库最终都会成为瓶颈。为解决这个问题,一般会采用数据库分割方法 。很多大型站点配备有多个只读数据库——主数据库负责数据更新,并将这些数据复制到只读数据库。如果在 LAMP架构下使用廉价的MySQL,这是一个很好 的解决方案;但如果运行OracleMS SQL Server,就不是那么回事了。

无论你在数据层动什么手脚,都回避不了这个问题。将数据访问操作限制在Web服务器内部不是很好吗?即便使用廉价的 Apache,系统也会运行得更为流畅。异步解决方案的本质,就是以较小的内存代价,换取对其他资源很大的节省。

异步解决方案

在异步 方案中,我们可将用户名/散列密码对缓存在Web服务器的内存中,并用缓存中的数据实现对用户的验证。首先,我们分析一下这种方法对内存的消耗量。

用户名 一般不超过12个字符,我们在这里大方些,假设平均为32个字符。使用 Unicode编码后,每用户名占用64字节。散列加密的密码因算法而异,会占用 256512位,即最长64字节。因此总的是 128字节。也就是说,使用Web服务器上1GB内存, 我们可以安全缓存8百万用户名/密码对。如果你有1百万用户 (也不错了,行啊你),则只需要消耗128MB内存——这对于不需要花多少钱就可以配备2GB内存的服务器来说,小意思了。

新用户注册时,我们可以在Web服务器缓存中检查是否存在该用户名。当然,若考虑到并发问题,还需要通过数据库再次检查,但毫无疑问,数据库的负载已经大大降低。另外值得一提的是,在这种方案中,已经不存在只读数据 库副本和数据复制操作。换个角度看,其实是我们的Web服务器充当了“数据库副本”。



 感谢原创者的辛勤劳动,希望对您有所帮助,转载请注明原出处。
 您可能对 [应用系统] 的这些文章也感兴趣:

Chrome源码剖析【三】Chrome的进程模型
人工神经网络(Artificial Neural Netwroks)笔记-基本的非确定性统计训练算法
十大开源CRM
XNA Game Studio 3.0 发布
为Arora浏览器添加了中文翻译文件
Oracle8i与Windows 2003标准版的不兼容问题
EAI选型的标准
Oracle四月发布的安全漏洞多达41个
开源技术选型手册
IT SOA vs 业务SOA?
 

回复: Web Farm中异步、高效的用户登录解决方案

验证服务

整个系统的核心模块是应用服务器上的“验证服务”,用于处理来自Web服务器的所有登录请求,当然包括新用户及其信息的注册。以前我们总是使用同步模式,新方案的不同之处在于新用户注册时,它会发出一个消息。此服务保证所有 Web服务器都能收到各自订阅的全部用户名/散列密码对信息。

在这里 ,我使用开源通讯框架nServiceBus说明此方案的实现过程,当然你也可以使用其他任何消息处理或ESB方案。在nServiceBus中,一条物理消息可包括多条逻辑消息,这样我们可以模拟单个更新消息发布,用相同类型的逻辑消息返回整个结果清单。我们定义如下消息:

[Serializable]
public class UsernameInUseMessage : IMessage
{
    private string username;
    public string Username
    {
        get { return username; }
        set { username = value; }
    }
    private byte[] hashedPassword;
    public byte[] HashedPassword
    {
        get { return hashedPassword; }
        set { hashedPassword = value; }
    }
}

定义需要整个清单时,Web服务器发出的消息:

[Serializable]
public class GetAllUsernamesMessage : IMessage
{
}

Web服务器启动时执行的代码大致如下(可在构造函数中注入依赖对象):

public class UserAuthenticationServiceAgent
{
    public UserAuthenticationServiceAgent(IBus bus)
    {
        this.bus = bus;
        bus.Subscribe(typeof(UsernameInUseMessage)); // 订阅更新类消息
          bus.Send(new GetAllUsernamesMessages());  // 请求整个清单的信息
    }
}

当验证服务收到消息GetAllUsernamesMessage时, 它的消息处理器将首先访问用户名/散列密码缓存,然后构造一个新的消息,并返回给请求者,代码如下:

public class GetAllUsernamesMessageHandler : BaseMessageHandler
{
    public override void Handle(GetAllUsernamesMessage message)
    {
        this.Bus.Reply(Cache.GetAll());
    }
}

消息 UsernameInUseMessage到达Web服务器时,负责处理的类定义如下:

public class UsernameInUseMessageHandler : BaseMessageHandler
{
    public override void Handle(UsernameInUseMessage message)
    {
        WebCache.SaveOrUpdate(message.Username, message.HashedPassword);
    }
}
 

回复: Web Farm中异步、高效的用户登录解决方案

应用服 务器向Web服务器发送整个清单时,UsernameInUseMessage类的多个实例会被包含在单独一条物理消息中。而Web服务器上的bus对象则 每次只会向如上的消息处理器发出一条逻辑消息。

这样, 实际验证一个用户时,Web页(如果你使用MVC,也可叫做控制器)将调用:

public class UserAuthenticationServiceAgent
{
    public bool Authenticate(string username, string password)
  {
        byte[]existingHashedPassword = WebCache[username];
        if (existingHashedPassword != null)
            return existingHashedPassword == this.Hash(password);
        return false;
    }
}

注册新 用户时,Web服务器当然会首先检查缓存,然后发出一条包含了用户名和散列密码的消息RegisterUserMessage

[Serializable]
[StartsWorkflow]
public class RegisterUserMessage : IMessage
{
    private string username;
    public string Username
    {
        get { return username; }
        set { username = value; }
    }
    private string email;
    public string Email
    {
        get { return email; }
        set { email = value; }
    }
    private byte[] hashedPassword;
    public byte[] HashedPassword
    {
        get { return hashedPassword; }
        set { hashedPassword = value; }
    }
}

消息 RegisterUserMessage到达应用服务器时,如下流程的新实例负责处理:

public class RegisterUserWorkflow :
    BaseWorkflow,IMessageHandler
{
    public void Handle(RegisterUserMessage message)
    {
        //通过message.Email发出包含了this.Id(一个guid号,是URL的组成部分)的确认请求
    }
    ///
    /// 用户点击email中的确认链接后,Web服务器发出包含了流程Id的消息UserualidatedMessage
    ///
    public void Handle(UserValidatedMessage message)
    {
        // 将用户存入数据库
        this.Bus.Publish(new UsernameInUseMessage(
            message.Username, message.HashedPassword));
    }
}

消息 UsernameInUseMessage最终将到达所有订阅了它的Web服务器。

性能/安全性的权衡

更深入考察整个流程,我们发现实际可实现为两个独立的消息处理器,并可用email地址代替流程Id。不过 ,在这个改进的替代方案中必须考虑安全问题。删除对流程Id的依赖,即表示我们可在未收到消息 RegisterUserMessage前收到UserValidatedMessage

因为 UserValidatedMessage的处理过程消耗的资源相对较多——写入数据库,并向所有 Web服务器发布消息,恶意用户用不多的消息就可以发起拒绝服务攻击(DOS),同时也能躲开多数探测系统的眼睛。而要想依靠GUID欺骗则困难得多。不过,因为注册流程的处理实例可以缓存于内存中,这将大大降低相关数据搜索带来的资源消耗——甚至可以小到在探测系统察觉前,DOS攻击不能发生作用。

对带宽和服务器资源的要求降低

这个解决方案,使我们可以通过扩展Web层,规避对数据层的巨大压力和扩展需求。同时,它也能大大节省带宽消耗。对于用户名和密码而言,这看起来不是大问题,但在其他一些情况下,需处理的数据量可能大很多。当然,在此方案中处理用户信息所需的时间也会大大缩短,因为我们不需要在Web服务器(位于DMZ)、应用服务器和数据库服务器间来回奔走。

在这个 解决方案中,我们应该谨记的部分是消息发布/订阅。nServiceBus提供的在消息发布/订阅基础上设计系统的API十分简单。消息发布,是实现系统扩展性的核心部分。随着用户的增长,你只需要增添Web服务器,而不是数据库服务器。在整个解决方案,每请求所消耗的平均要小很多 ,因为所有的工作都是在接收请求的服务器本地完成。

锦上添花:ETags

为方便高级用户,我们还将此解决方案封装成了ETagsWeb服务器停止运行时,缓存会被清空,我们能做的就是将缓存内容记录到磁盘上去(可用后台线程),并用服务器随UsernameInUseMessage消息一起传给我们的某种数据作为标记。这样,Web服务器重新启动后,它请求GetAllUsernamesMessage时可同时发出 ETag,应用服务器就只需要发送有变动的数据。使用“If-Modified-Since”HTTP GETREST(译者注:可参看 深入浅出 RESThttp://www.javaeye.com/post/260746),也能很好解决这个问题。所有这些措施,都可以依靠Web服务器上磁盘空间的较小消耗,大大降低对网络带宽的需求。

结束语

即便你只有一台机子,同时充当Web和数据库服务器,在这个解决方案基础上构建的系统的运行效率也会很高。如果服务器更多, 性能自然会更好。不仅如此,此方案还极具可扩展性——即使你得到了8百万 Facebook用户,也不会因为遭受重大冲击而必须修改整个系统架构。

更多信息

http://www.nServiceBus.com

nServiceBus是一个用于构建企业级 .NET系统的开源通讯框架。它在消息发布/订阅支持、工作流集成和高度可扩展性等方面表现优异,因此是很多分布式系统基础平台的理想选择。

Podcast on Autonomous Servers and Publish/Subscribe

我们在这里主要研究服务自治、消息发布/订阅、异常、数据复制、系统复用和监管等领域的问题。

 
1  /  1  页   1 跳转

快速回复帖子

标题
禁用 URL 识别
禁用表情
禁用 Discuz!NT 代码
使用个人签名
  [完成后可按 Ctrl+Enter 无刷新发布]  

版权所有 拼吾爱程序人生    Total Unique Visitors:

web counter

Powered by Discuz!NT 2.1.202   Copyright © 2001-2008 Comsenz Inc. 鄂ICP备07500843号
返顶部