The profile

Golang is the favorite of the cloud native era. Its biggest advantage lies in its simplicity and effectiveness. It works because it’s easy to write highly concurrent code and has a lot of processing power.

Golang can be applied to most scenarios such as Web background, database, cloud native, block chain and so on. Dachang’s related recruitment positions are also increasing year by year. Therefore, learning Golang, a relatively new language with good development prospects, we can achieve overtaking on the curve.

Niuniu has also launched a Golang learning package adhering to Golang’s simple and effective concept. This article is a quick start, and each executable code is also attached with the running results. I hope you can read this article, try it yourself, realize a quick start, and start a new journey with Golang.

Let’s start our Golang journey with the most basic environment deployment

Environment to prepare

Install Golang

Linux Installation

From the installation introduction on the official website, we can learn about the installation process of each system. For Linux:

1. Download the installation package download the installation package to the current directory

2. Decompress the package to the specified directory

Rm -rf /usr/local/go && tar -c /usr/local/xzf go1.16.2.linux-amd64.tar.gz

3. Set the PATH environment variable

export PATH=$PATH:/usr/local/go/bin

4. Check the Go version

go version

As you can see, a Linux installation simply downloads the installation package, extracts it to a specific directory, and sets the PATH environment variable to complete the installation.

Mac installation

Mac is even simpler. Download the installation package and click Install.

Windows installation

For Windows and Mac, click the installation package to go to the installation screen.

The official resource address of Golang package is:

https://golang.org/dl/

Friends can go up and choose the version they want, and usually the advice is to download the latest version.

If there is no external network, and do not want to be stuck, here niuniu also help you under the current latest version 1.14.12 package, you can pay attention to the public account in the background reply [G installation package] can be obtained.

Setting environment variables

Golang has an environment variable, GOPATH, which represents the local location of third-party dependencies. You can specify a path that is easy to access.

This way, third-party dependencies can be downloaded under GOPATH, and projects can automatically load third-party dependencies from GOPATH.

The IDE is recommended

GoLand is a great feature, right out of the box, and has a complete plugin ecosystem. GoLand can easily install viM plug-in. You can enjoy the powerful function of IDE and the efficient editing ability of VIM at the same time.

Goland is a paid software. Generally, companies will provide the legal version to employees. If they are still in school and have limited economic conditions, they can choose the 30-day experience version first and uninstall and reinstall it within 30 days.

Grammar is introduced

Syntax is the most basic part of any language, so let’s take a look at the syntax of Go.

The concept of the package

package main

import "fmt"

func main() {
   fmt.Println("niuniumart")
}
Copy the code

The above code is the basic three parts of an executable code, in other words, each executable code must contain Package, import, and function elements.

Golang manages code in packages. A directory holds the contents of a package, and the code files must be under a package. For example, here we create a main.go file in the code directory, and package indicates that the code belongs to the main package. The main function must be under the main package. Import is used to refer to external packages. If import refers to FMT packages in the example above, you can use the method fmt.println directly.

There are three types of guarantee engineering:

  1. GOPATH: Pull the dependency packages to the local GOPATH directory by using the go get command. The disadvantage is that the dependency packages cannot be version-managed.

  2. DEP: Packages dependent packages to the vendor directory under the project using the DEP command. Shopee’s financial team and Bytedance’s education team use DEP.

  3. GoMod: Pull dependent packages to a unified PKG directory and store them by version. Tencent cloud with GoMod team will be more.

We won’t expand too much on package management in this article, which will be covered in a future article.

Going back to our example, for the main.go file, do the following to run the program:

Go build main.go // get the binary main. /main // execute the codeCopy the code

Variable definition and initialization

package main

import "fmt"

var globalStr string
var globalInt int

func main() {
   var  localStr string
   var  localInt int
   localStr = "first local"
   localInt = 2021
   globalInt = 1024
   globalStr = "first global"
   fmt.Printf("globalStr is %s\n", globalStr)   //globalStr is first global
   fmt.Printf("globalStr is %d\n", globalInt)   //globalStr is 1024
   fmt.Printf("localInt is %s\n", localStr)     //localInt is first local
   fmt.Printf("localInt int is %d\n", localInt) //localInt int is 2021
}
Copy the code

The above code defines the following four variables:

A global string variable named globalStr;

A global integer named globalInt;

A local string variable named localStr;

A local integer variable named localInt;

Note that the global variable must be capitalized if it is to be accessed outside the package. Yes, you read that correctly. Golang is case-sensitive to whether it is visible outside the package.

