How do you handle your database connection in non-trivial applications?

polaris · · 473 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;m just getting started trying to make a simple web app in go. All of the examples I look at just define a global db object and use it like this</p> <pre><code>package main var db *some.DB func main() { db = NewDB() db.Create(&amp;User{id:1}) db.Delete(&amp;User{id:2}) } </code></pre> <p>What is a better way to handle the database connection while still keeping it accessible from various models/routes?</p> <p>Are there any open source examples or implementations I could take a look at?</p> <p>Thanks! </p> <hr/>**评论:**<br/><br/>junajted: <pre><p>Read this post &#34;Practical Persistence in Go: Organising Database Access&#34; by Alex Edwards. It gives you good directions, <a href="http://www.alexedwards.net/blog/organising-database-access">http://www.alexedwards.net/blog/organising-database-access</a></p></pre>asdasdasdaqqq: <pre><p>This is exactly the kind of thing I was hoping for. Thanks!</p></pre>titpetric: <pre><p>I am tempted to make a write up of how I use sqlx, with a different kind of pattern. I have a connection manager (ie, factory model) where you&#39;d request an <code>sqlx.DB</code> from a package that serves as a factory.</p> <pre><code>import &#34;app/factory&#34; main() { factory.Database.Add(name, dsn) ... } somefunc() { db := factory.Database.Get(&#34;name&#34;) db.Exec(&#34;...&#34;) } </code></pre> <p>I&#39;m oversimplifying a bit, but I&#39;m dealing more with the fact of how and where to create the database handle, rather than how I would move it from <code>somefunc</code> further down the execution stack. For those purposes, the above article is very valid :)</p></pre>alexfiori: <pre><p>Do you use the factory for managing sharding and the likes? </p></pre>titpetric: <pre><p>Mainly I use it for multiple connection endpoints, but at least round-robin sharding would be very easy to introduce. A bit more tricky would be algorithmic sharding or host ejection, because you would have to introduce some sharding algorythm that requires a key to determine the shard host, and to keep track of host connection status...</p> <p>Easy to do, harder to get right...</p></pre>Ploobers: <pre><p>I would keep sharding logic out of my app and use something like <a href="http://vitess.io" rel="nofollow">Vitess</a> for MySQL or <a href="https://www.citusdata.com/" rel="nofollow">Citus</a> for Postgres.</p></pre>ishanjain28: <pre><p><a href="/u/junajted" rel="nofollow">/u/junajted</a> The pattern described (i.e. env.Index or index(env)) in the blog post can get messy if you have a lot of middlewares. I use a adapter pattern combined with some of what he has suggested in his blog post. It is a lot more flexible, Code looks pretty good and it scales well in a large application</p> <p>A quick example can be found here: <a href="https://gist.github.com/ishanjain28/f85345455cf7b08af422156eb1cbc828" rel="nofollow">https://gist.github.com/ishanjain28/f85345455cf7b08af422156eb1cbc828</a></p></pre>hell_0n_wheel: <pre><p>This is a great post. Something to keep in mind while reading the post is not just &#34;how do I handle a DB connection?&#34; but also &#34;how do I decouple my data model and my business logic?&#34;</p> <p>The latter is just as important as the former, as it allows you to change your DB&#39;s schema (or switch DBs altogether) without having to rewrite a lot of your code. Hence, I use the interface method described in the post. The ability to mock out the DB for test purpose is super helpful, too.</p> <p>This method also unlocks an integration-style test. One can easily invoke a 2nd (ex. non-production) DB, load it with test data, and test the program - DB interaction without having to modify any of the business logic.</p></pre>jerf: <pre><p>I like to pass it around via a <a href="http://www.jerf.org/iri/post/2929">composed environment object</a>.</p> <p>I wrote that article before the popularity of the Context object, but I would simply compose any relevant Context objects into my local environment. The environment object is not really a particular object as the Context is, it&#39;s just &#34;whatever I need for this particular bit of code&#34;, which may include Contexts or not.</p> <p>As you get more used to composition, you will tend to find that you are explicitly passing around a *DB a lot less often than you&#39;d expect to than inheritance-based intuition would lead you to believe.</p> <p>(I know composition isn&#39;t news to the Go community but I still think there&#39;s more juice in the idea than is often understood. However, I also find that it&#39;s hard to extract the reasons why into cute little blog posts; I just find that as you build programs with composition that it tends to work itself out on this front pretty well.)</p></pre>fazalmajid: <pre><p>Read VividCortex&#39; excellent eBook (email registration required): <a href="https://www.vividcortex.com/blog/2015/01/14/the-ultimate-guide-to-building-database-driven-apps-with-go/" rel="nofollow">https://www.vividcortex.com/blog/2015/01/14/the-ultimate-guide-to-building-database-driven-apps-with-go/</a></p></pre>i-dontevenseethecode: <pre><p>For Mongodb this guide has a decent structure. </p> <p><a href="http://goinbigdata.com/how-to-build-microservice-with-mongodb-in-golang/" rel="nofollow">http://goinbigdata.com/how-to-build-microservice-with-mongodb-in-golang/</a></p></pre>jns111: <pre><p>I came from Java to Go so obviously I&#39;m bootstrapping my applications using dependency injection: <a href="https://godoc.org/github.com/facebookgo/inject" rel="nofollow">https://godoc.org/github.com/facebookgo/inject</a> Inject from Facebook is a useful tool which also offers named dependencies so you can have different sql.DB objects injected into your handlers. In my opinion it&#39;s not the easiest way to solve the problem but its a clean one.</p></pre>paul2048: <pre><p>Or write a factory? relying on reflection will make your code slower and harder to read.</p></pre>drvd: <pre><blockquote> <p>What is a better way to handle the database connection</p> </blockquote> <p>The answer depends a lot on your definition of &#34;better&#34;.</p></pre>asdasdasdaqqq: <pre><p>What is your definition of better?</p> <p>I&#39;m mostly looking for different approaches and views on the topic</p></pre>cohix: <pre><p>I like to create a struct type that only has an anonymous *sqlx.Db in it, add functions to that new struct for your database calls (separated into categories such as users, posts, tags, etc), then use Context.WithValue to pass it up and down the stack. Create the instance in main.go, then pass it to your handler generator functions so they can be used in the http.HandlerFunc&#39;s. PM me if you want more detail!</p> <p>TL;DR the context package was designed for this: use it. </p></pre>shovelpost: <pre><blockquote> <p>TL;DR the context package was designed for this: use it.</p> </blockquote> <p>No. You should NOT use the context package for this. </p> <p>Please read: <a href="http://www.alexedwards.net/blog/organising-database-access">http://www.alexedwards.net/blog/organising-database-access</a></p> <blockquote> <p>Personally I&#39;m not a fan of storing application-level variables in request-scoped context – it feels clunky and burdensome to me. The x/net/context documentation kinda advises against it too:</p> <blockquote> <p>Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.</p> </blockquote> </blockquote> <p>Instead add methods to your struct type that contains the anonymous *sql.DB.</p></pre>pinpinbo: <pre><p>Why should users not use <code>context</code> package for this?</p> <p>I haven&#39;t seen any failures that this approach cause and it is very trivial to construct a <code>context</code> object using per-environment-config files.</p> <p>Once that&#39;s done, the <code>context</code> object is available everywhere. It&#39;s a very simple to understand Dependency Injection.</p></pre>joncalhoun: <pre><p>The general reason for not doing this is it hides dependencies and can make testing and maintaining code much harder. </p> <p><a href="https://www.calhoun.io/pitfalls-of-context-values-and-how-to-avoid-or-mitigate-them/" rel="nofollow">https://www.calhoun.io/pitfalls-of-context-values-and-how-to-avoid-or-mitigate-them/</a> goes into this a bit and for longer lasting variables like a db connection the most common approach I see is to make your http handlers methods on a struct that has a db connection as a field.</p></pre>shovelpost: <pre><blockquote> <p>It&#39;s a very simple to understand Dependency Injection.</p> </blockquote> <p>Package context was not designed for dependency injection. </p> <p>From the official docs:</p> <blockquote> <p>Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.</p> </blockquote> <p>Also please read: <a href="https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39" rel="nofollow">https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39</a></p> <blockquote> <p>The most contentious part of Context is Value, which allows arbitrary values placed into a Context. The intended use for Context.Value, from the original blog post, is request-scoped values. A request scoped value is one derived from data in the incoming request and goes away when the request is over. As a request bounces between services, this data is often maintained between RPC calls. Let’s first try to clarify what is or is not a request scoped value.</p> <p>Obvious request scoped data could be who is making the request (user ID), how they are making it (internal or external), from where they are making it (user IP),and how important this request should be.</p> <p>A database connection is not a request scoped value because it is global for the entire server.</p> </blockquote></pre>

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

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