生活札记

golang学习笔记 - 标准包、依赖(四)

copylian    1 评论    8282 浏览    2022.05.18

十六、GRPC:一个现代开源的高性能远程过程调用 (RPC) 框架,rpc(remote procedure call)远程进程调用,例如:在分布式或者微服务架构中,服务器A想调用服务器B中的一个方法,由于不在同一个内存空间,不能直接调用,需要通过网络来表达调用的语义和调用的数据。

教程:https://www.bilibili.com/video/av766914502

笔记:https://golang-tech-stack.com/tutorial/grpc

1、rpc方法:net/rpc库、jsonrpc库(跨语言调用)

服务端:rpcservice.go

package main


import (

    "errors"

    "fmt"

    "log"

    "net"

    "net/rpc"

    "net/rpc/jsonrpc"

    "os"

)


// 算术运算结构体

type Arith struct{}


// 算术运算请求结构体

type ArithRequest struct {

    A int

    B int

}


// 算术运算响应结构体

type ArithResponse struct {

    Pro int //乘积

    Quo int //商

    Rem int //余数

}


// 乘法运算方法

func (a *Arith) Multiply(req ArithRequest, res *ArithResponse) error {

    res.Pro = req.A * req.B

    return nil

}


// 除法运算方法

func (a *Arith) Divide(req ArithRequest, res *ArithResponse) error {

    if req.B == 0 {

        return errors.New("divide by zero")

    }

    res.Quo = req.A / req.B

    res.Rem = req.A % req.B

    return nil

}


// main主体函数

func main() {

    //注册rpc服务

    rpc.Register(new(Arith))

    

    //采用HTTP作为rpc载体

    rpc.HandleHTTP()

    

    //监听端口

    lis, err := net.Listen("tcp", "127.0.0.1:8090")

    if err != nil {

        log.Fatalln("fatal error: ", err)

    }

    

    //输出信息

    fmt.Fprintf(os.Stdout, "%s", "start connection")

    

    //开启http服务

    // http.Serve(lis, nil)

    

    //jsonrpc 服务

    for {

        //接收客户端请求

        conn, err := lis.Accept()

        if err != nil {

            continue

        }

        

        //并发处理客户端请求

        go func(conn net.Conn) {

            fmt.Fprintf(os.Stdout, "%s", "new client in coming\n")

            jsonrpc.ServeConn(conn)

        }(conn)

    }

}


客户端:rpcclient.go

package main


import (

    "fmt"

    "log"

    "net/rpc/jsonrpc"

)


// 算术运算请求结构体

type ArithRequest struct {

    A int

    B int

}


// 算术运算响应结构体

type ArithResponse struct {

    Pro int //乘积

    Quo int //商

    Rem int //余数

}


// main主体函数

func main() {

    //rpc的HTTP拨号

    // conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8090")

    

    //jsonrpc拨号

    conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8090")

    

    if err != nil {

        log.Fatal("fatal error: ", err)

    }

    

    //请求

    req := ArithRequest{9, 2}

    

    //响应

    var res ArithResponse

    

    //执行call方法

    err2 := conn.Call("Arith.Multiply", req, &res)

    if err2 != nil {

        log.Fatal("arith error: ", err2)

    }

    

    //乘积

    fmt.Printf("%d * %d = %v\n", req.A, req.B, res.Pro)

    

    //商、余数

    err3 := conn.Call("Arith.Divide", req, &res)

    if err3 != nil {

        log.Fatal("arith error:", err3)

    }

    fmt.Printf("%d / %d = %v, rem = %v\n", req.A, req.B, res.Quo, res.Rem)

}


2、grpc和protobuf的使用

1)、安装依赖:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest


2)、第一个GRPC:

1)、protobuf:

syntax = "proto3";

option go_package = "./;hei";

package hei;


// 微服务

service SayHei {

    //send a msg

    rpc SayHello(HeiRequest) returns(HeiResponse) {};

}


// 请求

message HeiRequest {

    string name = 1;

}


// 响应

message HeiResponse {

    string message = 1;

}


构建生成:

protoc --go_out=./proto/hei ./proto/hei.proto

protoc --go-grpc_out=./proto/hei ./proto/hei.proto


2)、服务端:

package main


import (

    "context"

    pb "course-03/proto/hei"

    "fmt"

    "log"

    "net"

    

    "google.golang.org/grpc"

    "google.golang.org/grpc/reflection"

)


// 常量

const (

    port = ":50051"

)


// 服务对象

type server struct {

    pb.UnimplementedSayHeiServer

}


// SayHello 实现服务的接口 在proto中定义的所有服务都是接口

func (s *server) SayHello(ctx context.Context, req *pb.HeiRequest) (*pb.HeiResponse, error) {

    return &pb.HeiResponse{Message: "Hei " + req.Name}, nil

}


// main

func main() {

    // 监听端口

    lis, err := net.Listen("tcp", port)

    if err != nil {

        log.Fatalf("fail to listen:%v", err)

    }

    

    // 开启一个服务

    s := grpc.NewServer()

    pb.RegisterSayHeiServer(s, &server{})

    

    // 输出

    fmt.Printf("服务已开启: %v\n", "服务已开启")

    

    // 注册反射服务器 这个服务是CLI使用的 跟本服务没有关系

    reflection.Register(s)

    if err = s.Serve(lis); err != nil {

        log.Fatalf("fail to server:%v", err)

    }

}


3)、客户端:

package main


import (

    "context"

    pb "course-03/proto/hei"

    "fmt"

    "log"

    "os"

    "time"

    

    "google.golang.org/grpc"

)


const (

    address     = "localhost:50051"

    defaultName = "World"

)


func main() {

    // 拨号

    conn, err := grpc.Dial(address, grpc.WithInsecure()//grpc.WithInsecure()没有安全限制

    if err != nil {

        log.Fatalf("did not connentc:%v", err)

    }

    defer conn.Close()

    c := pb.NewSayHeiClient(conn)

    

    // 默认值

    name := defaultName

    if len(os.Args) > 1 {

        name = os.Args[1]

    }

    

    // 上下文

    ctx, cancle := context.WithTimeout(context.Background(), time.Second)

    defer cancle()

    

    res, err := c.SayHello(ctx, &pb.HeiRequest{Name: name})

    if err != nil {

        log.Fatalf("could not sayhei :%v", err)

    }

    // log.Printf("Hei:", res.Message)

    fmt.Printf("res.Message: %v\n", res.Message)

}


3、grpc的定义和种类

1)、定义:使用的是protocol buffers

// 微服务

service SayHeiService {

        //send a msg

        rpc SayHello(HeiRequest) returns(HeiResponse) {};

}


// 请求

message HeiRequest {

        string name = 1;

}


// 响应

message HeiResponse {

        string message = 1;

}


2)、种类:

一元rpc:rpc SayHello(HeiRequest) returns(HeiResponse) {};

服务器流的rpc:rpc SayHello(HeiRequest) returns(stream HeiResponse) {};

客户端流rpc:rpc SayHello(stream HeiRequest) returns(HeiResponse) {};

双向流rpc:rpc SayHello(stream HeiRequest) returns(stream HeiResponse) {};


