您的位置:

golang依赖注入db,golang依赖注入什么意思

本文目录一览:

go依赖注入dig包使用-来自uber公司

原文链接:

github:

Dependency Injection is the idea that your components (usually structs in go) should receive their dependencies when being created. This runs counter to the associated anti-pattern of components building their own dependencies during initialization. Let’s look at an example.

Suppose you have a Server struct that requires a Config struct to implement its behavior. One way to do this would be for the Server to build its own Config during initialization.

This seems convenient. Our caller doesn’t have to be aware that our Server even needs access to Config . This is all hidden from the user of our function.

However, there are some disadvantages. First of all, if we want to change the way our Config is built, we’ll have to change all the places that call the building code. Suppose, for example, our buildMyConfigSomehow function now needs an argument. Every call site would need access to that argument and would need to pass it into the building function.

Also, it gets really tricky to mock the behavior of our Config . We’ll somehow have to reach inside of our New function to monkey with the creation of Config .

Here’s the DI way to do it:

Now the creation of our Server is decoupled from the creation of the Config . We can use whatever logic we want to create the Config and then pass the resulting data to our New function.

Furthermore, if Config is an interface, this gives us an easy route to mocking. We can pass anything we want into New as long as it implements our interface. This makes testing our Server with mock implementations of Config simple.

The main downside is that it’s a pain to have to manually create the Config before we can create the Server . We’ve created a dependency graph here – we must create our Config first because of Server depends on it. In real applications these dependency graphs can become very large and this leads to complicated logic for building all of the components your application needs to do its job.

This is where DI frameworks can help. A DI framework generally provides two pieces of functionality:

A DI framework generally builds a graph based on the “providers” you tell it about and determines how to build your objects. This is very hard to understand in the abstract, so let’s walk through a moderately-sized example.

We’re going to be reviewing the code for an HTTP server that delivers a JSON response when a client makes a GET request to /people . We’ll review the code piece by piece. For simplicity sake, it all lives in the same package ( main ). Please don’t do this in real Go applications. Full code for this example can be found here .

First, let’s look at our Person struct. It has no behavior save for some JSON tags.

A Person has an Id , Name and Age . That’s it.

Next let’s look at our Config . Similar to Person , it has no dependencies. Unlike Person , we will provide a constructor.

Enabled tells us if our application should return real data. DatabasePath tells us where our database lives (we’re using sqlite). Port tells us the port on which we’ll be running our server.

Here’s the function we’ll use to open our database connection. It relies on our Config and returns a *sql.DB .

Next we’ll look at our PersonRepository . This struct will be responsible for fetching people from our database and deserializing those database results into proper Person structs.

PersonRepository requires a database connection to be built. It exposes a single function called FindAll that uses our database connection to return a list of Person structs representing the data in our database.

To provide a layer between our HTTP server and the PersonRepository , we’ll create a PersonService .

Our PersonService relies on both the Config and the PersonRepository . It exposes a function called FindAll that conditionally calls the PersonRepository if the application is enabled.

Finally, we’ve got our Server . This is responsible for running an HTTP server and delegating the appropriate requests to our PersonService .

The Server is dependent on the PersonService and the Config .

Ok, we know all the components of our system. Now how the hell do we actually initialize them and start our system?

First, let’s write our main() function the old fashioned way.

First, we create our Config . Then, using the Config , we create our database connection. From there we can create our PersonRepository which allows us to create our PersonService . Finally, we can use this to create our Server and run it.

Phew, that was complicated. Worse, as our application becomes more complicated, our main will continue to grow in complexity. Every time we add a new dependency to any of our components, we’ll have to reflect that dependency with ordering and logic in the main function to build that component.

As you might have guessed, a Dependency Injection framework can help us solve this problem. Let’s examine how.

The term “container” is often used in DI frameworks to describe the thing into which you add “providers” and out of which you ask for fully-build objects. The dig library gives us the Provide function for adding providers and the Invoke function for retrieving fully-built objects out of the container.

