有没有办法加速django中的身份验证功能?

时间:2013-01-22 23:57:44

标签: python mysql django performance apache

我们正在使用django为mysql创建一个json webservice前端。我们在EC2实例上运行apache和django,在RDS实例上运行MySQL。我们已经开始使用apache bench来对性能进行基准测试,并且得到了一些非常差的性能数据。我们还注意到,在运行测试时,我们的apache / django实例在非常低的负载下达到了100%的cpu使用率,并且MySQL实例的CPU使用率从未超过2%。

我们试图弄清楚这一点并将问题隔离开来,所以我们做了几次ab测试:

  1. 从apache请求静态html页面 - 约2000个请求/秒。
  2. 在django中执行小python函数的请求,没有数据库交互 - 约1000个请求/秒。
  3. 执行我们调用身份验证的django webservice函数之一的请求,然后执行一个非常简单的查询以从表中获取一条记录--11个请求/秒
  4. 与3相同,但注释了对身份验证的调用 - 每秒95次请求。
  5. 为什么验证这么慢?它是在向数据库写入数据,找到数十亿个数字的pi,是什么?

    我们希望继续在这些功能中进行身份验证,因为我们不想让任何可以猜到网址的人都可以使用,等等。有人在这里注意到身份验证很慢,任何人都可以建议补救它的方法?

    非常感谢!

1 个答案:

答案 0 :(得分:8)

我不是身份验证和安全方面的专家,但以下是关于为什么会发生这种情况以及可能如何在某种程度上提高性能的一些想法。

由于密码存储在数据库中,为了使其存储安全,不存储明文密码,而是存储其散列。这样,您仍然可以通过将键入的密码的计算哈希与存储在数据库中的密码进行比较来验证用户登录。这样可以提高安全性,这样,如果恶意方获得数据库的副本,解码明文密码的唯一方法就是使用彩虹表或进行暴力攻击。

这是事情变得有趣的地方。根据摩尔定律,计算机的指数速度正在快速增长,因此计算哈希函数在时间上变得便宜得多,特别是像md5或sha1这样的快速哈希函数。这带来了一个问题,因为现在拥有所有可用的计算能力和快速散列函数,黑客可以相对容易地强制散列密码。为了解决这个问题,可以做两件事。一个是循环哈希函数多次(哈希的输出被反馈到哈希)。然而,这不是非常有效,因为它仅通过常数增加散列函数的复杂性。这就是为什么第二种方法是优选的,这使得实际的散列函数更复杂并且计算成本更高。具有更复杂的功能,需要更多时间来计算散列。即使计算需要一秒钟,对最终用户来说也不是什么大问题,但对于暴力攻击来说这是一个大问题,因为必须计算数百万个哈希值。这就是为什么从Django 1.4开始,它使用了一个名为PBKDF2的计算量很大的函数。

回到你的答案。正是由于这个功能,当你启用身份验证时,你的基准测试数量急剧下降,你的CPU也会上升。

以下是一些可以提高效果的方法。

  • 从Django 1.4开始,您可以更改默认验证功能(docs)。如果您不需要太多安全性,可以将默认功能更改为SHA1或MD5。这应该会提高性能,但请记住,安全性会更弱。我的个人意见是安全性很重要,值得花费额外的时间,但如果在您的应用程序中没有保证,那么您可能需要考虑这一点。
  • 使用会话。昂贵的哈希函数仅在初始登录时计算。用户登录后,将为该会话创建会话,并使用会话ID将cookie发送给用户。然后在后续请求中,用户上传cookie,如果会话尚未到期,则会自动对用户进行身份验证(不要担心安全性,因为会话数据已签名...)。关键是与计算昂贵的散列函数相比,验证会话的计算成本更低。我想在ab测试中你没有发送会话cookie。尝试添加发送会话cookie并查看其执行情况来进行一些测试。如果您正在制作JSON API,那么发送cookie实际上不是一个选项,那么您可以通过会话GET参数而不是cookie来修改会话后端以接受会话数据。然而,不确定这样做的安全后果是什么。
  • 切换到nginx。我不是部署专家,但根据我的经验,与Apache相比,Django的nginx更快,更友好。我认为您可能特别感兴趣的一个优点是nginx具有多个工作进程的能力以及使用proxy_pass将请求传递给Django进程的能力。如果你有多个工作进程,你可以通过proxy_pass将每个worker指向一个单独的Django进程,这将有效地为Django添加多处理。另一种选择是如果你使用像gevent WSGI服务器这样的东西,你可以在Django进程中创建一个池,这也可以提高性能。不确定这些是否会大大提高您的性能,因为您的CPU负载已达到100%,但可能需要考虑一下。