-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearchindex.json
1 lines (1 loc) · 75.1 KB
/
searchindex.json
1
{"categories":[{"title":"中间件","uri":"https://neilliu9891.github.io/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"},{"title":"技术杂文","uri":"https://neilliu9891.github.io/categories/%E6%8A%80%E6%9C%AF%E6%9D%82%E6%96%87/"},{"title":"技术杂谈","uri":"https://neilliu9891.github.io/categories/%E6%8A%80%E6%9C%AF%E6%9D%82%E8%B0%88/"},{"title":"摄影","uri":"https://neilliu9891.github.io/categories/%E6%91%84%E5%BD%B1/"},{"title":"效率","uri":"https://neilliu9891.github.io/categories/%E6%95%88%E7%8E%87/"},{"title":"效率工具","uri":"https://neilliu9891.github.io/categories/%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7/"},{"title":"编程语言","uri":"https://neilliu9891.github.io/categories/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"},{"title":"链接","uri":"https://neilliu9891.github.io/categories/%E9%93%BE%E6%8E%A5/"},{"title":"随感","uri":"https://neilliu9891.github.io/categories/%E9%9A%8F%E6%84%9F/"}],"posts":[{"content":"go-ovn源码阅读思考 最近在学习ovn相关的内容,需要通过go-ovn库实现向ovn写入信息。 go-ovn的源码实现大致实现功能简单梳理一下: go-ovn 代码的核心思想就是封装了libovsdb库,将rpc接口修改为了api接口,定义了marshal unmarshal的转换。而libovsdb则基于RFC7047协议实现了RPC接口的基本功能,包括双方通信的方法。\n底层思想:\n 需要理解ovsdb manager protocol协议的内容,即协议中定义了操作的方法包括update、notify等等。 需要理解ovsdb表的内容便于理解json是如何转换的,对应的转换名称是什么。 学到内容: json-rpc json-rpc通信(来自百度百科):\n https://baike.baidu.com/item/json%20rpc/13675431?fr=aladdin\n rpc例子:\n https://github.com/cenkalti/rpc2\nhttps://github.com/neilliu9891/examples/tree/main/rpc-demo\n interface实现小技巧 使用_ 变量提前暴露struct是否完全实现interface所定义的函数. 例如当我们的接口有很多的定义方法时,如果不采用强制暴露的方式,很难在第一时间发现错误。 代码中的var _ Client = \u0026amp;ovndb{}, 目的是校验ovndb struct是否全部实现了Client接口的所有方法,否则编译报错。 如何保证struct不需要实现全部的interface方法,同样能够转换成Interface类型呢?将interface作为struct的匿名变量 type TestI1 interface { Test1() error } type TestS1 struct { TestI1 } var t1 TestI1 = \u0026amp;TestS1{} func main() { fmt.Println(\u0026quot;vim-go\u0026quot;) fmt.Printf(\u0026quot;%v\\n\u0026quot;, t1) } golang 中function的定义 经常看到函数的返回值被定义了名称,此时可以直接使用此名称而不用在函数内部定义 func main() { r1, r2 := NameReturn() fmt.Printf(\u0026quot;%s, %d\\n\u0026quot;, r1, r2) } func NameReturn() (r1 string, r2 int) { r1 = \u0026quot;r1\u0026quot; r2 = 2 return r1, r2 } https://www.jianshu.com/p/a5bc8add7c6e, struct中嵌入结构体的解答\n 函数定义应该知道的几个内容 函数无须前置声明 不支持命名嵌套定义,支持匿名嵌套 函数只能判断是否为nil,不支持其它比较操作 支持多返回值 支持命名返回值 支持返回局部变量指针 支持匿名函数和闭包 ","id":0,"section":"posts","summary":"go-ovn源码阅读思考 最近在学习ovn相关的内容,需要通过go-ovn库实现向ovn写入信息。 go-ovn的源码实现大致实现功能简单梳理一","tags":["ovn"],"title":"Go Ovn源码阅读思考","uri":"https://neilliu9891.github.io/2021/01/go-ovn%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E6%80%9D%E8%80%83/","year":"2021"},{"content":" https://zhuanlan.zhihu.com/p/147768910\n ","id":1,"section":"posts","summary":"https://zhuanlan.zhihu.com/p/147768910","tags":["光"],"title":"光的六要素","uri":"https://neilliu9891.github.io/2020/12/light/","year":"2020"},{"content":"引用:\u0026gt; https://blog.csdn.net/ruanhao1203/article/details/89705602\nPublish/Subscribe 发布订阅是消息中间件的最基本功能,也是相对传统 RPC 通信而言。\nMessage Priority 规范中描述的优先级是指在一个消息队列中,每条消息都有不同的优先级,一般用整数来描述,优先级高的消息先投递,如果消息完全在一个内存队列中,那么在投递前可以按照优先级排序,令优先级高的先投递。\n对于优先级问题,可以归纳为 2 类:\n只要达到优先级目的即可,不是严格意义上的优先级,通常将优先级划分为高、中、低,或者再多几个级别。每个优先级可以用不同的 topic 表示,发消息时,指定不同的 topic 来表示优先级,这种方法可以解决绝大部分的优先级问题,但是对业务的优先级精确性做了妥协。 严格的优先级,优先级用整数表示,例如 0 ~ 65535,这种优先级问题一般使用不同 topic 解决就非常不合适。如果要让 MQ 解决此问题,会对 MQ 的性能造成非常大的影响。这里要确保一点,业务上是否需要这种严格的优先级,如果将优先级压缩成几个,对业务的影响有多大?\nMessage Order 消息有序指的是一类消息消费时,能按照发送的顺序来消费。例如:一个订单产生了 3 条消息,分别是订单创建、订单付款、订单完成。消费时,要按照这个顺序消费才能有意义。但是同时订单之间是可以并行消费的。\nMessage Filter Broker 端消息过滤\n在 Broker 中,按照 Consumer 的要求做过滤,优点是减少了对于 Consumer 无用消息的网络传输。缺点是增加了 Broker 的负担,实现相对复杂。\n淘宝 Notify 支持多种过滤方式,包含直接按照消息类型过滤,灵活的语法表达式过滤,几乎可以满足最苛刻的过滤需求。 淘宝 RocketMQ 支持按照简单的 Message Tag 过滤,也支持 Message Header、Body 进行过滤。 CORBA Notification 规范中也支持灵活的语法表达式过滤。 Consumer 端消息过滤\n这种过滤方式可由应用完全自定义实现,但是缺点是很多无用消息要传输到 Consumer 端。\nMessage Persistence 消息中间件通常采用的几种持久化方式:\n 持久化到数据库 持久化到 KV 存储 文件记录形式持久化 对内存数据做一个持久化镜像 (1)、(2)、(3)三种持久化方式都具有将内存队列 Buffer 进行扩展的能力,(4)只是一个内存的镜像,作用是当 Broker 挂掉重启后仍能将之前内存的数据恢复出来。 JMS 与 CORBA Notification 规范没有明确说明如何持久化,但是持久化部分的性能直接决定了整个消息中间件的性能。 Message Reliablity 影响消息可靠性的几种情况:\n Broker 正常关闭 Broker 异常 Crash OS Crash 机器掉电,但是能立即恢复供电情况 机器无法开机(可能是 CPU、主板、内存等关键设备损坏) 磁盘设备损坏 (1)、(2)、(3)、(4)四种情况都属于硬件资源可立即恢复情况。 (5)、(6)属于单点故障,且无法恢复,一旦发生,在此单点上的消息全部丢失。\nLow Latency Messaging 在消息不堆积情况下,消息到达 Broker 后,能立刻到达 Consumer。\nAt least Once 是指每个消息必须投递一次。\nExactly Only Once 精确的 发送消息阶段,不允许发送重复的消息。 消费消息阶段,不允许消费重复的消息。 只有以上两个条件都满足情况下,才能认为消息是“Exactly Only Once”,而要实现以上两点,在分布式系统环境下,不可避免要产生巨大的开销。\nBroker 的 Buffer 满了怎么办? Broker 的 Buffer 通常指的是 Broker 中一个队列的内存 Buffer 大小,这类 Buffer 通常大小有限,如果 Buffer 满了以后怎么办? 下面是 CORBA Notification 规范中处理方式:\nRejectNewEvents 拒绝新来的消息,向 Producer 返回 RejectNewEvents 错误码 按照特定策略丢弃已有消息\n AnyOrder - Any event may be discarded on overflow. This is the default setting for this property. FifoOrder - The first event received will be the first discarded. LifoOrder - The last event received will be the first discarded. PriorityOrder - Events should be discarded in priority order, such that lower priority events will be discarded before higher priority events. DeadlineOrder - Events should be discarded in the order of shortest expiry deadline first. 回溯消费 回溯消费是指 Consumer 已经消费成功的消息,由于业务上需求需要重新消费,要支持此功能,Broker 在向 Consumer 投递成功消息后,消息仍然需要保留。并且重新消费一般是按照时间维度,例如由于 Consumer 系统故障,恢复后需要重新消费 1 小时前的数据,那么 Broker 要提供一种机制,可以按照时间维度来回退消费进度。\n消息堆积 消息中间件的主要功能是异步解耦,还有个重要功能是挡住前端的数据洪峰,保证后端系统的稳定性,这就要求消息中间件具有一定的消息堆积能力,消息堆积分以下两种情况:\n消息堆积在内存 Buffer,一旦超过内存 Buffer,可以根据一定的丢弃策略来丢弃消息,如 CORBA Notification 规范中描述。适合能容忍丢弃消息的业务,这种情况消息的堆积能力主要在于内存 Buffer 大小,而且消息堆积后,性能下降不会太大,因为内存中数据多少对于对外提供的访问能力影响有限。 消息堆积到持久化存储系统中,例如 DB、KV 存储、文件记录形式。当消息不能在内存 Cache 命中时,要不可避免的访问磁盘,会产生大量读 IO,读 IO 的吞吐量直接决定了消息堆积后的访问能力。 评估消息堆积能力主要有以下四点:\n消息能堆积多少条,多少字节?即消息的堆积容量。 消息堆积后,发消息的吞吐量大小,是否会受堆积影响? 消息堆积后,正常消费的 Consumer 是否会受影响? 消息堆积后,访问堆积在磁盘的消息时,吞吐量有多大?\n分布式事务 已知的几个分布式事务规范,如 XA、JTA 等。其中 XA 规范被各大数据库厂商广泛支持,如 Oracle、MySQL 等。\n分布式事务涉及到两阶段提交问题,在数据存储方面的方面必然需要 KV 存储的支持,因为第二阶段的提交回滚需要修改消息状态,一定涉及到根据 Key 去查找 Message 的动作。\n定时消息 定时消息是指消息发到 Broker 后,不能立刻被 Consumer 消费,要到特定的时间点或者等待特定的时间后才能被消费。 如果要支持任意的时间精度,在 Broker 层面,必须要做消息排序,如果再涉及到持久化,那么消息排序要不可避免的产生巨大性能开销。\n消息重试 Consumer 消费消息失败后,要提供一种重试机制,令消息再消费一次。Consumer 消费消息失败通常可以认为有以下几种情况:\n由于消息本身的原因,例如反序列化失败,消息数据本身无法处理(例如话费充值,当前的手机号被注销,无法充值)等。这种错误通常需要跳过这条消息,再消费其他消息,而这条失败的消息即使立刻重试消费,99% 也不成功,所以最好提供一种定时重试机制,即过 10s 秒再重试。 由于依赖的下游应用服务不可用,例如 db 连接不可用,外系统网络不可达等。遇到这种错误,即使跳过当前失败的消息,消费其他消息同样也会报错。这种情况建议应用 sleep 30s,再消费下一条消息,这样可以减轻 Broker 重试消息的压力。\n","id":2,"section":"posts","summary":"引用:\u0026gt; https://blog.csdn.net/ruanhao1203/article/details/89705602 Publish/Subscribe 发布订阅是消息中间件的最基本功能,也是相对传统 RPC 通信而言。 Message Priority 规范中描述的优先级是指在一个消息队列中,每条消息都有不同的优","tags":["消息中间件"],"title":"消息中间件解决的问题","uri":"https://neilliu9891.github.io/2020/12/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6%E8%A7%A3%E5%86%B3%E7%9A%84%E9%97%AE%E9%A2%98/","year":"2020"},{"content":"selenium + golang 测试 https://bbs.huaweicloud.com/blogs/172024\n ","id":3,"section":"posts","summary":"selenium + golang 测试 https://bbs.huaweicloud.com/blogs/172024","tags":["链接"],"title":"Reading List","uri":"https://neilliu9891.github.io/2020/12/readinglist/","year":"2020"},{"content":"Golang 记一次log日志不能输出的问题 由于线上运行程序过程中出现了log日志打印不出来的情况,分析代码模拟测试复现问题。由于没有锁住log变量的问题\n目录结构如下\n. ├── ccc │ └── ccc.go ├── go.mod ├── go.sum ├── hhh │ └── hhh.go ├── log │ └── log.go ├── main.go └── network-agent ├── network-agent.log -\u0026gt; network-agent.log.2020-11-06 └── network-agent.log.2020-11-06 4 directories, 8 files ccc.go\npackage ccc import ( \u0026quot;demo/log\u0026quot; \u0026quot;sync\u0026quot; ) var logger = log.GetNetworkLog(\u0026quot;ccc\u0026quot;) //var logger = capnslog.NewPackageLogger(\u0026quot;network\u0026quot;, \u0026quot;ccc\u0026quot;) //var logger = log.GetNetworkLog() func PrintI(i int, wg *sync.WaitGroup) { defer wg.Done() //fmt.Printf(\u0026quot;%p, %p \\n\u0026quot;, \u0026amp;logger, \u0026amp;logger.Mutex) logger.Infof(\u0026quot;INFO :CCC\u0026quot;, i) } func PrintE(i int, wg *sync.WaitGroup) { defer wg.Done() //fmt.Printf(\u0026quot;%p, %p \\n\u0026quot;, \u0026amp;logger, \u0026amp;logger.Mutex) logger.Errorf(\u0026quot;ERROR :CCC\u0026quot;, i) } func PrintII(i int) { //fmt.Printf(\u0026quot;%p, %p \\n\u0026quot;, \u0026amp;logger, \u0026amp;logger.Mutex) logger.Infof(\u0026quot;INFO :CCC\u0026quot;, i) } func PrintEE(i int) { //fmt.Printf(\u0026quot;%p, %p \\n\u0026quot;, \u0026amp;logger, \u0026amp;logger.Mutex) logger.Errorf(\u0026quot;ERROR :CCC\u0026quot;, i) } hhh.go\npackage hhh import ( \u0026quot;demo/log\u0026quot; \u0026quot;sync\u0026quot; ) var logger = log.GetNetworkLog(\u0026quot;hhh\u0026quot;) //var logger = capnslog.NewPackageLogger(\u0026quot;network\u0026quot;, \u0026quot;hhh\u0026quot;) //var logger = log.GetNetworkLog() func PrintI(i int, wg *sync.WaitGroup) { defer wg.Done() //fmt.Printf(\u0026quot;%p, %p \\n\u0026quot;, \u0026amp;logger, \u0026amp;logger.Mutex) logger.Infof(\u0026quot;INFO :HHH\u0026quot;, i) } func PrintE(i int, wg *sync.WaitGroup) { defer wg.Done() //fmt.Printf(\u0026quot;%p, %p \\n\u0026quot;, \u0026amp;logger, \u0026amp;logger.Mutex) logger.Errorf(\u0026quot;ERROR :HHH\u0026quot;, i) } func PrintII(i int) { //fmt.Printf(\u0026quot;%p, %p \\n\u0026quot;, \u0026amp;logger, \u0026amp;logger.Mutex) logger.Infof(\u0026quot;INFO :HHH\u0026quot;, i) } func PrintEE(i int) { //fmt.Printf(\u0026quot;%p, %p \\n\u0026quot;, \u0026amp;logger, \u0026amp;logger.Mutex) logger.Errorf(\u0026quot;ERROR :HHH\u0026quot;, i) } log.go\npackage log import ( \u0026quot;fmt\u0026quot; \u0026quot;io\u0026quot; \u0026quot;os\u0026quot; \u0026quot;path/filepath\u0026quot; \u0026quot;sync\u0026quot; \u0026quot;time\u0026quot; \u0026quot;github.com/coreos/pkg/capnslog\u0026quot; rotatelogs \u0026quot;github.com/lestrrat-go/file-rotatelogs\u0026quot; ) type NetworkLog struct { sync.Mutex pkg string format capnslog.Formatter } var networkLog = new(NetworkLog) func init() { SetupLogging() } type stop struct { error } func Retry(attempts int, sleep time.Duration, fn func() error) error { if err := fn(); err != nil { if e, ok := err.(stop); ok { return e.error } if attempts--; attempts \u0026gt; 0 { fmt.Printf(\u0026quot;retry func error: %s. attempts #%d after %s.\u0026quot;, err.Error(), attempts, sleep) time.Sleep(sleep) return Retry(attempts, sleep, fn) } return err } return nil } func IsPathExist(path string) bool { _, err := os.Stat(path) if err == nil { return true } if os.IsNotExist(err) { return false } return false } func EnsureTree(path string) error { err := Retry(10, 2*time.Second, func() error { if !IsPathExist(path) { err := os.MkdirAll(path, os.ModePerm) if err != nil { return err } return nil } return nil }) return err } func GetWriter(filename string) io.Writer { writer, err := rotatelogs.New( filename+\u0026quot;.%Y-%m-%d\u0026quot;, rotatelogs.WithLinkName(filename), // 生成软链,指向最新日志文 rotatelogs.WithMaxAge(365*24*time.Hour), // 文件最大保存时间 rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔 ) if err != nil { //ulog.Fatalf(\u0026quot;config local file system logger error. %+v\u0026quot;, errors.WithStack(err)) fmt.Println(\u0026quot;config local file system logger error\u0026quot;) } return writer } func SetupLogging() { logfile := \u0026quot;/home/neil/go/src/demo/network-agent/network-agent.log\u0026quot; dirname := filepath.Dir(logfile) EnsureTree(dirname) //capnslog.SetFormatter(capnslog.NewPrettyFormatter(writer, true)) networkLog.format = capnslog.NewPrettyFormatter(GetWriter(logfile), true) //capnslog.SetFormatter(capnslog.NewPrettyFormatter(GetWriter(logfile), true)) } func (log *NetworkLog) Infof(format string, args ...interface{}) { fmt.Println(\u0026quot;begin lock\u0026quot;) log.Lock() fmt.Println(\u0026quot;locked\u0026quot;) defer func() { fmt.Println(\u0026quot;begin unlock\u0026quot;) log.Unlock() fmt.Println(\u0026quot;unlocked\u0026quot;) }() if log.format != nil { log.format.Format(log.pkg, capnslog.INFO, 2, fmt.Sprintf(format, args...)) } //time.Sleep(time.Duration(2) * time.Second) } func (log *NetworkLog) Errorf(format string, args ...interface{}) { fmt.Println(\u0026quot;begin lock\u0026quot;) log.Lock() fmt.Println(\u0026quot;locked\u0026quot;) defer func() { fmt.Println(\u0026quot;begin unlock\u0026quot;) log.Unlock() fmt.Println(\u0026quot;unlocked\u0026quot;) }() if log.format != nil { log.format.Format(log.pkg, capnslog.ERROR, 2, fmt.Sprintf(format, args...)) } //time.Sleep(time.Duration(2) * time.Second) } func (log *NetworkLog) Fatalf(format string, args ...interface{}) { fmt.Println(fmt.Sprintf(format, args...)) } // 正确 func GetNetworkLog(pkg string) *NetworkLog { networkLog.pkg = pkg return networkLog } // 错误 //func GetNetworkLog() NetworkLog { //return *networkLog //} main.go\npackage main import ( \u0026quot;demo/ccc\u0026quot; \u0026quot;demo/hhh\u0026quot; \u0026quot;fmt\u0026quot; \u0026quot;sync\u0026quot; ) func main() { var wg sync.WaitGroup fmt.Println(\u0026quot;---\u0026quot;) //log.SetupLogging() for j := 0; j \u0026lt; 100; j++ { for i := 0; i \u0026lt; 4; i++ { wg.Add(1) switch i { case 0: fmt.Println(\u0026quot;---\u0026quot;, i) go ccc.PrintI(i, \u0026amp;wg) case 1: fmt.Println(\u0026quot;---\u0026quot;, i) go hhh.PrintI(i, \u0026amp;wg) case 2: fmt.Println(\u0026quot;---\u0026quot;, i) go ccc.PrintE(i, \u0026amp;wg) case 3: fmt.Println(\u0026quot;---\u0026quot;, i) go hhh.PrintE(i, \u0026amp;wg) } } } wg.Wait() } ","id":4,"section":"posts","summary":"Golang 记一次log日志不能输出的问题 由于线上运行程序过程中出现了log日志打印不出来的情况,分析代码模拟测试复现问题。由于没有锁住log变量的问","tags":["golang"],"title":"Golang 记一次log日志不能输出的问题","uri":"https://neilliu9891.github.io/2020/12/golang-bj2/","year":"2020"},{"content":"Golang笔记 20201208 记一次golang exec.Command操作 正常在命令行中执行的命令如下:\nvirsh qemu-agent-command ecs-j9v040bxt8pu '{\u0026quot;execute\u0026quot;:\u0026quot;guest-network-get-interfaces\u0026quot;}' 需要在代码中执行此命令,但是一直报错,不知道为什么,只知道erron=1 编码中进行了各种如下尝试:\n//output, err := exec.Command(\u0026quot;virsh\u0026quot;, \u0026quot;qemu-agent-command\u0026quot;, vmInst, \u0026quot;\\\\'{\\\u0026quot;execute\\\u0026quot;:\\\u0026quot;guest-network-get-interfaces\\\u0026quot;}\\\\'\u0026quot;).Output() //output, err := exec.Command(\u0026quot;sudo\u0026quot;, \u0026quot;virsh\u0026quot;, \u0026quot;qemu-agent-command\u0026quot;, \u0026quot;ecs-j9v040bxt8pu\u0026quot;, `'{\\\u0026quot;execute\\\u0026quot;:\\\u0026quot;guest-network-get-interfaces\\\u0026quot;}'`).Output() //output, err := exec.Command(\u0026quot;ls\u0026quot;, \u0026quot;main\u0026quot;).Output() output, err := exec.Command(\u0026quot;/bin/bash\u0026quot;, \u0026quot;-c\u0026quot;, cmd).Output() //t.Stderr = os.Stdout //t.Stdout = os.Stdout //err = t.Start() //output := \u0026quot;\u0026quot; //output, err := exec.Command(\u0026quot;/bin/bash\u0026quot;, \u0026quot;-c\u0026quot;, cmd).Stderr = os.Stdin //exec.Command().Stderr 说重点:可以看到os.Stdout, 但执行命令不知道失败原因时可以使用将Stderr输出到终端的形式,从而了解错误原因,其实就是操作被拒绝,我用的不是root权限执行的程序,导致程序本身没有root权限。\nchown root:root main //改变程序的所属用户和用户组均为root su root //切换到root进行执行 由于没有将错误信息打印出来导致各种猜测错误原因浪费了很长时间,下次注意。\n20201208 golang race操作检查竞态问题 测试命令\ngo build -race main.go or go run -race main.go 关于race竞争的检查如下文章讲解的很详细\n https://www.cnblogs.com/yjf512/p/5144211.html\n https://ms2008.github.io/2019/05/12/golang-data-race/https://ms2008.github.io/2019/05/12/golang-data-race/\n 通过创建新的用户隔离golang private lab的配置 需求:Linux编译的环境是共享的,大家如果都用root用户,当涉及到private lab时,需要设计到git 配置和 ssh访问的问题,目前不知道到怎么配置支持多个用户,所以想到通过创建用户隔离git信息和ssh private key等信息。\n 创建用户, 将用户创建到root组 useradd -g root ly //创建ly用户,并添加到root组 passwd ly //为ly用户分配密码 为用户分配root执行权限 root 用户执行vi /etc/sudoers,在 ## Allow root to run any commands anywhere 之下添加用户名,与root类似\n## Allow root to run any commands anywhere root ALL=(ALL) ALL ly ALL=(ALL) ALL 参考:\n http://www.manongjc.com/article/135688.html\n 配置golang环境 当切换用户之后,golang的环境配置就丢失了,导致执行go命令提示找不到命令,此时只需要修改~/.bash_profile文件内容,将golang 安装之后bin的所在位置添加到PATH变量中即可。 添加golang project的目录位置,放置到用户目录/go 目录中,再~/go/ 目录下创建pkg 、 bin 、 src目录 ~/.bash_profile 添加内容 # .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs GOPATH=$HOME/go PATH=$PATH:$HOME/.local/bin:$HOME/bin:$GOPATH PATH=$PATH:/usr/local/go/bin export PATH 配置git环境 配置ssh 公钥私钥信息能够通过ssh访问private gitlab仓库 关于golang gin框架中使用goroutine的问题 gin框架中如果在goroutine是用gin.context的话,需要使用context.Copy函数copy一份副本进行操作,否则会出现竞争问题。\n参考官方文章:\n https://learnku.com/docs/gin-gonic/2019/examples-goroutines-inside-a-middleware/6178\n func main() { r := gin.Default() r.GET(\u0026quot;/long_async\u0026quot;, func(c *gin.Context) { // 创建在 goroutine 中使用的副本 cCp := c.Copy() go func() { // 用 time.Sleep() 模拟一个长任务。 time.Sleep(5 * time.Second) // 请注意您使用的是复制的上下文 \u0026quot;cCp\u0026quot;,这一点很重要 log.Println(\u0026quot;Done! in path \u0026quot; + cCp.Request.URL.Path) }() }) r.GET(\u0026quot;/long_sync\u0026quot;, func(c *gin.Context) { // 用 time.Sleep() 模拟一个长任务。 time.Sleep(5 * time.Second) // 因为没有使用 goroutine,不需要拷贝上下文 log.Println(\u0026quot;Done! in path \u0026quot; + c.Request.URL.Path) }) // 监听并在 0.0.0.0:8080 上启动服务 r.Run(\u0026quot;:8080\u0026quot;) } ","id":5,"section":"posts","summary":"Golang笔记 20201208 记一次golang exec.Command操作 正常在命令行中执行的命令如下: virsh qemu-agent-command ecs-j9v040bxt8pu '{\u0026quot;execute\u0026quot;:\u0026quot;guest-network-get-interfaces\u0026quot;}' 需要在代码中执行此命令,但是一直报错,","tags":["golang"],"title":"Golang Bj","uri":"https://neilliu9891.github.io/2020/12/golang-bj/","year":"2020"},{"content":"时间管理读书笔记 哪有没时间这回事儿 当自己不想要努力前进的时候,读书能够使我重新认识自己,至少能够让我开心起来。 《哪有没时间这回事儿》一书,让我重新找回了一点儿过去的自己。\n人性的弱点:短视、逃避、冲动,这些表现都是写在我们人类历史长河中的基因。 短视:想想自己只顾眼前工作的内容,每天过着重复的生活,早晨起床想想自己吃什么,工作需要做哪些事情,却不曾想过我要怎样的人生,以及如何才能达到那样的人生;只有当公司开始裁员,焦虑涌上心头,却没有行动,只有焦虑。自律的人懂得未雨绸缪而非焦虑症患者。焦虑的基因只是我们面对未知的一种恐惧,适度的焦虑能让人类延续,但现代社会过渡焦虑却人我们生活的很疲惫。懂得规划自己的人生找到目标才是一个自律者该有的行动。 逃避:有时候自己不喜欢做费脑力的事情,很是喜欢做轻松的事情。比如自己读一本专业书集,命名知道专业书籍会更加的有益处,但偏偏喜欢读些心灵鸡汤,假装学习(听学习视频等)其实根本没有领会其中的内容,甚至不曾动手写下来,只是简单的摘录等等。没走心的学习,其实都是徒劳。逃避当然也是我们祖先留给我们的基因,让我们躲避危险。但敢于直面困难的人才能走的更远。 冲动:冲动是魔鬼,管理好自己的情绪至关重要,不好的情绪带给我们极地的效率以及不好的人际关系。\n我想做:\n 规划自己的人生目标,阶段目标 以正确的方式面对挑战,规划合理的行动克服困难 有意识的降低自己冲动的概率,同样需要行为来改变 所有的改变都是靠着一个个行为来实现的,正如优秀的运动员,都是通过千次万次的重复着一个动作练出了固定的某些行为模式,才变成了优秀的运动健将。\n行为-》思维-》习惯\n优秀的人都有优秀的习惯,而习惯的养成需要一个个符合人性逻辑的行动促成的。 行动:\n 行动前的准备工作 行动的目的 环境的准备 符合人性的行动 定制消除或者减弱阻碍行动的某些行为 小小挑战:\n 早起阅读,为什么选择早起阅读呢?因为早晨的时光没有人打扰,且精力比较充沛。 目的:坚持早起。我不需要打鸡血,每天必须几点起床,而应该提高周期内早起的概率,比如7天一个周期,不断进步。 准备,准备一些帮助早起的环境。双闹钟法则,第一个手表闹钟,让自己清醒;第二个闹钟声音大,让自己必须下床才能关掉。 准备2,准备好起床需要穿的衣服,睡衣方便穿;准备一杯温水(或者一杯咖啡),让自己清醒。 准备3,选好第二天需要阅读的书。 行动1:起床后去洗漱;喝一口温水;做30个俯卧撑。(增加可行性行动) 行动2:将手机收起来,放到盒子里。(削弱阻力行为) 行动3: 阅读 ","id":6,"section":"posts","summary":"时间管理读书笔记 哪有没时间这回事儿 当自己不想要努力前进的时候,读书能够使我重新认识自己,至少能够让我开心起来。 《哪有没时间这回事儿》一书,让","tags":["时间管理"],"title":"时间管理读书笔记","uri":"https://neilliu9891.github.io/2020/11/%E6%97%B6%E9%97%B4%E7%AE%A1%E7%90%86/","year":"2020"},{"content":"Golang交叉编译必知必会 需求 在Linux x86的操作系统上编译 Linux Aarch64平台的golang程序,程序中引用的cgo代码。 典型的交叉编译需求,Golang支持的平台列表参见官方文档:\n https://golang.google.cn/doc/install/source\n 实现 编译golang程序,如果是交叉编译只需要指定GOOS,GOARCH平台即可,根据需求编译如下:\nenv GOOS=linux GOARCH=arm64 go build -ldflags $(LDFLAGS) -o ./bin/$(BIN_NAME)-aarch64 *.go 由于代码中包含了cgo程序, CGO_ENABLED=1程序默认开启,此时golang编译时会查找本地的gcc进行编译,由于gcc并不是目标平台的gcc。所以我们在编译时要指定对应平台的gcc进行编译。指令如下:\nenv CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc-10 go build -ldflags $(LDFLAGS) -o ./bin/$(BIN_NAME)-aarch64 *.go 如何现在安装对应平台的gcc呢? ubuntu有自己的安装包管理器,所以通过如下命令查询并安装即可: 查询aarch64的编译器有哪些?\nsudo apt-cache search aarch64 | grep \u0026quot;GNU C\u0026quot; cpp-9-aarch64-linux-gnu - GNU C preprocessor cpp-aarch64-linux-gnu - GNU C preprocessor (cpp) for the arm64 architecture g++-9-aarch64-linux-gnu - GNU C++ compiler (cross compiler for arm64 architecture) g++-aarch64-linux-gnu - GNU C++ compiler for the arm64 architecture gcc-9-aarch64-linux-gnu - GNU C compiler (cross compiler for arm64 architecture) gcc-9-aarch64-linux-gnu-base - GCC, the GNU Compiler Collection (base package) gcc-aarch64-linux-gnu - GNU C compiler for the arm64 architecture cpp-10-aarch64-linux-gnu - GNU C preprocessor cpp-8-aarch64-linux-gnu - GNU C preprocessor g++-10-aarch64-linux-gnu - GNU C++ compiler (cross compiler for arm64 architecture) g++-8-aarch64-linux-gnu - GNU C++ compiler (cross compiler for arm64 architecture) gcc-10-aarch64-linux-gnu - GNU C compiler (cross compiler for arm64 architecture) gcc-10-aarch64-linux-gnu-base - GCC, the GNU Compiler Collection (base package) gcc-8-aarch64-linux-gnu - GNU C compiler (cross compiler for arm64 architecture) gcc-8-aarch64-linux-gnu-base - GCC, the GNU Compiler Collection (base package) 通过查询知道支持的gcc包括gcc-aarch64-linux-gnu , gcc-10-aarch64-linux-gnu等,我们就安装这个gcc-10*编译器。\nsudo apt-get install gcc-10-aarch64-linux-gnu 被安装在/usr/bin/目录下\n/usr/bin/aarch64-linux-gnu-gcc-10 查看可以编译的目标平台是否正确\nneil@TJ-YF-11W16P:~$ aarch64-linux-gnu-gcc-10 -v Using built-in specs. COLLECT_GCC=aarch64-linux-gnu-gcc-10 COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/aarch64-linux-gnu/10/lto-wrapper Target: aarch64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu 10-20200411-0ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-10/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-10 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --without-target-system-zlib --enable-multiarch --enable-fix-cortex-a53-843419 --disable-werror --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=aarch64-linux-gnu --program-prefix=aarch64-linux-gnu- --includedir=/usr/aarch64-linux-gnu/include Thread model: posix Supported LTO compression algorithms: zlib gcc version 10.0.1 20200411 (experimental) [master revision bb87d5cc77d:75961caccb7:f883c46b4877f637e0fa5025b4d6b5c9040ec566] (Ubuntu 10-20200411-0ubuntu1) Target表示目标平台是aarch64 架构,Linux系统,符合预期。\n问题 出现\u0026rsquo;GLIBC_2.14\u0026rsquo; not found 问题 原因是由于编译的cgo引用的glibc的版本与运行环境的glibc的版本不匹配,由于是动态链接所以找不到对应的版本报错。 解决:通过静态链接将glibc的版本链接进去。 LDFLAGS := \u0026quot;-s -w -extldflags \u0026quot;-static\u0026quot;\u0026quot; # -extldflags \u0026quot;-static\u0026quot; 就表示指定静态链接 参考链接 http://blog.sina.com.cn/s/blog_43a59c7a0102wz7w.html\n 名词解释: GNU:GNU不是一个公司名,而是一个软件项目名。它开发了许多应用程序。 GCC:GCC全称是 GNU C Compiler, 最早的时候就是一个c编译器。但是后来因为这个项目里边集成了更多其他不同语言的编译器,GCC就代表 the GNU Compiler Collection,所以表示一堆编译器的合集。 G++:G++则是GCC的c++编译器\n","id":7,"section":"posts","summary":"Golang交叉编译必知必会 需求 在Linux x86的操作系统上编译 Linux Aarch64平台的golang程序,程序中引用的cgo代码。 典型的交叉","tags":["golang","交叉编译"],"title":"Golang交叉编译2","uri":"https://neilliu9891.github.io/2020/10/golang%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%912/","year":"2020"},{"content":"Bit之git效率工具 bit: 建立再git之上的实验性现代化代码git\u0026rsquo;CLI 项目地址:https://github.com/chriswalz/bit\nInstall $ curl -sf https://gobinaries.com/chriswalz/bit | sh; curl -sf https://gobinaries.com/chriswalz/bit/bitcomplete | sh \u0026amp;\u0026amp; echo y | COMP_INSTALL=1 bitcomplete $ bit 从如下地址下载安装 https://github.com/chriswalz/bit/releases Go安装 确保使用了go module\n$ go get github.com/chriswalz/bit $ bit Platform iTerm2 (macOS) Terminal.app (macOS) Command Prompt (Windows) WSL/Windows Subsystem for Linux (Windows) gnome-terminal (Ubuntu) Special Cmd 1、bit save [commit message] 创建一个新的提交。 2、bit sync 同步对 origin 分支的更改。 大部分时候,bit sync 相当于 bit commit -m \u0026quot;I can still use git commands\u0026quot;, bit pull -r origin master Normal Work Flow 切换分支 $ bit \u0026gt; bit switch example-branch ? Branch does not exist. Do you want to create it? Yes Switched to a new branch 'example-branch' 做些改动 $ bit add * $ bit save \u0026quot;add important feature\u0026quot; 做其他改动 $ bit save push 改变到 origin $ bit sync 一段时间后,可以再同步别人的修改 $ bit sync 一般都直接输入 bit,然后回车。接着输入会自动提示,如开始的动图\n","id":8,"section":"posts","summary":"Bit之git效率工具 bit: 建立再git之上的实验性现代化代码git\u0026rsquo;CLI 项目地址:https://github.com/chri","tags":["git"],"title":"Git Bit效率工具","uri":"https://neilliu9891.github.io/2020/10/git-bit%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7/","year":"2020"},{"content":"Windows 操作系统 效率(摸鱼)技巧 Windows 10多桌面操作 Windows 多桌面功能可以让你的电脑像mac一样将不同的项目,需要长期保留的项目分别放在不同的虚拟桌面上,这样每个桌面都很整洁,切换也很方便。 命令如下:\n创建新的虚拟桌面:Win + Ctrl + D 关闭当前虚拟桌面:Win + Ctrl + F4 切换虚拟桌面:Win + Ctrl +左/右 像我就会将QQ音乐,微信放在一个单独的虚拟桌面,与工作切分开,不打扰工作(方便摸鱼)\n 将应用从一个虚拟桌面移动到另一个虚拟桌面操作\n 同时按下【Win徽标】和【Tab】键 选中要拖拽的应用,拖到相应的虚拟桌面即可。 参考连接: https://jingyan.baidu.com/article/046a7b3edf2839f9c27fa92f.html 参考连接: https://www.jianshu.com/p/68973f69cc79\n ","id":9,"section":"posts","summary":"Windows 操作系统 效率(摸鱼)技巧 Windows 10多桌面操作 Windows 多桌面功能可以让你的电脑像mac一样将不同的项目,需要长期保留的项目分别放在不同的虚拟桌面上,这","tags":["Work"],"title":"Windows桌面提升办公效率技巧","uri":"https://neilliu9891.github.io/2020/10/windows-work/","year":"2020"},{"content":"Golang 交叉编译 由于自己的开发环境是Linux开发环境,但自己编写的工具需要运行在windows的环境中,所以需要用到交叉编译工具.\nUbuntu下编译windows 程序 参考: \u0026gt; https://studygolang.com/articles/8167\n Install gcc-mingw-w64:gcc编译器 apt-get install gcc-mingw-w64 Cross platform compiler 64bit\nenv CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc go build -o main.exe main.go 32bit\nenv CGO_ENABLED=1 GOOS=windows GOARCH=386 CC=i686-w64-mingw32-gcc go build -o main.exe main.go Common Error Q:gcc: error: unrecognized command line option ‘-mthreads’; did you mean ‘-pthread’? A:CGO_ENABLED=1但是未指定CC编译器 Q:运行时出错(error=\u0026quot;Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work.) A:CGO_ENABLED未设置成1 ","id":10,"section":"posts","summary":"Golang 交叉编译 由于自己的开发环境是Linux开发环境,但自己编写的工具需要运行在windows的环境中,所以需要用到交叉编译工具. Ubuntu下","tags":["golang"],"title":"Golang交叉编译","uri":"https://neilliu9891.github.io/2020/10/golang%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/","year":"2020"},{"content":"Golang Tag Marshal and UnMarshal Golang 支持tag功能,对于固定结构的结构体非常友好,最近再尝试用golang重新编写netconf api,涉及到xml的生成与解析功能,着实研究了一下,作为总结。\n如果想要了解一个api功能,最好的方式还是尝试查看官网资料,有幸国人已经有了翻译版本:\n https://studygolang.com/pkgdoc\n 抄袭官方文档,理解官方文档,OYe!\n定义结构体,增加xml tag功能 Marshal example Example\ntype Address struct { City, State string } type Person struct { XMLName xml.Name `xml:\u0026quot;person\u0026quot;` Id int `xml:\u0026quot;id,attr\u0026quot;` FirstName string `xml:\u0026quot;name\u0026gt;first\u0026quot;` LastName string `xml:\u0026quot;name\u0026gt;last\u0026quot;` Age int `xml:\u0026quot;age\u0026quot;` Height float32 `xml:\u0026quot;height,omitempty\u0026quot;` Married bool Address Comment string `xml:\u0026quot;,comment\u0026quot;` } v := \u0026amp;Person{Id: 13, FirstName: \u0026quot;John\u0026quot;, LastName: \u0026quot;Doe\u0026quot;, Age: 42} v.Comment = \u0026quot; Need more details. \u0026quot; v.Address = Address{\u0026quot;Hanga Roa\u0026quot;, \u0026quot;Easter Island\u0026quot;} output, err := xml.MarshalIndent(v, \u0026quot; \u0026quot;, \u0026quot; \u0026quot;) if err != nil { fmt.Printf(\u0026quot;error: %v\\n\u0026quot;, err) } os.Stdout.Write(output) Output\n \u0026lt;person id=\u0026quot;13\u0026quot;\u0026gt; \u0026lt;name\u0026gt; \u0026lt;first\u0026gt;John\u0026lt;/first\u0026gt; \u0026lt;last\u0026gt;Doe\u0026lt;/last\u0026gt; \u0026lt;/name\u0026gt; \u0026lt;age\u0026gt;42\u0026lt;/age\u0026gt; \u0026lt;Married\u0026gt;false\u0026lt;/Married\u0026gt; \u0026lt;City\u0026gt;Hanga Roa\u0026lt;/City\u0026gt; \u0026lt;State\u0026gt;Easter Island\u0026lt;/State\u0026gt; \u0026lt;!-- Need more details. --\u0026gt; \u0026lt;/person\u0026gt; XMLName字段,如上所述,会省略 具有标签\u0026rdquo;-\u0026ldquo;的字段会省略 具有标签\u0026quot;name,attr\u0026quot;的字段会成为该XML元素的名为name的属性 具有标签\u0026rdquo;,attr\u0026quot;的字段会成为该XML元素的名为字段名的属性 具有标签\u0026rdquo;,chardata\u0026quot;的字段会作为字符数据写入,而非XML元素 具有标签\u0026rdquo;,innerxml\u0026quot;的字段会原样写入,而不会经过正常的序列化过程 具有标签\u0026rdquo;,comment\u0026quot;的字段作为XML注释写入,而不经过正常的序列化过程,该字段内不能有\u0026rdquo;\u0026ndash;\u0026ldquo;字符串 标签中包含\u0026quot;omitempty\u0026quot;选项的字段如果为空值会省略 空值为false、0、nil指针、nil接口、长度为0的数组、切片、映射 匿名字段(其标签无效)会被处理为其字段是外层结构体的字段 UnMarshal example Example\ntype Email struct { Where string `xml:\u0026quot;where,attr\u0026quot;` Addr string } type Address struct { City, State string } type Result struct { XMLName xml.Name `xml:\u0026quot;Person\u0026quot;` Name string `xml:\u0026quot;FullName\u0026quot;` Phone string Email []Email Groups []string `xml:\u0026quot;Group\u0026gt;Value\u0026quot;` Address } v := Result{Name: \u0026quot;none\u0026quot;, Phone: \u0026quot;none\u0026quot;} data := ` \u0026lt;Person\u0026gt; \u0026lt;FullName\u0026gt;Grace R. Emlin\u0026lt;/FullName\u0026gt; \u0026lt;Company\u0026gt;Example Inc.\u0026lt;/Company\u0026gt; \u0026lt;Email where=\u0026quot;home\u0026quot;\u0026gt; \u0026lt;Addr\u0026gt;[email protected]\u0026lt;/Addr\u0026gt; \u0026lt;/Email\u0026gt; \u0026lt;Email where='work'\u0026gt; \u0026lt;Addr\u0026gt;[email protected]\u0026lt;/Addr\u0026gt; \u0026lt;/Email\u0026gt; \u0026lt;Group\u0026gt; \u0026lt;Value\u0026gt;Friends\u0026lt;/Value\u0026gt; \u0026lt;Value\u0026gt;Squash\u0026lt;/Value\u0026gt; \u0026lt;/Group\u0026gt; \u0026lt;City\u0026gt;Hanga Roa\u0026lt;/City\u0026gt; \u0026lt;State\u0026gt;Easter Island\u0026lt;/State\u0026gt; \u0026lt;/Person\u0026gt; ` err := xml.Unmarshal([]byte(data), \u0026amp;v) if err != nil { fmt.Printf(\u0026quot;error: %v\u0026quot;, err) return } fmt.Printf(\u0026quot;XMLName: %#v\\n\u0026quot;, v.XMLName) fmt.Printf(\u0026quot;Name: %q\\n\u0026quot;, v.Name) fmt.Printf(\u0026quot;Phone: %q\\n\u0026quot;, v.Phone) fmt.Printf(\u0026quot;Email: %v\\n\u0026quot;, v.Email) fmt.Printf(\u0026quot;Groups: %v\\n\u0026quot;, v.Groups) fmt.Printf(\u0026quot;Address: %v\\n\u0026quot;, v.Address) Output\nXMLName: xml.Name{Space:\u0026quot;\u0026quot;, Local:\u0026quot;Person\u0026quot;} Name: \u0026quot;Grace R. Emlin\u0026quot; Phone: \u0026quot;none\u0026quot; Email: [{home [email protected]} {work [email protected]}] Groups: [Friends Squash] Address: {Hanga Roa Easter Island} 如果结构体字段的类型为字符串或者[]byte,且标签为\u0026rdquo;,innerxml\u0026rdquo;, Unmarshal函数直接将对应原始XML文本写入该字段,其余规则仍适用。 如果结构体字段类型为xml.Name且名为XMLName,Unmarshal会将元素名写入该字段 如果字段XMLName的标签的格式为\u0026quot;name\u0026quot;或\u0026quot;namespace-URL name\u0026rdquo;, XML元素必须有给定的名字(以及可选的名字空间),否则Unmarshal会返回错误。 如果XML元素的属性的名字匹配某个标签\u0026rdquo;,attr\u0026quot;为字段的字段名,或者匹配某个标签为\u0026quot;name,attr\u0026rdquo; 的字段的标签名,Unmarshal会将该属性的值写入该字段。 如果XML元素包含字符数据,该数据会存入结构体中第一个具有标签\u0026rdquo;,chardata\u0026quot;的字段中, 该字段可以是字符串类型或者[]byte类型。如果没有这样的字段,字符数据会丢弃。 如果XML元素包含注释,该数据会存入结构体中第一个具有标签\u0026rdquo;,comment\u0026quot;的字段中, 该字段可以是字符串类型或者[]byte类型。如果没有这样的字段,字符数据会丢弃。 如果XML元素包含一个子元素,其名称匹配格式为\u0026quot;a\u0026quot;或\u0026quot;a\u0026gt;b\u0026gt;c\u0026quot;的标签的前缀,反序列化会深入 XML结构中寻找具有指定名称的元素,并将最后端的元素映射到该标签所在的结构体字段。 以\u0026quot;\u0026gt;\u0026quot;开始的标签等价于以字段名开始并紧跟着\u0026quot;\u0026gt;\u0026rdquo; 的标签。 如果XML元素包含一个子元素,其名称匹配某个结构体类型字段的XMLName字段的标签名, 且该结构体字段本身没有显式指定标签名,Unmarshal会将该元素映射到该字段。 如果XML元素的包含一个子元素,其名称匹配够格结构体字段的字段名,且该字段没有任何模式选项 (\u0026quot;,attr\u0026rdquo;、\u0026quot;,chardata\u0026quot;等),Unmarshal会将该元素映射到该字段。 如果XML元素包含的某个子元素不匹配以上任一条,而存在某个字段其标签为\u0026rdquo;,any\u0026rdquo;, Unmarshal会将该元素映射到该字段。 匿名字段被处理为其字段好像位于外层结构体中一样。 标签为\u0026rdquo;-\u0026ldquo;的结构体字段永不会被反序列化填写。 实现UnmarshalXML方法 需求:实现一个如下形式的解析和xml生成 bgp下可能对应多个不同类型的子xml节点,其中Familys和F1s的结构体是固定的,但是BGP tag下存在多少个不通的类型不确定 \u0026lt;BGP\u0026gt; \u0026lt;Familys\u0026gt; \u0026lt;Family\u0026gt; \u0026lt;Name\u0026gt;Name\u0026lt;/Name\u0026gt; \u0026lt;VRF\u0026gt;VRF\u0026lt;/VRF\u0026gt; \u0026lt;Balance\u0026gt; \u0026lt;MaxBalance\u0026gt;4\u0026lt;/MaxBalance\u0026gt; \u0026lt;MinBalance\u0026gt;0\u0026lt;/MinBalance\u0026gt; \u0026lt;/Balance\u0026gt; \u0026lt;/Family\u0026gt; \u0026lt;Family\u0026gt; \u0026lt;Name\u0026gt;Name\u0026lt;/Name\u0026gt; \u0026lt;VRF\u0026gt;VRF\u0026lt;/VRF\u0026gt; \u0026lt;Balance\u0026gt; \u0026lt;MaxBalance\u0026gt;4\u0026lt;/MaxBalance\u0026gt; \u0026lt;MinBalance\u0026gt;0\u0026lt;/MinBalance\u0026gt; \u0026lt;/Balance\u0026gt; \u0026lt;/Family\u0026gt; \u0026lt;/Familys\u0026gt; \u0026lt;F1s\u0026gt; \u0026lt;F1\u0026gt; \u0026lt;f1\u0026gt;1\u0026lt;/f1\u0026gt; \u0026lt;f2\u0026gt;2\u0026lt;/f2\u0026gt; \u0026lt;/F1\u0026gt; \u0026lt;/F1s\u0026gt; \u0026lt;/BGP\u0026gt; import ( \u0026quot;encoding/xml\u0026quot; \u0026quot;fmt\u0026quot; ) // BGP define type BGP struct { XMLName xml.Name `xml:\u0026quot;BGP\u0026quot;` Bgp []interface{} } // BGPFamilys define type BGPFamilys struct { XMLName xml.Name `xml:\u0026quot;Familys\u0026quot;` BGPFamily []BGPFamily `xml:\u0026quot;Family\u0026quot;` //必须填写, 与BGPFamily的xml tag对应,否则解析不出来,此处如果填写 BGPFamily可以不定义XMLName但是建议定义,如果单独生成BGPFamily时需要使用 } // BGPFamily define type BGPFamily struct { XMLName xml.Name `xml:\u0026quot;Family\u0026quot;` Name string `xml:\u0026quot;Name\u0026quot;` VRF string `xml:\u0026quot;VRF\u0026quot;` MaxBalance int `xml:\u0026quot;Balance\u0026gt;MaxBalance\u0026quot;` //在嵌套标签 \u0026lt;Balance\u0026gt;\u0026lt;MaxBalance\u0026gt;0\u0026lt;/MaxBalance\u0026gt;\u0026lt;/Balance\u0026gt; MinBalance int `xml:\u0026quot;Balance\u0026gt;MinBalance\u0026quot;` } // 实现UnmarshalXML方法, 正确解析xml到GBP结构体中 func (b *BGP) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // 当调用到UnmarshalXML时,start.Name 就是BGP这个tag b.XMLName = start.Name // grab any other attrs // decode inner elements for { t, err := d.Token() // 向下找到子元素 if err != nil { return err } var i interface{} switch tt := t.(type) { case xml.StartElement: //判断是起始标签 switch tt.Name.Local { case \u0026quot;Familys\u0026quot;: i = new(BGPFamilys) // } if i != nil { err = d.DecodeElement(i, \u0026amp;tt) //DecodeElement函数,从tt这个起始标签向下解析出i所代表的结构体 if err != nil { return err } b.Bgp = append(b.Bgp, i) i = nil } case xml.EndElement://直到解析到\u0026lt;/BGP\u0026gt;这个标签退出,判断是结束标签 if tt == start.End() { return nil } } } //return nil } DecodeElement 函数适用于半自动化解析结构体,直接通过UnMarshal解析表示全自动解析,通过Deocode一点点调用Token()函数被称为手动的话,那么DecodeElement就被称为半自动化 根据官方资料,由于xml struct tag形式对字段顺序有着强要求,所以如果是自定义的化建议还是使用json\n","id":11,"section":"posts","summary":"Golang Tag Marshal and UnMarshal Golang 支持tag功能,对于固定结构的结构体非常友好,最近再尝试用golang重新编写netconf api,涉及到xml的生成与解析功能","tags":["golang","encode/xml"],"title":"Golang Xmltag","uri":"https://neilliu9891.github.io/2020/10/golang-xmltag/","year":"2020"},{"content":"Create Python Docker Image 记一次基于centos docker镜像创建python 3.7.9版本镜像的过程, 创建流程比较简单,目的记录使用了docker创建镜像的两种方式。\nQ: 为什么不使用官方的python镜像 A: 官方python镜像基于debian操作系统实现,而项目中多使用centos操作系统,为了保持一致,所以更新centos操作系统;构建的程序需要后台运行,基于systemctl 编写启动脚本实现,python官方镜像中并没有systemctl\n 文档记录并解决两个问题!\n 基于Centos7.8镜像如何安装生成python3.7.9的镜像文件 如何实现基于systemctl 服务的docker镜像的启动,以及如何编写k8s yaml文件实现systemctl 基础资源 官方docker仓库\n https://hub.docker.com/search?q=\u0026amp;type=image https://hub.docker.com/_/centos/?tab=description\n 生成python基础镜像 基于镜像commit方式生成新镜像 根据docker hub 搜索并下载合适的centos版本 docker pull centos:7.8.2003 通过run命令启动下载下来的image镜像并进入容器 docker run -it centos:7.8.2003 /bin/bash 执行安装python所必要的基础工具包(容器内执行) yum install wget yum install yum-utils yum-builddep python3 # 下载python依赖 yum install make # 编包使用 安装python wget https://www.python.org/ftp/python/3.7.9/Python-3.7.9.tgz #下载指定版本python包 tar xf Python-3.7.9.tgz # 解压包 cd Python-3.7.9 ./configure \u0026amp;\u0026amp; make \u0026amp;\u0026amp; make install # 配置、编译、安装 退出并执行如下命令生成新镜像 docker ps -a | grep centos:7.8.2003 # 查看刚才启动的docker 容器 cfe4d2255644 centos:7.8.2003 \u0026quot;/usr/sbin/init\u0026quot; 18 hours ago Up 18 hours docker diff cfe4d2255644 # 显示差异,由于安装内容太多没必要显示 docker commit -m \u0026quot;centos python3.7.9\u0026quot; cfe4d2255644 imagename:tag # -m提交注释 ,imagename:tag表示新生成的容器名称 基于文件的方式生成新镜像 基于文件的方式生成镜像其实就是将之前的命令基于Dockerfile的形式执行一遍。\n 生成Dockerfile,内容如下: FROM centos:7.8.2003 RUN yum install wget \u0026amp;\u0026amp; yum install yum-utils \u0026amp;\u0026amp; yum-builddep python3 \u0026amp;\u0026amp; yum install make RUN wget https://www.python.org/ftp/python/3.7.9/Python-3.7.9.tgz \u0026amp;\u0026amp; tar xf Python-3.7.9.tgz \u0026amp;\u0026amp; cd Python-3.7.9 \u0026amp;\u0026amp; ./configure \u0026amp;\u0026amp; make \u0026amp;\u0026amp; make install CMD [/bin/bash] 执行生成镜像命令 docker -f Dockerfile . -t imagesname:tag 推送镜像到远端仓库 给镜像打远端仓库的tag docker tag centos7.8-python3.7.9:latest 10.254.7.1:5000/imagesname:tag 执行push命令推送镜像 docker push 10.254.7.1:5000/imagesname:tag 如何在容器内运行systemctl命令以及后台运行服务 systemctl作为后台服务管理的工具,能够通过编写/etc/systemd/system/*.service的方式实现对服务的运行重启等功能。\nQ: python程序想要在不更换docker镜像的情况下,修改python代码并重新运行,只要docker不重启,服务就不会失效的功能,如何实现? A: 通过编写service文件通过systemctl监控实现。\nQ: systemctl 在docker中运行失败,错误提示\nSystem has not been booted with systemd as init system (PID 1). Can't operate. Failed to connect to bus: Host is down FailedtogetD-Busconnection: Operation not permitted 启动时添加\u0026ndash;privileged=true 使用/usr/sbin/init 使用-itd(即-d后台运行否则一直会卡在那里,没有命令行终端可用) example docker run -itd --privileged=true images:tag /usr/sbin/init docker ps -a | grep image:tag # 获取容器的id docker run -it container_id /bin/bash # container_id:实际的容器id ","id":12,"section":"posts","summary":"Create Python Docker Image 记一次基于centos docker镜像创建python 3.7.9版本镜像的过程, 创建流程比较简单,目的记录使用了docker创建镜像","tags":["docker"],"title":"Create Python Docker Image","uri":"https://neilliu9891.github.io/2020/10/create-python-docker-image/","year":"2020"},{"content":"Golang Private Lab 从gitlab私有仓库获取golang依赖包 例如golang go.mod文件中有如下配置信息\nmodule agent go 1.13 require ( ... moove/libvirt/libvirt-go v1.0.0 ) replace moove/libvirt/libvirt-go =\u0026gt; 10.0.45.221/moove/libvirt-go v1.0.5 # 替换成本地库 此时就需要配置10.0.45.221 这个gitlab仓库作为golang的私有仓库下载libvirt包\n配置通过ssh 访问gitlab 生成ssh公私钥对。 ssh-keygen -t rsa -C \u0026quot;[email protected]\u0026quot; -b 4096 #-t 表示秘钥类型 -C comment信息 秘钥名称可以默认,也可以自定义,如果自定义参考步骤5 passphrase 是指使用这个秘钥时需要的认证密码,一般我都不会再设置了,省的麻烦,毕竟是自己的电脑相对安全一些。如果非个人电脑建议配置简单秘钥。\n 复制~/.ssh/ 目录下生成的公钥信息到系统剪切板 登录gitLab账户,找到SSH选项,配置公钥信息 复制粘贴公钥到空白区域,填写title信息,title不重要,可以随便起 添加秘钥即可Add Key\n 如果生成的秘钥名称不是默认的秘钥名称,则需要进行如下操作,将秘钥信息纳管到ssh-agent服务中,并且需要在~/.ssh/config 文件中配置服务器地址信息,config文件没有创建\n 编写config文件如下 Host github.com │~ │~ HostName github.com │~ │~ PreferredAuthentications publickey │~ │~ IdentityFile ~/.ssh/11111 Host: 作为此配置的名称,可以随便命名。 HostName: 服务地址,如上就是github.com, 也可以直接配置ip地址 IdentityFile: 表示ssh生成的私钥文件路径\n 执行如下命令 eval $(ssh-agent -s) ssh-add ~/.ssh/other_id_rsa other_id_rsa 替换成自己的私钥文件\n 如果通过如下命令能够正确返回登录的用户信息,则说明配置成功了\nssh -T [email protected] 此时,我们就能够通过git git@example/example.git 命令以ssh的方式下载代码了。\ngit 配置 git config --global url.\u0026quot;[email protected]:\u0026quot;.insteadof \u0026quot;http://10.0.45.221/\u0026quot; 此处的\u0026ndash;global配置也可以改成\u0026ndash;local,只有某个项目使用这个替换方式,即将http访问数据包的方式修改为通过ssh访问\ngolang 环境配置 go env -w GO111MODULE=on # 重点,启用go mod go env -w GOPRIVATE=\u0026quot;10.0.45.221\u0026quot; # 使用私有库, go get -u -v -insecure 10.0.45.221/moove/libvirt-go@latest # 获取libvirt-go的库 提示:go get 操作 -insecure表示使用http方式请求,lastest表示获取最新版本,但是可能go.mod中需要的并不是lastest版本,所以此处要指定对版本。操作的目的是手动先下载下来版本,之后go build时就不会自动下载了。 go build去下载时都是采用https的方式,由于private lab可能不支持https所以导致下载失败。提示类似错误:go: moove/uni-network/ovn-store/[email protected]: unrecognized import path \u0026ldquo;10.0.45.221/moove/uni-network/ovn-store/go-ovn\u0026rdquo; (https fetch: Get https://10.0.45.221/moove/uni-network/ovn-store/go-ovn?go-get=1: dial tcp 10.0.45.221:443: connect: no route to host)\n重点是配置开启MODULE模式,以及配置私有golang仓库的地址\n","id":13,"section":"posts","summary":"Golang Private Lab 从gitlab私有仓库获取golang依赖包 例如golang go.mod文件中有如下配置信息 module agent go 1.13 require ( ... moove/libvirt/libvirt-go v1.0.0 ) replace moove/libvirt/libvirt-go =\u0026gt; 10.0.45.221/moove/libvirt-go v1.0.5 # 替换成本地","tags":["golang","ssh","git"],"title":"Golang Private Lab","uri":"https://neilliu9891.github.io/2020/10/golang-private-lab/","year":"2020"},{"content":"种草很久的neovim工具使用 由于一直羡慕大神们用vim工具快速的编辑代码,并且羡慕能够不断优化适合自己的IDE工具。所以选择了neovim作为自己的IDE工具进行配置。 其实,自己是在vscode中使用的vim插件进行编辑,但是还是不能完全摆脱鼠标的操作,且一些功能在windows和macos上使用的不太一样,所以最终狠心学习一下neovim,以及开始尝试搭建自己的vim IDE工具。\n目标 适合自己的才是最好的,之前看了很多大神使用的各种插件,确实效率很高,让人羡慕不已,但是单单是把他们的插件配置拿过来是没有用的,倒不如自己从头搭建一个属于自己的IDE。 插件的查找方式从自己的需求出发,列出自己可能会用到的一些功能,在搜索需要的插件,个人认为这种方式可能更适合自己。\n插件管理工具 要想安装插件,必须有个趁手的管理工具,从网上搜索好多人建议使用vim-plug这个插件。\nvim-plug插件安装过程 vim-plug插件github地址及安装方法:https://github.com/junegunn/vim-plug,安装方式参考github上的Readme说明即可。 raw.githubusercontent.com 访问不了的问题解决 打开https://site.ip138.com/raw.Githubusercontent.com/,输入raw.githubusercontent.com,查询解析IP地址,选取一个IP地址,更改/etc/hosts,直接将raw.githubusercontent.com域名指向该IP地址。 重新执行github上的安装命令即可。 vim插件搜索网站 https://vimawesome.com/ 仅了解这一个网站,如果有更好的网站欢迎给我留言反馈 根据需求搜索需要的plug vim-plug插件管理 neovim配置文件所在位置./config/nvim/init.vim 向init.vim中写入自己的插件内容 for example \u0026quot; Specify a directory for plugins \u0026quot; - For Neovim: stdpath('data') . '/plugged' \u0026quot; - Avoid using standard Vim directory names like 'plugin' call plug#begin('~/.vim/plugged') # 插件存放位置 \u0026quot; Plugin outside ~/.vim/plugged with post-update hook Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } # 插件在github上的名称 \u0026quot; Initialize plugin system call plug#end() 执行命令:PlugInstall命令安装插件 IDE一样的目录树结构 The NERD tree Plug \u0026lsquo;scrooloose/nerdtree\u0026rsquo; Plug \u0026lsquo;preservim/nerdtree\u0026rsquo; 都是可以的,对应的github路径都是:https://github.com/preservim/nerdtree init.vim文件内容 call plug#begin('~/.vim/plugged') \u0026quot; https://vimawesome.com/plugin/nerdtree-red \u0026quot; github: https://github.com/preservim/nerdtree \u0026quot; Plug ‘scrooloose/nerdtree’ is the same Plug 'scrooloose/nerdtree' autocmd vimenter call plug#end() 切换工作台和目录 ctrl + w + h 光标 focus 左侧树形目录 ctrl + w + l 光标 focus 右侧文件显示窗口 ctrl + w + w 光标自动在左右侧窗口切换 ctrl + w + r 移动当前窗口的布局位置 基本操作命令 o 在已有窗口中打开文件、目录或书签,并跳到该窗口 go 在已有窗口 中打开文件、目录或书签,但不跳到该窗口 t 在新 Tab 中打开选中文件/书签,并跳到新 Tab T 在新 Tab 中打开选中文件/书签,但不跳到新 Tab i split 一个新窗口打开选中文件,并跳到该窗口 gi split 一个新窗口打开选中文件,但不跳到该窗口 s vsplit 一个新窗口打开选中文件,并跳到该窗口 gs vsplit 一个新 窗口打开选中文件,但不跳到该窗口 ! 执行当前文件 O 递归打开选中 结点下的所有目录 x 合拢选中结点的父目录 X 递归 合拢选中结点下的所有目录 e Edit the current dif D 删除当前书签 P 跳到根结点 p 跳到父结点 K 跳到当前目录下同级的第一个结点 J 跳到当前目录下同级的最后一个结点 k 跳到当前目录下同级的前一个结点 j 跳到当前目录下同级的后一个结点 C 将选中目录或选中文件的父目录设为根结点 u 将当前根结点的父目录设为根目录,并变成合拢原根结点 U 将当前根结点的父目录设为根目录,但保持展开原根结点 r 递归刷新选中目录 R 递归刷新根结点 m 显示文件系统菜单 cd 将 CWD 设为选中目录 I 切换是否显示隐藏文件 f 切换是否使用文件过滤器 F 切换是否显示文件 B 切换是否显示书签 q 关闭 NerdTree 窗口 ? 切换是否显示 Quick Help 切换标签页 :tabnew [++opt选项] [+cmd] 文件 建立对指定文件新的tab :tabc 关闭当前的 tab :tabo 关闭所有其他的 tab :tabs 查看所有打开的 tab :tabp 前一个 tab :tabn 后一个 tab normal模式下: gT 前一个 tab gt 后一个 tab ##### 创建删除文件/文件夹 ma: 创建文件或者文件夹,文件名称以/结尾就是文件夹了 md: 删除光标所在的文件 自动代码补全 coc.vim\n配置 安装扩展插件,如下:\n:CocInstall coc-json coc-tsserver 配置go语言LSP,如下:\n:CocConfig { \u0026quot;languageserver\u0026quot;: { \u0026quot;go\u0026quot;: { \u0026quot;command\u0026quot;: \u0026quot;gopls\u0026quot;, \u0026quot;rootPatterns\u0026quot;: [\u0026quot;go.mod\u0026quot;], \u0026quot;trace.server\u0026quot;: \u0026quot;verbose\u0026quot;, \u0026quot;filetypes\u0026quot;: [\u0026quot;go\u0026quot;] } } } python语言配置 :CocInstall coc-python golang语言配置 方案一使用go-vim插件,添加如下配置到init.vim中 let g:go_def_mode='gopls' let g:go_info_mode='gopls' 方案二,使用coc.vim中配置lsp到coc-setting.json文件中 { \u0026quot;languageserver\u0026quot;: { \u0026quot;golang\u0026quot;: { \u0026quot;command\u0026quot;: \u0026quot;gopls\u0026quot;, \u0026quot;rootPatterns\u0026quot;: [\u0026quot;go.mod\u0026quot;, \u0026quot;.vim/\u0026quot;, \u0026quot;.git/\u0026quot;, \u0026quot;.hg/\u0026quot;], \u0026quot;filetypes\u0026quot;: [\u0026quot;go\u0026quot;], \u0026quot;initializationOptions\u0026quot;: { \u0026quot;usePlaceholders\u0026quot;: true } } } } 添加自动导入import 包,当文件中缺失要导入的包的时候,在init.vim中添加如下信息 autocmd BufWritePre *.go :call CocAction('runCommand', 'editor.action.organizeImport') 配置文件位置如下: 快速代码跳转 easymotion\n简单更改符号 vim-surround\ngolang开发环境 vim-go\nlet g:go_def_mode='gopls' let g:go_info_mode='gopls' 参考:https://github.com/fatih/vim-go/wiki\n ![入门指导]https://github.com/fatih/vim-go/wiki/Tutorial\n\u0026quot; autowrite, 执行:make时自动保存 set autowrite \u0026quot; shortcut for find error map \u0026lt;C-n\u0026gt; :cnext\u0026lt;CR\u0026gt; map \u0026lt;C-m\u0026gt; :cprevious\u0026lt;CR\u0026gt; nnoremap \u0026lt;leader\u0026gt;a :cclose\u0026lt;CR\u0026gt; \u0026quot; shortcut for go run, go build autocmd FileType go nmap \u0026lt;leader\u0026gt;b \u0026lt;Plug\u0026gt;(go-build) autocmd FileType go nmap \u0026lt;leader\u0026gt;r \u0026lt;Plug\u0026gt;(go-run) map: 递归映射 : 表示回车 : 表示Ctrl+n noremap: 表示非递归调用 nmap\nlet g:go_list_type = \u0026quot;quickfix\u0026quot; 在当前文件的基础上增加一个对此文件的test文件,可以直接使用edit xxx_test.go的方式,会自动添加此包的package信息\n:edit main_test.go Testing 执行go的Test测试\n:GoTest #执行go test命令 :GoTestFunc #执行光标所在Test函数的测试 # init.vim 中可以设置go test超时时间,由于go test并不是异步的,所以需要有超时时间,万一test挂了呢 let g:go_test_timeout = '10s' 进一步简化go build与go test,根据文件的结尾内容和.go的后缀来判断该执行go test or go build\n\u0026quot; run :GoBuild or :GoTestCompile based on the go file function! s:build_go_files() let l:file = expand('%') if l:file =~# '^\\f\\+_test\\.go$' call go#test#Test(0, 1) elseif l:file =~# '^\\f\\+\\.go$' call go#cmd#Build(0) endif endfunction autocmd FileType go nmap \u0026lt;leader\u0026gt;b :\u0026lt;C-u\u0026gt;call \u0026lt;SID\u0026gt;build_go_files()\u0026lt;CR\u0026gt; 格式化go文件, 当保存的时候会自动格式化\n:GoFmt # 关闭保存文件时自动格式化 let g:go_fmt_autosave = 0 导入包\n:GoImport strings # 导入strings包 :GoImport s # 通过tab键联想出要导入的包 :GoImportAs # 导入包的同时重定义名字 :GoDrop # 卸载包 自动导入缺失的包移除不需要的包,就是工具goimports\n:GoImports #多了一个s # 也可增加配置,在save的时候自动执行goimports let g:go_fmt_command = \u0026quot;goimports\u0026quot; if 和 af 快捷方式 if: 当光标所在函数范围时,执行dif/vif/yif,分别表示删除、选中、复制函数内容,但不包括函数标题和函数注释 af: 与if相似但是会包括函数的标题和注释,将函数注释作为函数的doc信息 如果不想将函数的注释作为doc信息可以执行\nlet g:go_textobj_include_function_doc = 0 gS和gJ:分别用来将结构体表达式拆分成多行和合并成一行,需要安装如下插件\nPlug 'AndrewRadev/splitjoin.vim' Snippets:快速添加代码片段,推荐安装插件 Ultisnips and neosnippet.此处我使用的是 Ultisnips\nPlug 'SirVer/ultisnips' snippets 的tab键与YouComplete的tab键冲突\ngolang相关的snippets可以参见如下路径\n https://github.com/fatih/vim-go/blob/master/gosnippets/UltiSnips/go.snippets\n 举例:\nerrp: if err != nil { panic( ) ^ cursor position } type Foo struct { Name string # 此处输入json,则会自动添加`json:\u0026quot;name\u0026quot;` } Q: 发现一个问题就是在输入errp后没有提示信息,即没有一个列表显示可选项。但是直接输入tab确实能够生成代码,非常考验记忆力 A: 将go.snippets移动到~/.config/nvim/UltiSnips/go.snippets 中,好处可以通过维护自己的go.snippets\n 快捷键:tab-j 跳向下一个可修改参数 tab-k 跳向上一个可修改参数 默认情况下json字符串的格式是下划线的形式,如果想要使用驼峰式,则可以进行如下修改:\nlet g:go_addtags_transform = \u0026quot;camelcase\u0026quot; 高亮type、function、caller等等\nlet g:go_highlight_types = 1 let g:go_highlight_fields = 1 let g:go_highlight_fields = 1 let g:go_highlight_function_calls = 1 let g:go_highlight_operators = 1 let g:go_highlight_extra_types = 1 let g:go_highlight_build_constraints = 1 let g:go_highlight_generate_tags = 1 tab转成4空格,默认VIM使用的是8个空格的tab\nautocmd BufNewFile,BufRead *.go setlocal noexpandtab tabstop=4 shiftwidth=4 设置配色,注意安装之后,需要将colors/molokai 拷贝到 .vim/目录下,否则找不到配色 colorscheme molokai这句话必须放在plug#begin()plug#end()之外,否则提示找不到这个主题\nPlug 'fatih/molokai' Navigate Alternate file 在foo.go 和foo_test.go 文件之间跳转,即在source file 和 test file之间跳转\n:GoAlternate Go to definition 跳转到函数定义的位置\n:GoDef # shortcut ctrl+] 或者 gd 返回前一个浏览位置,只包括函数跳转\n:GoDefPop # shortcut ctrl+t 返回前一个浏览位置,包括移动的过程\nctrl+o # 如果执行了gd后又执行了滚屏等操作,或者move操作,则再执行ctrl+o是不会直接跳转到上一级函数的,而是一层层将之前的移动依次回退 前进到前一个位置即ctrl+o的反方向操作\nctrl+i 显示函数调用栈\n:GoDefStack 清除调用栈信息\n:GoDefStackClear Move between functions 当我们不知道要查找的函数或者只知道函数名字的部分信息时,可以使用如下命令:\n:GoDecls # 显示当前文件中的函数 :GoDeclsDir #显示文件夹中的函数,但是不包括子文件夹 显示如下信息可以进行查找 安装插件ctrlp.vim,说是GoDecls必要插件,但是不装也可以,还是装上吧,毕竟装上之后显示的内容更多\nPlug 'ctrlpvim/ctrlp.vim' 快速的函数移动\n]] -\u0026gt; jump to next function [[ -\u0026gt; jump to previous function 设置快捷键GoAlternate\nautocmd Filetype go command! -bang A call go#alternate#Switch(\u0026lt;bang\u0026gt;0, 'edit') autocmd Filetype go command! -bang AV call go#alternate#Switch(\u0026lt;bang\u0026gt;0, 'vsplit') autocmd Filetype go command! -bang AS call go#alternate#Switch(\u0026lt;bang\u0026gt;0, 'split') autocmd Filetype go command! -bang AT call go#alternate#Switch(\u0026lt;bang\u0026gt;0, 'tabe') Understand it Documentation lookup 获取函数的comment信息,可以通过如下命令:\n:GoDoc GoDoc只是显示光标所在函数的comment信息,并不是文档管理器,如果想要使用文档管理器可以安装go-explorer插件\nIdentifier resolution 我们在编码时经常需要知道函数的参数和返回值等信息,可以使用如下命令显示\n:GoInfo # shortcut autocmd FileType go nmap \u0026lt;Leader\u0026gt;i \u0026lt;Plug\u0026gt;(go-info) # 自动显示info信息,当光标停留在合法的描述符上后,会自动显示info信息 let g:go_auto_type_info = 1 # 设置停留时间 set updatetime=100 Identifier highlighting 有时候我们需要高亮某个标识符,此时可以使用如下命令:\n:GoSameIds 清除高亮\n:GoSameIdsClear 自动高亮设置\n:GoSameIdsAutoToggle # 临时生效 #或者永久生效配置init.vim let g:go_auto_sameids = 1 Dependencies and files :GoFiles :GoDeps Guru Guru是一个导航工具,一般由于查看代码使用。vim-go集成了一部分guru的功能。\n guru: https://golang.org/s/using-guru\n 显示光标所在标识符的所有引用,包括标识符的定义\n:GoReferrers 显示光标所在标识符的详细信息, 与GoInfo相似不过信息更多,如果是个结构体,包括结构体定义的方法等都会显示出来\n:GoDescribe 显示一个方法对应的实现了那个接口\n:GoImplements 显示err可能的值是哪些,如果是自定义的可能没办法获取,go自带的应该可以\n:GoWhicherr 显示channel分配,send、recv的信息\n:GoChannelPeer 显示调用者信息以及调用栈信息\n:GoCallees :GoCallers :GoCallstack Refactor it Remane 替换光标所在标识符的名称\n:GoRename bar 当函数比较复杂的时候,可以通过GoFreeVars抽出部分代码生成一个函数,GoFreevars会根据你的选择判断哪些变量定义是需要作为入参的\n# 先选中一段代码,在执行如下 :GoFreevars # quickfix中显示的内容就是函数需要的入参定义 Generate it 通过快捷方式,实现快速定义某个接口的方法\n# 先定义一个struct, 光标移至此struct执行如下命令 :GoImpl 智能补全功能 通过安装coc.vim插件,并配置coc-setting.json文件\n{ \u0026quot;languageserver\u0026quot;: { \u0026quot;golang\u0026quot;: { \u0026quot;command\u0026quot;: \u0026quot;gopls\u0026quot;, \u0026quot;rootPatterns\u0026quot;: [\u0026quot;go.mod\u0026quot;], \u0026quot;disableWorkspaceFolders\u0026quot;: true, \u0026quot;filetypes\u0026quot;: [\u0026quot;go\u0026quot;] } } } 需要注意的是gopls必须是能够直接被调用的否则coc.vim插件执行gopls命令会失败\n显示函数和变量 tagbar\n模糊查找工具fzf fzf\nairline 提示文件类型,名称等信息插件 airline\nmarkdown插入剪切板图片 通过nvim编写markdown文章,但是导入图片却是个麻烦事儿,从网上找打vim插件,可以直接一键导入image\n\u0026quot;https://github.com/ferrine/md-img-paste.vim Plug 'ferrine/md-img-paste.vim' autocmd FileType markdown nmap \u0026lt;buffer\u0026gt;\u0026lt;silent\u0026gt; \u0026lt;leader\u0026gt;p :call mdip#MarkdownClipboardImage()\u0026lt;CR\u0026gt; \u0026quot; there are some defaults for image directory and image name, you can change them let g:mdip_imgdir = '.' let g:mdip_imgname = 'image' let g:mdip_imgdir_absolute = '/{xxx}/Blog/static/images' let g:mdip_imgdir_intext = '/images' mdip_imgname:表示不输入图片时的默认名称 mdip_imgdir:表示输入名称的前缀 p:表示插入剪切板的内容到markdown文件中,并提示输入名称 mdip_imgdir_absolute: 表示图片保存的绝对路径,由于我的图片存储位置与markdown中输入的内容不同,所以使用相对位置等方式或者让mdip_imgdir即表示markdown中内容又表示文件保存位置是不可行的,所以使用绝对路径和markdown中前缀名称 mdip_imgdir_intext: 表示markdown文本中的前缀名称 规则:如果存在absolute,则使用absolute路径代替imgdir路径;如果没有intext路径,则markdown中使用imgdir表示的路径。 参考https://www.cnblogs.com/mazhuang/p/12863702.html\n 系统剪切板 neovim 是不支持与系统剪切板互通的,可以通过如下设置开启 set clipboard+=unnamedplus # 需要安装剪切工具 # Linux sudo pacman -S xsel # MacOs brew pbcopy ","id":14,"section":"posts","summary":"种草很久的neovim工具使用 由于一直羡慕大神们用vim工具快速的编辑代码,并且羡慕能够不断优化适合自己的IDE工具。所以选择了neovim","tags":["neovim","golang"],"title":"Golang IDE工具搭建(neovim篇)","uri":"https://neilliu9891.github.io/2020/10/20201005-nvim/","year":"2020"},{"content":"github 显示照片 在访问别人的github项目时,阅读Readme经常发现不能显示照片。后来查看发现是由于picture所存储的图床地址不能通过dns解析\n修改方法 查找picture对应的服务器地址 根据服务器地址,通过https://site.ip138.com/ 网址查询网页获取这个服务器地址所对应的ip地址 更新/etc/hosts 文件,将ip地址与服务器地址对应,写入/etc/hosts中。如果是windows系统则修改C:\\Windows\\System32\\drivers\\etc\\hosts地址 参考链接:\n https://blog.csdn.net/qq_38232598/article/details/91346392 https://blog.csdn.net/dplovel/article/details/107356603\n 查询网址:\n https://site.ip138.com/\n ","id":15,"section":"posts","summary":"github 显示照片 在访问别人的github项目时,阅读Readme经常发现不能显示照片。后来查看发现是由于picture所存储的图床地址不能通过dn","tags":["git"],"title":"Github ShowPicture","uri":"https://neilliu9891.github.io/2020/10/20201005-githubshowpicture/","year":"2020"},{"content":"It\u0026rsquo;s a draft 他只是个草稿!!!\n没想到我的第一篇博客诞生的如此坎坷,Hugo 是一个很便的博客搭建程序,怎奈自己的技术水平有限,导致 About 页面一直刷不出来。 究其原因,就是我的一个 draft:true 搞的鬼!\n 被标记为 draft:true 的文件不会被展示 如果 About 目录或者 Posts 目录下没有可以展示的内容,则会返回 404 错误 反思:\n 英语是个好东西,至少对于编程来说是的 弄懂原理一切都不会太难,关键是要有耐心研究它 ","id":16,"section":"posts","summary":"It\u0026rsquo;s a draft 他只是个草稿!!! 没想到我的第一篇博客诞生的如此坎坷,Hugo 是一个很便的博客搭建程序,怎奈自己的技术水平有限,导致 About 页面一直刷不出来","tags":null,"title":"我的第一篇Blog:It's a draft","uri":"https://neilliu9891.github.io/2020/09/20200928-draft/","year":"2020"}],"tags":[{"title":"docker","uri":"https://neilliu9891.github.io/tags/docker/"},{"title":"encode/xml","uri":"https://neilliu9891.github.io/tags/encode/xml/"},{"title":"git","uri":"https://neilliu9891.github.io/tags/git/"},{"title":"golang","uri":"https://neilliu9891.github.io/tags/golang/"},{"title":"neovim","uri":"https://neilliu9891.github.io/tags/neovim/"},{"title":"ovn","uri":"https://neilliu9891.github.io/tags/ovn/"},{"title":"ssh","uri":"https://neilliu9891.github.io/tags/ssh/"},{"title":"Work","uri":"https://neilliu9891.github.io/tags/work/"},{"title":"交叉编译","uri":"https://neilliu9891.github.io/tags/%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91/"},{"title":"光","uri":"https://neilliu9891.github.io/tags/%E5%85%89/"},{"title":"时间管理","uri":"https://neilliu9891.github.io/tags/%E6%97%B6%E9%97%B4%E7%AE%A1%E7%90%86/"},{"title":"消息中间件","uri":"https://neilliu9891.github.io/tags/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/"},{"title":"链接","uri":"https://neilliu9891.github.io/tags/%E9%93%BE%E6%8E%A5/"}]}