Web开发Golang实现Web服务器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Web开发Golang实现Web服务器相关的知识,希望对你有一定的参考价值。

(本节内容如下:)

1、简介

  • Go是谷歌支持的一种开源编程语言
  • 易于学习和入门
  • 内置并发性和强大的标准库
  • 不断增长的合作伙伴、社区和工具生态系统

相关网站: https://go.dev/ https://golang.google.cn/ https://studygolang.com/dl https://www.runoob.com/go/go-tutorial.html

<font color=blue> Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 <font color=blue> Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。 Golang其高效而又友好的语法,赢得了很多后端开发人员的青睐,最适用于高并发网络编程的语言之一。 Golang(又称Go)是Google公司开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。

  • Build fast, reliable, and efficient software at scale
  • Go is an open source programming language supported by Google
  • Easy to learn and get started with
  • Built-in concurrency and a robust standard library
  • Growing ecosystem of partners, communities, and tools

2、安装和配置

2.1 下载

https://golang.google.cn/dl/ https://golang.google.cn/dl/go1.19.1.windows-amd64.msi

Go有多种安装方式,你可以选择自己喜欢的。这里我们介绍三种最常见的安装方式:

  • Go源码安装:这是一种标准的软件安装方式。对于经常使用Unix类系统的用户,尤其对于开发者来说,从源码安装可以自己定制。
  • Go标准包安装:Go提供了方便的安装包,支持Windows、Linux、Mac等系统。这种方式适合快速安装,可根据自己的系统位数下载好相应的安装包,一路next就可以轻松安装了。推荐这种方式
  • 第三方工具安装:目前有很多方便的第三方软件包工具,例如Ubuntu的apt-get、Mac的homebrew等。这种安装方式适合那些熟悉相应系统的用户。

这里直接使用Go标准包进行安装。

2.2 安装

直接点击安装包运行即可。 安装完毕之后,打开VsCode,在终端输入命令测试是否安装成功:

go version

2.3 配置

这里在VsCode里进行go语言的相关开发。 然后我们打开 VSCode 的扩展(Ctrl+Shift+P):

  • 为了能在国内网络环境下正常的安装go开发工具包,使用go mod 代理来安装go插件:
# 新版改成如下链接
go env -w GO111MODULE=on
go env -w GOPROXY=https://proxy.golang.com.cn,direct

  • 问题:go.mod file not found in current directory or any parent directory; see ‘go help modules‘?
go env -w GO111MODULE=auto

2.4 编译

  • Go语言目录结构 一个Go语言项目的目录一般包含以下三个子目录: src 目录:放置项目和库的源文件; pkg 目录:放置编译后生成的包/库的归档文件; bin 目录:放置编译后生成的可执行文件。

Go语言是编译型的静态语言(和C语言一样),所以在运行Go语言程序之前,先要将其编译成二进制的可执行文件。可以通过Go语言提供的go build或者go run命令对Go语言程序进行编译:

  • go build命令可以将Go语言程序代码编译成二进制的可执行文件,但是需要我们手动运行该二进制文件;
  • go run命令则更加方便,它会在编译后直接运行Go语言程序,编译过程中会产生一个临时文件,但不会生成可执行文件,这个特点很适合用来调试程序。
D:\\0607> go build .\\test\\test_go1.go
D:\\0607> .\\test_go1.exe
Hello World, 爱看书的小沐, 2022!

3、语法

3.1 关键词

  • Go 代码中会使用到的 25 个关键字或保留字:
break   default func    interface   select
case    defer   go  map struct
chan    else    goto    package switch
const   fallthrough if  range   type
continue    for import  return  var
  • Go 语言还有 36 个预定义标识符:
append	bool	byte	cap	close	complex	complex64	complex128	uint16
copy	false	float32	float64	imag	int	int8	int16	uint32
int32	int64	iota	len	make	new	nil	panic	uint64
print	println	real	recover	string	true	uint	uint8	uintptr

3.2 注释

//单行注释

/*
多行注释
多行注释
*/

3.3 数据类型

在 Go 编程语言中,数据类型用于声明函数和变量。