2-1)、服务器流的rpc:

protocol buffer:

syntax = "proto3";

option go_package = "./;stream";

package stream;

service Greeter {

    // 服务端 推送流

    rpc GetStream(GreeterReqData) returns (stream GreeterResData) {};

}


// 请求

message GreeterReqData {

    string data = 1;

}


// 响应

message GreeterResData {

    string data = 1;

}


服务端:

package main

import (

    pb "course-03/proto/stream/stc"

    "fmt"

    "net"

    "time"

    "google.golang.org/grpc"

)


const PORT = ":50051"


type server struct {

    pb.UnimplementedGreeterServer

}


// 服务端 推送流

func (s *server) GetStream(req *pb.GreeterReqData, res pb.Greeter_GetStreamServer) error {

    i := 0

    for {

        i++

        res.Send(&pb.GreeterResData{Data: fmt.Sprintf("%v", time.Now().Nanosecond())})

        time.Sleep(1 * time.Second)

        if i > 10 {

            break

        }

    }

    return nil

}


// main

func main() {

    //监听端口

    lis, err := net.Listen("tcp", PORT)

    if err != nil {

           return

    }

    

    //创建grpc服务

    s := grpc.NewServer()

    

    //注册事件

    pb.RegisterGreeterServer(s, &server{})

    

    //输出

    fmt.Printf("开启服务: %v\n", "start listen")

    

    //处理链接

    s.Serve(lis)

}


客户端:

package main

import (

    "context"

    pb "course-03/proto/stream/stc"

    "log"

    "google.golang.org/grpc"

)


const (

    ADDRESS = "localhost:50051"

)


func main() {

    // 通过grpc注册一个链接

    conn, err := grpc.Dial(ADDRESSgrpc.WithInsecure())

    if err != nil {

        return

    }

    defer conn.Close()

    

    // 通过链接生成一个客户端对象

    c := pb.NewGreeterClient(conn)

    

    // 调用服务推送流

    reqstreamData := &pb.GreeterReqData{Data: "aaa"}

    res, err1 := c.GetStream(context.Background(), reqstreamData)

    if err != nil {

        log.Println(err1)

    }

    for {

        aa, err := res.Recv()

        if err != nil {

        log.Println(err)

            break

        }

        log.Println(aa)

    }

}


2-2)、客户端流rpc:

protocol buffers:

syntax = "proto3";

option go_package = "./;stream";

package stream;


service Greeter {

        // 服务端 推送流

        rpc PutStream(stream GreeterReqData) returns (GreeterResData) {};

}


// 请求

message GreeterReqData {

        string data = 1;

}


// 响应

message GreeterResData {

        string data = 1;

}


服务端:

package main

import (

    pb "course-03/proto/stream/cts"

    "fmt"

    "log"

    "net"

    "google.golang.org/grpc"

)


const PORT = ":50051"


type server struct {

    pb.UnimplementedGreeterServer

}


// 客户端 推送流

func (s *server) PutStream(res pb.Greeter_PutStreamServer) error {

    for {

        if tem, err := res.Recv(); err == nil {

            log.Println(tem)

        } else {

            log.Println(err)

            break

        }

    }

    return nil

}


// main

func main() {

    //监听端口

    lis, err := net.Listen("tcp", PORT)

    if err != nil {

        return

    }

    

    //创建grpc服务

    s := grpc.NewServer()

    

    //注册事件

    pb.RegisterGreeterServer(s, &server{})

    

    //输出

    fmt.Printf("开启服务: %v\n", "start listen")

    

    //处理链接

    s.Serve(lis)

}


客户端:

package main

import (

    "context"

    pb "course-03/proto/stream/cts"

    "log"

    "time"

    "google.golang.org/grpc"

)


const (

    ADDRESS = "localhost:50051"

)


func main() {

    // 通过grpc注册一个链接

    conn, err := grpc.Dial(ADDRESS, grpc.WithInsecure())

    if err != nil {

        return

    }

    defer conn.Close()

    

    // 通过链接生成一个客户端对象

    c := pb.NewGreeterClient(conn)

    

    // 调用服务推送流

    reqstreamData := &pb.GreeterReqData{Data: "aaa"}

    

    //客户端推送

    res, err1 := c.PutStream(context.Background())

    if err != nil {

        log.Println(err1)

    }

    i := 1

    for {

        i++

        err := res.Send(reqstreamData)

        if err != nil {

            log.Println(err)

            break

        }

        time.Sleep(time.Second)

        if i > 10 {

            break

        }

    }

}


2-3)、双向流rpc:

protocol buffers:

syntax = "proto3";

option go_package = "./;stream";

package stream;

service Greeter {

        // 服务端 推送流

        rpc AllStream(stream GreeterReqData) returns (stream GreeterResData) {};

}


// 请求

message GreeterReqData {

        string data = 1;

}


// 响应

message GreeterResData {

        string data = 1;

}


服务端:

package main

import (

    pb "course-03/proto/stream/all"

    "fmt"

    "log"

    "net"

    "sync"

    "time"

    "google.golang.org/grpc"

)


const PORT = ":50051"


type server struct {

    pb.UnimplementedGreeterServer

}


// 双向 推送流

func (s *server) AllStream(allStr pb.Greeter_AllStreamServer) error {

    // sync

    wg := sync.WaitGroup{}

    wg.Add(2)

    

    //协程:接收数据

    go func() {

        for {

            data, _ := allStr.Recv()

            log.Println(data)

        }

    }()

    

    //协程:推送数据

    go func() {

        for {

            allStr.Send(&pb.GreeterResData{Data: "from server."})

            time.Sleep(time.Second)

        }

    }()

    wg.Wait()

    return nil

}


// main

func main() {

    //监听端口

    lis, err := net.Listen("tcp", PORT)

    if err != nil {

        return

    }

    

    //创建grpc服务

    s := grpc.NewServer()

    

    //注册事件

    pb.RegisterGreeterServer(s, &server{})

    

    //输出

    fmt.Printf("开启服务: %v\n", "start listen")

    

    //处理链接

    s.Serve(lis)

}


客户端:

package main

import (

    "context"

    pb "course-03/proto/stream/all"

    "log"

    "time"

    "google.golang.org/grpc"

)


const (

    ADDRESS = "localhost:50051"

)


func main() {

    // 通过grpc注册一个链接

    conn, err := grpc.Dial(ADDRESSgrpc.WithInsecure())

    if err != nil {

        return

    }

    defer conn.Close()

    

    // 通过链接生成一个客户端对象

    c := pb.NewGreeterClient(conn)

    

    //客户端推送

    allStr, err1 := c.AllStream(context.Background())

    if err != nil {

        log.Println(err1)

    }

    

    //协程:接收数据

    go func() {

        for {

            data, _ := allStr.Recv()

            log.Println(data)

        }

    }()

    

    //协程:推送数据

    go func() {

        for {

            allStr.Send(&pb.GreeterReqData{Data: "from client."})

            time.Sleep(time.Second)

        }

    }()

    

    select {}

}



4、grpc整合gin:server.go作为服务端client.go作为server.go的客户端,作为浏览器服务端,是一个中间角色

protocol buffers:

syntax = "proto3";