First, we build a new container.

Now we can add new providers. To do so, we call the Provide function on the container. It takes a single argument: a function. This function can have any number of arguments (representing the dependencies of the component to be created) and one or two return values (representing the component that the function provides and optionally an error).

The above code says “I provide a Config type to the container. In order to build it, I don’t need anything else.” Now that we’ve shown the container how to build a Config type, we can use this to build other types.

This code says “I provide a *sql.DB type to the container. In order to build it, I need a Config . I may also optionally return an error.”

In both of these cases, we’re being more verbose than necessary. Because we already have NewConfig and ConnectDatabase functions defined, we can use them directly as providers for the container.

Now, we can ask the container to give us a fully-built component for any of the types we’ve provided. We do so using the Invoke function. The Invoke function takes a single argument – a function with any number of arguments. The arguments to the function are the types we’d like the container to build for us.

The container does some really smart stuff. Here’s what happens:

That’s a lot of work the container is doing for us. In fact, it’s doing even more. The container is smart enough to build one, and only one, instance of each type provided. That means we’ll never accidentally create a second database connection if we’re using it in multiple places (say multiple repositories).

Now that we know how the dig container works, let’s use it to build a better main.

The only thing we haven’t seen before here is the error return value from Invoke . If any provider used by Invoke returns an error, our call to Invoke will halt and that error will be returned.

Even though this example is small, it should be easy to see some of the benefits of this approach over our “standard” main. These benefits become even more obvious as our application grows larger.

One of the most important benefits is the decoupling of the creation of our components from the creation of their dependencies. Say, for example, that our PersonRepository now needs access to the Config . All we have to do is change our NewPersonRepository constructor to include the Config as an argument. Nothing else in our code changes.

Other large benefits are lack of global state, lack of calls to init (dependencies are created lazily when needed and only created once, obviating the need for error-prone init setup) and ease of testing for individual components. Imagine creating your container in your tests and asking for a fully-build object to test. Or, create an object with mock implementations of all dependencies. All of these are much easier with the DI approach.

I believe Dependency Injection helps build more robust and testable applications. This is especially true as these applications grow in size. Go is well suited to building large applications and has a great DI tool in dig . I believe the Go community should embrace DI and use it in far more applications.

golang配制高性能sql.DB

有很多教程是关于Go的sql.DB类型和如何使用它来执行SQL数据库查询的。但大多数内容都没有讲述 SetMaxOpenConns() , SetMaxIdleConns() 和 SetConnMaxLifetime()方法, 您可以使用它们来配置sql.DB的行为并改变其性能。

转自:

整理:go语言中文文档:

在本文我将详细解释这些设置的作用,并说明它们所能产生的(积极和消极)影响。

一个sql.DB对象就是一个数据库连接池,它包含“正在用”和“空闲的”连接。一个正在用的连接指的是,你正用它来执行数据库任务,例如执行SQL语句或行查询。当任务完成连接就是空闲的。

当您创建sql.DB执行数据库任务时,它将首先检查连接池中是否有可用的空闲连接。如果有可用的连接,那么Go将重用现有连接,并在执行任务期间将其标记为正在使用。如果池中没有空闲连接,而您需要一个空闲连接,那么Go将创建一个新的连接。

默认情况下,在同一时间打开连接的数量是没有限制(包含使用中+空闲)。但你可以通过SetMaxOpenConns()方法实现自定义限制,如下所示:

在这个示例代码中,连接池现在有5个并发打开的连接数。如果所有5个连接都已经被标记为正在使用,并且需要另一个新的连接,那么应用程序将被迫等待,直到5个连接中的一个被释放并变为空闲。

为了说明更改MaxOpenConns的影响,我运行了一个基准测试,将最大打开连接数设置为1、2、5、10和无限。基准测试在PostgreSQL数据库上执行并行的INSERT语句,您可以在这里找到代码。测试结果:

对于这个基准测试,我们可以看到,允许打开的连接越多,在数据库上执行INSERT操作所花费的时间就越少(打开的连接数为1时,执行速度3129633ns/op,而无限连接:531030ns/op——大约快了6倍)。这是因为允许打开的连接越多,可以并发执行的数据库查询就越多。

默认情况下,sql.DB允许连接池中最多保留2个空闲连接。你可以通过SetMaxIdleConns()方法改变它,如下所示:

从理论上讲,允许池中有更多的空闲连接将提高性能,因为这样就不太可能从头开始建立新连接——因此有助于提升数据库性能。

让我们来看看相同的基准测试,最大空闲连接设置为none, 1,2,5和10:

当MaxIdleConns设置为none时,必须为每个INSERT从头创建一个新的连接,我们可以从基准测试中看到,平均运行时和内存使用量相对较高。

只允许保留和重用一个空闲连接对基准测试影响特别明显——它将平均运行时间减少了大约8倍,内存使用量减少了大约20倍。继续增加空闲连接池的大小会使性能变得更好,尽管改进并不明显。

那么,您应该维护一个大的空闲连接池吗?答案取决于应用程序。重要的是要意识到保持空闲连接是有代价的—它占用了可以用于应用程序和数据库的内存。

还有一种可能是,如果一个连接空闲时间太长,那么它可能会变得不可用。例如,MySQL的wait_timeout设置将自动关闭任何8小时(默认)内未使用的连接。

当发生这种情况时,sql.DB会优雅地处理它。坏连接将自动重试两次,然后放弃,此时Go将该连接从连接池中删除,并创建一个新的连接。因此,将MaxIdleConns设置得太大可能会导致连接变得不可用,与空闲连接池更小(使用更频繁的连接更少)相比,会占有更多的资源。所以,如果你很可能很快就会再次使用,你只需保持一个空闲的连接。

最后要指出的是,MaxIdleConns应该总是小于或等于MaxOpenConns。Go强制执行此操作,并在必要时自动减少MaxIdleConns。

现在让我们看看SetConnMaxLifetime()方法,它设置连接可重用的最大时间长度。如果您的SQL数据库也实现了最大连接生命周期,或者—例如—您希望方便地在负载均衡器后交换数据库,那么这将非常有用。

你可以这样使用它:

在这个例子中,所有的连接都将在创建后1小时“过期”,并且在过期后无法重用。但注意:

从理论上讲,ConnMaxLifetime越短,连接过期的频率就越高——因此,需要从头创建连接的频率就越高。为了说明这一点,我运行了将ConnMaxLifetime设置为100ms、200ms、500ms、1000ms和无限(永远重用)的基准测试,默认设置为无限打开连接和2个空闲连接。这些时间段显然比您在大多数应用程序中使用的时间要短得多,但它们有助于很好地说明行为。

在这些特定的基准测试中,我们可以看到,与无限生存期相比,在100ms生存期时内存使用量增加了3倍以上,而且每个INSERT的平均运行时也稍微长一些。

如果您在代码中设置了ConnMaxLifetime,那么一定要记住连接将过期(随后重新创建)的频率。例如,如果您总共有100个连接,而ConnMaxLifetime为1分钟,那么您的应用程序可能每秒钟杀死和重新创建1.67个连接(平均值)。您不希望这个频率太大,最终会阻碍性能,而不是提高性能。

最后,如果不说明超过数据库连接数量的硬限制将会发生什么,那么本文就不完整了。 为了说明这一点,我将修改postgresql.conf文件,这样总共只允许5个连接(默认是100个)…

然后在无限连接的情况下重新运行基准测试……

一旦达到5个连接的硬限制,数据库驱动程序(pq)立即返回一个太多客户端连接的错误消息,而无法完成INSERT。为了防止这个错误,我们需要将sql.DB中打开连接的最大总数(正在使用的+空闲的)设置为低于5。像这样:

现在,sql.DB在任何时候最多只能创建3个连接,基准测试运行时应该不会出现任何错误。但是这样做需要注意:当达到开放连接数限制,并且所有连接都在使用时,应用程序需要执行的任何新的数据库任务都将被迫等待,直到连接标记为空闲。例如,在web应用程序的上下文中,用户的HTTP请求看起来会“挂起”,甚至在等待数据库任务运行时可能会超时。

为了减轻这种情况,你应该始终在一个上下文中传递。在调用数据库时,启用上下文的方法(如ExecContext()),使用固定的、快速的超时上下文对象。

总结

1、根据经验,应该显式设置MaxOpenConns值。这应该小于数据库和基础设施对连接数量的硬性限制。

2、一般来说,更高的MaxOpenConns和MaxIdleConns值将带来更好的性能。但你应该注意到效果是递减的,连接池空闲连接太多(连接没有被重用,最终会变坏)实际上会导致性能下降。

3、为了降低上面第2点带来的风险,您可能需要设置一个相对较短的ConnMaxLifetime。但你也不希望它太短,导致连接被杀死或不必要地频繁重建。

4、MaxIdleConns应该总是小于或等于MaxOpenConns。

对于中小型web应用程序,我通常使用以下设置作为起点,然后根据实际吞吐量水平的负载测试结果进行优化。

golang私有仓库依赖配置

golang私有仓库依赖配置

    版本要求:go 1.14+

    go mod 配置:

        go env -w GOPRIVATE="gitlab.xxx.com"    //配置私有仓库域名 :重要

        go env -w GONOPROXY="gitlab.xxx.com"    //此配置下的域名默认不走代理

        go env -w GONOSUMDB="gitlab.xxx.com"    //此配置下的域名默认不进行gosumdb校验

        go env -w GOINSECURE="gitlab.xxx.com"    //此配置下的域名默认采用http协议 。有些公司的私有仓库采用http协议,而go mod默认采用                                                                                       https,请根据实际情况进行配置:重要

    账户及密码:

        因私有仓库一般都需要进行登录,所以可以通过隐藏文件进行用户名及密码配置。

        文件路径:~/.netrc    //默认Linux系统,

        文件内容:

            machine 域名    //gitlab.xxx.com

            login 账号

            password 密码

golang依赖注入db,golang依赖注入什么意思

2022-11-28
golang依赖注入框架,golang 依赖包

2022-11-27
Golang依赖冲突解决,golang 依赖倒置

2022-11-27
php为什么要用依赖注入,php什么是依赖注入

2023-01-04
golang脚本,go语言脚本

2022-11-27
golang语言识别,go语言图像识别

2022-11-27
java依赖注入标准jsr,依赖注入原则

本文目录一览: 1、java中的JSR75 JSR135 JSR180 JSR184 JSR185是什么意思? 2、java语言核心技术是什么? 3、什么是java的依赖注入? 4、JSR是什么指令

2023-12-08
golang入门视频课程,golang入门教程

本文目录一览: 1、Golang入门到项目实战 | golang简介及安装 2、Docker 占用资源膨胀那么快,你知道怎么清理? 3、Golang入门到项目实战 | golang接口和类型的关系 4

2023-12-08
golang用什么语言写,Golang语法

2022-11-27
golang方向,golang有什么优势

2022-11-28
golang与或非,golang什么意思

2022-11-28
golang成熟,golang goa

2022-11-28
golang=,golang是什么意思

2022-11-26
golang中的单元测试,golang测试框架

2022-11-26
PHP依赖注入

2023-05-16
golang题库,Golang语言

2022-11-27
golang&&,golang语言

2022-11-28
golang什么,golang什么不可以作为方法的接受者

本文目录一览: 1、Golang常用包有哪些 2、golang是什么意思? 3、go语言可以做什么 4、golang 有哪些比较稳定的 web 开发框架 Golang常用包有哪些 ⑴ Go Kit它本

2023-12-08
golang脚本,golang执行shell脚本

2022-11-27
php依赖注入的含义(php依赖注入的含义是什么)

2022-11-13