GO语言学习记录 函数/报错/相关 & 性能调优 & 编码规范治理

1.常用

//获取事件类型
 fmt.Println("a type by reflect: ", reflect.TypeOf(a))
//获取当下时间
 time.Now().Format("2006-01-02 15:04:05")
 //睡眠
 time.Sleep(2 *time.Second)
 //遍历目录
 var files []string
 err := filepath.Walk(monitorPath, func(path string, info os.FileInfo, err error) error {
	files = append(files, path)
	return nil
})

2.函数定义/常用函数

func 函数名(形式参数列表)(返回值列表){
    函数体
}
//检查字符串是否存在
//使用Contains()函数
res1 := strings.Contains(str1, "lalala")

go结构体设置默认值

func NewXXXEntry() *XXXEntry {
	return &XXXEntry{
		Debug: false, //默认值
	}
}

syncMap的使用

var eventMap sync.Map
//加入
eventMap.Store(event.Name, event)
//遍历
eventMap.Range(func(key, value interface{}) bool {
	event := value.(类型)
	eventMap.Delete(key)
	return true
})
  1. 文件类
1 读取文件
f, err := os.Open(filename)
if err != nil {
	log.Fatal(err)
}
fi, err := ioutil.ReadAll(f)
fmt.Println(string(fi))
if err != nil {
	log.Fatal(err)
}

2 判断所给路径是否为文件夹  
func IsDir(path string) bool {  
    s, err := os.Stat(path)  
    if err != nil {  
        return false  
    }  
    return s.IsDir()  
}  
  
3 判断所给路径是否为文件  
func IsFile(path string) bool {  
    return !IsDir(path)  
}

4.流式读取文件
func readFileContentByFlow(fileName string) bool {
	txtFile, err := os.OpenFile(fileName, os.O_RDONLY, 0777)
	if err != nil {
		util.HandleErr(err)
	}
	defer txtFile.Close()
	bufReader := bufio.NewReader(txtFile)
	for {
		data, _, err := bufReader.ReadLine() // 读一行日志
		if err == io.EOF {                   // 如果列表读完了,退出
			return
		}
	}
}
  1. 分隔符切分字符串, 遍历数组打印
//
arr := strings.Split(s,"\n")
for index, value := range arr{
	fmt.Println(index, value)
}

//切分字符串,以任意空格切分
arr:=strings.Fields(s)
  1. 结构体与json转换
var inf Information
inf.Name="Alice"
inf.Addr="Green Street"
data, err:=json.Marshal(inf)
if err!=nil{
	panic(err)
}
log.Println(string(data))
  1. string与int转换
int, err := strconv.Atoi(string)
int64, err := strconv.ParseInt(string, 10, 64)

7.进程相关

process模块
import "github.com/shirou/gopsutil/process"

process模块的NewProcess会返回一个持有指定PID的Process对象,方法会检查PID是否存在,如果不存在会返回错误,通过Process对象上定义的其他方法我们可以获取关于进程的各种信息。
p, _ := process.NewProcess(int32(os.Getpid()))

// 获取进程占用内存的比例
mp, _ := p.MemoryPercent()
  1. 读取linux控制台输出
func GetCommandLinuxCon(commandLinux string) []byte {
	//需要执行命令: commandLinux
	cmd := exec.Command("/bin/bash", "-c", commandLinux)
	// 获取输入
	output, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Println("无法获取命令的标准输出管道", err.Error())
		return nil
	}
	// 执行Linux命令
	if err := cmd.Start(); err != nil {
		fmt.Println("Linux命令执行失败,请检查命令输入是否有误", err.Error())
		return nil
	}
	// 读取输出
	bytes, err := ioutil.ReadAll(output)
	if err != nil {
		fmt.Println("打印异常,请检查")
		return nil
	}
	if err := cmd.Wait(); err != nil {
		fmt.Println("Wait", err.Error())
		return nil
	}
	return bytes
}
  1. 列表的使用
package main

import (
	"container/list"
	"fmt"
)

func main() {

	var siteList = list.New()

	siteList.PushBack("https://qiucode.cn")

	for i := siteList.Front(); i != nil; i = i.Next() {
		fmt.Println(i.Value)
	}

}


  1. 生产者消费者
package main
import (
        "fmt"
        "math/rand"
        "time"
)

// 数据生产者
func producer(header string, channel chan<- string) {
     // 无限循环, 不停地生产数据
     for {
            // 将随机数和字符串格式化为字符串发送给通道
            channel <- fmt.Sprintf("%s: %v", header, rand.Int31())
            // 等待1秒
            time.Sleep(time.Second)
        }
}
// 数据消费者
func customer(channel <-chan string) {
     // 不停地获取数据
     for {
            // 从通道中取出数据, 此处会阻塞直到信道中返回数据
            message := <-channel
            // 打印数据
            fmt.Println(message)
        }
}
func main() {
    // 创建一个字符串类型的通道
    channel := make(chan string)
    // 创建producer()函数的并发goroutine
    go producer("cat", channel)
    go producer("dog", channel)
    // 数据消费函数
    customer(channel)
}

问题汇总
1.报错信息:

exec: "gcc": executable file not found in %PATH%

解决方案: 安装GCC环境,下载压缩包 解压 配置环境变量

2.报错信息:

Exception in thread “main“ java.lang.ClassNotFoundException: org.sqlite.JDBC 

解决方案:要下载数据库的支持文件。

3.git push origin报错信息:

Unable to negotiate with 47.96.92.201 port 29418: no matching host key type found. Their offer: ssh-rsa,ssh-dss
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

新的ssh客户端不支持ssh-rsa算法,要修改本地配置重新使用ssh-rsa算法。
解决方案:
在用户.ssh文件夹中添加config文件
config文件代码:

Host *
HostkeyAlgorithms +ssh-rsa
PubkeyAcceptedKeyTypes +ssh-rsa

CGO_ENABLED是用来控制golang 编译期间是否支持调用 cgo 命令的开关,其值为1或0,默认情况下值为1,可以用 go env 查看默认值。
当CGO_ENABLED=1,进行编译时会将文件中引用libc的库(比如常用的net包),以动态链接的方式生成目标文件。 当CGO_ENABLED=0,进行编译时则会把在目标文件中未定义的符号(外部函数)一起链接到可执行文件中。

  1. slice元素删除时,由于下标越界会报错“slice bounds out of range"

安装go流程
1.需要先删除旧版本的go环境,忘记安装路径的可以通过环境变量查看

echo $GOROOT
通常是在/usr/local/go/目录下,删除
rm -rf $GOROOT
2.下载新版的go,这里的version是1.18.3
wget https://dl.google.com/go/go1.18.3.linux-amd64.tar.gz
或者到 Golang官网 手动下载压缩包
3.解压到usr/local下(官方推荐)
tar -C /usr/local/ -zxvf go1.18.3.linux-amd64.tar.gz
4.修改配置文件(系统配置为/etc/profile,用户配置为~/.profile),这里就修改系统配置
在文件最后加上两行(如果有旧版本的go配置就不用加,或者要修改路径)

export GOROOT=/usr/local/go
export PATH= P A T H : PATH: PATH:GOROOT/bin

或者

sed -i '$aexport GOROOT=/usr/local/go\nexport PATH=$PATH:$GOROOT/bin' /etc/profile

5.执行使配置文件生效

source /etc/profile

6.查看go版本

go version

go相关知识
go.sum工作机制
为了确保一致性构建,Go引入了go.mod文件来标记每个依赖包的版本,在构建过程中go命令会下载go.mod中的依赖包,下载的依赖包会缓存在本地,以便下次构建。 考虑到下载的依赖包有可能是被黑客恶意篡改的,以及缓存在本地的依赖包也有被篡改的可能,单单一个go.mod文件并不能保证一致性构建。

为了解决Go module的这一安全隐患,Go开发团队在引入go.mod的同时也引入了go.sum文件,用于记录每个依赖包的哈希值,在构建时,如果本地的依赖包hash值与go.sum文件中记录得不一致,则会拒绝构建。

go程序性能调优
pprof是GoLang程序性能分析工具,prof是profile(画像)的缩写
第一步 主程序启动协程监听

_"net/http/pprof"
go func() {
	http.ListenAndServe("0.0.0.0:8080", nil)
}()

第二步 生成相应文件

go tool pprof http://localhost:8080/debug/pprof/profile
go tool pprof http://localhost:6060/debug/pprof/heap     内存

第三步 通过命令查看

top
list

第四步 定位代码位置

traces

常用优化:
手动回收 垃圾内存:

runtime.GC()

释放资源避免泄露
1 获取http响应

resp, err := http.Get(url)
if resp != nil {
    defer resp.Body.Close()
}
if err != nil {
    return "", err
}

2 Sql rows

db, err := sql.Open("postgres", dataSourceName)
if err != nil {
    return err
}
rows, err := db.Query("SELECT * FROM MYTABLE")
if err != nil {
    return err
}
defer rows.Close()

3 文件打开

f, err := os.Open("events.log")
if err != nil {
    return err
}
defer f.Close()

4 压缩的写入和读取

var b bytes.Buffer 
w := gzip.NewWriter(&b)
defer w.Close()

var b bytes.Buffer
r, err := gzip.NewReader(&b)
if err != nil {
    return nil, err
}
defer r.Close()

在一个程序中,操作系统会给程序分配有限的文件描述符(File Descriptor,简称 fd),它们可以理解为打开的文件的标识符。当程序打开一个文件时,会占用一个 fd,而当程序关闭文件时,该 fd 会被释放。
检查系统对单个进程文件句柄的限制

ulimit -n
cat /proc/pid/limits

进程使用了多少文件句柄

lsof -p pid | wc -l

查看当前操作系统已经打开的文件总量

cat /proc/sys/fs/file-nr
第一个值是已开启的,第二个值是分配但未使用,第三个值是总限制数

连接池中的连接有没有正确关闭

cat /proc/pid/net/sockstat

程序内存上限超出后被kill掉
Linux 内核有个机制叫OOM killer(Out Of Memory killer),该机制会监控那些占用内存过大,尤其是瞬间占用内存很快的进程,然后防止内存耗尽而自动把该进程杀掉。

编码规范治理
1 Subprocess launched with variable

2 Potential file inclusion via variabl 解决方案:

f,err := os.Open(filepath.Clean(fname))

3 expect file permissions to be 0600 or less

os.OpenFile(logName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0600|0064)

deferring unsafe method “Close” on type “*os.File”
4 SQL query construction using format string
解决办法:把大写的WHERE、INSERT INTO等(sql语句关键字:SELECT FROM、 WHERE、 INSERT INTO、 UPDATE、 DELETE FROM)改成小写where、insert into
5 Audit use of command execution
解决办法:将后面的参数使用append([]string{}, args…)

你可能感兴趣的:(go,学习,ssh,git,go)