Go--常用工具和常见问题

原创
2018/09/25 17:48
阅读数 182

protobuf安装、使用、可能遇到问题

    1、安装  https://github.com/protocolbuffers/protobuf/releases

    2、解压进入目录进行如下操作

cd protoc-3.6.1-osx-x86_64
cp -r include/ /usr/local/include/
cp -r bin/ /usr/local/bin/

    3、导入以下包

package tools

import (
    _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
    _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
    _ "github.com/golang/protobuf/protoc-gen-go"
)

    4、运行 go mod tidy 解决版本依赖问题

        会在GOPATH下的pkg中发现mod文件夹

    5、安装生成bin文件

$ go install \
    github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \
    github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger \
    github.com/golang/protobuf/protoc-gen-go

    在GOPATH下的bin或GOBIN目录下生成的三个文件如下

    

    6、根据protobuf语法编写.proto文件

your_service.proto:

syntax = "proto3";
 package example;

 import "google/api/annotations.proto";

 message StringMessage {
   string value = 1;
 }

 service YourService {
   rpc Echo(StringMessage) returns (StringMessage) {
     option (google.api.http) = {
       post: "/v1/example/echo"
       body: "*"
     };
   }
 }

    7、使用命令生成grpc和grpc-gateway相关pb文件

        生成grpc命令:

protoc -I/usr/local/include -I. \
  -I$GOPATH/src \
  -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
  --go_out=plugins=grpc:. \
  proto/test.proto

        生成grpc-gateway命令:

protoc -I/usr/local/include -I. \
  -I$GOPATH/src \
  -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
  --grpc-gateway_out=logtostderr=true:. \
  proto/test.proto

    8、可能会遇到的问题

    问题描述:通过go get -u github.com/golang/protobuf/protoc-gen-go 命令安装protoc-gen-go工具时默认会去找最新的版本,假如其他人之前写的代码中是用旧版本 protoc-gen-go 生成的,那么可能你的版本过高,而 grpc 库的版本过低,从而出现如下错误:undefined: grpc.SupportPackageIsVersion6

    解决方案:经过一些网络搜索和尝试,大致有这么两种方案:

    ①grpc lib 升级:升级 grpc 库的版本,并将之前的 pb.go 重新生成一遍。 参考 https://github.com/grpc/grpc-go/issues/3347#issuecomment-580922023

    ②protoc-gen-go 降级:使用如下命令去指定某个 tagged version 进行安装go get -u -v github.com/golang/protobuf/protoc-gen-go@v1.3.0

Go Module

    1、goland配置如下图

        

    2、go mod 使用方法

  • 初始化模块
    go mod init <项目模块名称>

  • 依赖关系处理 ,根据go.mod文件
    go mod tidy

  • 将依赖包复制到项目下的 vendor目录。
    go mod vendor
    如果包被屏蔽(墙),可以使用这个命令,随后使用go build -mod=vendor编译

  • 显示依赖关系
    go list -m all

  • 显示详细依赖关系
    go list -m -json all

  • 下载依赖
    go mod download [path@version]
    [path@version]是非必写的

Go语言编辑器如何自动去除未使用的导入包

    1、在设置中找到Language&Framworks->go->On Save->go imports

    2、避免翻墙,使用gopm工具来获取无法下载的包 go get -v github.com/gpmgo/gopm\

    3、gopm get -g -v -u golang.org/x/tools/cmd/goimports

    4、go install golang.org/x/tools/cmd/goimports

Go语言中如何将中文当一个字符处理

    uft8.RuneCountInString获取字符数量、len获取字节长度、[]byte获得字节

    s := "hello world 世界你好"

    1、获取字符长度

        w1:使用uft8.RuneCountInString(s)

        w2:使用len([]rune(s))

    2、获取每个字符

        w1:

            bytes := []byte(s)

            for len(bytes) > 0 {

                ch, size := utf8.DecodeRune(bytes)  //utf8.DecodeRune(b) 返回b的第一个rune值和它所占用的字节数

                bytes = bytes[size:]

                fmt.Printf("%c", ch)

            }

        w2:

            for i, v := range []rune(s) {

                fmt.Printf("(%d %c)",  i, v)

            }