序号 类型 描述
1 布尔型 <div align=left>布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。
2 数字类型 <div align=left>整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
3 字符串类型 <div align=left>字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
4 派生类型 <div align=left>包括:<br>(a) 指针类型(Pointer)<br>(b) 数组类型<br> (c) 结构化类型(struct)<br>(d) Channel 类型<br>(e) 函数类型(function)<br>(f) 切片类型(slice切片:不定长数组)<br>(g) 接口类型(interface)<br>(h) Map 类型(字典)

3.4 变量赋值

Go是静态语言,是强类型的,但是Go语言也允许在赋值变量时确定类型。 Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。 声明变量的一般形式是使用 var 关键字:

// 1. 完整的申明并赋值
var a int
a = 1

// 2. 声明变量类型同时赋值
var a int = 1

// 3. 不声明类型,赋值时确定
var a = 1

// 4. 不用 var 关键字申明变量并赋值后确定类型
a := 1
package main
import "fmt"

func main() 
	fmt.Println("Hello, 爱看书的小沐, 2022!")
	fmt.Println("i am a robot!")

	var s string = "爱看书的小沐"
	fmt.Println(s)

	s = "tomcat"
	fmt.Println(s)

	s2 := "apple"
	fmt.Println(s2)

3.5 函数定义

Go用func定义函数,没有默认值参数、没有关键字参数,但是有很多其他特征。

func main() 
    println(foo(18, "tomcat"))


func foo(age int, name string) (r string) 
    r = fmt.Sprintf("my name is %s , age %d", name, age)
    return 

3.6 内置库

  • fmt fmt 包实现了格式化的标准输入输出,这与C语言中的 printf 和 scanf 类似。其中的 fmt.Printf() 和 fmt.Println() 是开发者使用最为频繁的函数。 格式化短语派生于C语言,一些短语(%- 序列)是这样使用:

  • io 这个包提供了原始的 I/O 操作界面。它主要的任务是对 os 包这样的原始的 I/O 进行封装,增加一些其他相关,使其具有抽象功能用在公共的接口上。

  • bufio bufio 包通过对 io 包的封装,提供了数据缓冲功能,能够一定程度减少大块数据读写带来的开销。 在 bufio 各个组件内部都维护了一个缓冲区,数据读写操作都直接通过缓存区进行。当发起一次读写操作时,会首先尝试从缓冲区获取数据,只有当缓冲区没有数据时,才会从数据源获取数据更新缓冲。

  • sort sort 包提供了用于对切片和用户定义的集合进行排序的功能。

  • strconv strconv 包提供了将字符串转换成基本数据类型,或者从基本数据类型转换为字符串的功能。

  • os os 包提供了不依赖平台的操作系统函数接口,设计像 Unix 风格,但错误处理是 go 风格,当 os 包使用时,如果失败后返回错误类型而不是错误数量。

  • sync sync 包实现多线程中锁机制以及其他同步互斥机制。

  • flag flag 包提供命令行参数的规则定义和传入参数解析的功能。绝大部分的命令行程序都需要用到这个包。

  • encoding/json JSON 目前广泛用做网络程序中的通信格式。encoding/json 包提供了对 JSON 的基本支持,比如从一个对象序列化为 JSON 字符串,或者从 JSON 字符串反序列化出一个具体的对象等。

  • html/template 主要实现了 web 开发中生成 html 的 template 的一些函数。

  • net/http net/http 包提供 HTTP 相关服务,主要包括 http 请求、响应和 URL 的解析,以及基本的 http 客户端和扩展的 http 服务。 通过 net/http 包,只需要数行代码,即可实现一个爬虫或者一个 Web 服务器,这在传统语言中是无法想象的。

  • reflect reflect 包实现了运行时反射,允许程序通过抽象类型操作对象。通常用于处理静态类型 interface 的值,并且通过 Typeof 解析出其动态类型信息,通常会返回一个有接口类型 Type 的对象。

  • os/exec os/exec 包提供了执行自定义 linux 命令的相关实现。

  • strings strings 包主要是处理字符串的一些函数集合,包括合并、查找、分割、比较、后缀检查、索引、大小写处理等等。 strings 包与 bytes 包的函数接口功能基本一致。

  • bytes bytes 包提供了对字节切片进行读写操作的一系列函数。字节切片处理的函数比较多,分为基本处理函数、比较函数、后缀检查函数、索引函数、分割函数、大小写处理函数和子切片处理函数等。

  • log log 包主要用于在程序中输出日志。 log 包中提供了三类日志输出接口,Print、Fatal 和 Panic。