option go_package = "./;grpcgin";

package grpcgin;


// 微服务

service SayHei {

        //send a msg

        rpc SayHello(HeiRequest) returns(HeiResponse) {};

}


// 请求

message HeiRequest {

        string name = 1;

}


// 响应

message HeiResponse {

        string message = 1;

}


服务端:

package main

import (

    "context"

    pb "course-03/proto/stream/grpcgin"

    "fmt"

    "net"

    "google.golang.org/grpc"

)


const (

    PORT = ":50051"

)


type server struct {

    pb.UnimplementedSayHeiServer

}


func (s *server) SayHello(ctx context.Context, req *pb.HeiRequest) (*pb.HeiResponse, error) {

    return &pb.HeiResponse{Message: "Hei " + req.Name}, nil

}


func main() {

    //监听端口

    lis, err := net.Listen("tcp", PORT)

    if err != nil {

        return

    }

    

    //创建grpc服务

    s := grpc.NewServer()

    

    //注册事件

    pb.RegisterSayHeiServer(s, &server{})

    

    //输出

    fmt.Printf("开启监听: %v\n", "start connection")

    

    //处理链接

    s.Serve(lis)

}


客户端(gin):

package main

import (

    pb "course-03/proto/stream/grpcgin"

    "fmt"

    "log"

    "net/http"

    "github.com/gin-gonic/gin"

    "google.golang.org/grpc"

)


const (

    ADDRESS = "localhost:50051"

)


func main() {

    //grpc拨号

    conn, err := grpc.Dial(ADDRESS, grpc.WithInsecure())

    if err != nil {

        return

    }

    defer conn.Close()

    

    //注册一个客户端

    client := pb.NewSayHeiClient(conn)

    

    //发起http

    r := gin.Default()

    r.GET("/rest/n/:name", func(c *gin.Context) {

        //获取参数

        name := c.Param("name")

        

        //发送参数

        req := &pb.HeiRequest{Name: name}

        res, err := client.SayHello(c, req)

        if err != nil {

            //错误json信息

            c.JSON(http.StatusInternalServerErrorgin.H{

                "error": err.Error(),

            })

            return

        }

        

        //显示json结果

        c.JSON(http.StatusOKgin.H{

            "result": fmt.Sprintf(res.Message),

        })

    })

    

    //启动 http 服务

    if err := r.Run(":8052"); err != nil {

        log.Fatalf("could not run server %v", err)

    }

}


浏览器:

http://localhost:8052/rest/n/abc


十七、http标准库、template标准库、HttpRouter:

教程:https://www.bilibili.com/video/av294842766

笔记:https://golang-tech-stack.com/tutorial/gin


package httptest

import (

    "bytes"

    "encoding/json"

    "fmt"

    "html/template"

    "io"

    "io/ioutil"

    "log"

    "net/http"

    "net/url"

    "os"

    "strings"

    "sync"

    "time"

    "github.com/julienschmidt/httprouter"

)


//1、http测试

func TestHttp() {

    //请求

    url := "http://apis.juhe.cn/simpleWeather/query?city=厦门&key=d61561b84a7dae5e5d4a3559fce677de"

    res, err := http.Get(url)

    if err != nil {

        log.Fatalf("get error:%v", err)

    }

    defer res.Body.Close()

    

    //读取数据

    data, _ := ioutil.ReadAll(res.Body)

    fmt.Printf("data: %v\n", string(data))

}


//2、http测试请求参数

func TestHttpParams() {

    //url

    urlStr := "http://apis.juhe.cn/simpleWeather/query"

    URL, err := url.Parse(urlStr)

    if err != nil {

        return

    }

    

    //参数

    params := url.Values{}

    params.Set("city", "厦门")

    params.Set("key", "d61561b84a7dae5e5d4a3559fce677de")

    

    //如果参数有中文会进行URLEncode

    URL.RawQuery = params.Encode()

    urlPath := URL.String()

    fmt.Printf("urlPath: %v\n", urlPath)

    

    //请求

    res, err := http.Get(urlPath)

    if err != nil {

        log.Fatalf("get error:%v", err)

    }

    defer res.Body.Close()

    

    //读取数据

    data, _ := ioutil.ReadAll(res.Body)

    fmt.Printf("data: %v\n", string(data))

}


//3、http的header头

func TestHttpHeader() {

    //结果结构体

    type Result struct {

        Args    string            `json:"args"`

        Headers map[string]string `json:"headers"`

        Origin  string            `json:"origin"`

        Url     string            `json:"url"`

    }

    

    //url

    url := "http://httpbin.org/get"

    res, err := http.Get(url)

    if err != nil {

        return

    }

    defer res.Body.Close()

    

    //读取数据

    data, _ := ioutil.ReadAll(res.Body)

    fmt.Printf("data: %v\n", string(data))

    

    //解密

    var result Result

    json.Unmarshal(data, &result)

    fmt.Printf("%#v\n", result) //%#v:实例的完整输出,包括它的字段(在程序自动生成 Go 代码时也很有用)

}


//4、http get请求添加header头

func TestHttpAddHeader() {

    //请求

    url := "http://httpbin.org/get"

    req, err := http.NewRequest(http.MethodGet, url, nil)

    if err != nil {

        return

    }

    

    //设置头信息

    req.Header.Add("name", "飓风呀")

    req.Header.Add("age", "666")

    

    //客户端

    client := &http.Client{}

    res, err := client.Do(req)

    if err != nil {

        return

    }

    

    //读取数据

    body, _ := ioutil.ReadAll(res.Body)

    fmt.Printf("body: %v\n", string(body))

}


//5、http post测试请求参数

func TestHttpPost() {

    //参数

    params := url.Values{}

    params.Set("city", "厦门")

    params.Set("key", "d61561b84a7dae5e5d4a3559fce677de")

    

    //url

    url := "http://apis.juhe.cn/simpleWeather/query"

    

    //post

    res, err := http.PostForm(url, params)

    if err != nil {

        log.Fatalf("get error:%v", err)

    }

    defer res.Body.Close()

    

    //读取数据

    data, _ := ioutil.ReadAll(res.Body)

    fmt.Printf("data: %v\n", string(data))

}

    

//6、http post测试请求参数2

func TestHttpPost2() {

    //参数

    params := url.Values{}

    params.Set("name", "飓风呀")

    params.Set("age", "666")

    

    //加密

    reqBody := params.Encode()

    

    //url

    url := "http://httpbin.org/post"

    

    //post

    res, err := http.Post(url, "text/html", strings.NewReader(reqBody))

    if err != nil {

        log.Fatalf("get error:%v", err)

    }

    defer res.Body.Close()

    

    //读取数据

    data, _ := ioutil.ReadAll(res.Body)

    fmt.Printf("data: %v\n", string(data))

}


//7、http 发送json的数据请求

func TestHttpPostJson() {

    //参数

    sdata := make(map[string]interface{})

    sdata["name"] = "飓风呀"

    sdata["age"] = 666

    

    //加密

    bytesData, _ := json.Marshal(sdata)

    

    //url

    url := "http://httpbin.org/post"

    

    //post

    res, err := http.Post(url, "application/json"bytes.NewReader(bytesData))

    if err != nil {

        log.Fatalf("get error:%v", err)

    }

    defer res.Body.Close()

    

    //读取数据

    data, _ := ioutil.ReadAll(res.Body)

    fmt.Printf("data: %v\n", string(data))

}


