golang 读一行_与golang合作的第一步

golang 读一行

If you like to understand know why we choose golang in the first as an alternative to python, see https://medium.com/@anil.k.nayak8/gopher-in-healthifyme-25eb8494305b

In this article I would like to outline the main points we had in mind while creating an application using golang, the problems encountered and some of the packages that helped us. We created a simple microservice that sat on top of multiple databases and acted as a security/filtering layer.

在本文中,我想概述在使用golang创建应用程序时要记住的要点,遇到的问题以及一些对我们有帮助的软件包。 我们创建了一个简单的微服务,它位于多个数据库之上,并充当安全/过滤层。

We knew what we had to do: Create a JSON rpc server that would be consumed mainly by our python clients. But golang does not have a framework like Django or Ruby on Rails, which means there are no standards set on how things are to be done.

我们知道我们要做的事情:创建一个主要由python客户端使用的JSON rpc服务器。 但是golang没有像Django或Ruby on Rails这样的框架,这意味着没有关于如何完成事情的标准。

We had to make all decisions based on the project we were doing combined with the experince we had with python. So we made a note of all the things we needed to clear before going ahead:

我们必须根据正在执行的项目以及结合python的经验来做出所有决策。 因此,我们记录了在继续操作之前需要清除的所有内容:

  • Folder structure

    资料夹结构
  • Logging Setup

    记录设定
  • Configuration with different environments with different sources of info like file, env etc

    使用不同的环境配置不同的信息源,例如文件,环境等
  • Connecting to database and executing queries.

    连接到数据库并执行查询。
  • Setting up rpc

    设置rpc
  • Writing tests

    编写测试

Deciding the folder structure

确定文件夹结构

This was difficult at the beginning of a new project. So we took inspiration from https://github.com/golang-standards/project-layout and modified it a bit to suit our needs. We are now using the new go modules and they’re amazing! We can easily manage the dependencies with go plugins for IDEs. Go will automatically take care of dependencies and their versions. The only thing we should attend to is importing the third party module as url.

在新项目开始时这很困难。 因此,我们从https://github.com/golang-standards/project-layout获得了灵感,并对其进行了一些修改以满足我们的需求。 我们现在正在使用新的go模块,它们很棒! 我们可以使用IDE的go插件轻松管理依赖项。 Go将自动处理依赖项及其版本。 我们唯一需要注意的是将第三方模块作为url导入。

The following is the folder structure we are using:

以下是我们正在使用的文件夹结构:

build: development and production binaries.

build :开发和生产二进制文件。

cmd: the main go file. Ends up being lean as most of the initialization work happens here.

cmd:主要的go文件。 最终变得精益,因为大多数初始化工作都在这里进行。

config: responsible for the configuration of the application. All the packages that need the current configuration will be in connection with this file.

config:负责应用程序的配置。 需要当前配置的所有软件包都将与此文件关联。

pkg: contains all individual packages which can depend on each other.

pkg:包含可以相互依赖的所有单个软件包。

Logging Setup

记录设定

Golang comes with a very handy logging package.However we had debates on whether the native package should be used or the popular package ‘logrus’ or create a new custom interface all together. Logrus is an extension of the interface for the native logging package. Lorgus won as it was just what we needed, with all methods like Print, Debug, Panic etc. We could also change the logging Output.

Golang附带了一个非常方便的日志记录程序包,但是我们对于是否应使用本机程序包还是流行的程序包'logrus'或共同创建一个新的自定义接口存在争议。 Logrus是本机日志记录程序包的接口的扩展。 Lorgus赢得了我们所需要的,它使用了诸如Print,Debug,Panic等所有方法。我们还可以更改日志记录Output。

We created a logger package in common that exposes an instance of logrus. And the same is used everywhere.

我们共同创建了一个记录器包,该记录器包公开了logrus的一个实例。 到处都使用相同的方法。

The final code was very simple:

最终代码非常简单:

var Log *logrus.Logger = logrus.New()

var Log * logrus.Logger = logrus.New()

And for any logging:

对于任何日志记录:

logger.Log.Debug(“I am a debug message”)

logger.Log.Debug(“我是调试消息”)

Setup Configuration

设定设定

Here we had to evaluate multiple factors. We had two options: either use a third party package or create our own configuration package. If we choose a third party, which one?

在这里,我们必须评估多个因素。 我们有两个选择:使用第三方程序包或创建我们自己的配置程序包。 如果我们选择第三方,哪一个?

So we asked ourselves: what do we need from the package?

因此,我们问自己:包装中需要什么?

It should be able to read from a file like json/yaml. Since we needed sensitive information like database user, database password etc from a secret manager, it also should be able to read from the environment. And finally, it should do both simultaneously. For example for a single struct DatabaseConfig, host can come from env and the database name can come from the config file based on the environment.

它应该能够从json / yaml这样的文件中读取。 由于我们需要来自机密管理器的敏感信息,例如数据库用户,数据库密码等,因此它也应该能够从环境中读取。 最后,它应该同时执行两个操作。 例如,对于单个结构DatabaseConfig,主机可以来自env,数据库名称可以来自基于环境的配置文件。