4、入门示例

4.1 hello world

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

func main() 
	fmt.Println("Hello, 爱看书的小沐!")


4.2 goroutine

package main

import (
	"fmt"
	"time"
)

func running() 
	var times int
	// 构建一个无限循环
	for 
		times++
		fmt.Println("时间1: ", times)
		// 延时1秒
		time.Sleep(time.Second)
	


func running2() 
	var times int
	// 构建一个无限循环
	for 
		times++
		fmt.Println("时间2: ", times)
		// 延时1秒
		time.Sleep(time.Second)
	


func main() 
	// 并发执行程序
	go running()
	go running2()
	// 接受命令行输入, 不做任何事情
	var input string
	fmt.Scanln(&input)

4.3 sqlite3

  • 安装sqlite3插件如下:
go get github.com/mattn/go-sqlite3

<font color=red>问题:cgo: C compiler "gcc" not found: exec: "gcc": executable file not found in %PATH%</font> 答:安装gcc即可。

  • 测试代码如下:
package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/mattn/go-sqlite3"
)

const (
	dbDriverName = "sqlite3"
	dbName       = "./Instruct.db3"
)

type user struct 
	Username string
	Age      int
	Job      string
	Hobby    string


func main() 
	db, err := sql.Open(dbDriverName, dbName)
	if checkErr(err) 
		return
	

	rows, err := db.Query("SELECT ID, 动作ID, 触发点ID FROM 触发映射表")
	if err != nil 
		panic(err)
	
	defer rows.Close()

	for rows.Next() 
		var uid int
		var aid int
		var tname string
		err = rows.Scan(&uid, &aid, &tname)
		if err != nil 
			panic(err)
		

		fmt.Println(uid, aid, tname)
	


func checkErr(e error) bool 
	if e != nil 
		log.Fatal(e)
		return true
	
	return false


  • 运行结果如下:

4.4 zip

package main

import (
	"archive/zip"
	"bytes"
	"fmt"
	"os"
)

func main() 
	// 创建一个缓冲区用来保存压缩文件内容
	buf := new(bytes.Buffer)
	// 创建一个压缩文档
	w := zip.NewWriter(buf)
	// 将文件加入压缩文档
	var files = []struct 
		Name, Body string
	
		"Golang.txt", "爱看书的小沐!",
	
	for _, file := range files 
		f, err := w.Create(file.Name)
		if err != nil 
			fmt.Println(err)
		
		_, err = f.Write([]byte(file.Body))
		if err != nil 
			fmt.Println(err)
		
	
	// 关闭压缩文档
	err := w.Close()
	if err != nil 
		fmt.Println(err)
	
	// 将压缩文档内容写入文件
	f, err := os.OpenFile("file.zip", os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil 
		fmt.Println(err)
	
	buf.WriteTo(f)

5、web 服务器(net/http)

5.1 net/http

Go语言里面提供了一个完善的 net/http 包,通过 net/http 包我们可以很方便的搭建一个可以运行的 Web 服务器。同时使用 net/http 包能很简单地对 Web 的路由,静态文件,模版,cookie 等数据进行设置和操作。

  • 第一个测试例子:
package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() 
    http.HandleFunc("/", index) // index 为向 url发送请求时,调用的函数
    log.Fatal(http.ListenAndServe("localhost:8000", nil))


func index(res http.ResponseWriter, req *http.Request) 
    fmt.Fprintf(res, "Hello world, 爱看书的小沐的家!")

  • 浏览器访问,运行结果如下:
  • 第二个测试例子:
package main

import (
    "io"
    "log"
    "net/http"
)

func main() 
    // Hello world, the web server

    helloHandler := func(w http.ResponseWriter, req *http.Request) 
        io.WriteString(w, "Hello, world,爱看书的小沐的家!\\n")
    

    http.HandleFunc("/hello", helloHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))

5.2 net/http + index.html

  • test.go:
package main

import (
	"io/ioutil"
	"log"
	"net/http"
)

func main() 
	http.HandleFunc("/", index)
	http.HandleFunc("/index", index)
	log.Fatal(http.ListenAndServe("localhost:8000", nil))


func index(w http.ResponseWriter, r *http.Request) 
	content, _ := ioutil.ReadFile("./index.html")
	w.Write(content)

  • index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>爱看书的小沐</title>