//8、http 客户端自定义请求

func TestHttpClient() {

    //请求

    url := "http://apis.juhe.cn/simpleWeather/query?city=厦门&key=d61561b84a7dae5e5d4a3559fce677de"

    req, err := http.NewRequest(http.MethodGet, url, nil)

    if err != nil {

        return

    }

    

    //自定义请求头

    req.Header.Add("refer", "http://httpbin.org/get")

    

    //客户端

    client := &http.Client{

        Timeout: time.Second * 5,

    }

    res, err := client.Do(req)

    if err != nil {

        return

    }

    defer res.Body.Close()

    

    //读取数据

    body, _ := ioutil.ReadAll(res.Body)

    fmt.Printf("body: %v\n", string(body))

}


//9、http server

func TestHttpServer() {

    //请求处理函数

    f := func(resp http.ResponseWriter, req *http.Request) {

        io.WriteString(resp, "Hei Wang")

    }

    

    //响应路径,前面有斜杆

    http.HandleFunc("/hello", f)

    

    //设置监听,端口有冒号

    err := http.ListenAndServe(":9999", nil)

    if err != nil {

        log.Fatalf("get error: %v", err)

    }

}

浏览器访问:localhost:9999/hello


//10、http 使用Handle实现并发处理

type countHandler struct {

    mu sync.Mutex

    n  int

}


func (c *countHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

    c.mu.Lock()

    defer c.mu.Unlock()

    c.n++

    fmt.Fprintf(w, "count is %d\n", c.n)

}


func TestHttpHandle() {

    http.Handle("/count", new(countHandler))

    log.Fatal(http.ListenAndServe(":9999", nil))

}

//end http 使用Handle实现并发处理

浏览器访问:localhost:9999/count


//11、template 模板

func TestTemplate() {

    //参数

    data := "飓风呀"

    

    //html 模板

    html := "我是:{{.}}"

    

    //新建模板

    t, err := template.New("test").Parse(html)

    if err != nil {

        panic(err)

    }

    

    // 解析模板

    t.Execute(os.Stdout, data)

}


//12、template 模板结构体

func TestTemplateStruct() {

    type person struct {

        Name string

        Age  int

    }

    

    // html 模板

    html := "我是:{{.Name}},年龄:{{.Age}}"

    t, err := template.New("test").Parse(html)

    if err != nil {

        panic(err)

    }

    

    //解析模板

    t.Execute(os.Stdoutperson{Name: "飓风呀", Age: 666})

}


//13、template html文件

func TestTemplateHtml() {

    //启动server

    s := http.Server{

        Addr: ":9999",

    }

    

    //请求处理函数

    f := func(resp http.ResponseWriter, req *http.Request) {

        t, err := template.ParseFiles("./templates/index.html", "./templates/header.html", "./templates/footer.html")

        if err != nil {

            panic(err)

        }

        s := []string{"aaa", "bbb", "ccc"}

        t.Execute(resp, s)

    }


    //响应路径,前面有斜杆

    http.HandleFunc("/template", f)

    

    //设置监听,端口有冒号

    err := s.ListenAndServe()

    if err != nil {

        log.Fatalf("get error: %v", err)

    }

}

浏览器访问:localhost:9999/template


html模板标签:

微信图片_20220616160450.png

html模板标签定义:

微信图片_20220616160502.png

微信图片_20220616160514.png


//14、HttpRouter:golang HTTP请求路由器,下载包:go get github.com/julienschmidt/httprouter

func UserList(w http.ResponseWriter, r *http.Request, p httprouter.Params) {

    io.WriteString(w, "action:UserList")

}


func AddList(w http.ResponseWriter, r *http.Request, p httprouter.Params) {

    io.WriteString(w, "action:UserList, Get Uid:"+p.ByName("uid"))

}


func AddUser(w http.ResponseWriter, r *http.Request, p httprouter.Params) {

    io.WriteString(w, "action:UserList, Post Uid:"+p.ByName("uid"))

}


func TestHttpRouter() {

    //初始化路由

    r := httprouter.New()

    

    // GET

    r.GET("/user", UserList)

    r.GET("/user/:uid", AddList)

    

    // POST

    r.POST("/adduser/:uid", AddUser)

    

    //启动端口

    err := http.ListenAndServe(":9999", r)

    if err != nil {

        panic(err)

    }

}

//end HttpRouter:golang HTTP请求路由器

浏览器访问:localhost:9999/user、localhost:9999/user/1、localhost:9999/adduser/1


十八、Gin:Gin是一个golang的微框架,基于httprouter,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点。

官网:https://gin-gonic.com/

教程:https://www.bilibili.com/video/BV1SF411z7XW?p=9

笔记:https://golang-tech-stack.com/tutorial/gin/gin-intro

//1、gin 第一个程序

func TestGin() {

    // 初始化gin

    g := gin.Default()

            

           // 路由

    g.GET("/gin", func(c *gin.Context) {

        c.String(http.StatusOK, "Hei Gin\n")

        c.JSON(http.StatusOK, gin.H{

            "result": "Hei Gin",

        })

    })

            

           // 启动端口服务

    g.Run(":9999")

}


//2、 Gin简单登录

func TestLogin() {

    // 初始化gin

    g := gin.Default()

    

    // 加载模板

    g.LoadHTMLGlob("gintest/login/*")

    

    // 路由

    g.GET("/login", func(c *gin.Context) {

        c.HTML(http.StatusOK, "login.html", nil)

    })

    

    // 登录

    g.POST("/login", func(c *gin.Context) {

        // var data = make(map[string]interface{})

        // data["username"] = c.PostForm("username")

        // data["password"] = c.PostForm("password")

        // c.HTML(http.StatusOK, "welcome.html", data)

        c.HTML(http.StatusOK, "welcome.html", gin.H{

            "username": c.PostForm("username"),

            "password": c.PostForm("password"),

        })

    })

    

    // 启动端口服务

    g.Run(":9999")

}


login.html:

<form action="/login" method="post">

            用户名:<input type="text" name="username">

            密码:<input type="password" name="password">

            <button type="submit">提交</button>

</form>


welcome.html:

欢迎你,{{.username}},密码:{{.password}}


//3、 Gin 获取请求参数

func TestGinParams() {

    // 初始化gin

    g := gin.Default()

    

    //get参数:http://localhost:9999?name=xxx&user=xxx

    g.GET("/query", func(c *gin.Context) {

        c.JSON(http.StatusOK, gin.H{

            "name": c.Query("name"),

            "user": c.DefaultQuery("user", "飓风呀"),

        })

    })

    

    // 加载模板

    g.LoadHTMLGlob("gintest/login/*")

    

    //post参数

    g.GET("/postdata", func(c *gin.Context) {

        c.HTML(http.StatusOK, "postdata.html", nil)

    })

    g.POST("/postdata", func(c *gin.Context) {

        c.JSON(http.StatusOK, gin.H{

            "name": c.PostForm("name"),

            "user": c.DefaultPostForm("user", "飓风呀"),

            "page": c.Query("page"),

        })

    })

    

    //restful参数

    g.GET("/restful/:name/:user", func(c *gin.Context) {

        c.JSON(http.StatusOK, gin.H{

            "name":      c.Param("name"),

            "user":      c.Param("user"),

            "name_get":  c.Query("name"),

            "user_post": c.PostForm("user"),

        })

    })


    // 启动端口服务

    g.Run(":9999")

}


