更新Go Web应用程序的最佳实践方法

huangapple go评论83阅读模式
英文:

Best practice for updating Go web application

问题

我正在思考如何在部署更新到一个(MVC)Go Web应用程序时采用最佳实践。想象以下场景:

1)为我的Go Web应用程序编写和测试一些更改。

2)在没有任何人使用先前版本的情况下部署更新,以免中断他们的使用。

我不知道如何确保第2)点可以得到覆盖 - 当有人向服务器发送请求,并且我在这个时刻重新构建/重启它时,他会收到一个错误 - 即使请求只使用了我没有修改过的代码部分,或者是向后兼容的,或者我只是添加了一个新的请求处理程序。

也许我错过了一些微不足道的东西,或者是一个众所周知的模式,因为我正在学习Go,并且我之前的Web应用程序是ASP.NET或PHP应用程序,在这种情况下,我不需要在代码更改时重新启动Web服务器。

英文:

I am wondering what would be the best practice for deploying updates to a (MVC) Go web application. Imagine the following scenario :

  1. Code and test some changes for my Go Web Application

  2. Deploy update without anyone currently using the previous version getting interrupted.

I don't know how to make sure point 2) can be covered - when somebody is sending a request to the server and I rebuild/restart it just in this moment, he gets an error - even if the request just uses a part of the code I did not touch or that is backwards-compatible, or if I just added a new Request-handler.

Maybe I'm missing something trivial or a well-known pattern as I am just in the process of learning go and my previous web applications were ASP.NET- or php-applications where this was no issue as I did not need to restart the webserver on code changes.

答案1

得分: 1

这不仅仅是Go的问题,一般来说,我们可以将问题分为两个独立的部分:

  1. 确保当前的请求不会被终止并影响用户体验。

  2. 确保没有停机时间,新的请求无法处理。

第一个问题比较容易解决:你只需要不强制关闭服务器,而是告诉它退出,进入一个"Drain phase",在这个阶段它不接受新的请求,只完成当前正在运行的请求,然后退出。可以通过监听信号的方式实现,将应用程序进入特殊状态。

在Go中这并不简单,因为默认的HTTP服务器不支持关闭,但你可以使用net.Listener启动一个服务器,然后在适当的时候关闭它。

现在,只采用第一种方法然后重新启动服务会导致在此期间无法接受新的请求,我们都知道在极端情况下可能需要几秒钟的时间。

所以我们需要另一个已经运行新代码的服务器实例,一旦旧的实例无法响应新的请求,对吗?这可以通过多种方式实现:

  1. 使用多个服务器和负载均衡器,允许一个(或多个)服务器承担负载,同时重新启动另一个服务器。这是最简单的方式,也是大多数人使用的方式。如果你需要N个服务器来承担用户的负载,只需保留N+1个服务器,并逐个重新启动。

  2. 使用套接字共享技巧。在较新的Linux内核中,许多进程可以在同一个端口上监听和接受连接。你只需启动新实例,然后告诉旧实例完成并退出。这样就没有停顿。可以通过在监听套接字上设置SO_REUSEPORT来实现。

  3. 可以使用现成的解决方案自动化上述过程,比如Einhorn,它会处理所有细节,详见https://github.com/stripe/einhorn

  4. 另一种方法在这篇博文中有详细介绍:http://blog.nella.org/?p=879

英文:

It's not just an issue with Go, but in general we can divide the problem into two separate ones:

  1. Making sure current requests do not get terminated and affect user experience.

  2. Making sure there is no down-time in which new requests cannot be handled.

The first one is easier to tackle: You just don't violently kill your server, but tell it to exit, causing a "Drain phase", in which it does not accept new requests and only finishes the currently running requests, and exits. This can be done by listening on signals for example, and entering the app into a special state.

It's not trivial with Go as the default http server doesn't support shutting it down, but you can start a server with a net.Listener, and then keep a reference to it an close it when the time is due.

Now, doing only approach one and then starting the service again will cause new requests not to be accepted while this is going on, and we all know this can take a number of seconds in extreme cases.

So what we need is another instance of the server already running with the new code, the instant the old one is not responding to new requests, right? That can be done in several ways:

  1. Having more than one server, and a load-balancer on top of them, allowing one (or more) server to take the load while we restart another. That's the simplest way, and the way most people do it. If you need N servers to take the load of your users, just keep N+1 and restart one at a time.

  2. Using socket sharing tricks. In Newer Linux kernels, Many processes can listen and accept on the same port. What you do is simply start the new instance and then tell the old one to finish and exit. This way there is no pause. This is done by setting SO_REUSEPORT on the listening socket.

  3. The above can be automated with ready to ship solutions, like Einhorn, that deals with all the details for you, see https://github.com/stripe/einhorn

  4. Another approach is documented in this blog post: http://blog.nella.org/?p=879

huangapple
  • 本文由 发表于 2015年1月4日 00:03:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/27756705.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定