go语言基础语法入门

简介: go语言基础语法入门

文章和代码已经归档至【Github仓库:https://github.com/timerring/backend-tutorial 】或者公众号【AIShareLab】回复 go 也可获取。

简介

什么是 Go 语言

  1. 高性能、高并发
  2. 语法简单、学习曲线平缓
  3. 丰富的标准库(很多情况下不需要借助第三方库就可以完成基础功能的开发)
  4. 完善的工具链(无论是编译,代码检查,补充提示等等都有对应的工具,还内置了完整的单元测试框架,性能检测等等)
  5. 静态链接(所有的编译结果都是静态链接的,只需要拷贝编译后的可执行文件,不加东西就可以直接运行,部署方便快捷。对比 java 需要一个庞大的 jre 才可以运行)
  6. 非常快速的编译
  7. 跨平台(可以在多种设备上运行,还有交叉编译特性,无需配置交叉编译环境)
  8. 垃圾回收(专注业务逻辑,无需考虑内存的分配与释放)

哪些公司使用 Go 语言

首先字节跳动已经全面拥抱了 go 语言,公司内部有上万个微服务使用 golang 来编写,不久前也开源了 GORPC 框架 KiteX。腾讯、百度、美团、滴滴、深信服、平安、OPPO、知乎、去哪儿、360、金山、微博、bilibili、七牛、PingCAP 等公司也在大量使用 Go 语言。
国外 Google Facebook 等公司也在大量使用 Go 语言。
从业务维度看过语言已经在云计算、微服务、大数据、区块链、物联网等领域蓬勃发展。然后在云计算、微服务等领域已经有非常高的市场占有率
Docker、Kubernetes、Istio、etcd、prometheus 几乎所有的云原生组件全是用 Go 实现的。

为什么转型 Go 语言

  • 最开始公司的后端业务主要是 web 后端,早期团队非 Java 背景,C++不太适合在线 Web 业务。
  • 最开始的服务都是 python ,从 2014 年开始,随着业务体量的增长,python 遇到一些性能问题。
  • 一些团队初步尝试使用了 Go,发现入门很简单,开发效率高,性能也比较好。
  • Go 语言的开发和部署非常简单,顺带解决了之前 python 带来的很头疼的依赖库版本问题。
  • —些业务尝到甜头之后,后面开始公司级大力推广,诞生了公司内部的基于 golang 的 rpc 和 http 框架。

Go 入门

Go 安装

访问 go.dev/ 或访问 studygolang.com/dl

Go 开发环境

  • 基于云的开发环境:gitpods.IO 在线编程环境,需要 GitHub 账号
  • 集成开发环境:Vs Code(免费)或者 Goland(收费)

基础语法

//  package main 代表这个文件属于main包的一部分, main包也就是程序的入口包。
package main
// 导入了标准库里面的 FMT 包。这个包主要是用来往屏幕输入输出字符串、格式化字符串。
import (
    "fmt"
)
// import 下面是 main 函数,main 函数的话里面调用了 fmt.Println 输出 helloworld要运行这个程序的话,我们就直接 go run heloworld.go。

func main() {
   
    fmt.Println("hello world")
}

如果想编译成二进制的话,可以在 go build 来编译,编译完成之后直接 ./helloword 就可以运行在 FMT 包里面还有很多的函教来做不同的输入输出格式化工作。大家可以在编辑器里面把鼠标悬浮在你的代码上,就可以看到每一个函数的文档。

你也可以进入 pkg.Go.Dev,后面加你的包名比如 FMT 然后就能看到这个包的在线文档,可以从里面去挑选你需要的函数来使用。

变量

package main

import (
    "fmt"
    "math"
)

func main() {
   
    var a = "initial"
    var b, c int = 1, 2
    var d = true
    var e float64

    f := float32(e)
    g := a + "foo"
    fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
    fmt.Println(g)                // initialapple

    const s string = "constant"
    const h = 500000000
    const i = 3e20 / h
    fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}

下面我们来看第二个例子,关于变量类型。

go 语言是一门强类型语言,每一个变量都有它自己的变量类型。常见的变量类型包括字符串整数浮点型、布尔型等,go 语言的字符串是内置类型,可以直接通过加号拼接,也能够直接用等于号去比较两个字符串。