//4、 Gin 绑定参数到结构体

func TestGinBindParams() {

    // 结构体

    type RegUser struct {

        Username string   `form:"username" json:"username"`

        Password string   `form:"password" json:"password"`

        Hobby    []string `form:"hobby" json:"hobby"`

        Gender   string   `form:"gender" json:"gender"`

        City     string   `form:"city" json:"city"`

        Type     string   `form:"type" json:"type"`

    }

    

    // 结构体

    type RegUser2 struct {

        Username string `uri:"username" json:"username"`

        Password string `uri:"password" json:"password"`

    }

    

    // 初始化gin

    g := gin.Default()

    

    // 加载模板

    g.LoadHTMLGlob("gintest/login/*")

    

    // 路由

    g.GET("/register", func(c *gin.Context) {

        c.HTML(http.StatusOK, "register.html", nil)

    })

    g.POST("/register", func(c *gin.Context) {

        // user实例

        var user RegUser

        

        // 绑定参数

        c.ShouldBind(&user)

        

        //输出JSON

        c.JSON(http.StatusOK, user)

    })

    

    // Get:http://localhost:9999/reg?username=xxx&password=xxx

    g.GET("/reg", func(c *gin.Context) {

        // user实例

        var user RegUser

        // 绑定参数

        c.ShouldBind(&user)

        

        //输出JSON

        c.JSON(http.StatusOK, user)

    })

    

    // restful:http://localhost:9999/restfulreg/飓风呀/777

    g.GET("/restfulreg/:username/:password", func(c *gin.Context) {

        // user实例

        var user RegUser2

        

        // 绑定参数

        c.ShouldBindUri(&user)

        

        //输出JSON

        c.JSON(http.StatusOK, user)

    })

    

    // 启动端口服务

    g.Run(":9999")

}


//5、 Gin 集成静态文件

func TestGinLoadStatic() {

    // 初始化gin

    g := gin.Default()

    

    // 加载静态文件

    g.Static("bt", "./assets/bootstrap/")

    

    // 加载模板

    g.LoadHTMLGlob("gintest/login/static.html")

    

    // 路由

    g.GET("/static", func(c *gin.Context) {

        c.HTML(http.StatusOK, "static.html", nil)

    })

    

    // 启动端口服务

    g.Run(":9999")

}


//6、 Gin 中间件

func TestGinMiddleware() {

    // 初始化gin

    g := gin.Default() // 默认中间件:Logger(), Recovery()

    // g := gin.New()     // 无中间件

    

    // 中间件

    g.Use(func(c *gin.Context) {

            fmt.Println("中间件1")

            c.String(http.StatusOK, "中间件1\n")

        }, func(c *gin.Context) {

            fmt.Println("中间件2")

            c.String(http.StatusOK, "中间件2\n")

    })

    

    // 路由

    g.GET("/middleware", func(c *gin.Context) {

        c.String(http.StatusOK, "middleware\n")

    })

    

    // 启动端口服务

    g.Run(":9999")

}


//7、 Gin BasicAuth认证中间件

func TestGinBasicAuthMiddleware() {


    // 私密数据

    var secrets = gin.H{

        "foo":    gin.H{"email": "copylian1@aikehou.com", "mobile": "15556525000"},

        "austin": gin.H{"email": "copylian2@aikehou.com", "mobile": "15556525001"},

        "lenna":  gin.H{"email": "copylian3@aikehou.com", "mobile": "15556525002"},

        "manu":   gin.H{"email": "copylian4@aikehou.com", "mobile": "15556525003"},

    }

    

    // 初始化gin

    g := gin.Default() // 默认中间件:Logger(), Recovery()

    // g := gin.New()     // 无中间件

    

    // 路由组使用 gin.BasicAuth 中间件,gin.Accounts 是 map[string]string 的快捷方式

    auth := g.Group("/admin", gin.BasicAuth(gin.Accounts{

        "foo":    "bar",

        "austin": "1234",

        "lenna":  "hello2",

        "manu":   "4321",

    }))

    

    // http://localhost:9999/admin/secrets

    auth.GET("/secrets", func(c *gin.Context) {

        // 获取用户,它是有 BasicAuth 中间件设置的

        user := c.MustGet(gin.AuthUserKey).(string)

        if secret, ok := secrets[user]; ok {

            c.JSON(http.StatusOK, gin.H{

                "user":   user,

                "secret": secret,

            })

        } else {

            c.JSON(http.StatusOK, gin.H{

                "user":   user,

                "secret": "NO SECRET",

            })

        }

    })

    

    // 启动端口服务

    g.Run(":9999")

}


//8、 Gin Cookie

func TestGinCookie() {

    // 初始化gin

    g := gin.Default() // 默认中间件:Logger(), Recovery()

    // g := gin.New()     // 无中间件

    

    // 路由

    g.GET("/cookie", func(c *gin.Context) {

        // cookie 名称

        cookieName := "testcookie"

        

        // 获取cookie

        s, err := c.Cookie(cookieName)

        if err != nil {

            s = "飓风呀"

            // 设置cookie

            c.SetCookie(cookieName, "abc", 3600, "/", "", false, true)

        }

        c.String(http.StatusOK, "测试cookie"+s)

    })

    

    // 启动端口服务

    g.Run(":9999")

}


//9、 Gin Session:go get github.com/gin-contrib/sessions

func TestGinSession() {

    // 初始化gin

    g := gin.Default() // 默认中间件:Logger(), Recovery()

    // g := gin.New()     // 无中间件

    

    // 开启存储方式

    store := cookie.NewStore([]byte("secret"))

    

    // 注册session中间件

    g.Use(sessions.Sessions("testsession", store))

    

    // 路由

    g.GET("/session", func(c *gin.Context) {

        // 启动session

        session := sessions.Default(c)

        var count int

        v := session.Get("count")

        if v == nil {

            count = 0

        } else {

            count = v.(int)

            count++

        }

        session.Set("count", count)

        session.Save()

        c.JSON(200, gin.H{"count": count})

    })

    

    // 启动端口服务

    g.Run(":9999")

}


//10、 Gin 路由组

func TestGinRouterGroup() {

    // 初始化gin

    g := gin.Default() // 默认中间件:Logger(), Recovery()

    // g := gin.New()     // 无中间件

    

    // 路由组

    group := g.Group("/group1")

    {

        group.GET("/a1", func(c *gin.Context) {

            c.String(http.StatusOK, "a1\n")

        })

        group.GET("/b1", func(c *gin.Context) {

            c.String(http.StatusOK, "b1\n")

        })

        group.GET("/c1", func(c *gin.Context) {

            c.String(http.StatusOK, "c1\n")

        })

    }

    

    group2 := g.Group("/group2")

    {

        group2.GET("/a2", func(c *gin.Context) {

            c.String(http.StatusOK, "a2\n")

        })

        group2.GET("/b2", func(c *gin.Context) {

            c.String(http.StatusOK, "b2\n")

        })

        group2.GET("/c2", func(c *gin.Context) {

            c.String(http.StatusOK, "c2\n")

        })

    }

    

    // 启动端口服务

    g.Run(":9999")

}


