Shutting down a Go "desktop" application

polaris · · 526 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>In the old days, I wrote desktop applications in Java using the Swing library for the graphical user interface.</p> <p>I&#39;ve now wised up and am writing most of my software in Go (:-)).</p> <p>I have a couple of &#34;desktop&#34; applications in mind, but I do not want to go back to the days of using a UI library. I would prefer to write them as client-server apps where the server only responds to localhost. When the server starts (via a double-click on the icon), it will open an <code>index.html</code> in the default browser, and use CSS and React (using GopherJS) as the front-end of a single-page app. The server will connect to a local database (probably <a href="https://github.com/boltdb/bolt" rel="nofollow">BoltDB</a>) and will serve information to the front-end using a REST API.</p> <p>The dilemma that I am struggling with is the best way to shut down the server when the user is finished. I am using Go 1.8, so I can call the <a href="https://golang.org/pkg/net/http/#Server.Shutdown" rel="nofollow">Server.Shutdown()</a> function. I can include a REST call that invokes this function (e.g. <code>/shutdown</code>). Two possibilities for calling this are</p> <ul> <li>An &#34;Exit&#34; button in the UI</li> <li>Catch the &#34;close tab&#34; event in the browser</li> </ul> <p>Either of these can then use a <code>fetch</code> call to invoke the <code>/shutdown</code> API.</p> <p>Are there other alternatives that might be better? I guess I could also just leave the serve running in the background, but there might be valid reasons for shutting it down when not in use.</p> <hr/>**评论:**<br/><br/>mixedCase_: <pre><p>It&#39;s likely you&#39;ll want to have real-time bidirectional communication between your frontend and backend, so you can just keep a websocket open to do so and kill the backend when the frontend closes the conncetion.</p></pre>grkuntzmd: <pre><p>I might be able to use a web socket as a &#34;close-me&#34; sentinel, without actually sending anything down the socket. I&#39;ve used Go channels for the same thing.</p></pre>brokedown: <pre><p>This is the obvious answer, there are other ways to do it but they won&#39;t be as good.</p></pre>jerf: <pre><p>An exit button in the UI is probably necessary, because users are going to want to be able to shut it down.</p> <p>Catching the close event would require more careful engineering; if you don&#39;t control the browser window you can&#39;t stop users from opening your app in a second tab, and you don&#39;t want to terminate if that tab closes. You can try to play games to deal with this but it would be fragile.</p> <p>One thing I&#39;d suggest is documenting that the &#34;exit&#34; button shuts the app down and that this is the official mechanism for terminating it, but also implement something like &#34;If I haven&#39;t seen a request for $SOME_TIME, I shut down.&#34; You can wrap a bit of middleware around the top-level router to track &#34;the last time I saw a request of any kind&#34; pretty easily, and if you do go with a websocket or something you can easily write something that refreshes that too.</p> <p>I&#39;d also suggest doing something like having the program manually count seconds rather than using the system time and just setting an alarm for an hour in the future. If you, say, want to shut down after an hour of not being used, by using a channel to ping you every second and counting those, the time that a computer may be suspended or hibernated won&#39;t count against that time, which is what you want. (Normally this is a bad idea precisely because it misses out on suspend/hibernate time.) You probably ought to set that timer to something like every 10 seconds or something, too, to avoid getting scheduled too often while idle, which eats battery.</p> <p>That just leaves making sure that the app itself can detect shut downs and handle them gracefully, giving the user clear instructions on how to restart the app.</p> <p>Unfortunately, there just isn&#39;t a way to fully bind these two elements together without embedding a full browser under your control, but if you can live without that, these things will be &#34;good enough&#34;.</p> <p>I wouldn&#39;t particularly recommend trying to use the presence of a websocket to determine whether the app is open; in addition to the challenges if a user opens your app in a second tab, in extreme situations such as memory deprivation I can&#39;t guarantee it&#39;ll do what you want.</p></pre>dinkumator: <pre><p>I&#39;ve been planning out an app with a similar setup, I was thinking about just building a &#34;lightweight&#34; native webkit wrapper that points at the localhost server. This way I can still make a mac bundle and get expected menu/shutdown semantics.</p> <p>It&#39;d be sorta like <a href="https://en.wikipedia.org/wiki/Adobe_AIR" rel="nofollow">Adobe AIR</a> or <a href="https://electron.atom.io/" rel="nofollow">Electron</a> but for Go apps.</p></pre>grkuntzmd: <pre><p>I had thought to use a webkit wrapper, but I&#39;m not sure if I would then need to have users install it before using/installing my server app. One thing I like about Go is that the executable is completely self-contained. I just need to send one file.</p></pre>dinkumator: <pre><p>The wrapper can call the Go executable as a child process. On mac it&#39;s fairly straightforward to make an .app bundle so it&#39;s still self-contained. I&#39;m not as experienced at .NET / windows but I&#39;m sure there&#39;s a similar method for modern windows apps.</p> <p>If you&#39;re concerned about users not being able to install things, I&#39;d be more concerned about doing the separate server process actually!</p></pre>grkuntzmd: <pre><p>Installation of the app is its own can of worms... :-)</p></pre>asticode: <pre><p>You can embed whatever you need for the installation in your self-contained GO executable using <a href="https://github.com/jteeuwen/go-bindata" rel="nofollow">https://github.com/jteeuwen/go-bindata</a> if you wish</p></pre>dinkumator: <pre><p>I ran across this project a while back, it could be useful too: <a href="https://github.com/alexflint/gallium" rel="nofollow">https://github.com/alexflint/gallium</a></p></pre>grkuntzmd: <pre><p>I looked at gallium. It currently only supports MacOS. I develop in Linux.</p></pre>asticode: <pre><p>This solution is cross platform : <a href="https://github.com/asticode/go-astilectron" rel="nofollow">https://github.com/asticode/go-astilectron</a> (<em>Disclaimer: I&#39;m the author</em>)</p> <p>It relies on Electron and I&#39;ve released it 2 days ago so feel free to test it and let me know what you think about it.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

526 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传