We evaluated the ability of a third party package viper to do the same. However it could not function with multiple sources at the same time. So we tried a different package cleanenv. Bingo! It had all the features we needed.

我们评估了第三方包装毒蛇执行此操作的能力。 但是,它不能同时与多个源一起使用。 因此,我们尝试了不同的包cleanenv。 答对了! 它具有我们需要的所有功能。

A sample code would look something like:

示例代码如下所示:

Here we specify that the yaml file can be taken on preference, but it will fallback to env if the key is not available in the yaml file.

在这里,我们指定可以优先使用yaml文件,但是如果密钥在yaml文件中不可用,它将回退到env。

Database Connection and Querying

数据库连接和查询

There are tons of libraries available for database interaction for go and each one has its own flavour. An added advantage here is that golang comes with a sql interface by default and most of the libraries implement the same. So now it’s easier to switch.

有大量库可供进行go的数据库交互,每个库都有自己的特色。 此处的另一个好处是,默认情况下,golang带有sql接口,并且大多数库都实现了该接口。 因此,现在切换起来更加容易。

Initially we went with the native sql package to create database connections and execute queries. But it seemed very basic. Inorder to execute each and every query and convert into a struct, each column needs to be scanned, which would look like a whole lot of boilerplate code.

最初,我们使用本地sql包创建数据库连接并执行查询。 但这似乎很基本。 为了执行每个查询并转换为结构,需要扫描每一列,这看起来像是很多样板代码。

For example: suppose you are trying to select a row with 4 columns

例如:假设您正在尝试选择4列的行

This whole process has to be done for each db query.

必须为每个数据库查询完成整个过程。

This is where sqlx [github.com/jmoiron/sqlx] comes into the picture.It has a feature which will directly take the struct and do the scanning for us. It also gives an interface to specify what the db column names mapped to each struct field. The below example specifies the same:

这就是sqlx [ github.com/jmoiron/sqlx ]出现的地方。它具有一个功能,可以直接获取struct并为我们进行扫描。 它还提供了一个接口,用于指定映射到每个struct字段的数据库列名称。 下面的示例指定相同的内容:

And now it’s simple! Bonus: this package comes with an optional sql interface!

现在很简单! 奖励:该软件包带有可选的sql接口!

Setting up RPC

设置RPC

Golang comes with its own json rpc package. So RPC setup can be very simple.

Golang带有自己的json rpc软件包。 因此,RPC设置非常简单。

But there is a glitch: it works on a TCP layer and it would make sense to write the rpc client also in golang. But our client is written in python. Hence using a tcp layer and maintaining consistent connection can create problems.

但是有一个小故障:它可以在TCP层上工作,并且也可以在golang中编写rpc客户端。 但是我们的客户端是用python编写的。 因此,使用tcp层并保持一致的连接会产生问题。

So we treated each rpc request as an independent http request.

因此,我们将每个rpc请求都视为一个独立的http请求。

So now, we used 2 packages github.com/gorilla/rpc/v2 and github.com/gorilla/rpc/v2/json.

所以现在,我们使用了2个软件包github.com/gorilla/rpc/v2和github.com/gorilla/rpc/v2/json。

Using this we could create a simple endpoint for example ‘/json’ and mount our RPC server on it.

使用这个我们可以创建一个简单的端点,例如'/ json',然后在上面安装我们的RPC服务器。

The sample code would look something like:

示例代码如下所示:

We can also add different endpoints to handle different kinds of requests. Becomes handy when adding a healthcheck route. Any client would be able to use it as it is very flexible.

我们还可以添加不同的端点来处理不同类型的请求。 添加健康检查路线时变得很方便。 任何客户端都可以使用它,因为它非常灵活。

Setting up Tests.

设置测试。

Golang comes with its own test system. Great! Now we don’t have to use external setups unless absolutely necessary.

Golang带有自己的测试系统。 大! 现在,除非绝对必要,否则我们不必使用外部设置。

Since we are dealing with different databases and executing queries, we need a proper system where we can mock the database interaction. All we need to have is some struct, that has the same interface as the database object. We also need to have a global database instance which can be replaced by the mock database instance. Thus, we can mock all the database interactions and test different cases out.

由于我们要处理不同的数据库并执行查询,因此需要一个合适的系统来模拟数据库交互。 我们需要的只是一些结构,该结构具有与数据库对象相同的接口。 我们还需要有一个可以由模拟数据库实例替换的全局数据库实例。 因此,我们可以模拟所有数据库交互并测试不同的情况。

We used two different packages to do this.

我们使用了两个不同的程序包来做到这一点。

  1. github.com/DATA-DOG/go-sqlmock which creates a mock database based on sql.

    github.com/DATA-DOG/go-sqlmock ,它基于sql创建一个模拟数据库。