//11、 Gin 渲染输出

func TestGinOutput() {

    // 初始化gin

    g := gin.Default() // 默认中间件:Logger(), Recovery()

    // g := gin.New()     // 无中间件

    

    // 加载模板

    g.LoadHTMLGlob("gintest/login/*")

    

    // 路由

    g.GET("/output", func(c *gin.Context) {

        

        // 输出字符串

        c.String(http.StatusOK, "输出字符串\n")

        

        // 输出XML

        c.String(http.StatusOK, "\n")

        c.XML(http.StatusOK, gin.H{"count": 1})

        

        // 输出HTML

        c.String(http.StatusOK, "\n")

        c.HTML(http.StatusOK, "test.html", nil)

        

        // 输出JSON

        c.String(http.StatusOK, "\n")

        c.JSON(http.StatusOK, gin.H{"count": 1})

        

        // 输出ProtoBuf

        // c.ProtoBuf(http.StatusOK, gin.H{"count": 1})

    })

    

    // 启动端口服务

    g.Run(":9999")

}


//12、 Gin 文件上传

func TestGinFileUploads() {

    // 初始化gin

    g := gin.Default() // 默认中间件:Logger(), Recovery()

    // g := gin.New()     // 无中间件

    

    // 加载模板

    g.LoadHTMLGlob("gintest/login/*")

    

    // 路由

    g.GET("/uploads", func(c *gin.Context) {

        c.HTML(http.StatusOK, "uploads.html", nil)

    })

    g.POST("/uploads", func(c *gin.Context) {

        // 获取上传文件

        fh, err := c.FormFile("file")

        if err != nil {

            panic(err)

        }

        

        // 上传保存移动文件

        err2 := c.SaveUploadedFile(fh, "uploads/"+fh.Filename)

        if err2 != nil {

            panic(err2)

        }

        c.String(http.StatusOK, fh.Filename)

    })

    

    // 启动端口服务

    g.Run(":9999")

}


url跳转:c.Redirect(302, "/register")

Markdown编辑器:https://pandao.github.io/editor.md/

Blackfriday :是一个用Go实现的Markdown处理器,下载:go get github.com/russross/blackfriday/v2

// Markdown编辑器的 Blackfriday 实现

func TestGinBlackFriday() {

    // 初始化gin

    g := gin.Default() // 默认中间件:Logger(), Recovery()

    // g := gin.New()     // 无中间件

    

    // 加载模板

    g.LoadHTMLGlob("gintest/login/*")

    

    // 路由

    g.GET("/blackfriday", func(c *gin.Context) {

    // 文本内容

    content := "<div><h3>标题</h3><p>内容... &nbsp; 空格</p></div>"

    

    // 装成切片

    b := blackfriday.Run([]byte(content))

    

    // 渲染输出

    c.HTML(http.StatusOK, "blackfriday.html", gin.H{

        "content": template.HTML(b),

        })

    })

    

    // 启动端口服务

    g.Run(":9999")

}


十九、Docker:是一种运行于 Linux 和 Windows 上的软件,用于创建管理编排容器

参考:https://www.copylian.com/life/503.html

微信图片_20230207155852.png

微信图片_20230207160346.png

教程1:https://www.bilibili.com/video/BV1zR4y1t7Wj?p=166

教程2:https://www.bilibili.com/video/BV1S14y1M7bs

笔记:https://golang-tech-stack.com/tutorial/docker

官网:https://www.docker.com/


1、安装:

1-1)、查看系统版本:Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看CentOS的内核版本。

uname -a 

Linux ABC 3.10.0-957.12.1.el7.x86_64 #1 SMP Mon Apr 29 14:59:59 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux


1-2)、查看是否安装了docker

yum list installed | grep docker

containerd.io.x86_64                   1.6.6-3.1.el7                  @docker-ce-stable

docker-ce.x86_64                        3:20.10.17-3.el7             @docker-ce-stable

docker-ce-cli.x86_64                   1:20.10.17-3.el7             @docker-ce-stable

docker-ce-rootless-extras.x86_64      20.10.17-3.el7         @docker-ce-stable

docker-scan-plugin.x86_64             0.17.0-3.el7                @docker-ce-stable


卸载删除docker:删除所有docker列表

yum remove -y docker-ce.x86_64 docker-ce-cli.x86_64  docker-ce-rootless-extras.x86_64 docker-scan-plugin.x86_64 

或者

yum remove docker  docker-common docker-selinux docker-engine


1-3)、安装需要的软件包

yum install -y yum-utils device-mapper-persistent-data lvm2


1-4)、设置Docker Yum源

yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo


1-5)、查看所有仓库中所有docker版本

yum list docker-ce --showduplicates | sort -r


1-6)、安装Docker

yum intall -y docker-ce

如果要安装特定版本:

yum install docker-ce-18.06.1.ce 


1-7)、启动

设置为开机启动systemctl enable docker

启动:systemctl start docker

重启:systemctl restart docker

查看启动状态:systemctl status docker

查看版本:docker version


1-8)、docker 的 hello-world镜像:

拉取镜像:docker pull hello-world

查看镜像:docker images

REPOSITORY    TAG       IMAGE ID          CREATED           SIZE

hello-world      latest     feb5d9fea6a5    8 months ago   13.3kB

运行镜像:docker run feb5d9fea6a5 


2、docker 镜像仓库:

Docker官方镜像:https://hub.docker.com/

DaoCloud镜像:https://hub.daocloud.io/


3、docker 镜像操作:

拉取:docker pull daocloud.io/library/tomcat:8.0.45

查看:docker images

REPOSITORY                            TAG       IMAGE ID          CREATED           SIZE

hello-world                              latest     feb5d9fea6a5     8 months ago   13.3kB

daocloud.io/library/tomcat     8.0.45    fa6ad595ba45    4 years ago       357M


删除:docker rmi feb5d9fea6a5

导出镜像:docker  save -o  保存路径  镜像ID  >>>>>>>  docker save -o /home/tomcat.img fa6ad595ba45    

导入镜像:docker load -i 镜像文件   >>>>>>>  docker load -i tomcat.img

修改镜像名称:docker tag 镜像ID 镜像名称:镜像版本  >>>>>>>  docker tag fa6ad595ba45 tomcat2:v8.01


4、Docker容器:

1)、开启容器

docker run 镜像ID  >>>>>>>  docker run fa6ad595ba45

常用的参数

docker run -d -p 宿主机端口∶容器端口 --name 容器名称 镜像ID  >>>>>>>  docker run -d -p 8080:8080 --name tomcat fa6ad595ba45

    -d∶ 代表后台运行容器

    -p 宿主机端口∶容器端口∶ 为了映射当前Linux的端口和容器的端口

    --name 容器名称∶指定容器的名称