Go语言之Mysql

package main

import (
   _ "github.com/go-sql-driver/mysql"
   "fmt"
   "database/sql"
)

func main()  {
   //通过mysql驱动连接mysql
   db, err := sql.Open("mysql", "root:111111@tcp(127.0.0.1:3306)/test?charset=utf8")
   if err != nil{
      panic(err)
   }
   //1、插入操作
   stmt1, _ := db.Prepare("insert user_info set username=?,departname=?,createtime=?")
   res1, _ := stmt1.Exec("zhangsan","市场部","2019-2-1")
   id1, _ := res1.LastInsertId()
   fmt.Println(id1)
   //2、更新操作
   stmt2, _ := db.Prepare("update user_info set username=? where id=?")
   res2, _ := stmt2.Exec("lisi",10)
   affected, _ := res2.RowsAffected()
   fmt.Println(affected)
   //3、查询操作
   rows, _ := db.Query("select * from user_info")
   for rows.Next(){
      var id int
      var username string
      var department string
      var createtime string
      err = rows.Scan(&id,&username,&department,&createtime)
      fmt.Println(id,username,department,createtime)
   }
   //4、删除操作
   stmt3, _ := db.Prepare("delete from user_info where id=?")
   info, _ := stmt3.Exec(10)
   fmt.Println(info)
}

Go语言之聊天室

1、服务端代码
/**
 * Description: 聊天室服务端
 */

package main

import (
   "net"
   "fmt"
   "strings"
   "os"
   "log"
)

var onlineConns = make(map[string]net.Conn)
var messageQueue = make(chan string, 1000)
var quitChan = make(chan bool)
var logFile *os.File
var logger *log.Logger

func CheckError(err error)  {
   if err != nil {
      panic(err)
   }
}

const (
   LOG_DIRECTORY = "./test.log"
)

func main()  {
   logFile,err := os.OpenFile(LOG_DIRECTORY, os.O_RDWR | os.O_CREATE, 0)
   if err != nil {
      fmt.Println("log file has created")
      os.Exit(-1)
   }
   defer logFile.Close()

   logger = log.New(logFile, "\r\n", log.Ldate|log.Ltime|log.Llongfile)

   listen_socket, err := net.Listen("tcp","127.0.0.1:8080")
   CheckError(err)
   defer listen_socket.Close()

   fmt.Println("service is waiting...")

   logger.Println("I am writing the logs...")

   go consumerMessage()

   for {
      conn, err := listen_socket.Accept()
      CheckError(err)
      //将conn存储到onlineConns映射表
      addr := fmt.Sprintf("%s",conn.RemoteAddr())
      onlineConns[addr] = conn

      for ip := range onlineConns{
         fmt.Print(ip)
      }
      go ProcessInfo(conn)
   }
}

func consumerMessage()  {
   for {
      select {
      case message := <- messageQueue :
         //对消息进行解析
         doProcessMessage(message)
      case <- quitChan :
         break
      }
   }
}

func doProcessMessage(message string)  {
   contents := strings.Split(message,"#")
   if len(contents) > 1 {
      addr := contents[0]
      addr = strings.Trim(addr," ")
      sendMessage := strings.Join(contents[1:],"#")
      if conn, ok := onlineConns[addr]; ok {
         _, err := conn.Write([]byte(sendMessage))
         if err != nil {
            fmt.Println("online conns send failure")
         }
      }
   }else {
      contents := strings.Split(message,"*")
      if strings.ToUpper(contents[1]) == "LIST" {
         var ips string = ""
         for i := range onlineConns {
            ips = ips + "|" + i
         }
         if conn, ok := onlineConns[contents[0]]; ok {
            _, err := conn.Write([]byte(ips))
            if err != nil {
               fmt.Println("online conns send failure")
            }
         }
      }
   }
}

