生活札记
golang学习笔记 - 标准包、依赖(四)
十六、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(ADDRESS, grpc.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(ADDRESS, grpc.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.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
//显示json结果
c.JSON(http.StatusOK, gin.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.Stdout, person{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模板标签:
html模板标签定义:
//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://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>内容... 空格</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
教程1:https://www.bilibili.com/video/BV1zR4y1t7Wj?p=166
教程2:https://www.bilibili.com/video/BV1S14y1M7bs
笔记:https://golang-tech-stack.com/tutorial/docker
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/
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命令构建一个自己的镜像。
2)、MAINTAINER 维护者信息:MAINTAINER是用来描述dockerfile编辑作者的信息,或者内容大致信息,便于读者学习。例如:
MAINTAINER copylian@aikehou.com
MAINTAINER this is docker
3)、RUN 基础镜像命令: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的镜像也不会出错。
4)、ADD/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。
5)、CMD命令:CMD命令是根据镜像创建的容器启动时,需要执行的命令,这个命令是在容器创建之后。一般用于执行项目,可以写到一个sh文件中,执行sh文件。格式如下:
CMD ["executable","param1","param2"] (执行可执行文件,优先)
CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
CMD command param1 param2 (执行shell内部命令)
后面直接跟执行的语句,CMD和RUN的区别一个是在容器启动之前一个是容器启动之后。如果有多个CDM命令只执行最后一个。
6)、ENTRYPOINT:ENTRYPOINT是容器启动后执行的命令,不会被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传参。
7)、ENV环境变量:ENV是给创建好的容器设置环境变量,设置好的环境变量可以被RUN使用。格式如下:
ENV <key> <value> #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
ENV <key>=<value> ... #可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
8)、ARG 环境变量:ARG和ENV都是设置环境变量,两者的区别是,ARG是对Dockerfile内有效,也就是在docker build的过程中有效,ENV是针对构建好容器的变量。格式:
ARG <参数名>[=<默认值>]
9)、VOLUME挂载路径:VOLUME是定义匿名数据卷,在启动容器是忘记挂在数据卷,会自动挂载到匿名卷中,作用是避免数据丢失,或着因容器重启导致数据流失会使容器慢慢变大。格式:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
在容器启动时可以使用-v参数修改挂载点。
10)、EXPOSE 端口: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: # 服务名称
restart: always # 代表只要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
文明上网理性发言!
已完结!