启动之后访问:http://139.196.86.20:8080/

微信图片_20220619174757.png

2)、查看运行的容器docker ps [-qa]

CONTAINER ID   IMAGE                  COMMAND             CREATED              STATUS                      PORTS                                          NAMES

24cba40ee452   fa6ad595ba45   "catalina.sh run"   About a minute ago   Up About a minute   8080/tcp, 0.0.0.0:9999->9999/tcp   tomcat


3)、查看容器日志docker logs -f 容器ID   >>>>>>>  docker logs -f 24cba40ee452

4)、进入容器内部:docker exec -it 容器ID bash   >>>>>>>  docker exec -it 24cba40ee452 bash


5)、删除容器:删除容器前,先要停止容器

停止:docker stop 容器ID

删除:docker rm 容器ID

启动:docker start 容器ID


6)、docker拷贝数据

复制:docker cp 源目录 容器ID:目标目录  >>>>>>>  docker cp demo/ a185ea8f3fed:/usr/local/tomcat/webapps


7)、docker数据卷:volume

创建:docker volume create v01

查看详情:docker volume inspect v02

[

        {

                "CreatedAt": "2022-06-19T23:02:44+08:00",

                "Driver": "local",

                "Labels": {},

                "Mountpoint": "/var/lib/docker/volumes/v02/_data", //默认挂载点存放路径

                "Name": "v02",

                "Options": {},

                "Scope": "local"

        }

]


查看列表:docker volume ls

DRIVER    VOLUME NAME

local         v02


删除:docker volume rm v02

映射:映射数据卷时,如果数据卷不存在,Docker会自动创建,会将容器内自带的文件,存储在默认的存放路径中

    先创建数据卷,再绑定容器:docker run -v 数据卷名称:容器内部路径 镜像ID   >>>>>>>  docker run -d -p 8080:8080 -v v01:/usr/local/tomcat/webapps --name tomcat fa6ad595ba45

    直接将目录路径作为数据卷:docker run -v 路径:容器内部路径 镜像ID   >>>>>>>  docker run -d -p 8080:8080 -v /home/webapps:/usr/local/tomcat/webapps --name tomcat fa6ad595ba45

复制:cp -r demo/ /copylian/www/webapps/


5、Dockerfile:是一个组合镜像命令的文件文档,Docker通过读取Dockerfile来自动生成镜像

Dockerfile基本结构,一般分为:基础镜像,维护者信息,镜像操作指令和容器启动时执行指令,"#"为dockerfile中的注释。在dockerfile文件中我们需要了解几个常用的基本命令,分别是FROM、MAINTAINER、RUN、CMD、EXPOSE、ENV、ADD、COPY、ENTRYPOINT、VOLUME、USER、WORKDIR、ONBUILD等13个指令。


1)、FROM系统基础:FROM是Dockerfile的第一个命令,这个命令关系到生成的镜像是什么版本,什么系统等等,看一下FROM命令涉及到的一下选项:

FROM <image> #系统,可以是centos也可以是ubuntu

FROM <image>:<tag> #系统:版本,默认不加版本就会选择的是最新版本

这里需要注意两个不能同时使用,比如写了两个FROM,一个指定的系统,一个指定的是系统:版本,这样重复了。我们可以根据自己的需求写一个基础的镜像系统以及版本:

FROM centos:7.2.1511

这是使用的Centos系统的7.2.1511版本,后面会把所有的Dockerfile命令总结一遍之后,我们使用我们创建好的Dockerfile命令构建一个自己的镜像。


2MAINTAINER 维护者信息:MAINTAINER是用来描述dockerfile编辑作者的信息,或者内容大致信息,便于读者学习。例如:

MAINTAINER copylian@aikehou.com

MAINTAINER this is docker


3RUN 基础镜像命令:RUN命令是我们在FROM的基础系统中需要做的一下操作,例如创建一个目录,或者下载安装一个工具等等,都需要使用RUN来完成,看下面的格式:

RUN linux命令

我们如果想在镜像中创建一个目录,或者安装一个nginx可以使用RUN,每执行一次就会在docker上新建一层,过多会导致镜像膨胀过大,所以可以使用&&进行连接,不过前提是执行的时候不出错。例如:

FROM centos

RUN mkdir /usr/local/python3

RUN mkdir /usr/local/python3/DevOps

RUN yum -y install gcc

可缩简为

RUN mkdir /usr/local/python3 \ && mkdir /usr/local/python3/DevOps \ && yum -y install gcc

这种创建文件是系统原本就有的命令,yum安装gcc不需要依赖其他的东西,所以这样的执行结果是没有问题的,build的镜像也不会出错。


4ADD/COPY复制:从本地向制作的镜像中复制文件,ADD可以使用url,也可以复制tar文件,并会自动解压到目标路径中,COPY不能访问网络资源,也不会自动解压

ADD <src>... <dest>

ADD ["<src>",... "<dest>"] 用于支持包含空格的路径

COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>

COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]

一般建议使用COPY复制,不过也要根据自己的环境,如果有tar文件可以使用ADD,不需要解压的可以使用COPY。


5CMD命令:CMD命令是根据镜像创建的容器启动时需要执行的命令,这个命令是在容器创建之后。一般用于执行项目,可以写到一个sh文件中,执行sh文件。格式如下:

CMD ["executable","param1","param2"] (执行可执行文件,优先)

CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)

CMD command param1 param2 (执行shell内部命令)

后面直接跟执行的语句,CMD和RUN的区别一个是在容器启动之前一个是容器启动之后如果有多个CDM命令只执行最后一个


6ENTRYPOINTENTRYPOINT是容器启动后执行的命令,不会被docker run提供的参数覆盖,只能有一个ENTRYPOINT。但是如果docker run时使用了--entrypoint选项,此选项的参数可当作要运行的程序覆盖ENTRYPOINT。例如

FROM nginx

ENTRYPOINT ["nginx", "-c"] # 定参

CMD ["/etc/nginx/nginx.conf"] # 变参

如果不传参运行:docker run nginx:test,在容器中会默认会启动 nginx -c /etc/nginx/nginx.conf

如果传参运行:docker run  nginx:test -c /etc/nginx/new.conf,会把CMD中定义的参数覆盖掉,在容器中会执行的会是 nginx -c /etc/nginx/new.conf

ENTRYPOINT搭配CMD使用时一般时使用CMD给ENTRYPOINT传参。


7ENV环境变量ENV是给创建好的容器设置环境变量,设置好的环境变量可以被RUN使用。格式如下:

ENV <key> <value>  #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量

ENV <key>=<value> ...  #可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行


8ARG 环境变量ARG和ENV都是设置环境变量,两者的区别是,ARG是对Dockerfile内有效,也就是在docker build的过程中有效,ENV是针对构建好容器的变量。格式:

ARG <参数名>[=<默认值>]


9VOLUME挂载路径:VOLUME是定义匿名数据卷,在启动容器是忘记挂在数据卷,会自动挂载到匿名卷中,作用是避免数据丢失,或着因容器重启导致数据流失会使容器慢慢变大。格式:

VOLUME ["<路径1>", "<路径2>"...]