go 语言的变量的声明,在 go 语言里变量的声明有两种方式

  • 一种是过 var name string=" " 这种方式来声明变量,声明变量的时候,一般会自动去推导变量的类型。如果有需要,也可以显示写出变量类型。
  • 另一种声明变量的方式是使用 变量 := 等于值

常量的话就是把 var 改成 const,值在一提的是 go 语言里面的常量,它没有确定的类型,会根据使用的上下文来自动确定类型。

if-else

package main

import "fmt"

func main() {
   

    if 7%2 == 0 {
   
        fmt.Println("7 is even")
    } else {
   
        fmt.Println("7 is odd")
    }

    if 8%4 == 0 {
   
        fmt.Println("8 is divisible by 4")
    }

    if num := 9; num < 0 {
   
        fmt.Println(num, "is negative")
    } else if num < 10 {
   
        fmt.Println(num, "has 1 digit")
    } else {
   
        fmt.Println(num, "has multiple digits")
    }
}

Go 语言里面的 if else 写法和 C 或者 C++类似。不同点是 if 后面没有括号,且Golang 里面的 if,它必须后面接大括号,不能像 C 或者 C++—样,直接把 if 里面的语句同一行。

循环

package main

import "fmt"

func main() {
   

    i := 1
    for {
   
        fmt.Println("loop")
        break
    }
    for j := 7; j < 9; j++ {
   
        fmt.Println(j)
    }

    for n := 0; n < 5; n++ {
   
        if n%2 == 0 {
   
            continue
        }
        fmt.Println(n)
    }
    for i <= 3 {
   
        fmt.Println(i)
        i = i + 1
    }
}

在 go 里面没有 while 循环、do while 循环,只有唯一的一种 for 循环。最简单的 for 循环就是在 for 后面什么都不写,代表一个死循环。可以用 break 或者 continue 来跳出或者继续循环。

switch

package main

import (
    "fmt"
    "time"
)

func main() {
   

    a := 2
    switch a {
   
    case 1:
        fmt.Println("one")
    case 2:
        fmt.Println("two")
    case 3:
        fmt.Println("three")
    case 4, 5:
        fmt.Println("four or five")
    default:
        fmt.Println("other")
    }

    t := time.Now()
    switch {
   
    //  case 里面写条件分支
    case t.Hour() < 12:
        fmt.Println("It's before noon")
    default:
        fmt.Println("It's after noon")
    }
}

在 switch 后面的那个变量名,并不是要括号。这里有个很大的一点不同的是,在 c++里面, switch case 如果不显示加 break 的话会然后会继续往下跑完所有的 case,在 go 语言里面的话是不需要加 break 的。
相比 C 或者 C++, go 语言里面的 switch 功能更强大。可以使用任意的变量类型,甚至可以用来取代任意的 if else 语句。你可以在 switch 后面不加任何的变量,然后在 case 里面写条件分支。这样代码相比你用多个 if else 代码逻辑会更为清晰。

数组

package main

import "fmt"

func main() {
   

    // 这里的话是一个可以存放 5 个 int 元素的数组 A
    var a [5]int
    a[4] = 100
    fmt.Println("get:", a[2])
    fmt.Println("len:", len(a))

    b := [5]int{
   1, 2, 3, 4, 5}
    fmt.Println(b)

    var twoD [2][3]int
    for i := 0; i < 2; i++ {
   
        for j := 0; j < 3; j++ {
   
            twoD[i][j] = i + j
        }
    }
    fmt.Println("2d: ", twoD)
}

数组就是一个具有编号且长度固定的元素序列。对于一个数组,可以很方便地取特定索引的值或者往特定索引取存储值,然后也能够直接去打印一个数组。不过,在真实业务代码里面,我们很少直接使用数组,因为它长度是固定的,我们用的更多的是切片。

切片

package main

import "fmt"

func main() {
   

    s := make([]string, 3)
    s[0] = "a"
    s[1] = "b"
    s[2] = "c"
    fmt.Println("get:", s[2])   // c
    fmt.Println("len:", len(s)) // 3

    s = append(s, "d")
    s = append(s, "e", "f")
    fmt.Println(s) // [a b c d e f]

    c := make([]string, len(s))
    copy(c, s)
    fmt.Println(c) // [a b c d e f]

    fmt.Println(s[2:5]) // [c d e]
    fmt.Println(s[:5])  // [a b c d e]
    fmt.Println(s[2:])  // [c d e f]

    good := []string{
   "g", "o", "o", "d"}
    fmt.Println(good) // [g o o d]
}