Var strAry = [10]string{"aa", "bb", "cc", "dd", Var sliceAry = make([]string, 0) sliceAry = strAry[1:3] var dic = map[string]int{"apple":1, "watermelon":2, } fmt.Printf("strAry %+v\n", strAry) fmt.Printf("sliceAry %+v\n", sliceAry) fmt.Printf("dic %+v\n", dic) }Copy the code

The above code demonstrates the definition and initialization of arrays, slicing, and dictionaries. You can see that the slice points to the array by index. Slicing can change the contents of an element, but arrays cannot. In development, slicing is mainly used for logical processing.

Conditional selective grammar

Package main import "FMT" func main() {localStr := "case3" You can also initialize the base variable directly with := if localStr == "case3" {fmt.printf ("into ture logic\n")} else {fmt.printf ("into false" Var var [string] {"apple": 1, "watermelon": 2,} if num, ok := dic["orange"]; ok { fmt.Printf("orange num %d\n", num) } if num, ok := dic["watermelon"]; ok { fmt.Printf("watermelon num %d\n", num) } switch localStr { case "case1": fmt.Println("case1") case "case2": fmt.Println("case2") case "case3": fmt.Println("case3") default: fmt.Println("default") } }Copy the code

The if statement in Golang is the same as in any other language. The above example also shows how to use if to determine if a key is empty in the map.

In switch, each case defaults to break. That is, if it is case1, the switch conditional selection will be popped up after execution. The fallthrough keyword is used if you want to execute down a case order.

Loop writing

package main import "fmt" func main() { for i := 0; i < 5; i++ { fmt.Printf("current i %d\n", i) } j := 0 for { if j == 5 { break } fmt.Printf("current j %d\n", J) j++} var strAry = string [] {" aa ", "bb", "cc", "dd", "ee"} / / yes. Var sliceAry = make([]string, 0) sliceAry = strAry[1:3] for I, STR := range sliceAry {FMT.Printf("slice I %d, STR %s\n", I, STR)} var dic = map[string]int{"apple": 1, "watermelon": 2, } for k, v := range dic { fmt.Printf("key %s, value %d\n", k, v) } }Copy the code

Language features

Goroutine

Coroutines are one of Golang’s most important features.

Before coroutines, threads were used as the smallest unit of scheduling. Coroutines can be understood as user-mode, logical level threads.

With coroutines, we can easily achieve high concurrency: suppose you want to do three things, suppose you want to execute methods A, B, and C. How do I write the code? The usual way we write it is:

package main

import (
  "fmt"
  "time"
)

func a() {
  time.Sleep(3 * time.Second)
  fmt.Println("it's a")
}
func b() {
  time.Sleep(2 * time.Second)
  fmt.Println("it's b")
}
func c() {
  time.Sleep(1 * time.Second)
  fmt.Println("it's c")
}
func main() {
  a()
  b()
  c()
  time.Sleep(1 * time.Second)
}
Copy the code

The above code can only do B when A is done, and C when B is done.

But Golang supports coroutines at the language level, and a coroutine is generated using the keyword go, followed by a method:

package main

import (
  "fmt"
  "time"
)

func a() {
  time.Sleep(3 * time.Second)
  fmt.Println("it's a")
}
func b() {
  time.Sleep(2 * time.Second)
  fmt.Println("it's b")
}
func c() {
  time.Sleep(1 * time.Second)
  fmt.Println("it's c")
}
func main() {
  go a()
  go b()
  go c()
  time.Sleep(5 * time.Second)
}
Copy the code

In a coroutine, all three methods can run concurrently, and as you can see, method A prints last because it takes the longest to execute. Coroutine Golang runtime scheduling is to take full advantage of Golang multi – core performance. In the following articles, Niuniu will explain the principle of coroutine in depth. Now, as a beginner, we only need to know how to use it.

You can also think about why niu Niu sleep5 seconds after a, B and C. Here is a suspense.

Channel

Key points of the channel:

1. Similar to PIPES in Unix, fifO;

2. Thread safety, multiple Goroutine access at the same time, do not need to lock;

3. A channel is typed. An integer channel can only store integers.

Definition of channel:

var ch0 chan int
var ch1 chan string
var ch2 chan map[string]string

type stu struct{}

var ch3 chan stu
var ch4 chan *stu
Copy the code

Channels can be used to transfer data between coroutines and are generally divided into buffered channels and unbuffered channels.

What if there is data exchange between two coroutines? At this point, you can use channels to pass. Golang’s design idea is to replace shared memory with communication.

package main

import (
  "fmt"
  "time"
)

var ch chan int

func a() {
  time.Sleep(3 * time.Second)
  a := 5
  ch <- a
  fmt.Println("out of a")
}
func b() {
  time.Sleep(1 * time.Second)
  fromA := <- ch
  b := fromA + 3
  fmt.Println("b is ", b)
}
func main() {
  ch = make(chan int, 1)
  go a()
  go b()
  time.Sleep(20 * time.Second)
  fmt.Println("out of main")
}
Copy the code

It can be seen that B, which is slower, waited for the pipeline to have data before continuing to run, and successfully got the number 5 that A put into the pipeline! This completes the communication between coroutines.

In addition, there is a common interview question: what is the difference between a buffered and an unbuffered channel?

The channel can be buffered, which means that multiple data can be put into the channel until it is full and blocked.

Niuniu has been mistaken for a period of time that the unbuffered channel is the buffered channel with capacity 1, so it is used as an example to explain:

ChSync := make(chan int) // make(chan int,1) // chSync := make(chan int) //Copy the code

Again, a single piece of data is inserted into the channel: chSync <-1

Unbuffered scenario: chSync<-1 will not continue until some other coroutine takes over the parameter via < -chsync, otherwise it will remain blocked.

Buffered scenarios: chAsyn<-1 does not block because the buffer size is 1, and blocks only if the second value is placed before the first value is taken away.

In fact, this is the difference between synchronous and asynchronous, no buffer must be synchronous wait, buffer will only block if the buffer is full and asynchronous can’t handle it.

No buffer 🌰

package main import ( "fmt" "time" ) var ch chan int func a() { time.Sleep(3 * time.Second) a := 5 ch <- a Println("out of a")} func b() {time.sleep (1 * time.second)} func main() {ch = make(chan int) go a() go b() time.Sleep(20 * time.Second) fmt.Println("out of main") }Copy the code

As you can see, in the absence of tap-in, A is blocked while writing to the pipe.

Have a buffer 🌰

package main

import (
  "fmt"
  "time"
)

var ch chan int

func a() {
  time.Sleep(3 * time.Second)
  a := 5
  ch <- a
  fmt.Println("out of a")
}
func b() {
  time.Sleep(1 * time.Second)
}
func main() {
  ch = make(chan int, 1)
  go a()
  go b()
  time.Sleep(20 * time.Second)
  fmt.Println("out of main")
}
Copy the code

As you can see, function A writes a piece of data to the pipe without blocking, even if there are no consumers.

Interface

The Go language provides a special data type, the interface, that defines all common methods together, and any other type that implements those methods implements the interface.

Without further ado, check out 🌰 :

package main

import "fmt"

type Shape interface {
  Area() float64
  Perimeter() float64
}
// type rect
type Rect struct {
  height float64
  weight float64
}
func (p *Rect) Area() float64 {
  return p.height * p.weight
}
func (p *Rect) Perimeter() float64 {
  return 2 * (p.height + p.weight)
}
func main() {
  var s Shape = &Rect{height:10, weight:8}
  fmt.Println(s.Area())
  fmt.Println(s.Perimeter())
}
Copy the code

Shape is an interface that declares two methods: Area and Perimeter.

We define a concrete structure Rect to implement this interface. As you can see, with the basic Shape interface, you can point to a Rect object and call its methods.

Interfaces provide object-oriented programming capabilities. If you know multiple languages, such as Golang, C++, Java, etc., then you must ask how Golang’s polymorphism differs from C++ polymorphism (using references of the same type to objects of different types, i.e., polymorphism).

The answer is that C++ or Java requires active declaration of base classes, whereas Golang, which implements only all methods of an interface, implements that type. Therefore, Golang’s inheritance relationship is non-invasive, which is also Golang’s feature and advantage.

Introduction to Unit Tests

To ensure quality code, many companies require unit tests to be written. Here are two common metrics for unit testing:

1. Function coverage: number of functions called/total number of functions, usually 100%;

2. Line coverage: number of called lines/total lines, usually >60%.

Through unit testing, we can test the code for different scenarios and develop our own quality control.

Niuniu had no special testers in the SaaS department of Bytedance before, so it had very high requirements for unit testing, and the line coverage was required to reach 80%.

go test

  • Go test usually takes xxx_test.go as the file name, XXX does not specifically require the file name to be measured.

  • TestMain as initialization test;

  • Testxxx (t * testing. T);

  • Go test can run unit tests;

  • Go test –v fileName –test.run funcName You can specify a single test for a method.

Let’s create a main_test.go file as an example. The main.go file uses the above interface example.

├ ─ ─ main. Go ├ ─ ─ main_test. GoCopy the code
package main import ( "testing" ) func TestRect(t *testing.T) { var s Shape = &Rect{height:10, weight:8} if s.Area() ! = 80 { t.Errorf("area %f\n", s.Area()) } if s.Perimeter() ! = 30 { t.Errorf("perimeter %f\n", s.Perimeter()) } }Copy the code

Go –v main.go–test.run TestRect

Perimeter does not make the Perimeter as expected.

go convey

Go Convey has good support for setup and teardown, which can be initialized once before running a single test case and destroyed after it is finished. In this way, if there are multiple child use cases, the same initialization environment can be reused.

Go Convey also has a number of defined assert functions that you can use directly, and you can also customize assert functions.

Common assert types are as follows:

var ( ShouldEqual = assertions.ShouldEqual ShouldNotEqual = assertions.ShouldNotEqual ShouldBeGreaterThan = assertions.ShouldBeGreaterThan ShouldBeGreaterThanOrEqualTo = assertions.ShouldBeGreaterThanOrEqualTo ShouldBeLessThan =  assertions.ShouldBeLessThan ShouldBeLessThanOrEqualTo = assertions.ShouldBeLessThanOrEqualTo ShouldBeBetween = assertions.ShouldBeBetween ShouldNotBeBetween = assertions.ShouldNotBeBetween ShouldBeBetweenOrEqual = assertions.ShouldBeBetweenOrEqual ShouldNotBeBetweenOrEqual = assertions.ShouldNotBeBetweenOrEqual ShouldContainSubstring = assertions.ShouldContainSubstring ShouldNotContainSubstring = assertions.ShouldNotContainSubstring ShouldPanic = assertions.ShouldPanic ShouldBeError = assertions.ShouldBeError )Copy the code

Examples:

package main

import (
 "testing"

 "github.com/smartystreets/goconvey/convey"
)

func TestRect(t *testing.T) {
 convey.Convey("TestRect", t, func() {
  var s Shape = &Rect{height: 10, weight: 8}
  convey.So(s.Area(), convey.ShouldEqual, 80)
  convey.So(s.Perimeter(), convey.ShouldEqual, 30)
 })
}
Copy the code

Perimeter does not meet expectations, so the following prompt will appear:

Does it make it clearer to make assertions with Convey?

Connect to the database using ORM

What is the ORM?

ORM stands for Object Relational Mapping. Its main function is to map object-oriented concepts to database tables in programming.

For example, if we define an object, that corresponds to a table, and an instance of that object corresponds to a record in that table.

GORM usage example:

package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) type User struct { Name  string Age int } func main() { username := "" pwd := "" addr := "" //ip:port database := "" args := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local", username, pwd, addr, database) // step1 : Db, err := gorm.Open("mysql", args) if err! = nil {fmt.println (err) //do something return} defer db.close () // step2: insert a line user := user {Name: "niuniu", Age: 18} err = db.Create(&user) if err ! Var tmpUser User err = db.Where("name =? , "niuniu").first (&tmpUser).error // Query User and save the information to tmpUser if err! = nil { fmt.Println(err) return } fmt.Println(tmpUser) }Copy the code

End with a Web Server

The simplest example

Golang HTTP Server has several ways to write it, but here’s the simplest one. Let’s see how simple it is: Here we implement a SayHello interface that returns the packet as a “Hello” string.

package main import ( "log" "net/http" ) func SayHello(w http.ResponseWriter, R * http.request) {w.write ([]byte("hello")) // return string "hello"} func main() {http.handlefunc ("/say_hello", SayHello) err := HTTP.ListenAndServe(":8080", nil) // Start an HTTP service if err! = nil { log.Print("ListenAndServe: ", err) return } }Copy the code

Use the frame

In real development, we rarely write sever naked using HTTP, because if we implement functionality, such as pluggable middleware implementation, we end up implementing the framework ourselves. In real development, we choose a proven framework, such as gin:

package main import ( "github.com/gin-gonic/gin" "log" "net/http" ) func SayHello(c *gin.Context) { c.String(http.StatusOK, "Hello ") // return package with string "hello"} func main() {engine := gin.Default() // Generate a Default gin engine Engine. Handle(http.MethodGet,"/say_hello", SayHello) err := engine.Run(":8080") = nil { log.Print("engine run err: ", err.Error()) return } }Copy the code

Let’s look at the results in the browser

summary

So far, the basic play of Golang, do you understand?

Hopefully, this series of articles on Go Up will help you get started quickly, but to become a veteran Golang developer, you need to do a lot of research on the details.

If you have any questions about Go or would like to further analyze any aspect of Niuniu, please let niuniu know in the comments section! Niuniu will also answer them in the next article