</head>
<body>
    这是爱看书的小沐的web服务器
</body>
</html>
  • 浏览器访问,运行结果:

5.2 net/http + HttpRouter

HttpRouter包为常用的HTTP方法提供了GET(),POST(),方法都提供了定义。 同时它为URL提供了两种匹配模式: /user/:pac 精准匹配 /user/pac /user/*pac 匹配所有模式 /user/hello

  • 安装go插件如下:
go get  github.com/julienschmidt/httprouter
  • 测试代码如下:
package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) 
	fmt.Fprint(w, "Welcome, 爱看书的小沐!\\n")


func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) 
	fmt.Fprintf(w, "hello, %s!\\n", ps.ByName("name"))


func Test(w http.ResponseWriter, r *http.Request, _ httprouter.Params) 
	w.Write([]byte("Test, httprouter!"))


func main() 
	router := httprouter.New()
	router.GET("/", Index)
	router.GET("/hello/:name", Hello)
	router.GET("/test", Hello)

	log.Fatal(http.ListenAndServe(":8000", router))


  • 浏览器访问,运行结果:

5.3 net/http + FileServer

package main

import (
	"log"
	"net/http"
)

func main() 
	// Simple static webserver:
	log.Fatal(http.ListenAndServe(":8000", http.FileServer(http.Dir("D:/3D XML Player/win_b64/resources"))))

5.4 net/http + NewServeMux


package main

import (
	"fmt"
	"net/http"
)

func newPeopleHandler() http.Handler 
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) 
		fmt.Fprintln(w, "This is the people handler.")
	)


// 设置多个处理器函数
func handler1(w http.ResponseWriter, r *http.Request) 
	fmt.Fprintf(w, "1 >> apple")


func handler2(w http.ResponseWriter, r *http.Request) 
	fmt.Fprintf(w, "1 >> pear")


func handler3(w http.ResponseWriter, r *http.Request) 
	fmt.Fprintf(w, "1 >> banana")


func main() 
	mux := http.NewServeMux()

	// Create sample handler to returns 404
	mux.Handle("/resources", http.NotFoundHandler())

	// Create sample handler that returns 200
	mux.Handle("/resources/people/", newPeopleHandler())

	mux.HandleFunc("/h1", handler1)
	mux.HandleFunc("/h2", handler2)
	mux.HandleFunc("/h3", handler3)

	//log.Fatal(http.ListenAndServe(":8000", mux))

	// 设置服务器
	server := &http.Server
		Addr:    "127.0.0.1:8000",
		Handler: mux,
	

	// 设置服务器监听请求端口
	server.ListenAndServe()

5.5 net/http + ssl

  • Golang Web SSL证书 创建生成 pem x.509
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"net"
	"os"
	"time"
)

func main() 
	max := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, _ := rand.Int(rand.Reader, max)

	// 定义:引用IETF的安全领域的公钥基础实施(PKIX)工作组的标准实例化内容
	subject := pkix.Name
		Organization:       []string"WWW.xiaomu.COM",
		OrganizationalUnit: []string"ITs",
		CommonName:         "xiaomu.COM Web",
	

	// 设置 SSL证书的属性用途
	certificate509 := x509.Certificate
		SerialNumber: serialNumber,
		Subject:      subject,
		NotBefore:    time.Now(),
		NotAfter:     time.Now().Add(100 * 24 * time.Hour),
		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:  []x509.ExtKeyUsagex509.ExtKeyUsageServerAuth,
		IPAddresses:  []net.IPnet.ParseIP("127.0.0.1"),
	

	// 生成指定位数密匙
	pk, _ := rsa.GenerateKey(rand.Reader, 1024)

	// 生成 SSL公匙
	derBytes, _ := x509.CreateCertificate(rand.Reader, &certificate509, &certificate509, &pk.PublicKey, pk)
	certOut, _ := os.Create("cert.pem")
	pem.Encode(certOut, &pem.BlockType: "CERTIFICATE", Bytes: derBytes)
	certOut.Close()

	// 生成 SSL私匙
	keyOut, _ := os.Create("key.pem")
	pem.Encode(keyOut, &pem.BlockType: "RAS PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk))
	keyOut.Close()

  • https服务器测试代码如下:
package main

import (
	"io"
	"log"
	"net/http"
)

func main() 
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) 
		io.WriteString(w, "Hello, 爱看书的小沐, TLS!\\n")
	)

	// One can use generate_cert.go in crypto/tls to generate cert.pem and key.pem.
	log.Printf("About to listen on 8443. Go to https://127.0.0.1:8443/")
	err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil)
	log.Fatal(err)

  • 浏览器测试如下:

5.3 net/http + client

  • 客户端例子1:
package main

import (
	"fmt"
	"net/http"
)

func main() 
	resp, err := http.Get("http://baidu.com/")
	fmt.Println(resp, err)

  • 客户端例子2:
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() 
	res, err := http.Get("http://www.baidu.com/robots.txt")
	if err != nil 
		log.Fatal(err)
	
	body, err := io.ReadAll(res.Body)
	res.Body.Close()
	if res.StatusCode > 299 
		log.Fatalf("Response failed with status code: %d and\\nbody: %s\\n", res.StatusCode, body)
	
	if err != nil 
		log.Fatal(err)
	
	fmt.Printf("%s", body)


  • 客户端例子3:
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() 
	req, err := http.NewRequest("GET", "https://icanhazdadjoke.com", nil)
	if err != nil 
		log.Fatalln(err)
	

	req.Header.Set("Accept", "application/json")

	client := &http.Client
	resp, err := client.Do(req)
	if err != nil 
		log.Fatalln(err)
	

	defer resp.Body.Close()

	// b, err := io.ReadAll(resp.Body)
	// // b, err := ioutil.ReadAll(resp.Body)  Go.1.15 and earlier
	// if err != nil 
	// 	log.Fatalln(err)
	// 

	b, err := httputil.DumpResponse(resp, true)
	if err != nil 
		log.Fatalln(err)
	

	fmt.Println(string(b))


6、web 框架

https://golang.google.cn/solutions/webdev https://github.com/speedwheel/awesome-go-web-frameworks/blob/master/README.md#popularity

6.1 echo

$ mkdir myapp
$ cd myapp
$ go mod init myapp
$ go get github.com/labstack/echo/v4

安装过程如下:

  • 入门例子:
package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
)

func main() 
	e := echo.New()
	e.GET("/", func(c echo.Context) error 
		return c.String(http.StatusOK, "Hello, 爱看书的小沐!")
	)
	e.Logger.Fatal(e.Start(":1323"))

  • 不同类型的路由:
e.POST("/users", saveUser)
e.GET("/users/:id", getUser)
e.PUT("/users/:id", updateUser)
e.DELETE("/users/:id", deleteUser)

// e.GET("/users/:id", getUser)
func getUser(c echo.Context) error 
  	// User ID from path `users/:id`
  	id := c.Param("id")
	return c.String(http.StatusOK, id)


//e.GET("/show", show)
func show(c echo.Context) error 
	// Get team and member from the query string
	team := c.QueryParam("team")
	member := c.QueryParam("member")
	return c.String(http.StatusOK, "team:" + team + ", member:" + member)


// e.POST("/save", save)
func save(c echo.Context) error 
	// Get name and email
	name := c.FormValue("name")
	email := c.FormValue("email")
	return c.String(http.StatusOK, "name:" + name + ", email:" + email)

6.2 beego

https://github.com/beego/beego https://beego.vip/

库安装命令如下:

mkdir hello
cd hello
go mod init
go get github.com/beego/beego/v2@v2.0.0

测试代码如下:

package main

import "github.com/beego/beego/v2/server/web"

func main() 
	web.Run()

package main

import (
	"github.com/beego/beego/v2/server/web"
)

type MainController struct 
	web.Controller


func (this *MainController) Get() 
	this.Ctx.WriteString("hello world, beego, 爱看书的小沐!")


func main() 
	web.Router("/", &MainController)
	web.Run()

结语

如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;╮( ̄▽ ̄)╭ 如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O??? 如果您需要相关功能的代码定制化开发,可以留言私信作者;(✿◡‿◡) 感谢各位大佬童鞋们的支持!( ´ ▽´ )ノ ( ´ ▽´)っ!!!

以上是关于Web开发Golang实现Web服务器的主要内容,如果未能解决你的问题,请参考以下文章

golang echo(二)---请求与响应

golang 接口笔记

Golang web 框架对比

golang(10):web开发 & 连接数据库

Go语言入门

go语言的开发优势