切片不同于数组可以任意更改长度,然后也有更多丰富的操作。可以用 make 来创建一个切片,可以像数组一样去取值, 使用 append 来追加元素。

注意 append 的用法的话,你必须把 append 的结果赋值为原数组。
因为 slice 的原理实际上是它有一个它存储了一个长度和一个容量,加一个指向一个数组的指针,在你执行 append 操作的时候,如果容量不够的话,会扩容并且返回新的 slice。

Slice 此初始化的时候也可以指定长度。
Slice 拥有像 python 一样的切片操作,比如这个代表取出第二个到第五个位置的元素,不包括第五个元素。不过不同于 python,这里不支持负数索引

map

package main

import "fmt"

func main() {
   
    // 用 make 来创建一个空 map,这里会需要两个类型。
    // 第一个是那个 key 的类型,这里是 string 另一个是 value 的类型,这里是 int。
    m := make(map[string]int)
    // 可以从里面去存储或者取出键值对。
    m["one"] = 1
    m["two"] = 2
    fmt.Println(m)           // map[one:1 two:2]
    fmt.Println(len(m))      // 2
    fmt.Println(m["one"])    // 1
    fmt.Println(m["unknow"]) // 0

    r, ok := m["unknow"]
    fmt.Println(r, ok) // 0 false

    // 可以用 delete 从里面删除键值对。
    delete(m, "one")

    m2 := map[string]int{
   "one": 1, "two": 2}
    var m3 = map[string]int{
   "one": 1, "two": 2}
    fmt.Println(m2, m3)
}

Map 是实际使用过程中最频繁用到的数据结构。
Golang 的 map 是完全无序的,遍历的时候不会按照字母顺序,也不会按照插入顺序输出,而是随机顺序。

range

package main

import "fmt"

func main() {
   
    nums := []int{
   2, 3, 4}
    sum := 0
    for i, num := range nums {
   
        sum += num
        if num == 2 {
   
            fmt.Println("index:", i, "num:", num) // index: 0 num: 2
        }
    }
    fmt.Println(sum) // 9

    m := map[string]string{
   "a": "A", "b": "B"}
    for k, v := range m {
   
        fmt.Println(k, v) // b 8; a A
    }
    for k := range m {
   
        fmt.Println("key", k) // key a; key b
    }
}

对于一个 slice 或者一个 map 的话,我们可以用 range 来快速遍历,这样代码能够更加简洁。Range 遍历的时候, 对于数组会返回两个值,第一个是索引,第二个是对应位置的值。如果我们不需要索引的话,我们可以用下划线来忽略。

函数

package main

import "fmt"

func add(a int, b int) int {
   
    return a + b
}

func add2(a, b int) int {
   
    return a + b
}

func exists(m map[string]string, k string) (v string, ok bool) {
   
    v, ok = m[k]
    return v, ok
}

func main() {
   
    res := add(1, 2)
    fmt.Println(res) // 3
    // 第一个是真正的返回结果,第二个值是一个错误信息
    v, ok := exists(map[string]string{
   "a": "A"}, "a")
    fmt.Println(v, ok) // A True
}

这个是 Golang 里面一个简单的实现两个变量相加的函数。Golang 和其他很多语言不一样的是,变量类型是后置的。
Golang 里面的函数原生支持返回多个值。在实际的业务逻辑代码里面几乎所有的函数都返回两个值,第一个是真正的返回结果,第二个值是一个错误信息。

指针

package main

import "fmt"

func add2(n int) {
   
    n += 2
}

func add2ptr(n *int) {
   
    *n += 2
}

func main() {
   
    n := 5
    add2(n)
    fmt.Println(n) // 5
    add2ptr(&n)
    fmt.Println(n) // 7
}

Go 里面也支持指针。当然,相比 C 和 C++里面的指针,支持的操作很有限。指针的一个主要用途就是对于传入参数进行修改。

例如上面这个函数试图把一个变量+2。但是单纯像上面这种写法其实是无效的。因为传入函数的参数实际上是一个持贝, 那也说这个+2,是对那个拷贝进行了+2,并不起作用。如果我们需要起作用的话,那么我们需要把那个类型写成指针类型,那么为了类型匹配,调用的时候会加一个&符号。