func ProcessInfo(conn net.Conn)  {
   buf := make([]byte, 1024)
   defer func(conn net.Conn) {
      addr := fmt.Sprintf("%s",conn.RemoteAddr())
      delete(onlineConns,addr)
      conn.Close()
      for i := range onlineConns{
         fmt.Println("now online conns:" + i)
      }
   }(conn)

   for {
      numofBytes, err := conn.Read(buf)
      if err != nil {
         break
      }
      if numofBytes != 0 {
         message := string(buf[:numofBytes])
         messageQueue <- message
         //remoteAddr := conn.RemoteAddr()
         //fmt.Println(remoteAddr)
         //fmt.Printf("Has received this message: %s\n", string(buf[:numofBytes]))
      }
   }
}
2、客户端代码
/**
 * Description: 聊天室客户端
 */

package main

import (
   "net"
   "fmt"
   "bufio"
   "os"
   "strings"
)

func CheckError(err error)  {
   if err != nil {
      panic(err)
   }
}

func messageSend(conn net.Conn)  {
   var input string
   for {
      reader := bufio.NewReader(os.Stdin)
      data, _, _ := reader.ReadLine()
      input = string(data)

      if strings.ToUpper(input) == "EXIT" {
         conn.Close()
         break
      }

      _, err := conn.Write([]byte(input))
      if err != nil {
         conn.Close()
         fmt.Println("client connection failure:" + err.Error())
         break
      }
   }
}

func main()  {
   conn, err := net.Dial("tcp","127.0.0.1:8080")
   CheckError(err)
   defer conn.Close()

   //conn.Write([]byte("hell world"))
   go messageSend(conn)

   buf := make([]byte, 1024)
   for {
      length, err := conn.Read(buf)
      if err != nil {
         fmt.Println("您已经退出")
         os.Exit(0)
      }
      if length != 0 {
         fmt.Println("Has received server message:" + string(buf))
      }
   }
   fmt.Println("Client program end!")
}

变量逃逸-Escape Analysis

    go语言编译机制:自动决定变量分配方式,提高运行效率

    Go语言编程时,Go语言的设计者不希望开发者将精力放在内存应该分配在栈还是堆上的问题。编译器会自动帮助开发者完成这个纠结的选择。但变量逃逸分析也是需要了解的一个编译器技术。

    关于栈堆进行内存分配,Go语言将这个过程整合到编译器,命名为“变量逃逸分析”,这个技术由编译器分析代码的特征和代码生命期,决定应该如何堆还是栈进行内存分配,即使程序员使用Go完成了整个工程后也不会感受到这个过程

    1.逃逸分析

package main

import "fmt"

//逃逸分析
//变量c整形,值通过dummy()的返回值逃出函数
//c变量值被复制并作为函数dummy的返回值返回

func main()  {
   var a int
   void()
   fmt.Println(a,dummy(0))
}

func void()  {

}

func dummy(b int) int {
   var c int
   c = b
   return c
}

go run -gcflags "-m -l" escape1.go 
# command-line-arguments
./escape1.go:8:13: a escapes to heap
./escape1.go:8:21: dummy(0) escapes to heap
./escape1.go:8:13: main ... argument does not escape
0 0

    2、取地址发生逃逸

package main

import "fmt"

type Data struct {

}

func main()  {
   fmt.Println(dummys())
}

func dummys() *Data {
   var c  = Data{}
   return &c
}

go run -gcflags "-m -l" escape2.go 
# command-line-arguments
./escape2.go:15:9: &c escapes to heap
./escape2.go:14:6: moved to heap: c
./escape2.go:10:20: dummys() escapes to heap
./escape2.go:10:13: main ... argument does not escape
&{}

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部