VOLUME <路径>

在容器启动时可以使用-v参数修改挂载点。


10EXPOSE 端口:EXPOSE申明的仅仅是端口,在镜像服务中使用随机端口做映射,也就是docker run -P时,会自动随机映射EXPOSE的端口,格式:

EXPOSE <端口1> [<端口2>...]


11)、WORKDIR 工作目录:只工作目录,用于WORKDIR指定的工作目录,会在构建镜像的每一层中都存在,WORKDIR的工作目录必须是创建好的,docker build构建镜像过程中,每一个RUN命令都是新建的一层,只有通过WORKDIR创建的目录才会一直存在。格式:

WORKDIR <工作目录路径>


12)、USER 用户和组:用于指定后续执行命令的用户和用户组,这边只是切换后续命令执行的用户,用户名和用户组必须提前创建好。格式:

USER <用户名>[:<用户组>]


13)、HEALTHCHECK 监控服务状态:HEALTHCHECK用来指定或指定来监控docker容器服务的运行状态,格式:

HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令

HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。


14)、ONBUILD 延迟构建:ONBUILD用来延迟构建命令的执行,简单的说,就是在dockerfile里用ONBUILD指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这是执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。格式:

ONBUILD <其它指令>


15)、LABLE 为镜像添加源数据:用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。格式:

LABLE <key>=<value> ... <key>=<value>  >>>>>>> LABLE version="1.0" desc="这是一个描述" by="飓风呀"


16)、Dockerfile示例

# This my first nginx Dockerfile

# Version 1.0


# Base images 基础镜像

FROM centos


# MAINTAINER 维护者信息

MAINTAINER copylian


# ENV 设置环境变量

ENV PATH /user/lcoal/nginx/sbin:$PATH


# ADD 文件必须放在当前目录下,拷过去会自动解压

ADD nginx-1.8.0.tar.gz /usr/local # nginx

ADD epel-release-latest-7.noarch.rpm /usr/local/ # 企业Linux扩展包


# RUN 执行以下命令

RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm

RUN yum install -y wget lftp gcc gcc-c++ make openssl-devel pcre-devel pcre && yum clean all

RUN useradd -s /sbin/nologin -M www


# WORKDIR 相当于cd

WORKDIR /usr/local/nginx-1.8.0

RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install

RUN echo "daemon off;" >> /etc/nginx.conf


# EXPOSE 端口映射

EXPOSE 80


# CMD 运行以下命令

CMD["nginx"]


构建镜像:

Dockerfile

    FROM daocloud.io/library/tomcat:8.0.45

    ADD demo.zip /usr/local/tomcat/webapps

构建:

    docker bulid -t 镜像名称:tag  当前目录 >>>>>>>>  docker build -t tomcat:5.2.0 .


6、Docker compose管理容器

1)、仓库:https://github.com/docker/compose/

2)、下载对应环境版本文件:https://github.com/docker/compose/releases/download/v2.6.0/docker-compose-linux-x86_64


3)、添加docker compose到环境变量:

mv docker-compose-linux-x86_64 docker-compose # 重命名

mv docker-compose /usr/local/bin/ # 移动到目录

chmod 777 docker-compose # 设置可执行权限

vim /etc/profile # 对应目录添加到path中,export PATH=/usr/local/bin:/usr/sbin


4)、docker-compose.yml文件:

# 对应go版本文件:https://docs.docker.com/compose/compose-file/compose-file-v3/

version: "3.8"


# 服务集合

services:

        tomcat: # 服务名称

                restart: always # 代表只要Docker启动,那么这个容器就跟着一起启动

                image: daocloud.io/library/tomcat:8.0.45 # 镜像

                container_name: tomcat # 容器名称

                ports: 

                        - 8080:8080 # 指定端口号的映射

                environment: 

                        TZ: Asia/Shanghai # 环境变量

                volumes: 

                        - /copylian/www/tomcat_webapps:/usr/local/tomcat/webapps # 映射数据卷

                        - /copylian/www/tomcat_log:/usr/local/tomcat/logs # 映射数据卷


5)、docker-compose 命令:需要在docker-compose.yml文件目录

启动基于docker-compose.yml文件的容器:docker-compose up -d

关闭并删除:docker-compose down

开启、关闭、重启容器:docker-comose start|stop|restart

查看由docker-compose管理的容器:docker-compose ps

查看日志:docker-compose logs -f


6)、使用docker-compose和Dockerfile管理镜像和容器

docker-compose.yml:

# 对应go版本文件:https://docs.docker.com/compose/compose-file/compose-file-v3/

version: "3.8"


# 服务集合

services:

        tomcat# 服务名称

                restartalways # 代表只要Docker启动,那么这个容器就跟着一起启动

                build:                 # 构建自定义镜像 

                        context: ../ # 指定Dockerfile文件所在路径

                        dockerfile: Dockerfile # 指定Dockerfile文件名称

                image: tomcat:8.0.45 # 镜像

                container_name: tomcat # 容器名称

                ports: 

                        - 8080:8080 # 指定端口号的映射

                environment: 

                        TZ: Asia/Shanghai # 环境变量

                volumes: 

                        - /copylian/www/tomcat_webapps:/usr/local/tomcat/webapps # 映射数据卷

                        - /copylian/www/tomcat_log:/usr/local/tomcat/logs # 映射数据卷

Dockerfile

    FROM daocloud.io/library/tomcat:8.0.45

    ADD demo.zip /usr/local/tomcat/webapps


参考资料&教程

1、go get 快速导入GitHub中的包:https://www.jianshu.com/p/16aceb6369b6

2、goland编写go语言导入自定义包出现: package xxx is not in GOROOT (/xxx/xxx) 的解决方案:https://blog.csdn.net/qq_27184497/article/details/122160400

3、Vscode中Golang引入自定义包报错 package xxx is not in GOROOT:https://blog.csdn.net/qq_40209780/article/details/123133467

4、GO111MODULE的设置(及GOPROXY):https://www.cnblogs.com/pu369/p/12068645.html

5、关于go反射中的NumMethod方法的一点发现:https://www.bilibili.com/read/cv8985976/

6、VS Code 安装go插件失败分析及解决方案:https://blog.csdn.net/qq_36564503/article/details/124509832

7、VSCode: Could not import Golang package:https://stackoverflow.com/questions/58518588/vscode-could-not-import-golang-package

8、vscode 远程开发 go 提示 You are outside of a module and outside of $GOPATH/src:https://www.jianshu.com/p/089efae0bdd3


基础教程:

视频1:https://www.bilibili.com/video/BV1gf4y1r79E

笔记1:https://www.yuque.com/aceld/mo95lb/zwukev


视频2:https://www.bilibili.com/video/BV1s341147US

笔记2:https://www.bilibili.com/read/readlist/rl496566


教程:https://www.bilibili.com/video/BV1hv411x7we


进阶教程:

视频:https://www.bilibili.com/video/BV1zR4y1t7Wj

笔记:https://golang-tech-stack.com/tutorial/golang


只袄早~~~
感谢你的支持,我会继续努力!
扫码打赏,感谢您的支持!
golang 笔记 go Google 

文明上网理性发言!