结构体

package main

import "fmt"

type user struct {
   
    name     string
    password string
}

func main() {
   
    a := user{
   name: "wang", password: "1024"}
    b := user{
   "wang", "1024"}
    c := user{
   name: "wang"}
    c.password = "1024"
    var d user
    d.name = "wang"
    d.password = "1024"

    fmt.Println(a, b, c, d)                 // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
    fmt.Println(checkPassword(a, "haha"))   // false
    fmt.Println(checkPassword2(&a, "haha")) // false
}

func checkPassword(u user, password string) bool {
   
    return u.password == password
}
// 同样的结构体我们也能支持指针,这样能够实现对于结构体的修改,也可以在某些情况下避免一些大结构体的拷贝开销。
func checkPassword2(u *user, password string) bool {
   
    return u.password == password
}

结构体的话是带类型的字段的集合。
比如这里 user 结构包含了两个字段, name 和 password。我们可以用结构体的名称去初始化一个结构体变量,构造的时候需要传入每个字段的初始值。也可以用这种键值对的方式去指定初始值,这样可以只对一部分字段进行初始化。

结构体方法

package main

import "fmt"

type user struct {
   
    name     string
    password string
}

func (u user) checkPassword(password string) bool {
   
    return u.password == password
}

func (u *user) resetPassword(password string) {
   
    u.password = password
}

func main() {
   
    a := user{
   name: "wang", password: "1024"}
    a.resetPassword("2048")
    fmt.Println(a.checkPassword("2048")) // true
}

在 Golang 里面可以为结构体去定义一些方法。会有一点类似其他语言里面的类成员函数。比如这里,我们把上面一个例子的checkPassword 的实现,从一个普通函数,改成了结构体方法。这样用户可以像a.checkPassword ("x )这样去调用。D 具体的代码修改,就是把第一个参数,加上括号,写到函数名称前面。

在实现结构体的方法的时候也有两种写法,一种是带指针,一种是不带指针。如果带指针的话,就可以对这个结构体去做修改。如果不带指针的话,实际上操作的是一个拷贝,你就无法对结构体进行修改。

错误处理

package main

import (
    "errors"
    "fmt"
)

type user struct {
   
    name     string
    password string
}
// 在函数里面,我们可以在那个函数的返回值类型里面,后面加一个 error,就代表这个函数可能会返回错误。
func findUser(users []user, name string) (v *user, err error) {
   
    for _, u := range users {
   
        if u.name == name {
   
            return &u, nil
        }
    }
    return nil, errors.New("not found")
}

func main() {
   
    u, err := findUser([]user{
   {
   "wang", "1024"}}, "wang")
    if err != nil {
   
        fmt.Println(err)
        return
    }
    fmt.Println(u.name) // wang

    if u, err := findUser([]user{
   {
   "wang", "1024"}}, "li"); err != nil {
   
        fmt.Println(err) // not found
        return
    } else {
   
        fmt.Println(u.name)
    }
}

错误处理在 go 语言里面符合语言习惯的做法就是使用一个单独的返回值来传递错误信息。

不同于 Java 自家家使用的异常。Go 语言的处理方式,能够很清晰地知道哪个函数返回了错误,并且能用简单的 if else 来处理错误。
在函数里面,我们可以在那个函数的返回值类型里面,后面加一个 error,就代表这个函数可能会返回错误。
那么在函数实现的时候, return 需要同时 return 两个值,要么就是如果出现错误的话,那么可以 return nil 和一个 error。如果没有的话, 那么返回原本的结果和 nil。

字符串操作

package main

import (
    "fmt"
    "strings"
)

func main() {
   
    a := "hello"
    fmt.Println(strings.Contains(a, "ll"))                // true
    fmt.Println(strings.Count(a, "l"))                    // 2
    fmt.Println(strings.HasPrefix(a, "he"))               // true
    fmt.Println(strings.HasSuffix(a, "llo"))              // true
    fmt.Println(strings.Index(a, "ll"))                   // 2
    fmt.Println(strings.Join([]string{
   "he", "llo"}, "-")) // he-llo
    fmt.Println(strings.Repeat(a, 2))                     // hellohello
    fmt.Println(strings.Replace(a, "e", "E", -1))         // hEllo
    fmt.Println(strings.Split("a-b-c", "-"))              // [a b c]
    fmt.Println(strings.ToLower(a))                       // hello
    fmt.Println(strings.ToUpper(a))                       // HELLO
    fmt.Println(len(a))                                   // 5
    b := "你好"
    fmt.Println(len(b)) // 6
}

在标准库 strings 包里面有很多常用的字符串工具函数,比如 contains 判断一个字符串里面是否有包含另一个字符串,count 字符串计数, index 查找某个字符串的位置。Join 连接多个字符串 repeat 重复多个字符串 replace 替换字符串。

字符串格式化

package main

import "fmt"

type point struct {
   
    x, y int
}

func main() {
   
    s := "hello"
    n := 123
    p := point{
   1, 2}
    fmt.Println(s, n) // hello 123
    fmt.Println(p)    // {1 2}

    fmt.Printf("s=%v\n", s)  // s=hello
    fmt.Printf("n=%v\n", n)  // n=123
    fmt.Printf("p=%v\n", p)  // p={1 2}
    fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
    fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}

    f := 3.141592653
    fmt.Println(f)          // 3.141592653
    fmt.Printf("%.2f\n", f) // 3.14
}