2. github.com/jmoiron/sqlx which has the feature to create a database having the same interface as sqlx from other database instances. This is extremely useful as we use sqlx to interact with databases.

2. github.com/jmoiron/sqlx ,具有从其他数据库实例创建与sqlx具有相同接口的数据库的功能。 当我们使用sqlx与数据库进行交互时,这非常有用。

And now the final step: replace our global database instance with the mock database we created. We wrapped this behaviour in a single function and used it in all our testing methods. This made sure that we have a new fresh database for each test.

现在是最后一步:用我们创建的模拟数据库替换全局数据库实例。 我们将此行为包装在一个函数中,并在所有测试方法中使用了它。 这确保了每个测试都有一个新的新数据库。

Now the sample code looks like:.

现在,示例代码如下:

Handling Errors

处理错误

Golang gives no special status to errors. Error is treated as a normal value, just like any other interface. Now it is very important that all kinds of errors are handled.

Golang没有为错误赋予特殊的状态。 就像任何其他接口一样,错误被视为正常值。 现在,处理各种错误非常重要。

If any error is left out it can crash production. Fortunately there is a package to check if any errors are missed.https://github.com/kisielk/errcheck helps in finding error cases which should have been checked but has been left out. We also needed to make sure the errors have contextual information. For e.g a rpc can fail due to several reasons: database connection error, query execution error, rows scanning error etc. The client that uses a function to execute a database query needs more meta information, including the error trace to understand the error in detail. For this we have used the package “github.com/pkg/errors” which helps in wrapping an error with additional parameters. E.g: errors.Wrap(err, “Error in statement execution”). This become handy while debugging.

如果遗漏任何错误,可能会导致生产崩溃。 幸运的是,有一个软件包可以检查是否遗漏了任何错误。 https://github.com/kisielk/errcheck帮助查找应该检查但已排除的错误情况。 我们还需要确保错误具有上下文信息。 例如,rpc可能由于以下几个原因而失败:数据库连接错误,查询执行错误,行扫描错误等。使用函数执行数据库查询的客户端需要更多元信息,包括错误跟踪以详细了解错误。 为此,我们使用了软件包“ github.com/pkg/errors ”,该软件包有助于使用其他参数包装错误。 例如:errors.Wrap(err,“语句执行中的错误”)。 这在调试时变得很方便。

Bonus lessons from our experience:

我们的经验教训:

  1. Not to use the ‘init’ feature unless absolutely necessary. The dependency system might break as there is no way of knowing which package will run first. This will also cause problems while writing tests.

    除非绝对必要,否则请勿使用“初始化”功能。 依赖系统可能会中断,因为无法知道哪个程序包将首先运行。 这也会在编写测试时引起问题。
  2. Not to use Panic unless the application cannot run when the error occurs. Always try to handle errors gracefully.

    除非错误发生时应用程序无法运行,否则不要使用Panic。 始终尝试优雅地处理错误。
  3. Always wrap errors to give them more meta information.

    总是包装错误以给他们更多的元信息。
  4. Always defer to cleanups like closing database connection, closing a file etc.

    始终遵循清理操作,例如关闭数据库连接,关闭文件等。
  5. Keep interfaces as lean as possible. This would bring out the most from golang’s powerful interface concept.

    保持接口尽可能紧凑。 这将充分利用golang强大的界面概念。

Conclusion.

结论。

Though a low level one, Go is a great language. Unlike others of its kind, memory management is very flexible. It has a proper way to format code. Gofmt does all the work and kind of universally accepted*. The type system is also simple. The code we write becomes very clear and understandable with very little effort. Most importantly, it creates a single binary. It can run anywhere* with no setup and no dependencies. Also the compiler is superfast with a great IDE support. When we created a build using docker to deploy the docker image to production size, it was just 8MB, a very small size and hence easy to handle.

尽管是一个较低级别的语言,但Go是一门很棒的语言。 与同类产品不同,内存管理非常灵活。 它具有格式化代码的正确方法。 Gofmt可以完成所有工作,并且被广泛接受*。 类型系统也很简单。 我们编写的代码变得非常清晰和易于理解。 最重要的是,它创建了一个二进制文件。 它可以在任何地方运行*,而无需设置和依赖。 而且,编译器超快,并具有强大的IDE支持。 当我们使用docker创建一个构建以将docker映像部署到生产大小时,它只有8MB,非常小,因此易于处理。

However there is no proper standard for code organisation yet, maturity of different third party packages etc. But there is hope it will become the next go to language for server side development once it establishes its ground. It was worth while experimenting in golang and we will definitely extend its use for new projects and in some of our older projects as well.

但是,尚无适当的代码组织标准,不同的第三方软件包的成熟度等。但是,一旦它奠定了基础,就有望成为服务器端开发的下一代语言。 在golang上进行实验是值得的,我们一定会在新项目和某些旧项目中扩展它的用途。

翻译自: https://medium.com/healthify-tech/working-with-golang-our-first-steps-34dc38a2ee92

golang 读一行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值