在 go 语言里,你可以很轻松地用 %v 来打印任意类型的变量,而不需要区分数字字符串。你也可以用 %+v 打印详细结果, %#v 则更详细。

JSON 处理

package main

import (
    "encoding/json"
    "fmt"
)

type userInfo struct {
   
    Name  string
    Age   int `json:"age"`
    Hobby []string
}

func main() {
   
    a := userInfo{
   Name: "wang", Age: 18, Hobby: []string{
   "Golang", "TypeScript"}}
    buf, err := json.Marshal(a)
    if err != nil {
   
        panic(err)
    }
    fmt.Println(buf)         // [123 34 78 97...]
    fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}

    buf, err = json.MarshalIndent(a, "", "\t")
    if err != nil {
   
        panic(err)
    }
    fmt.Println(string(buf))

    var b userInfo
    err = json.Unmarshal(buf, &b)
    if err != nil {
   
        panic(err)
    }
    fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}

go 语言里面的 JSON 操作非常简单,对于一个已有的结构体,我们可以什么都不做,只要保证每个字段的第一个字母是大写,也就是是公开字段。那么这个结构体就能用 JSON. Marshaler 去序列化,变成一个 JSON 的字符串。
序列化之后的字符串也能够用 JSON. Unmarshaler 去反序列化到一个空的变量里面。
这样默认序列化出来的字符串的话,它的风格是大写字母开头,而不是下划线。我们可以在后面用 json tag 等语法来去修改输出 JSON 结果里面的字段名。

时间处理

package main

import (
    "fmt"
    "time"
)

func main() {
   
    now := time.Now()
    fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
    t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
    t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
    fmt.Println(t)                                                  // 2022-03-27 01:25:36 +0000 UTC
    fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
    fmt.Println(t.Format("2006-01-02 15:04:05"))                    // 2022-03-27 01:25:36
    diff := t2.Sub(t)
    fmt.Println(diff)                           // 1h5m0s
    fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
    t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
    if err != nil {
   
        panic(err)
    }
    fmt.Println(t3 == t)    // true
    fmt.Println(now.Unix()) // 1648738080
}

下面是时间处理,在 go 语言里面最常用的就是 time.Now ()来获取当前时间,然后你也可以用 time.Date 去构造一个带时区的时间,构造完的时间。上面有很多方法来获取这个时间点的年月日小时分钟秒,然后也能用点 sub 去对两个时间进行减法,得到一个时间段。时间段又可以去得到它有多少小时,多少分钟、多少秒。

在和某些系统交互的时候,我们经常会用到时间戳。那您可以用 .UNIX() 来获取时间戳。time.formattime.parse 用于解析时间,具体可以参考文档。

数字解析

package main

import (
    "fmt"
    "strconv"
)

func main() {
   
    f, _ := strconv.ParseFloat("1.234", 64)
    fmt.Println(f) // 1.234

    n, _ := strconv.ParseInt("111", 10, 64)
    fmt.Println(n) // 111

    n, _ = strconv.ParseInt("0x1000", 0, 64)
    fmt.Println(n) // 4096

    n2, _ := strconv.Atoi("123")
    fmt.Println(n2) // 123

    n2, err := strconv.Atoi("AAA")
    fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}

下面我们来学习一下字符串和数字之间的转换。在 go 语言当中,关于字符串和数字类型之间的转换都在 strconv 这个包下,这个包是 string convert 这两个单词的缩写。
我们可以用 parselnt 或者 parseFloat 来解析一个字符串。Parseint 参数
我们可以用 Atoi 把一个十进制字符串转成数字。可以用 itoA 把数字转成字符串。如果输入不合法, 那么这些函数都会返回 error。

进程信息

package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
   
    // 比如我们编译的一个二进制文件,command。后面接 abcd 来启动,输出就是os.argv会是一个长度为5的 slice ,第一个成员代表:进制自身的名字。
    // go run example/20-env/main.go a b c d
    fmt.Println(os.Args)           // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
    fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
    fmt.Println(os.Setenv("AA", "BB"))

    buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
    if err != nil {
   
        panic(err)
    }
    fmt.Println(string(buf)) // 127.0.0.1       localhost
}

在go 里面,我们能够用os.argv来得到程序执行的时候的指定的命令行参数。
我们可以用os.getenv来读取环境变量。

目录
相关文章
|
5月前
|
人工智能 安全 算法
Go入门实战:并发模式的使用
本文详细探讨了Go语言的并发模式,包括Goroutine、Channel、Mutex和WaitGroup等核心概念。通过具体代码实例与详细解释,介绍了这些模式的原理及应用。同时分析了未来发展趋势与挑战,如更高效的并发控制、更好的并发安全及性能优化。Go语言凭借其优秀的并发性能,在现代编程中备受青睐。
162 33
|
22天前
|
Cloud Native 安全 Java
Go语言深度解析:从入门到精通的完整指南
🌟 蒋星熠Jaxonic,执着的星际旅人,用Go语言编写代码诗篇。🚀 Go语言以简洁、高效、并发为核心,助力云计算与微服务革新。📚 本文详解Go语法、并发模型、性能优化与实战案例,助你掌握现代编程精髓。🌌 从goroutine到channel,从内存优化到高并发架构,全面解析Go的强大力量。🔧 实战构建高性能Web服务,展现Go在云原生时代的无限可能。✨ 附技术对比、最佳实践与生态全景,带你踏上Go语言的星辰征途。#Go语言 #并发编程 #云原生 #性能优化
|
2月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
2月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
3月前
|
JSON 前端开发 Go
Go语言实战:创建一个简单的 HTTP 服务器
本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。
|
3月前
|
Go
如何在Go语言的HTTP请求中设置使用代理服务器
当使用特定的代理时,在某些情况下可能需要认证信息,认证信息可以在代理URL中提供,格式通常是:
275 0
|
4月前
|
JSON 编解码 API
Go语言网络编程:使用 net/http 构建 RESTful API
本章介绍如何使用 Go 语言的 `net/http` 标准库构建 RESTful API。内容涵盖 RESTful API 的基本概念及规范,包括 GET、POST、PUT 和 DELETE 方法的实现。通过定义用户数据结构和模拟数据库,逐步实现获取用户列表、创建用户、更新用户、删除用户的 HTTP 路由处理函数。同时提供辅助函数用于路径参数解析,并展示如何设置路由器启动服务。最后通过 curl 或 Postman 测试接口功能。章节总结了路由分发、JSON 编解码、方法区分、并发安全管理和路径参数解析等关键点,为更复杂需求推荐第三方框架如 Gin、Echo 和 Chi。
|
5月前
|
分布式计算 Go C++
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
129 10
|
8月前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
11月前
|
存储 负载均衡 监控
如何利用Go语言的高效性、并发支持、简洁性和跨平台性等优势,通过合理设计架构、实现负载均衡、构建容错机制、建立监控体系、优化数据存储及实施服务治理等步骤,打造稳定可靠的服务架构。
在数字化时代,构建高可靠性服务架构至关重要。本文探讨了如何利用Go语言的高效性、并发支持、简洁性和跨平台性等优势,通过合理设计架构、实现负载均衡、构建容错机制、建立监控体系、优化数据存储及实施服务治理等步骤,打造稳定可靠的服务架构。
230 1