关于go:GoLand-go-mod配置

以下是我应用中的一些问题经验总结:前置条件: win10零碎go版本:go version go1.19 windows/amd64问题:在 零碎 cmd 窗口中看到的 mod 启用配置为: 而后再 goland 中查看: 所以我在应用go get装置第三方包时,会找不到; 解决:在 goland 中勾选 Enable Go modules integration 须要重启一下 goland 以便应用 go env GO111MODULE 显示为 on 。 参考文档: Enable Go modules integration在做什么

March 19, 2023 · 1 min · jiezi

关于go:使用go切片应注意的一个小问题copy函数的重要性

起因是在写一个函数的时候 func (g *Grammar) extractCommonFactors() { flag := false newNonTerminalCounter := 0 for i, production := range g.Productions { newAlternatives := []Alternative{} prefixMap := make(map[Symbol][]Alternative) for _, alternative := range production.Right { if len(alternative.Symbols) > 0 { firstSymbol := alternative.Symbols[0] if _, ok := prefixMap[firstSymbol]; !ok { prefixMap[firstSymbol] = []Alternative{} } prefixMap[firstSymbol] = append(prefixMap[firstSymbol], alternative) } } for _, alternatives := range prefixMap { if len(alternatives) > 1 { flag = true commonPrefix := findLongestCommonPrefix(alternatives) newNonTerminalCounter++ newValue := fmt.Sprintf("A%d", newNonTerminalCounter) newNonTerminal := Symbol{ Value: newValue, IsTerminal: false, } newRight := removeCommonPrefix(alternatives, commonPrefix) newProduction := Production{ Left: newNonTerminal, Right: newRight, } g.Productions = append(g.Productions, newProduction) var newAlternative Alternative newAlternative.Symbols = commonPrefix //bug呈现的中央 newAlternative.Symbols = append(newAlternative.Symbols, newNonTerminal) newAlternatives = append(newAlternatives, newAlternative) } else { newAlternatives = append(newAlternatives, alternatives[0]) } } g.Productions[i].Right = newAlternatives } if flag { fmt.Println("extractCommonFactors grammar:") for _, prod := range g.Productions { rightParts := make([]string, len(prod.Right)) for i, alt := range prod.Right { symbols := make([]string, len(alt.Symbols)) for j, sym := range alt.Symbols { symbols[j] = sym.Value } rightParts[i] = strings.Join(symbols, "") } fmt.Printf("%s -> %s\n", prod.Left.Value, strings.Join(rightParts, "|")) } }}发现每次在执行 ...

March 18, 2023 · 1 min · jiezi

关于go:Go-Get-安装文档

Golang 装置第三方包

March 18, 2023 · 1 min · jiezi

关于go:GO-env环境变量配置

Go env环境变量配置能够用 go env 查看以后的go环境变量,如下图所示:

March 18, 2023 · 1 min · jiezi

关于go:去深圳见了几个大佬我悟了……

大家好,我是良许。 回到福州 8 个月,第一次以出差的形式回了趟广州/深圳。 这一趟出行,见到了几个老朋友,也结交了几个新大佬,最重要的是悟到了一个真谛,原来我之前走了太多弯路了…… 第一站:深圳,见到了久仰已久的韦东山老师,并对产品、课程、服务进行了深刻的交换。 之前在广州的时候就想访问他,但因为疫情而无奈前行。 第二站:广州,必须去访问老友——晚点原子,与洋哥、军哥、左盟主都见了个遍。 老读者都晓得,我始终都跟晚点原子团队有着十分好的关系,我也在公众号里屡次举荐过他们。 顺便说一下,大家如果买晚点原子的板子,只有说是良许介绍的,就会间接打折!没错,良许是能够用来刷脸的! 我刚开始进军自媒体的时候,晚点原子团队就给了我很多的反对与帮忙,而且当年我自学转行的时候,看的也是他们的视频,所以我对他们团队始终很感恩。 而且,他们对我的帮忙都是自私的,不谋求任何的回报,我真的是十分荣幸能跟如此有格局的老板成为敌人! 在广深的这 3 天,还见到了几个老友,比方:吴师兄、帅地、卡神、张晓宇,等等。 他们这几个屌毛,一个比一个赚得比我多,一个个又在我背后哭穷,想抽他们的心都有了。 通过与他们深刻交换,我终于晓得为何他们赚得比我多。 原来我之前走了太多的弯路了! 你们别看我表面光鲜亮丽,跟各种大佬见面,但实际上,我心田的纠结、徘徊、焦虑,是你们所设想不到的。 在流量与内容之间,我就做错了抉择,但这个抉择是无奈的。 在之前,我始终埋怨公众号改版,导致我浏览量降落。但实际上,那些放弃高质量原创的公众号,浏览量仍然还是很坚挺。 说得直白一些,就是我的内容太少了! 尽管我全网有 50 多万的粉丝,但实际上,很多人其实对我没有什么记忆点的。 尽管我也写了两三百篇原创文章,但这些文章大多数不成零碎,东一点西一点,零零散散,看完之后基本没印象。 而真正搞内容的人,基本上都是成体系的。 比方卡神,在 B 站上更新了 140 多个 LeetCode 刷题视频,粉丝对他的认可度十分高。 再如小林Coding,更新了很多计算机基础知识,这些内容也间接让他原地腾飞。 而对于我——良许,你们能记住我啥? 我始终更新 Linux 的文章,后果——很多人认为我是搞运维的,但实际上,我是嵌入式 Linux 开发工程师! 这就是我这些年做自媒体最大的弯路,太塌实于搞流量,而疏忽了高质量内容的生产。 实际上,我其实也明确自媒体内容为王的情理,但做自媒体都会有流量焦虑,而我就始终深陷于流量焦虑之中。 还有一个货色是我所疏忽的,那就是——好内容自带流量。 再比方这次新见到的大佬——程序员猿白瑞,他也是专一于搞好内容,没有任何的套路,所以他的流量也十分牛逼,直播的时候有人一眼就认出了他。 在出差之前,我就曾经定下了指标:往年好好搞内容。在这次广深之行之后,更加动摇了我做好内容的想法。 也是基于这点,我决定近期将我去年录制的 Linux 命令课程开源进去。不为了赚钱,而是纯正为了分享。 当你不为流量而分享的时候,流量就自然而然找你而来。越是想搞流量,流量可能越离你而去。 这也是大佬们常说的,越分享,越侥幸。怪不得那么多大佬都在无偿分享,外面的起因只有本人经验过才晓得。 这也是我此次出行最大的感悟。 说这些货色,有点像把本人的不足之处展现给大家,但也没关系,我对我的粉丝敌人都是十分真挚的,我也心愿让大家看到我的成长。 我也曾经做好布局了,新课程争取在这个月内录完,之后就致力做内容,大家刮目相待吧!

March 18, 2023 · 1 min · jiezi

关于go:2023Q1-公众号内容全部整合到-Github-啦

我的项目地址: https://github.com/duanbiaowu/go-examples-for-beginners

March 17, 2023 · 1 min · jiezi

关于go:Goravel-ORM-新增模型关联用-Golang-写关联也可以跟-Laravel-简单

对于 GoravelGoravel 是一个性能齐备、具备良好扩大能力的 Web 应用程序框架。作为一个起始脚手架帮忙 Golang 开发者疾速构建本人的利用。框架格调与 Laravel 保持一致,让 PHPer 不必学习新的框架,也能够欢快的玩转 Golang! ORM 模块上新「模型关联」,好用的飞起,有码有假相! ### 定义模型 一个用户能够公布多篇文章 type User struct { orm.Model Name string Posts []*Post}type Post struct { orm.Model UserID uint Name string}查找(同时预加载关联模型)var user models.Userfacades.Orm.Query().With("Post").Find(&user)// 预加载多个关联模型(嵌套预加载)facades.Orm.Query().With("Post").With("Phone.Contact").Find(&user)// 为预加载增加束缚facades.Orm.Query().With("Post", "name = ?", "author").Find(&user)facades.Orm.Query().With("Post", func(query orm.Query) orm.Query { return query.Where("name = ?", "author")}).Find(&book)提早预加载var user models.Userfacades.Orm.Query().Find(&user)facades.Orm.Query().Load(&user, "Post")// 为预加载增加束缚facades.Orm.Query().Load(&book, "Post", "name = ?", "author").Find(&book)facades.Orm.Query().Load(&book, "Post", func(query orm.Query) orm.Query { return query.Where("name = ?", "author")}).Find(&book)创立(同时创立关联模型)user := User{Name: "user", Post: &Post{Name: "post"}}// 创立 User 的同时创立所有子关联facades.Orm.Query().Select(orm.Associations).Create(&user)更多应用细节请 查看文档,Welcome Star, PR and Issues! ...

March 16, 2023 · 1 min · jiezi

关于go:用-Go-写一个简单消息队列六服务器实现

本篇咱们实现音讯队列的收尾工作,将咱们的服务器搭建起来。咱们的构想是消费者通过 tcp 连贯到服务端,生产者则是通过 http 协定间接发送音讯。 监听消费者连贯在 server 目录下新建一个 tcp.go 文件,用于监听消费者的 tcp 连贯,代码如下: package serverimport ( "context" "log" "net")func TcpServer(ctx context.Context, addr, port string) { fqAddress := addr + ":" + port listener, err := net.Listen("tcp", fqAddress) if err != nil { panic("tcp listen(" + fqAddress + ") failed") } log.Printf("listening for clients on %s", fqAddress) for { select { case <-ctx.Done(): return default: conn, err := listener.Accept() if err != nil { panic("accept failed: " + err.Error()) } client := NewClient(conn, conn.RemoteAddr().String()) go client.Handle(ctx) } }}每当监听到 tcp 连贯,就新建一个 client 来解决,同时传入一个 context 不便对立进行退出治理。client 和 protocol 也都要同时加上对这个 context 的监听代码,具体可参考代码仓库。 ...

March 16, 2023 · 5 min · jiezi

关于go:用-Go-写一个简单消息队列五协议与后台队列实现

协定本篇咱们先来实现一下协定,所谓的协定,说白了就是规定了消费者的行为,并将这些行为转化成对 topic、channel 或者 message 的操作。例如,客户端发送 SUB order pay,咱们就创立一个名为 order 的 topic,再在 topic 上面创立一个名为 pay 的 channel,最初将该客户端与该 channel 绑定,后续该客户端就能接管到生产者的音讯了。 现阶段咱们筹备实现四种协定,别离是 SUB(订阅)、GET(读取)、FIN(实现)和 REQ (重入),有需要的话前期再加。因为这种不确定性,咱们能够在运行时再确定咱们要调用的办法,这样能够保障不必改变咱们的外围代码。所以咱们在这里应用 Go 语言的反射机制来实现协定的外围代码,等到前期协定成熟了之后,咱们能够用 switch 重构外围,毕竟反射的性能还是比拟差的。 咱们先来定义一些客户端的谬误以及须要用到的常量和接口等等,对立放在 protocol/client_error 文件下了: package protocolimport ( "io")const ( ClientInit = iota ClientWaitGet ClientWaitResponse)type StatefulReadWriter interface { io.ReadWriter GetState() int SetState(state int) String() string}type ClientError struct { errStr string}func (e ClientError) Error() string { return e.errStr}var ( ClientErrInvalid = ClientError{"E_INVALID"} ClientErrBadTopic = ClientError{"E_BAD_TOPIC"} ClientErrBadChannel = ClientError{"E_BAD_CHANNEL"} ClientErrBadMessage = ClientError{"E_BAD_MESSAGE"})执行代码咱们来看一下协定的执行代码: ...

March 15, 2023 · 6 min · jiezi

关于go:用-Go-写一个简单消息队列四topic-设计

上一篇文章中咱们曾经实现了 channel 的设计,这里咱们持续实现 topic 的设计工作。 topic字段设计topic 的作用是接管客户端的音讯,而后同时发送给所有绑定的 channel 上,所以它的设计和 channel 很相似,蕴含的字段有: name:名称newChannelChan:新增 channel 的管道channelMap:保护的 channel 汇合incomingMessageChan:接管音讯的管道msgChan:有缓冲管道,相当于音讯的内存队列readSyncChan:和 routerSyncChan 配合应用保障 channelMap 的并发平安routerSyncChan:见上exitChan:接管退出信号的管道channelWriteStarted:是否已向 channel 发送音讯topic 工厂咱们须要保护一个全局的 topic map,在消费者订阅时生成新的 topic,相似于一个工厂,逻辑与第一篇中生成 uuid 相似: 注:Router 是 topic 的事件处理办法,详情见后文。 var ( TopicMap = make(map[string]*Topic) newTopicChan = make(chan util.ChanReq))func NewTopic(name string, inMemSize int) *Topic { topic := &Topic{ name: name, newChannelChan: make(chan util.ChanReq), channelMap: make(map[string]*Channel), incomingMessageChan: make(chan *Message), msgChan: make(chan *Message, inMemSize), readSyncChan: make(chan struct{}), routerSyncChan: make(chan struct{}), exitChan: make(chan util.ChanReq), } go topic.Router(inMemSize) return topic}func GetTopic(name string) *Topic { topicChan := make(chan interface{}) newTopicChan <- util.ChanReq{ Variable: name, RetChan: topicChan, } return (<-topicChan).(*Topic)}func TopicFactory(inMemSize int) { var ( topicReq util.ChanReq name string topic *Topic ok bool ) for { topicReq = <-newTopicChan name = topicReq.Variable.(string) if topic, ok = TopicMap[name]; !ok { topic = NewTopic(name, inMemSize) TopicMap[name] = topic log.Printf("TOPIC %s CREATED", name) } topicReq.RetChan <- topic }}保护 channeltopic 保护 channel 的逻辑和 channel 保护消费者类似,也是“老熟人” chan + slice 的组合: ...

March 14, 2023 · 3 min · jiezi

关于go:Go-遥测将可选加入Google-收集数据的黑历史对-Go-有负面影响

大家好,我是煎鱼。 之前我写过一篇《Go 工具链想被动上报应用数据,你违心吗?》文章分享。外围形容的是以下这件事。 Go 外围团队负责人 Russ Cox(下称 rsc)想要抽样采集用户的应用数据、性能信息等,便于 Go 团队更好的开发和调研。 文章的投票后果: Go 遥测开还是不开近日 Go 团队曾经在《Opting In to Transparent Telemetry》中敲定了大方向,将会把 Go 工具链的遥测设计为可抉择退出(默认敞开)的形式。 也就是不会强制开启遥测,咱们的数据不会被被动上报和采集。 另一方面,做了这个斗争会带来两个新的老本问题,别离是: Go 团队须要继续的教育和疏导用户,抉择退出遥测是一个很好的抉择,对大家都有好处。因为是可选的,意味着能采集上报上来的数据是比拟少的,因而施加在任何特定用户身上的遥测老本较高。简略来讲,就是数据少了。得想方法多要些,能力有真正的数据价值。 Go 遥测疏导策略遥测改为抉择退出后,如何教育和疏导用户退出遥测,变成一个关键问题(间接影响数据多少)。 Go 团队将打算施行如下: 在 Go 的图形装置过程中,有两个不同的选项按钮,用于抉择是否退出遥测。在新 Go 版本的博客文章和发行阐明中进行疏导。在每年的 Go 用户考察期间进行疏导。在 VS Code 上第一次调用 Go 代码时进行抉择和疏导。在具体的大会和分享上演讲、解释和激励用户退出。这些数据是 Go 团队决策过程的一个输出,而不是决定因素。有数据简直总比没有数据好。因而也不必过于放心被上报数据的同学所齐全代表。 与日俱增的数据量大了后,指不定当前 Go 还能搞个数据智能的饼进去。(doge Google 的黑历史影响 Go贯通这个探讨和探讨的一个点,Google 有过收集用户隐衷信息的黑历史,也就是在 Chrome 和应用程序中收集了用户数据,这些数据会被用做用户画像,被用于有针对性的个性化广告营销等行为。 登陆了后更刺激,地位信息等都是可选被分享的。 基于 Google 和 Go 之间的关系,让 rsc 在 Go GitHub 的探讨中,受到了不少的无建设价值的各类攻打,这个探讨也因而蒙上了一层灰。 Go 这方面还是很稳的,广泛没有因而和对方拉开骂战和 PK,只是大部分抉择忽视这类情绪化发言和批评。这点值得咱们好好学习了。 ...

March 14, 2023 · 1 min · jiezi

关于go:golang-如何处理栈

前言咱们先看看其余语言是如何解决栈,在Java中,默认的栈大小是1M,能够通过-XX:ThreadStackSize参数管制,当新建一个线程时,会向内核申请指定的栈大小。然而固定的栈大小如果设置过大,可能会节约内存,设置过小,可能导致栈溢出。在golang 语言中,goroutine 是轻量的用户级协程,使用者不须要有很大的累赘,不必像Java,C++一样设置线程池来解决工作。所以一个利用能够开启上万个goroutine。go 采纳的就是按需分配,默认每个栈大小为2k,当空间不够,能够 "栈增长"。 一个协程的栈空间次要对应办法执行过程中的栈帧的压栈出栈。每个办法对应一个栈帧 int a(int m, int n) { if (m == 0) { return n + 1; } else if (m > 0 && n == 0) { return a(m - 1, 1); } else { return a(m - 1, a(m, n - 1)); }}如下办法将产生极大的递归调用,从而导致栈空间特地大,如果采纳固定栈大小,和容易栈溢出。 golang 在晚期采纳分段栈的形式来进行栈增长,然而会带来hot split 问题,栈拷贝解决了该问题,目前默认是采纳栈拷贝。 分段栈(segment stack)goroutine 初始化是默认是2k的栈空间,那么是如何检测到栈空间不够?其实编译器会在每个办法的入口处,插入一个办法 morestack判断是否须要栈增长,当栈帧一直出栈,goroutine 曾经不须要应用那么多栈空间,编译器在办法的返回处插入一个办法lessstack用来判断是否膨胀栈大小。 如下图所示,当执行Foobar办法时,进行了栈增长,那么此时会栈决裂(stack split),在新的栈分段的栈底压入一个stack info ,记入上一个栈分段的信息(包含地址等),而后压入lessstack 和 Foobar栈帧,若Foobar 执行完并没有新的栈帧入栈,那么会执行lessstack 进行膨胀。 go build -gcflags -S main.go //查看汇编代码,能够看到morestack +---------------+ | | | unused | | stack | | space | +---------------+ | Foobar | | | +---------------+ | | | lessstack | +---------------+ | Stack info | | |-----+ +---------------+ | | | +---------------+ | | Foobar | | | | <---+ +---------------+ | rest of stack | | |Hot split?如上若是,如果Foobar 办法是在一个循环中调用,那么会导致该goroutine 的栈空间频繁的增长和膨胀,这个对性能损耗是极大的,这个就是 hot split 问题。咱们来看看栈拷贝是如何解决 ...

March 13, 2023 · 1 min · jiezi

关于go:用-Go-写一个简单消息队列三增加完成确认和重入队列功能

上一篇文章咱们做了 channel 的根底设计,这一篇文章咱们来实现音讯队列两个必备的性能:实现确认和队列重入。 实现确认咱们都晓得,在分布式系统的消息传递过程中有三种语义,也就是三种的质量保证: At most once: 至少一次。音讯在传递时,最多会被送达一次。换一个说法就是,没什么音讯可靠性保障,容许丢音讯。At least once: 至多一次。音讯在传递时,至多会被送达一次。也就是说,不容许丢音讯,然而容许有大量反复音讯呈现。Exactly once:恰好一次。音讯在传递时,只会被送达一次,不容许失落也不容许反复。显然 Exactly once 语义是咱们最心愿零碎可能提供的,然而它实现起来非常复杂,所以绝大部分音讯队列零碎提供的都是 At least once 语义,而后用幂等性来保障业务的正确性。而 At least once 语义最常见的实现形式就是接管方在接管到音讯后进行回复确认,相似 TCP 握手中的 ACK。 要达到这个目标,咱们首先要在 channel 中保护一个 map 用于存储曾经发送的音讯,以及一个管道来辅助写入: type Channel struct { ... inFlightMessageChan chan *Message inFlightMessages map[string]*Message}在发送音讯的时候,咱们也往 inFlightMessageChan 中写入,同时在事件处理函数 Router 中减少对该管道的监听,将接管到的音讯增加到 inFlightMessages 中: func (c *Channel) pushInFlightMessage(msg *Message) { c.inFlightMessages[util.UuidToStr(msg.Uuid())] = msg}func (c *Channel) popInFlightMessage(uuidStr string) (*Message, error) { msg, ok := c.inFlightMessages[uuidStr] if !ok { return nil, errors.New("UUID not in flight") } delete(c.inFlightMessages, uuidStr) return msg, nil}// Router handles the events of Channelfunc (c *Channel) Router() { ... go c.RequeueRouter(closeChan) go c.MessagePump(closeChan) ...}func (c *Channel) RequeueRouter(closeChan chan struct{}) { for { select { case msg := <-c.inFlightMessageChan: c.pushInFlightMessage(msg) case <-closeChan: return } }}func (c *Channel) MessagePump(closeChan chan struct{}) { for { ... if msg != nil { c.inFlightMessageChan <- msg } c.clientMessageChan <- msg }}接下来编写实现确认相干逻辑,还是在 channel 构造体中增加一个管道 finishMessageChan,提供写入办法,并减少相干事件处理逻辑,代码如下: ...

March 13, 2023 · 3 min · jiezi

关于go:Go工具箱GoCSV包一个能将结构体和csv内容互转的工具

大家好,我是渔夫子。本号新推出「Go工具箱」系列,意在给大家分享应用go语言编写的、实用的、好玩的工具。同时理解其底层的实现原理,以便更深刻地理解Go语言。 大家在开发中肯定遇到过将数据导出成csv格式文件的需要。go规范库中的csv包是只能写入字符串类型的切片。而在go中个别都是将内容写入到构造体中。所以,若应用规范的csv包,就须要将构造体先转换成对应的字符串类型,再写入文件。那可不可以将构造体对象间接输入成csv格局内容呢? 明天给大家举荐的就是一个能将构造体和csv内容进行疾速互转的工具包:gocsv gocsv小档案gocsv 小档案 star1.5 kused by1.6kcontributors80作者gocarina性能简介提供一个简略、高效地将csv内容和构造体进行互转的性能 我的项目地址https://github.com/gocarina/gocsv 相干常识reflect、构造体tag gocsv的基本功能gocsv包的最根本的作用就是可能不便的将csv内容转换到对应的构造体上,或者将构造体的内容疾速的转换成csv格局(包含写入文件)。 gocsv.UnmarshalFile函数:csv内容转成构造体假如文件中的内容如下: client_id,client_name,client_age1,Jose,422,Daniel,263,Vincent,32而后从文件中读取出内容,并间接转换到构造体Client上,如下: package mainimport ( "fmt" "os" "github.com/gocarina/gocsv")type NotUsed struct { Name string}type Client struct { // Our example struct, you can use "-" to ignore a field Id string `csv:"client_id"` Name string `csv:"client_name"` Age string `csv:"client_age"` NotUsedString string `csv:"-"` NotUsedStruct NotUsed `csv:"-"` }func main() { clientsFile, err := os.OpenFile("clients.csv", os.O_RDWR|os.O_CREATE, os.ModePerm) if err != nil { panic(err) } defer clientsFile.Close() clients := []*Client{} if err := gocsv.UnmarshalFile(clientsFile, &clients); err != nil { // Load clients from file panic(err) } for _, client := range clients { fmt.Println("Hello", client.Name) }}gocsv.MarshalFile函数:构造体转成csv文件package mainimport ( "fmt" "os" "github.com/gocarina/gocsv")type NotUsed struct { Name string}type Client struct { // Our example struct, you can use "-" to ignore a field Id string `csv:"client_id"` Name string `csv:"client_name"` Age string `csv:"client_age"` NotUsedString string `csv:"-"` NotUsedStruct NotUsed `csv:"-"` }func main() { clientsFile, err := os.OpenFile("clients.csv", os.O_RDWR|os.O_CREATE, os.ModePerm) if err != nil { panic(err) } defer clientsFile.Close() clients := []*Client{} clients = append(clients, &Client{Id: "12", Name: "John", Age: "21"}) // Add clients clients = append(clients, &Client{Id: "13", Name: "Fred"}) clients = append(clients, &Client{Id: "14", Name: "James", Age: "32"}) clients = append(clients, &Client{Id: "15", Name: "Danny"}) err = gocsv.MarshalFile(&clients, clientsFile) // Use this to save the CSV back to the file if err != nil { panic(err) }}自定义类型转换器gocsv包还能够给自定义的构造体类型定义csv和构造体的互转函数。只有自定义的类型实现如下接口即可: ...

March 13, 2023 · 3 min · jiezi

关于go:用-Go-写一个简单消息队列二客户端处理和-channel-设计

上一篇文章咱们定义了音讯体和根底工具,这一篇咱们开始着手客户端的处理函数和 channel 的根底设计。 客户端处理函数这里所谓的客户端指的是消费者,处理函数也就是解决消费者同咱们服务之间的 tcp 连贯。咱们定义一个构造体 Client,外面蕴含有连贯和状态字段,而后就是编写读写状态和 tcp 连贯的相干函数。 client.go package serverimport ( "encoding/binary" "io" "log")type Client struct { conn io.ReadWriteCloser name string state int}func NewClient(conn io.ReadWriteCloser, name string) *Client { return &Client{conn, name, -1}}func (c *Client) String() string { return c.name}func (c *Client) GetState() int { return c.state}func (c *Client) SetState(state int) { c.state = state}func (c *Client) Read(data []byte) (int, error) { return c.conn.Read(data)}func (c *Client) Write(data []byte) (int, error) { var err error err = binary.Write(c.conn, binary.BigEndian, int32(len(data))) if err != nil { return 0, err } n, err := c.conn.Write(data) if err != nil { return 0, err } return n + 4, nil}func (c *Client) Close() { log.Printf("CLIENT(%s): closing", c.String()) c.conn.Close()}这里的逻辑比较简单,惟一值得一提的是 Write 办法。在给消费者写音讯之前,咱们先往连贯中写入音讯体的长度,固定为 4 个字节,这样客户端读取的时候就能够先读取长度,而后按长度读取音讯。 ...

March 13, 2023 · 3 min · jiezi

关于go:用-Go-写一个简单消息队列一定义消息和基础工具

概述音讯队列置信大家都不生疏,平时在一些须要解耦、高并发的场景下常常能看见它们的身影,Kafka、RabbitMQ 这些罕用的音讯队列当初甚至曾经成为后端程序员的必须技能了。那么一个音讯队列的根底性能有哪些,是如何实现的?当初咱们就用 Go 语言来实现一个简略的单机版音讯队列,借以理解音讯队列的原理。 这个音讯队列的实现参考了 Go 语言中最受欢迎的音讯队列 nsq 的实现,取名就叫 smq (simple message queue),代码曾经上传到 Github 上:https://github.com/yhao1206/SMQ。为了尽可能简洁明了,这个音讯队列的性能都很根底。因为工夫紧迫,加上自己也是 Go 语言的菜鸟,程度无限,有什么谬误或是倡议,欢送一起探讨。 次要组件topic:一个 topic 就是程序公布音讯的一个逻辑键,当程序第一次公布音讯时就会创立 topic。channel:channel 与消费者相干,每当一个发布者发送一条音讯到一个 topic,音讯会被复制到 topic 上面所有的 channel 上,消费者通过 channel 读取音讯。同一个 channel 能够由多个消费者同时连贯,以达到负载平衡的成果。message:s音讯是数据流的形象,消费者能够抉择完结音讯,表明它们已被失常解决,或者从新将它们排队待到前面再进行解决。smqd:smqd 是一个守护过程,负责接管,排队,投递音讯给客户端。topic、channel、consumer 之间的关系能够参考下图:话不多说,让咱们直入主题,首先第一步就是定义音讯。 音讯咱们定义的音讯就是一般的字节数组,前 16 位是 uuid,用作音讯的惟一标识,前面实现和重排音讯时须要用到。前面就是音讯自身的内容,还提供了几个导出的封装办法。 message.go package messagetype Message struct { data []byte}func NewMessage(data []byte) *Message { return &Message{ data: data, }}func (m *Message) Uuid() []byte { return m.data[:16]}func (m *Message) Body() []byte { return m.data[16:]}func (m *Message) Data() []byte { return m.data}工具库音讯体须要 uuid 作为惟一标识,那么咱们须要一个 uuid 生成器,咱们间接应用一个工厂,一直地朝一个 chan 中写入uuid,代码如下: ...

March 13, 2023 · 1 min · jiezi

关于go:go官方的一个bug导致不会主动关闭http-Request-Body

代码版本:go1.20.2咱们晓得在用go的http client时不须要咱们被动敞开Request Body,上面你Client.Do的源码应用阐明: //src/net/http/client.go//...// 这里说了底层的Transport会被动敞开request Body// The request Body, if non-nil, will be closed by the underlying// Transport, even on errors.//// ...func (c *Client) Do(req *Request) (*Response, error) { return c.do(req)}咱们在我的项目中须要用到这个个性,就是须要client被动帮忙咱们敞开request.Body,然而咱们发现协程泄露,最初定位到可能是因为request.Body没有被被动敞开导致.难懂是官网的形容有问题吗?最初咱们在github issue中看到了有人提出request.Body在特定状况下不会被关掉的场景, 最初官网也进行了修复. 咱们先来看下这个issue(https://github.com/golang/go/issues/49621):他还写了演示示例(https://play.golang.com/p/lku8lEgiPu6)重点次要是这上图中说的在writeLoop()里,可能pc.writech和pc.closech都有内容,然而执行到了<-pc.closech导致Request.Body没有被close咱们先来看下writeLoop()源码,重点看下中文正文: //src/net/http/transport.gofunc (pc *persistConn) writeLoop() { defer close(pc.writeLoopDone) for { select { case wr := <-pc.writech: startBytesWritten := pc.nwrite // 这外面会去敞开Request.Body,具体细节就不去看了 err := wr.req.Request.write(pc.bw, pc.isProxy, wr.req.extra, pc.waitForContinue(wr.continueCh)) if bre, ok := err.(requestBodyReadError); ok { //... } if err == nil { err = pc.bw.Flush() } if err != nil { if pc.nwrite == startBytesWritten { err = nothingWrittenError{err} } } pc.writeErrCh <- err // to the body reader, which might recycle us wr.ch <- err // to the roundTrip function if err != nil { pc.close(err) return } case <-pc.closech: //间接退出 return } }}咱们能够看到如果失常申请下须要进入到case wr := <-pc.writech才会对request进行操作,才会在外面close request.Body.如果case wr := <-pc.writech和case <-pc.closech都满足,然而进入到了case <-pc.closech就会导致request.Body不会被敞开。那么这种状况在什么时候会产生了呢? ...

March 11, 2023 · 2 min · jiezi

关于go:go-v120-使用influxdb124-老版本-查询数据

package mainimport ( "encoding/json" "fmt" "github.com/gin-gonic/gin" client "github.com/influxdata/influxdb1-client/v2" "net/http" "os" "time")// 创立一个clientfunc ExampleClient()client.Client { // NOTE: this assumes you've setup a user and have setup shell env variables, // namely INFLUX_USER/INFLUX_PWD. If not just omit Username/Password below. cli, err := client.NewHTTPClient(client.HTTPConfig{ Addr: "http://10.xx.x5.x5:8086", Username: os.Getenv("ltx"), Password: os.Getenv("x6xxx"), }) if err != nil { fmt.Println("Error creating InfluxDB Client: ", err.Error()) } return cli}// 把数据写入influxdbfunc ExampleClient_write(cli client.Client) { // Make client c, err := client.NewHTTPClient(client.HTTPConfig{ Addr: "http://10.xx.x5.x5:8086", }) if err != nil { fmt.Println("Error creating InfluxDB Client: ", err.Error()) } defer c.Close() // Create a new point batch bp, _ := client.NewBatchPoints(client.BatchPointsConfig{ Database: "BumbleBeeTuna",//数据库名 Precision: "s",//工夫精度秒 }) // Create a point and add to batch tags := map[string]string{"cpu": "cpu-total"}//查问的索引 fields := map[string]interface{}{ "idle": 10.1, "system": 53.3, "user": 46.6, }//记录值 pt, err := client.NewPoint("cpu_usage", tags, fields, time.Now())//将创立的表名为cpu_usage的表以及内容字段放入pt if err != nil { fmt.Println("Error: ", err.Error()) } bp.AddPoint(pt)//把表放入创立的point中 // Write the batch c.Write(bp)//写入创立的client中}// 查问func ExampleClient_query(cli client.Client) []client.Result { // Make client c, err := client.NewHTTPClient(client.HTTPConfig{ Addr: "http://10.xx.x5.x5:8086", }) if err != nil { fmt.Println("Error creating InfluxDB Client: ", err.Error()) } defer c.Close() q := client.NewQuery("SELECT * FROM ipad_electricitytable where time > now() - 3m and electricity < 90 group by ip order by time desc limit 1", "ipad", "ns") if response, err := c.Query(q); err == nil && response.Error() == nil { //fmt.Println(response.Results) var lll []client.Result ll,_ := json.Marshal(response.Results) err = json.Unmarshal(ll, &lll) return lll }else { return nil }}func main() { r := gin.Default() r.GET("/test56", func(c *gin.Context) { conn := ExampleClient() mes:=ExampleClient_query(conn) c.JSON(http.StatusOK, gin.H{ "message": mes, }) }) //conn := ExampleClient() ////ExampleClient_write(conn) //ExampleClient_query(conn) r.Run() //默认在本地8080端口启动服务} ...

March 11, 2023 · 2 min · jiezi

关于go:为什么-Go-语言-struct-要使用-tags

原文链接:为什么 Go 语言 struct 要应用 tags 在 Go 语言中,struct 是一种常见的数据类型,它能够用来示意简单的数据结构。在 struct 中,咱们能够定义多个字段,每个字段能够有不同的类型和名称。 除了这些根本信息之外,Go 还提供了 struct tags,它能够用来指定 struct 中每个字段的元信息。 在本文中,咱们将探讨为什么 Go 语言中须要应用 struct tags,以及 struct tags 的应用场景和劣势。 struct tags 的应用struct tags 应用还是很宽泛的,特地是在 json 序列化,或者是数据库 ORM 映射方面。 在定义上,它以 key:value 的模式呈现,跟在 struct 字段前面,除此之外,还有以下几点须要留神: 应用反引号在申明 struct tag 时,应用反引号 ` 突围 tag 的值,能够避免转义字符的影响,使 tag 更容易读取和了解。例如: type User struct { ID int `json:"id" db:"id"` Name string `json:"name" db:"name"` Email string `json:"email" db:"email"`}防止应用空格在 struct tag 中,应该防止应用空格,特地是在 tag 名称和 tag 值之间。应用空格可能会导致编码或解码谬误,并使代码更难以保护。例如: ...

March 11, 2023 · 2 min · jiezi

关于go:golang-占位符还傻傻分不清

xdm ,写 C/C++ 语言的时候有格局控制符,例如 %s , %d , %c , %p 等等 在写 golang 的时候,也是有对应的格局控制符,也叫做占位符,写这个占位符,须要有对应的数据与之对应,不能瞎搞 根本常见罕用的占位符%s%d%v , %v+ , %+v%T , %q写一个 demo 来看看下面占位符的成果,具体都是啥样的 type Animal struct { hobby string}func main() { name := "xiaomotong" age := 19 hh := Animal{"basketball"} fmt.Printf("name = %s , age = %d , hh = %v\n\n", name, age, hh) fmt.Printf("hh = %+v , hh= %#v\n\n", hh, hh) fmt.Printf("name = %T , age = %T , hh = %T\n\n", name, age, hh) fmt.Printf("%q", 0x8989)}下面的代码执行成果如下: ...

March 10, 2023 · 2 min · jiezi

关于go:gitlab1114继续升级到1593

背景:基于:gitlab远古版本备份&还原&降级,twang2218/gitlab-ce-zh镜像指反对到了11.1.4筹备持续降级一下。持续降级反正就两个思路: dockerhub找更高版本的汉化版本依据https://github.com/twang2218/gitlab-ce-zh本人打包更高版本镜像反正都是依据https://github.com/sameersbn/docker-gitlab 外面的版本批改吧! gitlab-11.1.4持续降级注:前面总结这样胜利都是运气,尽量还是依照https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/中举荐的降级程序操作降级!! 基于dockerhub汉化镜像dockerhub学来的:找到了一个其他人基于wwang2218版本的汉化版本:https://hub.docker.com/search?q=gitlab-ce-zh先尝试一下小版本升级:就到11.11.3吧!批改镜像tag caeret/gitlab-ce-zh:11.11.3 docker-compose downdocker-compose up -ddocker logs -f github-gitlab-1 docker exec -it github-gitlab-1 chown git /var/opt/gitlab/.ssh/authorized_keys docker-compose downdocker-compose up -ddocker logs -f github-gitlab-1 docker exec -it github1-gitlab-1 chmod 2770 -R /var/opt/gitlab/git-data/repositoriesdocker stop github1-gitlab-1docker start github1-gitlab-1 docker logs -f github1-gitlab-1 其余的瞎尝试天真的批改tag制作镜像天真的认为批改一下Dockerfile中镜像版本就能够做一个高版本的镜像....尝试一下本人制作镜像......比照了一下10.6 11.1的Dockerfile感觉只是批改了版本tag: git clone https://github.com/twang2218/gitlab-ce-zhcd gitlab-ce-zh/diff 10.6 11.1尝试一下 12.10.6的根底镜像的的构建(依据sameersbn的仓库为例子。当然了这样的做法是错 ,强调一下,降级也是先降级到12.0的版本再持续降级!)做一个12.10.6的镜像试试?copy 11.1文件夹 生成一个12.10.6的文件夹 ,替换Dockerfile中的tag: cp -Ra 11.1 12.10.6cd 12.10.6/sed -i "s/11.1.4/12.10.6/g" Dockerfilecat Dockerfile docker build -t xxxxx/xxxxx/gitlab-ce-zh:12.10.6 .node版本过低?下次达版本13的时候考虑一下降级node一下?来不及更多思考,发现这样打包是不对的...原有的gitlab仓库中并没有汉化的包这些资源的........此办法放弃......就算是一次尝试吧! ...

March 10, 2023 · 3 min · jiezi

关于go:你是使用什么工具调试-golang-程序的

写过 C/C++ 的都是到,调试程序的时候通常应用 gdb 工具来进行调试,用起来可爽了,那么 gdb 是否也适宜 golang 程序的调试的 我集体到是通常应用 dlv 来进行 golang 程序的调试,分享一波 dlv 是什么,全称 Delve Delve 能够让你通过控制程序的执行来与程序进行交互,他能够计算变量,并提供线程 / goroutine 状态、CPU 寄存器状态等信息 Delve 的指标是为调试 Go 程序提供一个简略弱小的调试性能 尝试看一下 dlv 的 help 信息 Usage: dlv [command]Available Commands: attach Attach to running process and begin debugging. connect Connect to a headless debug server. core Examine a core dump. dap [EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP). debug Compile and begin debugging main package in current directory, or the package specified. exec Execute a precompiled binary, and begin a debug session. help Help about any command run Deprecated command. Use 'debug' instead. test Compile test binary and begin debugging program. trace Compile and begin tracing program. version Prints version.通过 help 咱们能够看到能够应用这些命令来调试咱们的程序,依据不同的利用场景 ...

March 9, 2023 · 4 min · jiezi

关于go:金三银四这些Go面试题看看你会答几道

前言 昨天群友问我能不能整顿Go支流框架方面的面试题,安顿! 这篇文章整顿了gRPC、GoFrame、GoZero、GoMicro、GORM、Gin等支流框架的30道面试题。 须要大厂面经的敌人们也能够关注我,我在思否会继续更新。 gRPC1.gRPC是什么,有哪些长处?gRPC是一种高性能、开源的近程过程调用(RPC)框架,它能够使不同平台和语言之间的服务互相通信。它的长处包含:高效性、跨平台、异步流解决、反对多种语言、平安、易于应用和开源。 2.gRPC和REST的区别是什么?REST是基于HTTP协定的一种格调,而gRPC是一个独立于协定的RPC框架。 REST基于资源的状态转移,应用规范的HTTP办法,而gRPC应用协定缓冲区(Protocol Buffers)进行序列化和反序列化。 gRPC反对异步流解决和双向流,而REST通常只反对申请/响应模式。 3.Protocol Buffers是什么,为什么它被用于gRPC中?Protocol Buffers是一种语言中立、平台中立、可扩大的序列化格局,它能够用于数据交换和长久化。它被用于gRPC中,因为它能够实现高效的序列化和反序列化,从而进步了gRPC的性能和效率。 4.gRPC的流程是什么?gRPC流程分为四个步骤:定义服务、生成源代码、实现服务、启动服务。首先,须要定义要实现的服务及其接口,应用Protocol Buffers编写接口定义文件。其次,应用编译器生成客户端和服务器端的源代码。而后,实现生成的接口。最初,启动服务器并将其部署在适当的地位。 5.gRPC反对哪些类型的序列化?gRPC反对两种类型的序列化:二进制(应用Protocol Buffers)和JSON。其中,二进制序列化在效率和性能方面比JSON序列化更优良。然而,JSON序列化在可读性方面更好,能够不便地进行调试和测试。 GoFrame1.什么是 GoFrame?与 Go 规范库有什么区别?GoFrame 是一个弱小的 Go Web 利用开发框架,它提供了一系列优良的功能模块和常用工具,不便开发者疾速构建高性能、高可用的 Web 应用程序。 相较于 Go 规范库,GoFrame 提供了更多的功能模块,例如:ORM、Cache、Session、WebSocket、邮件发送等等。此外,GoFrame 也提供了更敌对的 API 和更好的性能。 2.goframe框架中,如何应用中间件?在goframe框架中应用中间件很简略。只须要在路由定义时应用中间件函数,例如: s := g.Server()s.Group("/", func(group *ghttp.RouterGroup) { group.Middleware(MiddlewareFunc) group.ALL("/user", UserHandler)})3.goframe框架中,如何实现定时工作?在goframe框架中实现定时工作很容易。能够应用gcron插件。该插件提供了简略的API用于创立和治理定时工作,例如: // 创立定时工作s := gcron.NewScheduler()s.Every(1).Hour().Do(TaskFunc)// 开始定时工作s.Start()4.goframe框架中,如何实现文件上传和下载?在goframe框架中实现文件上传和下载很容易。能够应用ghttp的相干办法进行操作,例如: // 文件上传uploadFile, err := r.UploadFile("file")if err != nil { return err}uploadFile.Save("/path/to/save")// 文件下载r.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))r.Response().ServeFile(filepath)5.GoFrame 中的 gvalid 组件是什么?如何应用?goframe框架提供了功能强大、应用便捷、灵便易扩大的数据/表单校验组件,由gvalid组件实现。 gvalid组件实现了十分弱小的数据校验性能,内置了数十种罕用的校验规定,反对单数据多规定校验、多数据多规定批量校验、自定义错误信息、自定义正则校验、自定义校验规定注册、反对i18n国际化解决、反对struct tag规定及提示信息绑定等等个性,是目前性能最弱小的Go数据校验模块。 Go-Zero1.Go-Zero 是什么?它的次要性能是什么?它与其余 Go 框架有什么不同?Go-Zero 是一个基于 Go 语言的微服务开发框架。它旨在提供简略、高效和牢靠的微服务开发解决方案。Go-Zero 次要性能包含 RPC、缓存、限流、熔断、监控等。相较于其余 Go 框架,如 Gin 或 Beego,Go-Zero 更加专一于微服务开发,并提供了更多的开箱即用的性能。 ...

March 9, 2023 · 3 min · jiezi

关于go:golang-操作-excel

package mainimport ( "encoding/json" "errors" "fmt" "github.com/xuri/excelize/v2" "os" "path" "time")// 参考文档:https://www.bookstack.cn/read/excelize-v2.0/spilt.4.1.mdfunc ReadContent(path string, title []string) (*[][]string, error) { //关上文件 f, err := excelize.OpenFile(path) if err != nil { return nil, err } rows, err := f.GetRows("Sheet1") var list [][]string //对sheet1中的内容进行操作 for i, row := range rows { //题目不操作 if i == 0 { list = append(list, title) //continue } list = append(list, row) } fmt.Println(list) return &list, nil}//AppendSaveExcel 追加/* dirPath := "log" fileName := "Book1.xlsx" dataList := [][]interface{}{{"姓名", "电话", "公司", "职位", "退出工夫"}, {1, 2, "刘犇,刘犇,刘犇", "4", "5"}} AppendSaveExcel(dirPath, fileName, &dataList) //会存到log/Book1.xlsx里 文件没有创立并写入,有追加写*/func AppendSaveExcel(dirPath, fileName string, dataList *[][]interface{}) (err error) { if len(*dataList) == 0 { return errors.New("数据不能为空") } //不存在创立目录 _ = CreateFolder(dirPath, true) activeSheetName := "Sheet1" //文件门路 fileNamePath := path.Join(dirPath, fileName) // 从第几行开始写数据 rowNum := 0 // excel最初数据所有行数 lastLineNum := 0 var f *excelize.File // 创立excel //判断文件是否存在,不存在新建 fileExistsBool := FileExists(fileNamePath) if !fileExistsBool { f = excelize.NewFile() } else { // 追加写入excel f, _ = excelize.OpenFile(fileNamePath) rows, _ := f.GetRows(activeSheetName) lastLineNum = len(rows) //找到最初一行 } // Create a new sheet. index, _ := f.NewSheet(activeSheetName) // 设置工作簿的默认工作表 f.SetActiveSheet(index) //设置列宽度为16 var ColAbc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" //须要存入数据的列长度 dataColLen := len((*dataList)[0]) if dataColLen > 26 { err = f.SetColWidth("Sheet1", "A", "Z", 16) } else { err = f.SetColWidth("Sheet1", "A", ColAbc[dataColLen-1:dataColLen], 16) } if err != nil { fmt.Println(err) return errors.New(fmt.Sprintf("f.SetColWidth failed, err:%v", err)) } // 从文件内容尾行写入 rowNum = lastLineNum // 循环按行赋值 for _, list := range *dataList { rowNum += 1 //按行赋值 从aN开始按行赋值 f.SetSheetRow(activeSheetName, fmt.Sprintf("A%d", rowNum), &list) } // 保留excel if err := f.SaveAs(fileNamePath); err != nil { fmt.Println(err) return errors.New(fmt.Sprintf("save file failed, path:(%s)", fileNamePath)) } return nil}func CreateFolder(p string, ignoreExists bool) error { _, FolderExists := os.Stat(p) if FolderExists != nil && ignoreExists == false { err := errors.New("folder exists") return err } if FolderExists == nil { err := os.MkdirAll(p, os.ModePerm) if err != nil { return err } } return nil}func FileExists(filename string) bool { info, err := os.Stat(filename) if os.IsNotExist(err) { return false } if info == nil { return false } return true}func main() { var title = []string{"ip", "保健组", "lables", "大城市"} ll, _ := ReadContent("./data/target.xlsx", title) //fmt.Println(ll) b, _ := json.Marshal(&ll) var aa *[][]interface{} _ = json.Unmarshal(b, &aa) lname := fmt.Sprintf("fftest_%s.xlsx", time.Now().Format("20060102150405")) AppendSaveExcel("./data", lname, aa)} ...

March 8, 2023 · 2 min · jiezi

关于go:黑科技普通人也能把个人电脑变成服务器的手段

我又来分享黑科技了。即便你不懂技术,也能上。 什么黑科技实质上让本地的网络服务能够被外网拜访。 这种技术叫做内网穿透。 益处内网穿透有什么益处? 能够把本人的电脑变成文件服务器,进行文件上传下载。能够在开发合作的时候,无需部署,进行接口联调。疾速验证对接微信小程序等接口,毋庸繁琐的部署。免公网ip、无需在路由器映射端口,黑客用扫描端口的形式难以攻打。特定条件下,能够做到近程连贯公司电脑等操作。公有服务器,让你的电视机接管视频,回家后就能观看。一些开发板做的监控等信息,每台设施运行一条隧道,能够不便的治理监控各个设施的运行状况。一些本地运行的游戏,想和好基友一起联网玩,一条命令即可实现联网游戏。随时随地在任何中央能够拜访到群辉上利用。好多到说不完,另外,这种形式是收费的。 内网穿透平安吗?当初服务器被黑的状况,多半是服务器上一些软件/破绽/端口导致的。 你的利用如果放在公网服务器,因为短少系统安全保护常识,会变得很危险。 而将服务器放在本地,裸露给公网的也仅仅是利用层面的一个端口,其余零碎上的破绽/端口都被暗藏起来,从这个层面来说,进步了很多安全性。 用法全平台反对,下一个二进制文件、windows就是exe。 运行命令 chmod 755 ./natapp./natapp -authtoken e8f05cbd1e1b139dxxxx是一个authtoken 须要申请。间接用我这个立马就能体验。当初就胜利把本地80端口映射到公网上了。 拜访图片中的http://uh4bju.natappfree.cc在任何一台电脑、手机上就都能拜访到我本地80端口。 具体教程在这,我就不开展讲了。 https://natapp.cn/article/natapp_newbie 目前看不出有什么用,厉害的来了。 文件服务器假如你当初想把电脑上的货色分享给有数的陌生人,即便他没有加你好友。看到宣传信息就能下载。 而后你再把这个链接用转码平台,做成二维码主动跳转,手机一扫就能下载货色了。 再比方你在机房想给你敌人传个文件,假如机房能够拜访外网,但又不能下载通信工具的状况下。 成果如图,我间接写了一个。 留神看我的网址,是一个公网地址,这就是内网穿透失去的! 疾速体验代码和应用办法和具体的阐明文档我都放这了。有趣味钻研一下吧! https://github.com/golang-minibear2333/simple-file-server 曾经编译好的文件服务器,反对mac、linux、windows。内网穿透工具也下好了。mac和windows版本。甚至密钥都间接给了,就是为了让你间接就能体验。如果你齐全不懂程序也能用了,拜访不了就在我号上回 文件服务器 给你发压缩包。 一起提高你好,我是小熊,是一个爱技术然而更爱钱的程序员。上进且佛系自律的人。喜爱发小机密/臭屁又爱夸耀。 奋斗的大学,激情的当初。赚了钱买了房,写了书出了名。当过面试官,带过师傅搬过转。 大厂外来务工人员。是我,我是小熊,是不一样的烟火欢送围观。 我的博客 机智的程序员小熊 欢送珍藏

March 7, 2023 · 1 min · jiezi

关于go:关于-interface-会有啥注意事项下

咱们一起来回顾一下上一次说到的 interface{} 能够用来做多态接口类型分为空接口类型和非空接口类型,他们的底层数据结构不太一样这里顺便说一下,用来作态须要满足这样的条件: 首先得有父类指针指向子类的对象这个接口还必须是非空接口,外面得蕴含办法,也就是应用的底层数据结构是 iface子类会去实现父类的具体方法interface{} 留神断言失常的应用断言,写一个简略的 断言 demo type Animal interface{}func main() { var a Animal = "xiaomotong" v, ok := a.(string) if !ok{ fmt.Println("type error") } fmt.Println("v == ",v)}断言留神写成 2 个返回值的,一个是具体的值,一个是 bool,判断断言是否胜利,若胜利则阐明断言正确,且 v 会被赋值为理论变量的值 切忌没头脑的强转 func main() { var a Animal = "xiaomotong" v := a.(int) fmt.Println("v == ",v)}上述这种写法,若不判断是否断言胜利,间接强转,程序是会 panic 的 ,执行上述程序后成果如下: >go run main.gopanic: interface conversion: main.Animal is string, not intgoroutine 1 [running]:main.main() D:/mycode/my_new_first/interface_test/main.go:13 +0x4cexit status 2程序崩掉,那就是线上问题了 xdm ,这里须要留神 ...

March 7, 2023 · 2 min · jiezi

关于go:Go框架go中的平滑关闭究竟是怎么关闭的

大家好,我是渔夫子。本号新推出「Go工具箱」系列,意在给大家分享应用go语言编写的、实用的、好玩的工具。同时理解其底层的实现原理,以便更深刻地理解Go语言。 敞开软件能够分为平滑敞开(软敞开)和硬敞开。就像咱们在敞开电脑的时候,有时候遇到电脑死机,会间接长按开要害,直至电脑关机,这就是硬关机。 而通过电脑上的菜单抉择“关机”,则属于软关机(平滑敞开)。在软关机的时候,大家应该会留神到工夫会比拟长,时不时还会有弹窗弹出 询问是否要退出。 在咱们本人编写的web利用中,实际上也是须要有软敞开的。明天我就golang中的gin框架为例,来聊聊平滑敞开背地的解决逻辑。 一、为什么平滑敞开如此重要性?硬敞开就是当程序收到敞开的信号后,立刻将正在做的事件敞开。比方,咱们在强制关机时,代码还没有保留,就会造成失落。而平滑敞开具备如下长处: 第一,平滑敞开可能及时的开释资源。第二,平滑敞开可能保障事务的完整性。比方在web服务中,一个申请还没有返回,就被敞开了,那么影响体验。平滑敞开,可能期待申请解决实现后 连贯再被敞开。所以,平滑敞开实质上就是当程序收到敞开的信号后,会期待程序把正在做的事件做完,开释掉所有的资源后再敞开服务。 二、web服务是如何接管和解决申请的?无论是失常敞开还是平滑敞开服务,实质上都是敞开服务的资源。所以,有必要先理解下web服务是如何启动的以及启动后是如何解决http申请。这样在敞开的时候就能对应的晓得应该敞开哪些资源以及如何敞开了。 咱们以gin框架为例来阐明解决http申请的流程。 先构建一个server对象依据传入的网络地址,建设网络监听器listener。理论是建设了一个socket。将listener退出到server对象的一个资源池中,以代表server监听正在应用的listener资源。listner开始监听对应网络地址上(socket)的申请。当有用户发动http申请时,Accept函数就能监听到。对新接管的申请创立一个一个TCP连贯将新的TCP连贯包装成一个conn对象,同时将该conn对象退出到server的关羽conn的资源池中。这样server就能跟踪以后有多少个申请连贯正在解决申请。启动一个新的协程,异步解决该连贯 读取申请内容执行具体的解决逻辑输入响应申请完结,敞开本次的TCP连贯。同时,从资源池中开释掉对应的conn资源。持续回到Accept监听后续的HTTP申请。 通过以上流程图,咱们实际上能够将web server解决http申请的整个过程分为两局部:创立网络监听器listener(socket)阶段以及监听并解决HTTP申请阶段。 相应的,和这两个阶段绝对应的应用到的资源就是网络监听器listner以及每个HTTP申请连贯conn。即上图中的server中的两个资源池。 对于两种资源,别离有存在不同的状态。上面咱们简略看下两种资源的各自状态以及转换。 listener资源的状态listner的作用就是监听网络连接。所以该资源有两种状态:失常和敞开状态。 conn资源的状态conn实质上是一个TCP的连贯,但server对象为了容易跟踪目前监听到的连贯,所以将TCP连贯包装成了conn,并给conn定义了以下状态:新连贯(New)、沉闷状态(Active)、敞开状态(Closed)、闲暇状态(Idle)和被劫持状态(Hijacked)。以下是各状态之间的转化关系: 在启动阶段是建设资源。那么,在server的敞开阶段,次要也就是要开释这些资源。那么该如何开释这些资源就是咱们接下来要探讨的重点。 三、间接敞开web服务是敞开了什么?在web框架中,server的Close函数对应性能就是间接敞开server服务。在gin框架中,对应的代码如下: 从代码中能够看到,基本上是首先是给server对象设置敞开的标记位;而后敞开doneChan;敞开所有的listener资源,以进行接管新的连贯;最初,循环conn资源,顺次敞开。 这里有一点须要留神,在敞开conn资源的时候,不论conn以后处于什么状态,都是立刻敞开。也就是说如果一个conn正处于Active状态,代表着该申请还没解决完,那么也会立刻终止解决。对于客户端的体现来说 就是收到“连贯被回绝,网站无法访问”的谬误。 咱们试验一下。如下代码是注册了路由"/home",在处理函数中咱们期待了5秒,以便模仿在敞开的时候,咱们咱们的申请解决还没有实现的场景。而后,往下就是通过signal.Notify函数在quit通道上注册了程序终止的信号os.Interrupt(即按Ctrl+C),当通过在终端上按Ctrl+C发送了中断信号给quit通道时,就执行server.Close()函数。如下代码: package mainimport ( "context" "log" "net/http" "os" "os/signal" "time" "github.com/gin-gonic/gin")func main() { router := gin.Default() router.GET("/home", func(c *gin.Context) { time.Sleep(5 * time.Second) c.String(http.StatusOK, "Welcome Gin Server") }) server := &http.Server{ Addr: ":8080", Handler: router, } quit := make(chan os.Signal) signal.Notify(quit, os.Interrupt) server.RegisterOnShutdown(func(){ log.Println("start execute out shutown") }) go func() { if err := server.ListenAndServe(); err != nil { if err == http.ErrServerClosed { log.Println("Server closed under request") } else { log.Fatal("Server closed unexpect") } } }() <-quit log.Println("receive interrupt signal") //ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) //defer cancel() if err := server.Close(); err != nil { log.Fatal("Server Close:", err) } log.Println("Server exiting")}好了,咱们总结下间接敞开的特点就是:先敞开server对象;再敞开监听对象listener不再接管新连贯;最初敞开所欲已建设的连贯,无论该连贯是否正在解决申请都立刻敞开。 大家留神,这里敞开是有一个从大到小范畴的程序:先敞开范畴大的(server和lisener),最初敞开具体的解决连贯(conn)。 ...

March 7, 2023 · 2 min · jiezi

关于go:关于-interface-会有啥注意事项上

学习 golang ,对于 interface{} 接口类型,咱们肯定绕不过,咱们一起来看看 应用 interface{} 的时候,都有哪些注意事项吧 interface {} 能够用于模仿多态xdm 咱们写一个简略的例子,就举动物的例子 写一个 Animal 的接口,相似于 java 外面的抽象类 ,Animal 的接口 中有 2 个计划待实现 写一个 Cat 来继承 Animal , 实现 Eat 办法和 Drink 办法 动物都有吃和喝的行为,小猫吃的行为是吃鱼,小猫的喝的行为是喝可乐最初在主函数中,应用父类的指针,来指向子类的实例化的一个子类地址type Animal interface { Eat(string) string Drink(string) string}type Cat struct{}func (c *Cat) Eat(food string) string { if food != "fish" { return "i dislike" } else { return "i like" }}func (c *Cat) Drink(drink string) string { if drink == "coke" { return "i love" }else{ return "abandon" }}func main(){ var a Animal = &Cat{} fmt.Println(a.Eat("fish")) fmt.Println(a.Drink("water"))}看到上述代码,会不会有这样的疑难,命名是 &Cat{} 是取地址的,为什么 var a Animal 不写成指针呢? ...

March 6, 2023 · 2 min · jiezi

关于go:基本数据类型

数据类型长度默认值阐明slice nil援用类型,切片map nil援用类型,mapchannel nil援用类型,通道interface nil援用类型,接口function nil援用类型,函数援用类型留神:

March 6, 2023 · 1 min · jiezi

关于go:工作中对git的简单和进阶使用

Git简略介绍本篇不会介绍根本的git原理之类的,只记录在工作中的git应用教训。如果要学习git的根底与进阶,能够参考这个网站git-tower 本篇会简略介绍我在工作中都应用git做了哪些工作: 1、根底应用。应用gitlab治理咱们的源代码 2、自动化。咱们应用gitlab的CI进行主动代码查看、单元测试、代码打包 3、多零碎合作。咱们应用gitlab的issue+webhook+钉钉作为简略的工单零碎,撑持咱们进行日常撑持工作 1. 治理源码进行开发以下都以gitlab为例,所有开发人员应用同一个仓库,基于不同的个性分支进行开发 咱们的分支: 1、 master分支: 正式分支,也是打包分支 2、develop分支: 开发分支,放弃和master分支保持一致 3、feature分支: 性能个性分支,不同的性能别离在对应的个性分支上进行开发测试。 咱们的开发流程: 1、 建分支。建设个性/bug对应的issue,依据issue创立对应的分支(在web页面间接操作), 或者间接从develop分支迁出一个新的个性分支 2、开发。在个性分支上进行开发/测试,直到测试验收结束,每次提交都会触发部分动态代码查看 3、合并前rebase并整顿提交记录。分两步,先rebase develop放弃个性分支为最新的develop代码,再rebase -i 变基commitid 整顿个性分支的commit记录 4、合并前code-review。在gitlab网页界面创立merge-request,并指派其余开发进行review,直到所有人都无异议,此时会触发全局动态代码查看。 5、合并更新版本信息。研发主管将代码合并到develop分支后,在develop分支批改代码内的版本信息,再合并到master分支 6、 打包公布。研发主管基于master分支进行打tag,此时会触发CI主动基于tag打包。 这样,咱们的一次性能需要的开发就算实现了。目前整个流程还存在一些问题: 1、无奈主动创立及更新CHANGELOG.md,即版本记录无奈主动生成,跟咱们的提交不标准有关系 2、没法主动触发CD,跟咱们的服务架构有关系。 2.基于gitlab的CI进行自动化代码查看、打包gitlab如何搭建CI,此处不做赘述,查看官网文档即可。Runner局部教你如何搭建CI环境,CICD局部教你如何让CI运行起来 咱们一共有4个工作: test,code_check_push,code_check_merge,feat_build,tag_build test。执行单元测试代码,每次push时触发code_check_push。执行部分动态代码查看,每次提交代码时触发,查看范畴为提交的Py文件代码,且只查看Error级别的code_check_merge。执行全局动态代码查看,创立merge-request后触发,查看范畴为工程内的次要python包,只查看error级别的feat_build。执行打包,每次push时触发,用于个性分支提测时配置tag_build。执行打包,只有提交创立tag时触发,用于正式环境的打包。3.基于webhook和issue+钉钉的繁难工单零碎本零碎波及三个局部gitlab服务、钉钉服务、简略的直达服务 直达服务 为一个简略的http服务,用于接管gitlab内的issue相干变更的告诉,并转发给钉钉机器人。 创立一个转发接口api,将该api设置到gitlab的webhook局部即可接管gitlab的告诉保护一个反对人员列表(转发钉钉时以对应手机号为准),收到gitlab告诉时转发到钉钉时艾特对应的反对人员进行解决gitlab服务 建设一个公开的我的项目,用于接管issue,内部人员基于该项目标issue局部提交工单依据不同的业务类型,建设不同的工单模板,模板为markdown文件,须要搁置于.gitlab/issue_templates/目录下,须要手动创立在该项目标webhook局部增加上述直达服务的接管告诉的api,后续所有issue的变更(建设、更新、敞开)都会告诉到该api钉钉服务 建设反对钉钉群,并增加webhook机器人,此机器人用于接管直达服务的告诉这样,一个繁难的工单零碎就建设起来了。内部人员建设issue反馈的时候,钉钉群内机器人就能够立马艾特对应反对人员进行解决,处理完毕后在issue内同步后果信息,issue创建者会收到gitlab告诉邮件 git罕用场景记录我了解的以及我应用的业务中场景的解决方法,如果有谬误的或者有差别的还请斧正 1、拉取近程我的项目的指标(如develop)分支 git clone 我的项目地址 我的项目下来通常只蕴含master分支,这个时候又不想应用git fetch all拉取所有分支下来,而只想拉取指定的分支。 能够应用 git checkout -b develop origin/develop 这样就切换到想要的分支了,并且曾经拉取到了对应分支的代码 2、我须要为我的性能新建一个分支,并且同步到近程 办法1:创立分支并间接关联近程分支 git checkout -b new_branch origin/new_branch 办法2:先创立分支,先迁出新分支git checkout -b new_branch 再手动设置关联近程分支,手动设置近程分支也有两种办法 第一种 git branch --set-upstream-to=origin/new_branch new_branch第二种 git push --set-upstream origin new_branch(近程分支名)3、我在本人的feature分支进行开发,然而develop分支产生了改变,我须要更新下来 ...

March 6, 2023 · 1 min · jiezi

关于go:未设置标题的文章

1. 以后次要性能开一个 custome iframe 预览 hugo (via https://github.com/Ellpeck/ObsidianCustomFrames)后盾启动 hugo server2. 遇到的问题

March 5, 2023 · 1 min · jiezi

关于go:Whats-new-in-dubbogopixiu-v060

pixiu 0.6.0 尽管艰苦,但在社区小伙伴的致力下公布了。这个版本不止有惯例社区性能,还有很多性能个性是来源于ASoC-2022 Alibaba Summer of Code,OSPP(中科院软件所[开源软件供应链点亮打算]) , GSoC(Google Summer of Code) 提案内容。 New Features In v0.6.0nacos config背景:pixiu 的配置,启动配置项(conf.yaml),日志配置(log.yml)均是从本地文件加载,没有接入动静配置核心的能力,而日常生产环境中,配置通过配置核心集中下发治理是更为灵便和平安的形式之一。 动静配置下发的场景还有很多扩大,接入的组件也因公司的选型有所不同,以后咱们次要思考接入 nacos 配置核心。 注:在设计上能够思考,如何在二次开发场景下更为“规范、平安、简略”的接入其余配置核心。 Pixiu 启动时从配置核心Nacos 拉取配置 conf.yaml 数据 pixiu.conf的配置不必配置static_resources,须要配置config-center和nacos: config-center: type: nacos enable: truenacos: server-configs: - ip_addr: "127.0.0.1" port: 8848 client-config: username: nacos password: nacos原来咱们相熟pixiu.conf配置内容放到了nacos中: static_resources: listeners: - name: "net/http" protocol_type: "HTTP" address: socket_address: address: "0.0.0.0" port: 8881 filter_chains: filters: - name: dgp.filter.httpconnectionmanager config: route_config: routes: - match: prefix: "*" http_filters: - name: dgp.filter.http.dubboproxy config: dubboProxyConfig: auto_resolve: true registries: "zookeeper": protocol: "zookeeper" timeout: "3s" address: "127.0.0.1:2181" username: "" password: "" timeout_config: connect_timeout: 5s request_timeout: 5s server_name: "test_http_dubbo" generate_request_id: false config: idle_timeout: 5s read_timeout: 5s write_timeout: 5s shutdown_config: timeout: "60s" step_timeout: "10s" reject_policy: "immediacy"更具体的配置参考samples: https://github.com/apache/dubbo-go-pixiu-samples/tree/main/dubbogo/simple/farconfnacos ...

March 4, 2023 · 3 min · jiezi

关于go:爬取全国省市区县乡镇街道数据来源于京东

go-city 演示go-city sync --dbname=webcron --driver=database --table=city100% |█████████████████████████████████████████| (34/34, 2 it/min)time="2023-03-04T10:29:26+08:00" level=info msg="数据同步实现,其中省级行政区:34,城市:452,区县:5234,乡镇街道:43564"installgo install https://github.com/xiaoxuan6/go-city.gitUsageSqiltego-city sync --db=./sqlite.dbDatabasego-city sync --driver=database --host=127.0.0.1 --port=3306 --username=root --password=root --dbname=city --table=cityMoreNAME: go-city syncUSAGE: go-city sync [command options] [arguments...]DESCRIPTION: 同步省市区到数据库OPTIONS: --driver value, -d value db driver,Support:sqlite、database、memory (default: "sqlite") [%GO_CITY_DRIVER%] --db value database file (default: "./sqlite.db") [%GO_CITY_DB%] --host value database host (default: "127.0.0.1") [%GO_CITY_DB_HOST%] --port value database port (default: "3306") [%GO_CITY_DB_PORT%] --username value database username (default: "root") [%GO_CITY_DB_USERNAME%] --password value database password (default: "root") [%GO_CITY_DB_PASSWORD%] --dbname value database dbname [%GO_CITY_DB_NAME%] --table value database table name (default: "cities") [%GO_CITY_DB_TABLE_NAME%] --help, -h show help

March 4, 2023 · 1 min · jiezi

关于go:golang实现IP地址转归属地国家省份城市获取网络运营商在线客服系统获取访客地址功能唯一客服

一个自由职业独立开发者,在线客服零碎的开发日志客服零碎里须要展现出访客的 IP 归属地,并且把归属地作为访客的名称展现。上面是波及的技术知识点总结。 当初很多网络应用曾经都在展现网友的 IP 归属地,通过 golang 以及 qqzengIP 地址库,能够很不便的实现这个性能 package toolsimport ( "io/ioutil" "log" "strconv" "strings")/** * @author xiao.luo * @description This is the go version for IpSearch */type CityInfo struct { CountryName string `json:"country_name"` RegionName string `json:"region_name"` CityName string `json:"city_name"` AreaName string `json:"area_name"`}type ipIndex struct { startip, endip uint32 local_offset, local_length uint32}type prefixIndex struct { start_index, end_index uint32}type ipSearch struct { data []byte prefixMap map[uint32]prefixIndex firstStartIpOffset uint32 prefixStartOffset uint32 prefixEndOffset uint32 prefixCount uint32}var ips *ipSearch = nilfunc NewIpdb(ipPath string) (ipSearch, error) { if ips == nil { var err error ips, err = loadIpDat(ipPath) if err != nil { log.Fatal("the IP Dat loaded failed!") return *ips, err } } return *ips, nil}func loadIpDat(ipPath string) (*ipSearch, error) { p := ipSearch{} //加载ip地址库信息 data, err := ioutil.ReadFile(ipPath) if err != nil { log.Fatal(err) } p.data = data p.prefixMap = make(map[uint32]prefixIndex) p.firstStartIpOffset = bytesToLong(data[0], data[1], data[2], data[3]) p.prefixStartOffset = bytesToLong(data[8], data[9], data[10], data[11]) p.prefixEndOffset = bytesToLong(data[12], data[13], data[14], data[15]) p.prefixCount = (p.prefixEndOffset-p.prefixStartOffset)/9 + 1 // 前缀区块每组 // 初始化前缀对应索引区区间 indexBuffer := p.data[p.prefixStartOffset:(p.prefixEndOffset + 9)] for k := uint32(0); k < p.prefixCount; k++ { i := k * 9 prefix := uint32(indexBuffer[i] & 0xFF) pf := prefixIndex{} pf.start_index = bytesToLong(indexBuffer[i+1], indexBuffer[i+2], indexBuffer[i+3], indexBuffer[i+4]) pf.end_index = bytesToLong(indexBuffer[i+5], indexBuffer[i+6], indexBuffer[i+7], indexBuffer[i+8]) p.prefixMap[prefix] = pf } return &p, nil}func (p ipSearch) Get(ip string) string { ips := strings.Split(ip, ".") x, _ := strconv.Atoi(ips[0]) prefix := uint32(x) intIP := ipToLong(ip) var high uint32 = 0 var low uint32 = 0 if _, ok := p.prefixMap[prefix]; ok { low = p.prefixMap[prefix].start_index high = p.prefixMap[prefix].end_index } else { return "" } var my_index uint32 if low == high { my_index = low } else { my_index = p.binarySearch(low, high, intIP) } ipindex := ipIndex{} ipindex.getIndex(my_index, &p) if ipindex.startip <= intIP && ipindex.endip >= intIP { return ipindex.getLocal(&p) } else { return "" }}// 二分迫近算法func (p ipSearch) binarySearch(low uint32, high uint32, k uint32) uint32 { var M uint32 = 0 for low <= high { mid := (low + high) / 2 endipNum := p.getEndIp(mid) if endipNum >= k { M = mid if mid == 0 { break // 避免溢出 } high = mid - 1 } else { low = mid + 1 } } return M}// 只获取完结ip的数值// 索引区第left个索引// 返回完结ip的数值func (p ipSearch) getEndIp(left uint32) uint32 { left_offset := p.firstStartIpOffset + left*12 return bytesToLong(p.data[4+left_offset], p.data[5+left_offset], p.data[6+left_offset], p.data[7+left_offset])}func (p *ipIndex) getIndex(left uint32, ips *ipSearch) { left_offset := ips.firstStartIpOffset + left*12 p.startip = bytesToLong(ips.data[left_offset], ips.data[1+left_offset], ips.data[2+left_offset], ips.data[3+left_offset]) p.endip = bytesToLong(ips.data[4+left_offset], ips.data[5+left_offset], ips.data[6+left_offset], ips.data[7+left_offset]) p.local_offset = bytesToLong3(ips.data[8+left_offset], ips.data[9+left_offset], ips.data[10+left_offset]) p.local_length = uint32(ips.data[11+left_offset])}// / 返回地址信息// / 地址信息的流地位// / 地址信息的流长度func (p *ipIndex) getLocal(ips *ipSearch) string { bytes := ips.data[p.local_offset : p.local_offset+p.local_length] return string(bytes)}func ipToLong(ip string) uint32 { quads := strings.Split(ip, ".") if len(quads) < 4 { return 0 } var result uint32 = 0 a, _ := strconv.Atoi(quads[3]) result += uint32(a) b, _ := strconv.Atoi(quads[2]) result += uint32(b) << 8 c, _ := strconv.Atoi(quads[1]) result += uint32(c) << 16 d, _ := strconv.Atoi(quads[0]) result += uint32(d) << 24 return result}//字节转整形func bytesToLong(a, b, c, d byte) uint32 { a1 := uint32(a) b1 := uint32(b) c1 := uint32(c) d1 := uint32(d) return (a1 & 0xFF) | ((b1 << 8) & 0xFF00) | ((c1 << 16) & 0xFF0000) | ((d1 << 24) & 0xFF000000)}func bytesToLong3(a, b, c byte) uint32 { a1 := uint32(a) b1 := uint32(b) c1 := uint32(c) return (a1 & 0xFF) | ((b1 << 8) & 0xFF00) | ((c1 << 16) & 0xFF0000)}针对下面的代码进行单元测试 ...

March 3, 2023 · 4 min · jiezi

关于go:面试Go-被defer的几个盲区坑了

大家好,我是二条,是一位从事后端开发的程序员。 上一篇,咱们讲到了Go中的字符串为什么不能被批改,这一篇来总结defer语句中的几个暗藏的细节。 对于Go中的defer,是做什么的?执行程序是怎么样的?置信学过Go语言的同学,曾经不在生疏,明天就来讲讲其中须要把握的几个知识点。 要讲到这几个知识点,还是大抵总结一下defer这个内置关键字。 1、defer是一种提早解决机制,是在函数进行return之前进行执行。 2、defer是采纳栈的形式执行,也就是说先定义的defer后执行,后定义的defer最先被执行。 正因为defer具备这种机制,能够用在函数返回之前,敞开一些资源。例如在某些操作中,连贯了MySQL、Redis这样的服务,在函数返回之前,就能够应用defer语句对连贯进行敞开。就相似oop语言中的 finally 操作一样,不论产生任何异样,最终都会被执行。 其语法格局也十分的简略。 package mainimport "fmt"func main() { function1()}func function1() { fmt.Printf("1") defer function2() fmt.Printf("2")}func function2() { fmt.Printf("3")}上述代码执行的后果是: 213上面就来总结这六个小知识点: 1、defer 的执行程序。 采纳栈的形式执行,先定义后执行。 2、defer 与 return 谁先谁后。return 之后的语句先执行,defer 后的语句后执行。 3、函数的返回值初始化与 defer 间接影响。defer中批改了返回值,理论返回的值是依照defer批改后的值进行返回。 4、defer 遇见 panic。依照defer的栈程序,输入panic触发之前定义好的defer。 5、defer 中蕴含 panic。依照defer的栈程序,输入panic触发之前的defer。并且defer中会接管到panic信息。 6、defer 下的函数参数蕴含子函数。会先进行子函数的后果值,而后在依照栈的程序进行输入。 defer的执行程序是什么样的对于这个问题,后面的示例代码也提到过了,采纳栈的程序执行。在定义时,压入栈中,执行是从栈中获取。 defer与return谁先谁后先来看如下一段代码,最终的执行后果是怎么样的。 func main() { fmt.Println(demo2())}func demo2() int { defer func() { fmt.Println("2") }() return func() int { fmt.Println("1") return 4 }()}运行上述代码,失去的后果是: 124可能你会有一个疑难❓ ,既然都提到了defer是在函数返回之前执行,为什么还是先输入1,而后在输入2呢?对于defer的定义,就是在函数返回之前执行。这一点毋庸置疑,必定是在return之前执行。须要留神的是,return 是非原子性的,须要两步,执行前首先要失去返回值 (为返回值赋值),return 将返回值返回调用处。defer 和 return 的执行程序是先为返回值赋值,而后执行 defer,而后 return 到函数调用处。 ...

March 3, 2023 · 2 min · jiezi

关于go:Glacier-Framework-支持依赖注入的-Go-应用开发框架

其实这个我的项目在 4 年前就曾经开始了,因为所有的性能都是基于日常工作中的需要来的,断断续续的补充和欠缺性能,之前都是在本人公司这边的各种 Go 我的项目和我开源的一些我的项目中应用。很早之前就想把它开源进去,然而始终懒得写文档(感觉写文档是最难得事儿了),所以始终让它静静地躺 Github 。明天终于补充了个简版的文档,是时候把它拿进去了。 感兴趣的敌人们欢送来看看啊,有砖拍砖,有需要提需要,肯定虚心向大家学习! Glacier 是一款反对依赖注入的模块化的利用开发框架,它以 go-ioc 依赖注入容器外围,为 Go 利用开发解决了依赖传递和模块化的问题。 个性应用执行流程外围概念 依赖注入 BinderResolverProvider ProviderBootDaemonProviderProviderAggregateServiceModuleLoadPolicyPriorityWeb 框架 Usage控制器事件治理 本地内存作为事件存储后端Redis 作为事件存储后端定时工作日志Eloquent ORM平滑退出第三方框架集成示例我的项目个性依赖注入:通过依赖注入的形式来治理对象的依赖,反对单例、原型对象创立模块化:通过 Provider 个性,轻松实现利用的模块化内置 Web 开发反对:Glacier 内置了对 Web 利用开发的反对,提供了功能丰富的 API 简化 web 开发应用创立一个新的我的项目,应用上面的命令装置 Glacier 开发框架 go get github.com/mylxsw/glacier为了简化利用的创立过程,咱们个别能够通过 starter 模板来创立利用 import "github.com/mylxsw/glacier/starter/app"...// 办法一:快捷启动利用app.MustStart("1.0", 3, func(app *app.App) error { // 这里实现利用的初始化 // ... return nil})// 办法二: 分步骤启动利用ins := app.Create("1.0", 3)// 利用初始化// ...app.MustRun(ins)示例: app.MustStart("1.0", 3, func(ins *app.App) error { ins.AddStringFlag("listen", ":8080", "http listen address") ins.Provider(web.Provider( listener.FlagContext("listen"), web.SetRouteHandlerOption(func(cc infra.Resolver, router web.Router, mw web.RequestMiddleware) { router.Get("/", func(ctx web.Context) web.Response { return ctx.JSON(web.M{}) }) }), )) return nil})代码示例能够参考以后我的项目的 example 目录。执行流程 ...

March 2, 2023 · 8 min · jiezi

关于go:数组Array基本使用

占位符占位符占位符占位符占位符占位符占位符占位符占位符占位符占位符占位符占位符占位符

March 1, 2023 · 1 min · jiezi

关于go:公众号文章整合到-Github

仓库地址: https://github.com/duanbiaowu... 在 Go - 疾速入门 的根底上,新增了 Go 工程化, Go 常见陷阱 两个栏目,欢送大家关注,一起添砖加瓦。

March 1, 2023 · 1 min · jiezi

关于go:gokratos微服务框架开发

简述Kratos是bilibili开源的一套微服务框架 设计准则:简略:不适度设计,代码平实简略;通用:通用业务开发所须要的根底库的性能;高效:进步业务迭代的效率;稳固:根底库可测试性高,覆盖率高,有线上实际安全可靠;强壮:通过良好的根底库设计,缩小错用;高性能:性能高,但不特定为了性能做 hack 优化,引入 unsafe ;扩展性:良好的接口设计,来扩大实现,或者通过新增根底库目录来扩大性能;容错性:为失败设计,大量引入对 SRE 的了解,鲁棒性高;工具链:蕴含大量工具链,比方 cache 代码生成,lint 工具等等; 个性:APIs:协定通信以 HTTP/gRPC 为根底,通过 Protobuf 进行定义;Errors:通过 Protobuf 的 Enum 作为错误码定义,以及工具生成断定接口;Metadata:在协定通信 HTTP/gRPC 中,通过 Middleware 规范化服务元信息传递;Config:反对多数据源形式,进行配置合并铺平,通过 Atomic 形式反对动静配置;Logger:规范日志接口,可不便集成三方 log 库,并可通过 fluentd 收集日志;Metrics:对立指标接口,能够实现各种指标零碎,默认集成 Prometheus;Tracing:遵循 OpenTelemetry 标准定义,以实现微服务链路追踪;Encoding:反对 Accept 和 Content-Type 进行主动抉择内容编码;Transport:通用的 HTTP/gRPC 传输层,实现对立的 Middleware 插件反对;Registry:实现对立注册核心接口,可插件化对接各种注册核心; 更加具体的介绍详见官网:https://go-kratos.dev/docs/ Kratos框架外围,次要蕴含了根底的CLI工具,以及内置的HTTP/gRPC接口生成和服务生命周期治理 gRPC是Google开源的高性能、通用RPC框架,次要面向挪动利用开发并基于HTTP/2协定规范而设计,基于ProtoBuf(Protocol Buffers)序列化协定开发,反对少数的开发语言 个性:双向流控、头部压缩、单TCP连贯上的多复用申请 概念:客户端利用能够像调用本地对象一样间接调用另一台机器上的服务端利用 protobuf定义接口:编写.proto文件compile工具生成特定语言的执行代码 ProtoBuf全称为protocol buffers,协定缓冲区,是一种与语言无关、与平台无关的可扩大机制,用于序列化结构化数据。 二进制格局 在 gRPC里客户端利用能够像调用本地对象一样间接调用另一台不同的机器上_服务端_利用的办法,使得您可能更容易地创立分布式应用和服务。与许多 RPC 零碎相似,gRPC 也是基于以下理念:定义一个服务,指定其可能被近程调用的办法(蕴含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来解决客户端调用,在客户端领有一个存根像服务端一样的办法。 gRPC 默认应用 protocol buffers,这是 Google 开源的一套成熟的构造数据序列化机制(当然也能够应用其余数据格式如 JSON)。 新建我的项目kratos run <project-name># 拉取我的项目依赖go mod download# 运行我的项目kratos run定义接口:protobuf文件示例syntax = "proto3";package helloworld.v1;import "google/api/annotations.proto";option go_package = "github.com/go-kratos/service- layout/api/helloworld/v1;v1";option java_multiple_files = true;option java_package = "dev.kratos.api.helloworld.v1";option java_outer_classname = "HelloWorldProtoV1";// The greeting service definition.service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) { option (google.api.http) = { // 定义一个 GET 接口,并且把 name 映射到 HelloRequest get: "/helloworld/{name}", // 能够增加附加接口 additional_bindings { // 定义一个 POST 接口,并且把 body 映射到 HelloRequest post: "/v1/greeter/say_hello", body: "*", } }; }}// The request message containing the user's name.message HelloRequest { string name = 1;}// The response message containing the greetingsmessage HelloReply { string message = 1;}生成接口# 生成 proto 模板kratos proto add api/helloworld/v1/greeter.proto# 生成 client 源码kratos proto client api/helloworld/v1/greeter.proto# 生成 server 源码kratos proto server api/helloworld/v1/greeter.proto -t internal/service以上命令会生成: ...

February 28, 2023 · 2 min · jiezi

关于go:SkeyePlayer-libSkeyePlayer中的回调机制介绍

常常咱们会在流媒体推送端提到“数据回调”这个词,在多媒体编程中,咱们会比拟罕用到线程数据回调,在SkeyeClient治理类代码中用到了两个数据回调函数,别离是DShow原始音视频数据采集回调函数和SkeyeRTSPClient网络接管线程中回调音视频编码数据回调函数;尽管两者采集到的数据不同,然而咱们的用处是统一的,都是用来推送,所以咱们通常会用一个数据回调治理函数来进行对立治理。 int CSourceManager::SourceManager(int _channelId, int *_channelPtr, int _frameType, char *pBuf, RTSP_FRAME_INFO* _frameInfo)一、DirectShow采集库中的回调DirectShow采集库中的回调机制在我的另一篇文章SkeyeDarwin SkeyeLive中DirectShow采集音视频流程及几种采集形式介绍中第三点提到过,两种模式都是通过对立的设置回调函数接口函数实现: virtual void WINAPI SetDShowCaptureCallback(RealDataCallback realDataCalBack, void* pMaster) = 0;回调函数的设置函数通常都带有一个设置参数,该设置参数通常是一个指针变量,次要用于在回调函数体中进行调用管制;最罕用的做法是:将其设置为以后类的实例指针this,通过该指针调用不同的实例类的处理函数对回调数据进行解决。 二、libSkeyePlayer库中的回调 libSkeyePlayer库提供的设置回调函数的接口次要来自其所依赖的库SkeyeRTSPClient,该回调函数次要是回调网络接管的Rtsp流解析的音视频编码流数据,用于转发或者解码播放;因为libSkeyePlayer库(及其依赖库)均不是自己的作品(libSkeyePlayer库及其依赖库的作者是SkeyeDarwin团队的Gavin大神,向大神致敬~~~!!!),所以,我对这个库也只有大抵的理解,如果有了解不对或者不合理的中央,欢送斧正,大家互相学习! 1、网络Rtsp流回调流回调函数在SkeyeClient中提供了设置接口函数,底层用libSkeyePlayer提供的接口函数中进行设置,对应SkeyeRTSPClient库提供的接口函数进行设置,三者对应的程序代码如下:SkeyeLive中的回调设置函数接口: int SkeyePlayerManager::Start(char* szURL, HWND hShowWnd, RENDER_FORMAT eRenderFormat, int rtpovertcp, const char *username, const char *password, MediaSourceCallBack callback, void *userPtr) { //Stop if (m_sSourceInfo.rtspSourceId > 0) { Close(); return -1; } m_sSourceInfo.rtspSourceId = SkeyePlayer_OpenStream(szURL, hShowWnd, eRenderFormat, rtpovertcp, username, password, callback, userPtr); return m_sSourceInfo.rtspSourceId ;}libSkeyePlayer中次要的性能在类CChannelManager中实现,该类提供了回调设置函数接口: ...

February 28, 2023 · 2 min · jiezi

关于go:Skeye全景AR及IVMS技术助力地铁安防视频监控系统建设

Skeye全景AR及IVMS技术助力地铁安防视频监控零碎建设 地铁近程视频监控零碎是保障城市轨道交通保护和运输平安的重要伎俩,它可能无效地监控各个地铁站的出入情况,为地铁控制中心的调度员、各车站值班员、列车司机等提供无关列车运行、防灾救灾、旅客疏导以及社会治安等方面的视觉信息,是进步行车指挥工作效率的辅助通信工具,为保障地铁平安作出安防奉献。  个别状况,地铁处于关闭环境,人流量大,如果产生突发事件,救济难度极大;另外地铁零碎智能化水平高,软硬件零碎宏大,整个零碎长期处于半自动和全自动运行状态。因而地铁的视频监控零碎,一方面要对人流进行24小时全方位监控,对突发事件做出预警,视频存储7*24小时以便帮助公安人员更直观高效的解决各类安全事件。另一方面随着治安局势的日益严厉,地铁视频监控零碎中,摄像设施数量多,因而对视频监控零碎的稳定性、一致性、并发性、安全性等性能都具备较强要求。 SkeyeARS 全景AR加强监视系统是视开科技携手电子科技大学长三角研究院智能交通研究所独特研制的一款基于宽场景多路视频无缝拼接、视频实时加强、监督指标加强显示、指标主动跟踪、视频存储回放、近程数据传输和多通道全景视频同步显示等性能的综合视频AR加强监视系统,反对全景拼接最大反对程度视场角360度,反对超高清8K全景视频(7680*1080)多路路编码存储与回放,让乘客在享受地铁带来的高速便捷的出行时多一分平安保障。 SkeyeIVMS分布式集群视频云管控平台是一套集流媒体服务集群、视频资源管理、软硬件服务节点保障、容灾双机热备、主备主动切换为一体的综合性流媒体集群管控云服务,零碎采纳Spring Security架构,平安有保障;反对私有云、私有化部署,保障系统的稳定性、数据强一致性、高并发应用性、高安全性等准则。 1、 稳定性:该平台保障系统的可用性,健壮性为准则。任何状况只有部署服务器的散布状况不同,则应用不变,确保地铁场景监控不间断;2、 数据强一致性:在集群部署的零碎中,某个服务在宕机正在解决的业务局部也会容许ZK选举机制进行节点数据的同步,保障用户所看到的与操作的工夫都是统一的;3、 高并发应用性:基于集群的分布式部署架构,利用负载平衡技术,零碎很轻松在高并发场景下应用,正当防止了因地铁监控设施泛滥导致的并发应用问题;4、 安全性:零碎采纳Spring Security架构,零碎接口和验证鉴权更加严格,即便在公共网络中应用,也能保障地铁零碎数据、文件等信息安全不泄露。

February 28, 2023 · 1 min · jiezi

关于go:长江流域重点水域禁渔视频监控系统建设

长江流域重点水域禁渔视频监控零碎建设 长江作为我国“淡水鱼类的摇篮”,是世界上生物多样性最为丰盛的河流之一。近年来的高强度开发、粗放式利用让长江不堪重负,流域生态性能进化,珍稀特有鱼类大幅衰减,位于长江生物链顶层的珍稀物种奄奄一息,经济鱼类资源濒临枯竭。为了爱护长江渔业资源,农业农村部公布了《长江十年禁渔打算》,期间禁止人造渔业资源的生产性捕捞。近日,长江流域各地区均对重点水域“十年禁渔”守法捕捞等发展专项执法,并着重晋升技防能力,在重点单位、要害部位推广以视频监控为主的技术防备。 长江流域禁渔视频监控零碎次要是将前端监控设施采集的视频、图片等信息通过有线、无线、4G/5等形式传送至后端指挥核心,指挥核心通过对数据的解决,无效辨认进入禁捕区域内的人员、船只、车辆等信息,将视频数据、告警信息推送至禁捕网格责任人,做到第一工夫预警、第一工夫响应、第一工夫出动,全面晋升长江“十年禁渔”现代化管理水平。 SkeyeVSS视频安防综合治理平台作为一站式定制化部署视频安防综合管理系统,秉持网络化、集成化、智能化的理念,采纳先进的软硬件开发技术,可全面满足长江流域禁渔期间重点水域视频实时传输、视频图片数据智能化剖析、可疑人员辨认、船只车辆定位等监控需要。 1、 实时视频剖析:视频实时上传到keyeVSS视频安防综合治理平台,零碎获取视频后进行指定的视频剖析,并把视频剖析后果实时反馈至长江禁捕指挥核心。指挥核心依据剖析后果,能够无效的防止信息脱漏、使非法捕捞口头在产生的之初就立刻告诉解决人员从而防止捕捞事件的产生。SkeyeVSS视频安防综合治理平台全智能的视频剖析性能,大大提高整个视频监控零碎的成果和品质,缩小禁渔工作小组的工作强度。 2、 人脸识别、车船识别系统交融:SkeyeVSS视频安防综合管理系统低耦合,采纳散布式微服务架构,可灵便配置安防零碎中的各个子系统服务;反对横向互联,纵向级联,流媒体集群化部署;零碎可交融人脸识别、车辆辨认、船只辨认等子系统,并针对可疑捕捞行为向指挥核心收回正告,从而精准打击各类涉渔守法违规行为。  此外,因禁渔水域面积宽泛,SkeyeARS全景AR加强监视系统宽场景多路视频无缝拼接、视频实时加强、监督指标加强显示、指标主动跟踪、视频存储回放、近程数据传输和多通道全景视频同步显示等性能可解决视频监控难笼罩等突出问题,为长江重点区域打造全天候、无死角全线禁渔视频监控网。

February 28, 2023 · 1 min · jiezi

关于go:SkeyeExPlayerWindows开发系列之快放慢放的实现

在解说SkeyeExPlayer快加快放之前,咱们首先要解说下SkeyeExPlayer的音视频同步机制,咱们采纳视频同步音频的形式进行工夫戳同步,无音频状况下视频自同步;视频自同步的状况比较简单这里就不做形容,上面咱们着重解说下音视频同步存在时的快加快放并如何放弃音视频同步。 1.音视频同步原理首先,音频解码后获取到的PCM原始数据应用waveout间接进行播放,并记录以后播放工夫戳,音频在任何时候都是失常播放,不做同步操作;而后,视频工夫戳来同步音频工夫戳: DWORD tickcur = GetTickCount(); int tickdiff = tickcur - c->ticklast; int64_t avdiff = apts - vpts - c->tickavdiff; c->ticklast = tickcur; if (apts == -1 && vpts != -1&&c->play_speed>0) //无音频状况做非凡解决 { c->tickframe = 100000/(c->framerate*c->play_speed); if (c->play_speed != 100)//倍速播放时变速放慢 { if (tickdiff - c->tickframe > 2) c->ticksleep-=2; if (tickdiff - c->tickframe < -2) c->ticksleep+=2; } } { if (tickdiff - c->tickframe > 2) c->ticksleep--; if (tickdiff - c->tickframe < -2) c->ticksleep++; } if (apts != -1 && vpts != -1) { if (avdiff > 5) c->ticksleep-=2; if (avdiff <-5) c->ticksleep+=2; } if (c->ticksleep < 0) c->ticksleep = 0; if (c->ticksleep > 0) Sleep(c->ticksleep);同步大抵流程如上段代码所示,首先,计算音频工夫戳和视频工夫戳以及以后工夫戳和上一次工夫戳的差值,而后,依据帧率计算以后帧和上一帧的差值,从而得出视频帧显示的工夫是快于音频还是慢于音频,而在上面进行调整,进而实现音视频的动静东同步: ...

February 28, 2023 · 1 min · jiezi

关于go:SkeyeExPlayerWindows开发系列之解决分片录像时间戳不正常的问题

本篇文章基于上一篇SkeyeExPlayer(Windows)开发系列之采纳ffmpeg进行录像进行补充,测试发现录像的切片除了第一个工夫戳是失常的,其余的初始工夫戳均不失常而且是之前切片的工夫长度的总和;更有甚者很长一段时间的录像都是黑屏无奈播放的,为了解决这个问题,咱们须要将工夫戳进行修改。 通过剖析,初始工夫戳是ffmpeg读取网络流自带的工夫戳,在过程中读取流进行录像,那么势必初始的工夫戳须要进行减掉,执行过程如下: 首先,申请几个变量用以记录每次开始录像时的开始音视频工夫戳,以及音视频是否进行从新录像标记: int64_t audio_start_pts = -1; int64_t audio_start_dts = -1; int64_t video_start_pts = -1; int64_t video_start_dts = -1; bool audio_re_record = false; bool video_re_record = false;2.当达到切片条件时,置从新开启标记为1,并记录以后帧的工夫戳为了下一个切片的开始工夫戳: if (play->record_duration > 0 && fRecTime > play->record_duration && i_pkt.flags == AV_PKT_FLAG_KEY) { audio_re_record = true; video_re_record = true; }这里做了个简略的解决,也就是当以视频为工夫戳检测规范时,须要要在关键帧到来时进行判断,从而保障下一个切片的开始是以关键帧开始的。3.当从新录像标记为真的时候,则重置开始工夫戳,从而在下一次切片时保障工夫戳是从0开始的; if (in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO)//不反对的视频 过滤 { if (video_start_pts < 0) video_start_pts = i_pkt.pts; if (video_start_dts < 0) video_start_dts = i_pkt.dts; if (video_re_record) { video_start_pts = i_pkt.pts; video_start_dts = i_pkt.dts; video_re_record = false; } i_pkt.pts = i_pkt.pts - video_start_pts; i_pkt.dts = i_pkt.dts - video_start_dts; } if (in_stream->codec->codec_type == AVMEDIA_TYPE_AUDIO )//不反对的音频 过滤 { if (audio_start_pts < 0) audio_start_pts = i_pkt.pts; if (audio_start_dts < 0) audio_start_dts = i_pkt.dts; if (audio_re_record) { audio_start_pts = i_pkt.pts; audio_start_dts = i_pkt.dts; audio_re_record = false; } i_pkt.pts = i_pkt.pts - audio_start_pts; i_pkt.dts = i_pkt.dts - audio_start_dts; }为了保障录像的胜利,须要把小于0的工夫戳置为0: ...

February 28, 2023 · 1 min · jiezi

关于go:SkeyeExPlayerWindows开发系列之采用ffmpeg进行录像

这篇和ffmpeg进行截图相似,不过省略掉编码的过程,从网络上或者文件读取的数据为编码后的数据,间接进行写文件即可,本文以写MP4文件为例进行解说。 1.创立线程执行开启录像 player->record_duration = duration*60; player->record_piece_id = 0; player->record_time = 0.0f; memset(player->record_path, 0, sizeof(MAX_PATH_LENGTH)); strcpy(player->record_path, file); player->bRecording = true; //开启录像线程 player->record_thread = CreateThread(NULL, 0, av_record_thread_proc, player, 0, NULL);2.初始化拉去流进行录像 void* av_record_thread_proc(void *thread_param){ PLAYER* play = (PLAYER*)thread_param; if (!play) { return NULL; } AVFormatContext *i_fmt_ctx = NULL; AVStream *i_video_stream = NULL; AVFormatContext *o_fmt_ctx = NULL; AVStream *o_video_stream = NULL; av_register_all(); avcodec_register_all(); avformat_network_init(); /* should set to NULL so that avformat_open_input() allocate a new one */ i_fmt_ctx = NULL; if (avformat_open_input(&i_fmt_ctx, play->file_url, NULL, NULL) != 0) { //fprintf(stderr, "could not open input file\n"); return NULL; } int nRet = avformat_find_stream_info(i_fmt_ctx, NULL); if (nRet<0) { ///fprintf(stderr, "could not find stream info\n"); return NULL; } //bool bSupportVideo = true; //bool bSupportAudio = true; int nVideoIndex = -1; int nAudioIndex = -1; int nPathLen = strlen(play->record_path); char *csFileName = new char[nPathLen]; memset(csFileName, 0, nPathLen); strncpy(csFileName, play->record_path, nPathLen-4); char sSliceupName[MAX_PATH_LENGTH] = {0,}; if (play->record_duration > 0) { sprintf(sSliceupName, "%s_%d.mp4", csFileName, play->record_piece_id); } else { sprintf(sSliceupName, "%s.mp4", csFileName); } //创立MP4文件 if (CreateMediaFile(&o_fmt_ctx, i_fmt_ctx, sSliceupName) == 0) { return 0; } /* * since all input files are supposed to be identical (framerate, dimension, color format, ...) * we can safely set output codec values from first input file */ int nOutStreamId = 0; for (int i = 0; i < i_fmt_ctx->nb_streams; i++) { AVStream *in_stream = i_fmt_ctx->streams[i]; if (in_stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) { AVCodecID codecId = in_stream->codec->codec_id; //音频格式过滤(just support aac,mp3) if ((codecId != AV_CODEC_ID_MP3 && codecId != AV_CODEC_ID_AAC )/*|| in_stream->codec->extradata_size==0*/) ///< preferred ID for decoding MPEG audio layer 1, 2 or 3 { //bSupportAudio = false; continue; } nAudioIndex = nOutStreamId; } if (i_fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { AVCodecID codecId = in_stream->codec->codec_id; //视频频格局过滤(just support h264,265) if ((codecId != AV_CODEC_ID_H264 && codecId != AV_CODEC_ID_H265) /*|| in_stream->codec->extradata_size == 0*/) ///< preferred ID for decoding MPEG audio layer 1, 2 or 3 { //bSupportVideo = false; continue; } nVideoIndex = nOutStreamId; } nOutStreamId++; } int start_pts = -1; int start_dts = -1; int64_t audio_start_pts = -1; int64_t audio_start_dts = -1; int64_t video_start_pts = -1; int64_t video_start_dts = -1; bool audio_re_record = false; bool video_re_record = false; int64_t pts, dts; //int total_frame = 3000;//写3000帧文件 //while (total_frame--) while (play->bRecording) { AVPacket i_pkt; av_init_packet(&i_pkt); i_pkt.size = 0; i_pkt.data = NULL; if (av_read_frame(i_fmt_ctx, &i_pkt) <0) break; //ret = av_interleaved_write_frame(pOFormat, tmppkt); AVStream *in_stream = i_fmt_ctx->streams[i_pkt.stream_index]; AVStream *out_stream = NULL; if (in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO && nVideoIndex > -1 ) { int nTimeBase = in_stream->time_base.den; if (nTimeBase>0) play->record_time = (float)(i_pkt.dts - start_dts) / nTimeBase; //TRACE("Video Timestamp: %f time_base = %d %lld %lld duration = %d \n", m_fRecordTime, in_stream->time_base.den, i_pkt.pts, i_pkt.dts, i_pkt.duration); if (start_pts < 0) start_pts = i_pkt.pts; if (start_dts < 0) start_dts = i_pkt.dts; //录像工夫,单位: S float fRecTime = 0.0f; if (nTimeBase>0) fRecTime = (float)(i_pkt.dts - start_dts) / nTimeBase; //判断是否达到切片的要求 if (play->record_duration > 0 && fRecTime > play->record_duration && i_pkt.flags == AV_PKT_FLAG_KEY) { play->record_piece_id++; //敞开曾经实现切片的文件 CloseMediaFile(o_fmt_ctx); memset(sSliceupName, 0x00, MAX_PATH_LENGTH); sprintf(sSliceupName, "%s_%d.mp4", csFileName, play->record_piece_id); //创立MP4文件 if (CreateMediaFile(&o_fmt_ctx, i_fmt_ctx, sSliceupName) == 0) { return 0; } start_pts = i_pkt.pts; start_dts = i_pkt.dts; audio_re_record = true; video_re_record = true; } } else if (in_stream->codec->codec_type == AVMEDIA_TYPE_AUDIO && nAudioIndex > -1 && nVideoIndex == -1) { int nTimeBase = in_stream->time_base.den; if (nTimeBase>0) play->record_time = (float)(i_pkt.dts) / nTimeBase; //TRACE("Audio Timestamp: %f time_base = %d %lld %lld duration = %d \n", play->record_time, in_stream->time_base.den, i_pkt.pts, i_pkt.dts, i_pkt.duration); if (start_pts < 0) start_pts = i_pkt.pts; if (start_dts < 0) start_dts = i_pkt.dts; //录像工夫,单位: S float fRecTime = 0.0f; if (nTimeBase>0) fRecTime = (float)(i_pkt.dts - start_dts) / nTimeBase; //判断是否达到切片的要求 if (play->record_duration > 0 && fRecTime > play->record_duration) { play->record_piece_id++; //敞开曾经实现切片的文件 CloseMediaFile(o_fmt_ctx); memset(sSliceupName, 0x00, 512); sprintf(sSliceupName, "%s_%d.mp4", csFileName, play->record_piece_id); //创立MP4文件 if (CreateMediaFile(&o_fmt_ctx, i_fmt_ctx, sSliceupName) == 0) { return 0; } start_pts = i_pkt.pts; start_dts = i_pkt.dts; audio_re_record = true; video_re_record = true; } } if (in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO)//不反对的视频 过滤 { if( nVideoIndex == -1) continue; out_stream = o_fmt_ctx->streams[nVideoIndex]; if (video_start_pts < 0) video_start_pts = i_pkt.pts; if (video_start_dts < 0) video_start_dts = i_pkt.dts; if (video_re_record) { video_start_pts = i_pkt.pts; video_start_dts = i_pkt.dts; video_re_record = false; } i_pkt.pts = i_pkt.pts - video_start_pts; i_pkt.dts = i_pkt.dts - video_start_dts; } if (in_stream->codec->codec_type == AVMEDIA_TYPE_AUDIO )//不反对的音频 过滤 { if (nAudioIndex == -1) continue; out_stream = o_fmt_ctx->streams[nAudioIndex]; if (audio_start_pts < 0) audio_start_pts = i_pkt.pts; if (audio_start_dts < 0) audio_start_dts = i_pkt.dts; if (audio_re_record) { audio_start_pts = i_pkt.pts; audio_start_dts = i_pkt.dts; audio_re_record = false; } i_pkt.pts = i_pkt.pts - audio_start_pts; i_pkt.dts = i_pkt.dts - audio_start_dts; } if (!out_stream) continue; i_pkt.pts = (i_pkt.pts > 0) ? i_pkt.pts : 0; i_pkt.dts = (i_pkt.dts > 0) ? i_pkt.dts : 0; i_pkt.duration = (i_pkt.duration > 0) ? i_pkt.duration : 0; i_pkt.pts = av_rescale_q_rnd(i_pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF); i_pkt.dts = av_rescale_q_rnd(i_pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF); i_pkt.duration = av_rescale_q(i_pkt.duration, in_stream->time_base, out_stream->time_base); i_pkt.pos = -1; int ret = av_interleaved_write_frame(o_fmt_ctx, &i_pkt); if (ret < 0) continue; //break; } avformat_close_input(&i_fmt_ctx); av_write_trailer(o_fmt_ctx); avcodec_close(o_fmt_ctx->streams[0]->codec); av_freep(&o_fmt_ctx->streams[0]->codec); av_freep(&o_fmt_ctx->streams[0]); avio_close(o_fmt_ctx->pb); av_free(o_fmt_ctx); av_bitstream_filter_close(aacBsf); if (csFileName) delete[] csFileName; return NULL;}纵观以上录像代码,通过另外开拓线程进行录像,线程执行过程分为以下几个局部:1.拉流读取流数据模块对于这块不做过多赘述,大家有趣味能够参考系列文章的前几篇文章; ...

February 28, 2023 · 5 min · jiezi

关于go:SkeyeExPlayerWindows功能介绍及应用场景

SkeyeExPlayer(Windows)通过为期一个月的开发曾经根本实现,尽管目前仍存在一些小问题,然而总体性能还是趋于比较稳定和弱小的,上面对其性能和利用场景做简要介绍。 一 SkeyeExPlayer性能介绍Windows平台初始版本界面如下图所示: 如上截图SkeyeExPlayer目前已反对 SkeyeExPlayer RTSP/RTMP/HLS/HTTP全功能流媒体播放器开发计划中列举的绝大部分性能,诸如大部分流媒体和媒体文件的反对,单帧播放,快进和慢放,抓图,录像(反对分片)等性能;OSD和图片叠加如下图所示: 此外,SkeyeExPlayer减少了音频波形WAV和频谱显示,如下图所示: 后续,将持续减少更多的画中画缩放,丑化渲染等特效解决,以及基于全景VR的视频显示等;对于SkeyeExPlayer接口调用和demo程序的解说将在“SkeyeExPlayer开发系列”中进行解说,敬请期待。 二 SkeyeExPlayer利用场景 SkeyeExPlayer播放器可广泛应用于流媒体客户端,IPTV,赛事直播等我的项目中。

February 28, 2023 · 1 min · jiezi

关于go:RTMP推送扩展支持HEVCH265之Metadata结构填写详解

鉴于宽广码友对上一篇文章RTMP推送扩大反对HEVC(H265)的Metadata数据结构还存在不分明的中央,这里对RTMP推送Metadata的构造进行详解。 首先, 咱们先解说下 H.264/AVC metadata 语法,不便大家了解,这相干的文章大家在网上很容易找到,如果大家比拟相熟能够跳过; 参考:《ISO/IEC 14496-15 NAL unit structured video》AVCDecoderConfigurationRecord构造:(最小长度7字节) 阐明: libSkeyeRTMPPusher咱们称之为Metadata,而在FFmpeg中,则称之为extradata,extradata解析,详见ff_h264_decode_extradata() 留神: 第5字节的最初2位,示意的就是NAL size的字节数。在AVCC格局中,每个NAL后面都会有NAL size字段。NAL size可能是1字节、2字节或4字节(4字节较常见),解析extradata重要目标就是确认这个值。(而Annex-B格局,要split NAL,只有去探测0x000001就能够了) H.264 metadata 示例(AVCC格局) metadata 如上 metasize 4705| FF NAL size: 4字节06| E1 SPS num: 107| 00 1F SPS size: 31字节09| 67 NAL type: SPS40| 01 PPS num: 141| 00 05 PPS size: 5字节42| 68 NAL type: PPS H.265/HEVC metadata语法参照HEVCDecoderConfigurationRecord:(最小长度23字节) HEVC metadata 示例 metadata 如上 metasize 111 24| 20 NAL type: VPS 25| 00 01 VPS num: 1 27| 00 19 VPS size: 25字节 54| 21 NAL type: SPS 55| 00 01 SPS num: 1 57| 00 29 SPS size: 41字节100| 22 NAL type: PPS ...

February 28, 2023 · 2 min · jiezi

关于go:GB28181-国标视频云平台方案安装使用文档

GB28181 国标视频云平台计划装置应用文档SkeyeVSS架构图SkeyeVSS国标级联框架图 SkeyeVSS国标流媒体服务框架图 SkeyeCMS SIP 核心信令服务, 单节点, 自带一个 Redis Server, 随 SkeyeCMS 自启动, 不须要手动运行; SkeyeSMS SIP 流媒体服务, 依据须要可部署多套,集群部署; 端口应用TCP 端口 : 10000(CMS - HTTP), 26379(CMS - Redis), 10001(SMS - HTTP), 11935(SMS - RTMP Live), 30000 ~ 40000(SMS - RTP over TCP) UDP 端口 : 5060(CMS - SIP), 5070(SMS - SIP), 50000 ~ 60000(SMS - RTP over UDP) 1.1 配置信令服务 skeyecms.ini[sip] -> host SIP 核心信令服务器 IP [sip] -> serial SIP 核心信令服务器 ID ...

February 28, 2023 · 2 min · jiezi

关于go:RoaringBitmap-的原理及在-Go-中如何使用

明天咱们聊聊 RoaringBitmap(怒吼位图)。在海量数据背景下,咱们通常须要疾速对数据计算、两头存储的需要。一系列专门为大数据筹备的数据结构应运而生,常见的有 HyperLogLog、BloomFilter等。 咱们看一道陈词滥调的面试题: 给定含有40亿个不反复的位于[0, 2^32 - 1]区间内的整数的汇合,如何疾速断定某个数是否在该汇合内?首先,40 亿在存储上咱们须要耗费 40亿 * 32 位 = 160 Byte,大抵是 16000 MB 即 14.9 GB 的内存,显然这是咱们不能承受的。如果你给出的是这个答案,那么你就曾经输了! 咱们能够用位图来存储,第 0 个 bit 示意数字 0,第 1 个 Bit 示意数字 1,以此类推。如果某个数位于原汇合内,就将它对应的位图内的 bit 置为 1,否则放弃为 0。这样只占用了 512MB 的内存,不到原来的 3.4%。 咱们会发现当数据稠密的时候,也须要要开拓这么大的内存空间,就施展不出其存储效率。为了解决位图不适应稠密存储的问题,RoaringBitmap(怒吼位图)诞生了,因而本文重点探讨它。上面简称 RBM。 1 什么是 RoaringBitmap是一种基于位图的数据结构,能够高效地存储大量的非负整数,并反对多种汇合运算,如并集、交加、差集等。它能够高效地判断一个元素是否在汇合中,并且能够应用很少的空间来存储汇合。 2 数据结构源码: short[] keys;Container[] values;int size;RoaringBitmap 以后有两个版本,别离用来存储 32 位和 64 位整数。以 32 位为例,RBM 会将 32 位的整形(int)拆分成高 16 位和低 16位 两局部来解决。其中 高 16位 会被作为 key 存储到 short[] keys中低 16 位则被看做 value,存储到 Container[] values中的某个 Container 中keys 和 values 通过下标一一对应。size 则标示了以后蕴含的 key-value pair的数量,即 keys 和 values 中无效数据的数量。 ...

February 28, 2023 · 2 min · jiezi

关于go:切片Slice基本使用

Slice 简介切片是对数组的一种形象,数组是一种不可变长的汇合切片是一种可变长度的汇合可追加元素,可能会导致切片的容量增大Slice 切片的定义// 办法一:应用var关键字申明var sliceExample []string// 办法二:应用make办法var sliceExample = make([]string, 1)// 办法三:办法二的简化sliceExample := make([]string, 1)// 办法四:间接初始化var sliceExample = []string{"1", "2", "3"}// 办法五:办法四简化sliceExample := []string{"1", "2", "3"}切片初始化// 第一种办法:

February 27, 2023 · 1 min · jiezi

关于go:一个账号能同时登录若干台设备终极方案

1、一个账号在登录的时候,都给其调配一个token. token在 redis中的对应关系是【key: token , vaule: userId】(应用Redis字符串类型保留),而后userId在 redis中的对应关系也还是【key: userId , value: userInfo】(应用Redis有序汇合来保留).然而 userInfo 外面不再是用户的根本信息了,这外面须要减少一个属性 loginTokenList,用来保留此账号曾经登录的 token 汇合. 2、当账号登录的时候,依据 userId 取出 userInfo ,而后拿到loginTokenList,逐个判断 token 是否生效,而后简略判断一下过滤后的loginTokenList size 即可.留神:这里更新 userInfo 信息时候同样是须要原子操作的(lua 脚本),因为是登录,所以显著这里的并发要小很多,所以这里原子性的更新对 redis 零碎简直没有影响. 3、当用户拜访页面的时候,咱们还是同样的依据token获取userId,再依据userId获取userInfo.这里同上,用户拜访页面的时候尽量不要去检查和更新 userInfo 里的loginTokenList, 直接判断通过token是否获取到数据即可。

February 27, 2023 · 1 min · jiezi

关于go:Go-在信创这一块会输给-Java想不通

大家好,我是煎鱼。 Go 语言曾经开源 10+ 年,从高的关注度,再到云原生时代引爆学习 Go 的浪潮,再到当初的数年后。 其搜寻热度和 TIOBE 排名根本维持如下: 最新的排名第是第 11 名。 以往我在网上经常看到大家探讨的 Go vs Java,大家根本都是从技术的角度剖析,比照两者的各类老本、个性,孰强孰弱。技术角度上决定如何选型、语言的优劣势等。 联合近来的世界大事件。这篇文章会抛开单纯技术层面来看。 企业的一些要求最近几年接触各类国央企的业务多了后,在国内发现了一个影响 Go 语言倒退的致命问题或景象。 好家伙,人家的预投标、招标书上,就会间接要求须要 Java 语言开发、要可能反对云原生环境等。 起因一:信创,对于这类的必须替换准则,将会导致这家企业非 Java 语言的招标可能不能入选。 起因二:Java 群体过于宏大,很多有年代的企业零碎晚期建设、外包都采取了 Java 体系来开发。成熟、便宜、好招人等。 看完你会想这就是非 Java 别来,咱们 Go 能不能行? 不能用 Go,因为 Google如果你是用 Go 语言写的,难堪的状况就会呈现。一旦你向甲方解释,并给出对应的语言清单。就会呈现一个新的问题:Go 语言是?背景?归属? 或是去搜寻。就会发现以下的状况: 又或是: Go 语言是 Google 开发的,如果一旦 ZM 全面脱钩,GitHub 会被封,这门语言也很容易就会被禁止应用。 这将会使得 Go 语言在局部国央企上,在私有化上间接无奈与 Java 竞争。Go 就被卡脖子了。 企业自身也会受到较大的冲击。 总结Go 语言的外围开发团队,大多仍在 Google。Google 为他们提供了工作、薪酬、资源。让这门语言在孵化、成长期不须要遇到太多的生存问题。 咱们在进一步利用中,很多会思考到企业外部的全面国产化、自主可控、技术体系建设等问题。在 Go 语言试图和 Java 语言分蛋糕时,依然是会处于绝对劣势的一个大坑。 ...

February 27, 2023 · 1 min · jiezi

关于go:一文读懂Go-120引入的PGO性能优化

背景Go 1.20版本于2023年2月份正式公布,在这个版本里引入了PGO性能优化机制。 PGO的英文全称是Profile Guided Optimization,基本原理分为以下2个步骤: 先对程序做profiling,收集程序运行时的数据,生成profiling文件。编译程序时启用PGO选项,编译器会依据.pgo文件里的内容对程序做性能优化。咱们都晓得在编译程序的时候,编译器会对程序做很多优化,包含大家熟知的内联优化(inline optimization)、逃逸剖析(escape analysis)、常数流传(constant propagation)。这些优化是编译器能够间接通过分析程序源代码来实现的。 然而有些优化是无奈通过解析源代码来实现的。 比方一个函数里有很多if/else条件分支判断,咱们可能心愿编译器主动帮咱们优化条件分支程序,来放慢条件分支的判断,晋升程序性能。 然而,编译器可能是无奈晓得哪些条件分支进入的次数多,哪些条件分支进入的次数少,因为这个和程序的输出是有关系的。 这个时候,做编译器优化的人就想到了PGO: Profile Guided Optimization。 PGO的原理很简略,那就是先把程序跑起来,收集程序运行过程中的数据。而后编译器再依据收集到的程序运行时数据来分析程序的行为,进而做针对性的性能优化。 比方程序能够收集到哪些条件分支进入的次数更多,就把该条件分支的判断放在后面,这样能够缩小条件判断的耗时,晋升程序性能。 那Go语言如何应用PGO来优化程序的性能呢?咱们接下来看看具体的例子。 示例咱们实现一个web接口/render,该接口以markdown文件的二进制格局作为输出,将markdown格局转换为html格局返回。 咱们借助 gitlab.com/golang-commonmark/markdown 我的项目来实现该接口。 环境搭建$ go mod init example.com/markdown新建一个 main.go文件,代码如下: package mainimport ( "bytes" "io" "log" "net/http" _ "net/http/pprof" "gitlab.com/golang-commonmark/markdown")func render(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed) return } src, err := io.ReadAll(r.Body) if err != nil { log.Printf("error reading body: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } md := markdown.New( markdown.XHTMLOutput(true), markdown.Typographer(true), markdown.Linkify(true), markdown.Tables(true), ) var buf bytes.Buffer if err := md.Render(&buf, src); err != nil { log.Printf("error converting markdown: %v", err) http.Error(w, "Malformed markdown", http.StatusBadRequest) return } if _, err := io.Copy(w, &buf); err != nil { log.Printf("error writing response: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return }}func main() { http.HandleFunc("/render", render) log.Printf("Serving on port 8080...") log.Fatal(http.ListenAndServe(":8080", nil))}编译和运行该程序: ...

February 26, 2023 · 2 min · jiezi

关于go:GO-中的-defer-有哪些注意事项下

上次一起写了 3 个案例,咱们这一次持续,这一次的会比上一次的略微不太一样 案例 1还有一个也十分罕用的案例,应用 defer 来捕捉异样 ,也就是当程序解体的时候,defer 语句能够帮咱们兜底,能够捕捉异样后依照咱们冀望的逻辑进行执行,让程序回到正确的轨道下面 个别使程序解体很简略, C/C++ 的时候,咱们能够造异样,例如除 0 ,或者是数组越界等等就会导致程序解体GO 外面造异样也能够依照下面这种形式,然而咱们也能够应用 panic 函数来实现程序解体写一个简略的例子,把本人的程序搞崩func testDefer() { defer func() { fmt.Println(" panic 前 ") }() panic("i panic") defer func() { fmt.Println(" panic 后") }()}func main() { testDefer() fmt.Println("program over")}其实咱们看 goland 工具就能够看到,上面这一句话是不会被执行的 `defer func() { fmt.Println(" panic 后")}()` 理论运行程序后,后果如下: 咱们能够看到实际效果,程序是解体了,因为 fmt.Println("program over") 没有打印进去,且看图片,是有具体的 panic 信息的 从上述能够看明确, panic 之后的程序是不会执行的, panic 之前的 defer 语句会执行,因为他先入栈了 那么咱们来捕捉一下异样还是下面的代码,咱们来捕捉一下异样,就是加一句话就能够了 func testDefer() { defer func() { fmt.Println(" panic 前 ") if err := recover(); err != nil { fmt.Println(" xdm , 我捕捉到异样了,程序不必解体了 ") } }() panic("i panic") //触发defer出栈 defer func() { fmt.Println(" panic 后") }()}func main() { testDefer() fmt.Println("program over")}看上述代码,panic 之后的程序依然是不会执行的,然而咱们退出了 recover() 语句, 他会帮忙咱们捕捉异样,解决异样 ...

February 25, 2023 · 2 min · jiezi

关于go:go-python-开发对接企微自建应用发消息-实例

dochttps://developer.work.weixin... appgopackage mainimport ( "bytes" "encoding/json" "fmt" "io/ioutil" "log" "net/http")const ( CorpId = "xxxxxxxxx" CorpSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-k" AgentId int = 1xxxxxxx)type T struct { Errcode int `json:"errcode"` Errmsg string `json:"errmsg"` AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"`}type T2 struct { Touser interface{} `json:"touser"` Msgtype string `json:"msgtype"` Agentid int `json:"agentid"` Text struct { Content interface{} `json:"content"` } `json:"markdown"` Safe int `json:"safe"`}type T3 struct { Errcode int `json:"errcode"` Errmsg string `json:"errmsg"` Msgid string `json:"msgid"`}func GetToken(CorpId string, CorpSecret string) (*T) { url := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s", CorpId, CorpSecret) client := &http.Client{} req, err := http.NewRequest("GET", url, nil) resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Println("######", string(body)) var assetList T err = json.Unmarshal(body, &assetList) return &assetList}func SendMessage(token string, comment string, userName string) *T3 { var j T2 j.Agentid = AgentId j.Text.Content = comment j.Touser = userName j.Msgtype = "markdown" j.Safe = 0 data, err := json.Marshal(j) if err != nil { fmt.Println("err was %v", err) } fmt.Println(string(data)) reader := bytes.NewReader(data) uri := "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + token client := &http.Client{} req, err := http.NewRequest("POST", uri, reader) req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Println("####send msg##", string(body)) var assetList T3 err = json.Unmarshal(body, &assetList) return &assetList}func main() { token1 := GetToken(CorpId, CorpSecret ) //fmt.Println(token1.AccessToken) SendMessage(token1.AccessToken, "### hallo, ich bin TixnxxxXiang \n <font color=\\\"info\\\">Gutgen Tag</font> \n * Lexbzenhn", "lixxxxg@xiaoxxxxshu.com")} ...

February 25, 2023 · 2 min · jiezi

关于go:goquery的认识使用源码分析及实现原理

转载于:https://scriptrunz.com/zh-cn/... goquery 是什么goquery 是用 Go 实现的一个相似于 jQuery 的库,它封装了 Go 规范库 net/html 和 CSS 库 cascadia,提供了与 jQuery 相近的接口。 Go 驰名的爬虫框架 colly 就是基于 goquery 实现的。 goquery 能用来干什么goquery 提供了与 jQuery 相近的接口,能够对爬取到的 HTML 进行过滤以取得本人想要的数据。 goquery quick startDocument 是 goquery 包的外围类之一,创立一个 Document 是应用 goquery 的第一步: type Document struct { *Selection Url *url.URL rootNode *html.Node}func NewDocumentFromNode(root *html.Node) *Document func NewDocument(url string) (*Document, error)func NewDocumentFromReader(r io.Reader) (*Document, error)func NewDocumentFromResponse(res *http.Response) (*Document, error)通过源码能够晓得 Document 继承了 Selection(先不论 Selection 是什么),除此之外最重要的是rootNode,它是 HTML 的根节点,Url这个字段作用不大,在应用NewDocument和NewDocumentFromResponse时会对该字段赋值。 ...

February 24, 2023 · 3 min · jiezi

关于go:如何5分钟跑起来一个完整项目

明天熊哥和大家聊聊,我怎么在5分钟之内生成一个残缺的我的项目。 成果看看这个面板,这竟然是我花了5分钟胜利跑起来的我的项目。 居然具备超过三十项性能。还能够间接在页面上生成代码。 它是什么?它是 go-gin-api 它反对哪些性能?可能上面有一些性能你没听过,或者听不懂。没关系,先看看。我当前都会讲。 反对 rate 接口限流反对 panic 异样时邮件告诉反对 cors 接口跨域反对 Prometheus 指标记录反对 Swagger 接口文档生成反对 GraphQL 查询语言反对 trace 我的项目外部链路追踪反对 pprof 性能分析反对 errno 对立定义错误码反对 zap 日志收集反对 viper 配置文件解析反对 gorm 数据库组件反对 go-redis 组件反对 RESTful API 返回值标准反对 生成数据表 CURD、控制器办法 等代码生成器反对 cron 定时工作,在后盾可界面配置反对 websocket 实时通信,在后盾有界面演示反对 web 界面,应用的 Light Year Admin 模板不懂的关键字,如果感兴趣也能够本人下来查查我的宝。 三行代码跑起来git clone https://github.com/xinliangnote/go-gin-api.gitcd go-gin-apigo run main.go -env dev 跑完当前立马就会弹出一个页面。 不得不说go-gin-api的作者切实想得周全,跑起来不报错,会提醒你填写环境信息。 当初曾经过了1分钟了,熊哥还有4分钟。 3分钟启一个环境既然面板提醒须要mysql和redis,立马关上hub.docker.com 搜寻mysql和redis取得他们的启动命令。 docker容器,能够最疾速在本地提供开发环境。不懂就问熊哥 间接在概述里拿到最简略的启动命令如下。 docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tagdocker run --name some-redis -d redis稍做批改,加一下明码。 ...

February 24, 2023 · 1 min · jiezi

关于go:Map使用

Map 定义

February 23, 2023 · 1 min · jiezi

关于go:我说我为什么抽不到SSR原来是这段代码在作祟

本文是龚国玮所写,熊哥有所新增批改删减,原文见文末。 我说我为什么抽不到SSR,原来是加权随机算法在作怪 浏览本文须要做好心理准备,倡议带着深究到底的信心和毅力进行学习!灵魂拷问为什么有 50% 的几率取得金币? 为什么有 40% 的几率取得钻石? 为什么只有 9% 的几率取得配备? 为什么才有 1% 的几率取得极品配备? 是兽性的扭曲,还是道德的沦丧,请和我一起走进今日说法 ! 介绍元素被选中的机会并不相等,而是由绝对“权重”(或概率)被选中的,是偏心的,这就是加权随机。 举个栗子,如果当初有一个权重数组 w = {1, 2, 4, 8},它们代表如下规定。 $ \frac{1}{(1+2+4+8)} = \frac{1}{15} \approx 6.6$ \% 几率选中第1个$ \frac{2}{(1+2+4+8)} = \frac{2}{15} \approx 13.3$ \% 几率选中第2个$ \frac{3}{(1+2+4+8)} = \frac{4}{15} \approx 26.6$ \% 几率选中第3个$ \frac{1}{(1+2+4+8)} = \frac{8}{15} \approx 53.3$ \% 几率选中第4个一个小小的概率问题,竟然引出了大大的思考。 计划一、笨笨的方法所以要设计一个加权算法的程序,你会怎么写呢? 第一个办法把权重所在的地位开展,而后从该列表中随机抉择。 假如当初有权重列表 {1, 2, 4, 8}。 那咱们失去的候选列表将是 {0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3}而后通过 rand.Intn() ,获取一个随机数,就实现了,代码如下。 ...

February 22, 2023 · 2 min · jiezi

关于go:Go框架iris框架中mvc使用进阶

大家好,我是渔夫子。本号新推出「Go工具箱」系列,意在给大家分享应用go语言编写的、实用的、好玩的工具。同时理解其底层的实现原理,以便更深刻地理解Go语言。 以下是本文的提纲,如果你对提纲中的内容曾经齐全把握,那么本文并不适宜你。否则,请持续浏览本文。 一、 Controller中的BeforeActivation和AfterActivation如果在Controller中定义的办法的第一个单词不是申请办法,那么该如何将该办法定义成申请处理器呢?这就须要在Controller中定义BeforeActivation办法了。该办法定义如下: func (c *testController) BeforeActivation(b mvc.BeforeActivation) { b.Handle("GET", "/custom_path", "CustomHandler")}func (c *testController) CustomHandler() { fmt.Println("this is custom handler")}和BeforeActivation对应的,还有一个是AfterActivation函数,该函数个别用来解决给特定的处理器减少中间件等操作。如下: func (c *testController) AfterActivation(a mvc.AfterActivation) { // 依据办法名 获取对应的路由 index := a.GetRoute("GetMyHome") // 给该路由的处理器减少前置中间件或后置中间件 index.Handlers = append([]iris.Handler{cacheHandler}, index.Handlers...)}二、 Controller中的BeginRequest和EndRequest函数咱们当初定义一个AuthController,并做如下路由注册: package mainimport ( "fmt" "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/mvc")func main() { app := iris.New() // New一次,相当于New了一个路由 mvcApplication := mvc.New(app.Party("/")) mvcApplication.Handle(new(AuthController)) app.Listen(":8080")}type AuthController struct { UserID int64}// BeginRequest saves login state to the context, the user id.func (c *AuthController) BeginRequest(ctx iris.Context) { ctx.Write([]byte("BeginRequest\n"))}func (c *AuthController) EndRequest(ctx iris.Context) { ctx.Write([]byte("EndRequest\n"))}func (c *AuthController) GetLogin(ctx iris.Context) { ctx.Write([]byte("Login\n"))}在AuthController中,咱们定义了两个办法BeginRequest和EndRequest。注册的路由有GET /login。在浏览器中输出http://localhost:8080/login,则会有如下输入: ...

February 22, 2023 · 3 min · jiezi

关于go:写给go开发者的gRPC教程metadata

本篇为【写给go开发者的gRPC教程系列】第五篇 第一篇:protobuf根底 第二篇:通信模式 第三篇:拦截器 第四篇:错误处理 第五篇:metadata 本系列将继续更新,欢送关注获取实时告诉 导语 和在一般HTTP申请中一样,gRPC提供了在每一次RPC中携带的上下文构造:metadata。在Go语言中,它与context.Context紧密结合,帮忙咱们实现服务端与客户端之间相互传递信息 什么是metadata?gRPC 的 metadata 简略了解,就是 HTTP Header 中的 key-value 对 metadata 是以 key-value 的模式存储数据的,其中 key 是 string 类型,而 value 是 []string,即一个字符串数组类型metadata 使得 client 和 server 可能为对方提供对于本次调用的一些信息,就像一次HTTP申请的Request Header和Response Header一样HTTP Header 的生命周期是一次 HTTP 申请,那么 metadata 的生命周期就是一次 RPC 调用Metadata 创立 应用New():md := metadata.New(map[string]string{"key1":"value1","key2":"value2"}) 应用Pairs():要留神如果有雷同的 key 会主动合并 md := metadata.Pairs( "key1", "value1", "key1", "value1.2", // "key1" will have map value []string{"value1", "value1.2"} "key2", "value2",) 合并多个metadatamd1 := metadata.Pairs("k1", "v1", "k2", "v2")md2 := metadata.New(map[string]string{"key1":"value1","key2":"value2"})md := metadata.Join(md1, md2) 存储二进制数据在 metadata 中,key 永远是 string 类型,然而 value 能够是 string 也能够是二进制数据。为了在 metadata 中存储二进制数据,咱们仅仅须要在 key 的前面加上一个 - bin 后缀。具备 - bin 后缀的 key 所对应的 value 在创立 metadata 时会被编码(base64),收到的时候会被解码: ...

February 22, 2023 · 4 min · jiezi

关于go:你知道-GO-中的-协程可以无止境的开吗

GO语言天生高并发的语言,那么是不是应用 go 开拓协程越多越好的,那么在 go 外面,协程是不是能够开有限多个呢? 那么咱们就一起来看看尝试写写 demo 吧 尝试开拓尽可能多的 协程写一个 demo ,循环开 1 << 31 - 1 个协程看看会是什么成果 func main() { goroutineNum := math.MaxInt32 for i := 0; i < goroutineNum; i++ { go func(i int) { fmt.Println(" i == ", i, "func == ", runtime.NumGoroutine()) }(i) }}执行后,我人都傻了,间接是没有输入,2 核 1 G 的服务器间接卡死 , 感兴趣的 xdm 能够尝试一波 这里说一下,呈现上述景象的起因是: 咱们迅速的疯狂开拓协程,又不管制并发数量,那么在那段很短的工夫外面,go 程序会尽可能多的占用操作系统资源,直到被操作系统被动杀掉 一旦主协程被杀掉,那么其余的协程也全副 game over , 因为他们占用的资源是用户态的共享资源,一个协程挂掉,是会影响到其余协程的 尝试管制协程数量咱们实现的办法是,应用 channel ,设置 channel 的缓冲个数来管制理论并发的协程个数,一起来看看是否有成果 func processGo(i int, ch chan struct{}) { fmt.Println(" i == ", i, "func == ", runtime.NumGoroutine()) <-ch}func main() { goroutineNum := math.MaxInt32 ch := make(chan struct{}, 5) for i := 0; i < goroutineNum; i++ { ch <- struct{}{} go processGo(i, ch) }}成果见如下截图,因为数据打印太长,如下为局部数据 ...

February 21, 2023 · 3 min · jiezi

关于go:妙啊空结构体还能这么用Go语言的结构体看这篇就够了

本文详解了Go语言构造体的各个知识点,最初介绍了空构造体的3种妙用。心愿对你有帮忙。定义构造体,是一种自定义的数据类型,由多个数据类型组合而成。用于形容一类事物相干属性。 定义形式: type 类型名 struct { 字段名 字段类型 …}//示例:type Animal struct { Name string Age int}实例化构造体和构造体指针,两者的实例化有所区别 提供多种写法,灵便应用: //构造体实例化//写法1//var a Animal //a.Name = "aaa"//a.Age = 18//写法2a := Animal{ Name: "dog", Age: 18,}fmt.Println(fmt.Sprintf("%T - %v", a, a)) //main.Animal - {dog 18}//构造体指针实例化//写法1var b *Animal b = new(Animal)//写法2//b := new(Animal) //写法3//b := &Animal{} b.Name = "cat" //在底层是(*b).Name = "cat",这是Go语言帮咱们实现的语法糖fmt.Println(fmt.Sprintf("%T - %v", b, b)) //*main.Animal - &{cat 0}留神:构造体指针必须手动初始化,调配内存地址 匿名构造体实用于长期数据存储的场景 var v struct { Name string Age int}fmt.Println(v)空构造体不占用内存空间 ...

February 21, 2023 · 3 min · jiezi

关于go:Golang-编程珠玑

作者:崔国科—— MO 研发工程师 导读2017 年左右开始接触 golang,那时国内对于 golang 的材料还很少。 当初随着云原生生态的蓬勃发展,在 kubernetes、docker 等等泛滥明星我的项目的带动下,国内有越来越多的守业公司以及大厂开始拥抱 golang,各种介绍 golang 的书籍、博客和公众号文章也变得越来越多,其中不乏品质极高的材料。 相干的材料曾经足够丰盛,因而这篇文章不会详述 golang 的某一个方面,而是次要从工程实际的角度登程,去介绍一些货色。因为在工作过程中,我留神到一些令人丧气的代码,其中有些甚至来自于高级程序员。 上面是本文目录概览,咱们将从内存无关的话题开始: 内存相干Golang Profiling如何写性能测试Part 1 内存相干1.1 编译器内存逃逸剖析先看这样一段代码: package main//go:noinlinefunc makeBuffer() []byte { return make([]byte, 1024)}func main() { buf := makeBuffer() for i := range buf { buf[i] = buf[i] + 1 }}示例代码中函数 makeBuffer 返回的内存位于函数栈上,在 C 语言中,这是一段谬误的代码,会导致未定义的行为。 在 Go 语言中,这样的写法是容许的,Go 编译器会执行 escape analysis:当它发现一段内存不能搁置在函数栈上时,会将这段内存搁置在堆内存上。例如,makeBuffer 向上返回栈内存,编译器主动将这段内存放在堆内存上。 通过 -m 选项能够查看编译器剖析后果: $ go build -gcflags="-m" escape.go# command-line-arguments./escape.go:8:6: can inline main./escape.go:5:13: make([]byte, 1024) escapes to heap除此之外,也存在其余一些状况会触发内存的“逃逸”: ...

February 21, 2023 · 6 min · jiezi

关于go:Go-基础篇之-Map

Go 语言是一门十分风行的开源编程语言,它的语法简略易学,同时也具备杰出的性能体现。其中,Map 是 Go 语言中的一种数据结构,它能够用来存储键值对。明天咱们来聊聊 Map,我将介绍一些对于 Go 语言中 Map 的基本知识和高级用法。 1. Map 基础知识1.1 什么是 Map在 Go 语言中,Map 是一种无序的键值对数据结构。它相似于Python中的字典,C++ 中的 Map,Java 中的HashMap 等。Map 中的每个元素都蕴含一个键和一个值。键必须是惟一的,而值则能够反复。 1.2 申明一个 Map在 Go 语言中,能够应用 make 函数来创立一个 Map。make 函数须要传递两个参数,第一个参数是 Map 的类型,第二个参数是 Map 的初始大小。Map 的类型能够应用 Map 关键字来定义,例如: // 创立一个类型为map[string]int的map,初始大小为10m := make(map[string]int, 10)上述代码将创立一个类型为 Map[string]int的 Map,其中键为字符串类型,值为整数类型。初始大小为10,这个值能够依据理论须要进行调整。 1.3 增加元素到 Map 中能够应用赋值操作符来向 Map 中增加元素。例如: // 向map中增加元素m["one"] = 1m["two"] = 2上述代码将向 Map 中增加两个元素,键为"one" 和 "two",值别离为 1 和 2。 1.4 Map 中获取元素能够应用下标操作符来从Map中获取元素。例如: // 从map中获取元素fmt.Println(m["one"])fmt.Println(m["two"])上述代码将输入 Map 中键为 "one" 和 "two" 的值。 ...

February 21, 2023 · 2 min · jiezi

关于go:golang-野指针和悬空指针

野指针与悬空指针区别与分割野指针野指针是指一种指向的内存地位不可知的指针,个别是由与指针变量在申明时未初始化导致。 对野指针的操作会引发panic空指针悬空指针是指指针所指向的内存空间曾经被开释了。 在Go语言中,若果函数内变量的地址作为函数的返回值,编译器就会判断出该变量在函数调用实现后还须要被援用,因而不在栈中为其分配内存空间,而是在堆中为其分配内存空间,这样的变量在函数调用实现后其占用的内存空间不会立刻被开释,也就是防止了悬空指针的状况。

February 20, 2023 · 1 min · jiezi

关于go:读源码为什么注册路由时没有传入上下文在接口方法中却能取到

作为一个工作8年的老程序员通知你:浏览源码和查看官网文档,是解决问题最高效的方法。不信你来看,这个困扰了读者半天的问题我查了源码和文档后霎时解决。前言上周五有位读者私信我一个问题,说困扰了他半天,钻研了一个上午也没搞明确。 是一位运维转Go的敌人,最近有不少运维、测试、甚至Java、PHP转Go的敌人加我。 这个问题并不是喋喋不休就能讲清楚的,我就整顿了一篇文章给他。 高兴 正如上图所示,反馈特地的好,更文的高兴又找到了,我把这个问题和解答又好好整顿了一下,分享给大家,心愿对大家有帮忙. 也分享一下我作为一个工作8年的老程,解决问题的心得: 浏览源码和查看官网文档,是解决问题最高效的方法。发问在gofame框架的demo案例中,如下图所示: 为什么左侧路由绑定这里没有向controller中传入context的值,在controller中却能取到值?如何赋值和接管context?先说论断 对于ctx context.Context上下文,Server组件会主动从申请中获取并传递给接口办法,申明并初始化了context初始值为context.Background()能够通过GetCtx、SetCtx、GetCtxVar、SetCtxVar这些办法轻松的为context赋值和取值通过示例代码轻松可知:咱们能够通过ghttp.Request实例轻松的操作context。解答问题1. 为什么左侧路由绑定这里没有向controller中传入context的值,在controller中却能取到值?先说论断:对于ctx context.Context上下文,Server组件会主动从申请中获取并传递给接口办法。 要害代码是上图中的s := g.Server(): 追踪一下它的源码能够发现:申明并初始化了ctx的初始值:context.Background() 再带大家看一下context.Background()的源码和正文。 // Background returns a non-nil, empty Context. It is never canceled, has no// values, and has no deadline. It is typically used by the main function,// initialization, and tests, and as the top-level Context for incoming// requests.func Background() Context { return background}咱们能够发现这里返回了一个non-nil, empty Context 问题2. 如何赋值和接管context?在GoFrame框架中,官网举荐的正是应用Context上下文对象来解决流程共享的上下文变量,甚至将该对象进一步传递到依赖的各个模块办法中。 该Context对象类型实现了规范库的context.Context接口,该接口往往会作为模块间调用办法的第一个参数,该接口参数也是Golang官网举荐的在模块间传递上下文变量的举荐形式。 办法列表:func (r *Request) GetCtx() context.Contextfunc (r *Request) SetCtx(ctx context.Context)func (r *Request) GetCtxVar(key interface{}, def ...interface{}) *gvar.Varfunc (r *Request) SetCtxVar(key interface{}, value interface{})简要阐明:GetCtx办法用于获取以后的context.Context对象,作用同Context办法。SetCtx办法用于设置自定义的context.Context上下文对象。GetCtxVar办法用于获取上下文变量,并可给定当该变量不存在时的默认值。SetCtxVar办法用于设置上下文变量。应用示例示例1,SetCtxVar/GetCtxVarpackage mainimport ( "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp")const ( TraceIdName = "trace-id")func main() { s := g.Server() s.Group("/", func(group *ghttp.RouterGroup) { group.Middleware(func(r *ghttp.Request) { //向context中赋值 r.SetCtxVar(TraceIdName, "1234567890abcd") r.Middleware.Next() }) group.ALL("/", func(r *ghttp.Request) { //从context中取值 r.Response.Write(r.GetCtxVar(TraceIdName)) }) }) s.SetPort(8199) s.Run()}能够看到:咱们能够通过SetCtxVar和GetCtxVar来设置和获取自定义的变量,该变量生命周期仅限于以后申请流程。 ...

February 20, 2023 · 1 min · jiezi

关于go:通过dial-operation-was-canceled错误来看go-http-client获取连接conn过程

上面是演示代码(不肯定能复现): client端演示代码: package mainimport ( "context" "fmt" "io" "math" "net" "net/http" "sync" "time")func main() { dialer := &net.Dialer{ Timeout: 10 * time.Second, KeepAlive: 15 * time.Second, } tr := &http.Transport{ MaxIdleConnsPerHost: math.MaxInt, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { before := time.Now() conn, err := dialer.DialContext(ctx, network, addr) if err != nil { // 这里报错operation was canceled fmt.Printf("dial err=%v,cost=%v[ms]\n", err, time.Now().Sub(before).Milliseconds()) } return conn, err }, } httpClient := &http.Client{ Transport: tr, Timeout: 10 * time.Second, } var wg sync.WaitGroup maxGoroutines := 100 eachRunTimes := 100000 for i := 0; i < maxGoroutines; i++ { wg.Add(1) go func() { for j := 0; j < eachRunTimes; j++ { head(httpClient) } wg.Done() }() } wg.Wait()}func head(client *http.Client) { resp, err := client.Head("http://localhost:9080/nop") if err != nil { // 这里并没有报错 fmt.Printf("httpClient.Head err=%v\n", err) return } io.Copy(io.Discard, resp.Body) defer resp.Body.Close()}server端演示代码: ...

February 19, 2023 · 6 min · jiezi

关于go:如何用Go快速实现规则引擎

一、出师之名提到规定引擎,大部分人都会先想到DSL(Domain Specific Language),进而联想令人生畏的编译原理、递归降落、LL或LR。但规定引擎有大有小,它们在不同场景的存在不一样,并不一定都要这么简单。 比方在一个小型领取零碎的简略风控场景里,产品同学想设置一些规定防止用户的银行卡被盗刷或者商户被薅羊毛: 24小时内领取总金额超10w的用户1小时应用信用卡领取金额超5w的用户1小时被发动退款金额超10w的商户30分钟内频繁领取又退款超过10次的用户......为了疾速实现需求,咱们开发一个独自的规定类或者库,类外面有不同规定判断函数。规定一直减少,规定函数就一直扩大,这个收缩的规定类库就是一个渺小的规定引擎。尽管在业务调用的中央会有很多switch或者if...else..,但这个渺小的规定引擎并不需要引入DSL,一样能好好工作。 而在另外一些业务零碎,比方贷款零碎,同样是风控场景,产品同学也会设置一些规定做不同的金额审批决策: 征信评分达到650,申请金额2000元以下能够间接审批征信评分达到650,申请金额在5000以下,如果月均生产达到2000块能够间接审批征信评分在550~600之间,申请金额在5000以下的三四线城市用户,如果月均生产达到1000块还须要其余生产评估,如果月支出未达到1w须要工资流水证实......在这些规定未增多之前,咱们发现单单写规定类库,业务方通过调用不同函数判断,就曾经痛不欲生。这些风控规定相比上文来说,波及的用户属性多,判断条件简单,而且还有不同的优先级。如果没有更好的规定形象,代码复杂度只会越来越高,这时就须要设计一套DSL来满足更多的规定判断。 所以,在咱们真正要实现一个规定引擎之前,下定决心设计DSL与编译原理拉扯之前,咱们首先看简略的规定类库是否就已满足需要。 二、需要背景咱们组用Go自研了一个API网关,作为一个网关,网关必不可少的能力是路由转发,精细化路由更是高频需要。业务须要依据不同需要场景做不同的路由配置,比方灰度公布、A/B 测试、限流、负载平衡等。 但网关现有的转发规定只能基于申请Method和URL(Host/Path/Query)作简略配置,欠缺依据申请Header、Cookie、CIP(Client IP)/VIP等更多申请属性配置,与及这些属性规定的组合配置,比方业务须要配置API的读写拆散,并且灰度测试,就须要配置申请Method和申请Header这样的并集规定。 三、Json实现我最开始没有往DSL的方向想,写几个像上面的简略的规定函数,用json格局定义规定,再用Go的反射库解析json,三下五除二就实现了规定判断。不同的规定函数: // IRuletype IRule interface { Match(req *http.Request) bool}// HeaderMatch 匹配headerfunc HeaderMatch(key, value string) bool { ... return false}// CookieMatch 匹配Cookiefunc CookieMatch(key, value string) bool { ... return false}...规定定义: { "op":"AND", "matcher":[ "header", "cookie" ], "header":{ "key":"X-APP-ID", "value":"xx" }, "cookie":{ "name":"feature", "value":"dev/kyrie/qualify-rule" }}规定解析框架(非反射库版): // 遍历判断规定for _, matcher := range matchers { var m map[string]interface{} err := json.Unmarshal([]byte(data["mather"]), &m) if err != nil { log.Fatal(err) } switch matcher { case "header": ... result[matcher] = HeaderMatch(rule.key, rule.Vaule) case "coolkie": ... result[matcher] = CookieMatch(rule.name, rule.Vaule) } ... }...// 综合计算规定后果switch op {case "AND": ...case "OR"...}....下面是一个常见的二元表达式规定,从规定定义到规定解析能够看出,用Json的形式实现十分不便,曾经满足简略的规定场景。不好的中央就是解析的代码太灵便,一条龙的switch case,如果退出更多逻辑,复杂度就会回升,维护性也只会越来越差。 ...

February 19, 2023 · 6 min · jiezi

关于go:Go-快速入门指南发布到-Github-了

仓库地址: https://github.com/duanbiaowu...

February 18, 2023 · 1 min · jiezi

关于go:20230217-周报

Go 1.20 更新概览 Go 陷阱 - 数组和切片参数传递差别 Go 高性能 - 字符串拼接 1000+ 倍优化 Docker 背地的根底撑持技术有哪些? Go 陷阱 - 缓冲区内容不输入 Go 陷阱 - 接口办法调用

February 18, 2023 · 1 min · jiezi

关于go:20230210-周报

Go 陷阱 - nil != nil ? Go 陷阱 - 错误处理三剑客 Go 工程化 - 应用 expvar 监控接口状态 Go 工程化 - embed 嵌入动态文件 Go 并发编程 - 数据竞态

February 18, 2023 · 1 min · jiezi

关于go:Go-121的2个语言变化

语言行为变动Go 1.20曾经于往年2月份公布,Go 1.21也不远了,咱们来先睹为快,看看Go 1.21版本里几个乏味的变动。 文末附送2道面试题。 panic(nil)func main() { defer func() { print(recover() == nil) }() panic(nil)}大家先想一想这段代码会输入什么?是true还是false。 在Go 1.20版本及以前会输入true。 然而在Go 1.21版本开始会输入false。这是因为Go 1.21定义了一个新的类型*runtime.PanicNilError。 panic(nil)后,recover()会返回一个类型为*runtime.PanicNilError,值为panic called with nil argument的变量,具体能够参考如下代码: func main() { defer func() { r := recover() fmt.Printf("%T\n", r) // *runtime.PanicNilError fmt.Println(r) // panic called with nil argument }() panic(nil)}clear函数Go 1.21会新增一个clear函数,用于清理map和slice里的元素。示例代码如下: package mainimport "fmt"var x = 0.0var nan = x / xfunc main() { s := []int{1, 2, 3} clear(s) fmt.Println(s) // [0 0 0] m := map[float64]int{0.1: 9} m[nan] = 5 clear(m) fmt.Println(len(m)) // 0}官网源码阐明如下: ...

February 18, 2023 · 2 min · jiezi

关于go:Go框架深入理解iris中的mvc之原理篇

一、mvc的根本应用在iris中,还封装了mvc包,该包能够让开发者疾速的搭建出基于mvc(model-view-controller)分层的业务零碎。其根本应用如下: package mainimport ( "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/context" "github.com/kataras/iris/v12/mvc")func main() { app := iris.New() // 基于app.Party("/")分组 初始化mvcApplication mvcApplication := mvc.New(app.Party("/")) // 注册controller mvcApplication.Handle(new(testController)) app.Listen(":8080")}// 定义controller处理器type testController struct { Ctx *context.Context}func (c *testController) GetHome() { c.Ctx.Writef(ctx.Method())}func (c *testController) Post() { c.Ctx.Writef(ctx.Method())}这样,就能搭建一个最简略的controller处理器了。而后运行该服务,在浏览器中输出 http://localhost:8080/home 就能拜访到testController的GetHome办法。 咱们晓得,在iris框架中,是须要注册路由的,行将门路对应到特定的context.Handler类型(函数类型)的处理器,当用户拜访对应的门路时,能力执行对应处理器上的函数体的逻辑的。那么,mvc包是如何做到门路到controller中函数的映射的呢? 二、mvc的实现原理通过下面应用基于mvc包的示例,咱们能够理解到,mvc包的操作实际上是基于mvc.Application类型的实例进行的。在初始化mvc.Application实例的时候,传入参数是一个app.Party的对象,即一个路由组。因而,一个mvc.Application实例实质上就是一个路由分组。 而后,通过mvc.Application.Handle办法,将testController类型的对象进行注册。实际上是将controller依赖的对象(model、service等)注入到controller中、将Controller中的办法转换成路由、将controller中执行的后果渲染到view的过程。 2.1 从controller到ControllerActivator的转换在第一局部的代码示例中,当初始化了mvc.Application对象后,就是通过如下这行代码对controller中的办法进行转换的: mvcApplication.Handle(new(testController))代码就一行,很简略。但就是这个Handle函数将testController中的办法转换成了路由,使用户能够通过对应的门路拜访到controller中的办法。接下来咱们看看该Handle办法中都做了哪些事件。 点击进入源代码,直到iris/mvc/mvc.go文件中的handle办法,如下图示所示。该办法显示,首先将controller包装成了一个ControllerActivator对象c。其构造体如下:各字段含意如下: injector:该controller中依赖的字段类型。即该controller构造体中有哪些字段。这个咱们前面具体解释。Value:该字段即在一开始new进去的controller的对象值。比方new(testController)的值。Typ:该字段是controller的具体类型。比方下面示例中的testController类型。routes:controller中每个函数名对应的具体的路由。而后该对象c做了3件事件:执行对象c的BeforeActivation办法(如果controller中定义了该办法)、activate办法和AfterActivation办法(如果定义了该办法)。咱们先跳过BeforeActivation和AfterActivation办法,重点看下activate办法。 2.2 ControllerActivator的activate办法c.activate办法的源码如下图所示,次要是parseMethods办法,依据名字也能猜到是要解析办法了。 咱们再进入c.parseMethods办法的源代码,如下: 这里的逻辑也很简略,先是获取该类型(即controller的构造体类型,例如示例中的testController类型)的办法个数,而后顺次遍历所有的办法,并对每个办法进行解析,也就是代码中的c.parseMethod(m)办法。 接下来进入到c.parseMethod(m)的代码逻辑中,看看该办法做了些什么。 这里看到,通过parseMethod解析进去了申请的办法名称httpMethod(例如GET、POST等)、申请的门路httpPath。而后再通过c.Handle函数将对应的申请办法和申请门路以及办法名注册成iris的惯例路由。 到这里咱们先总结一下controller转换的过程: 首先,先将controller对象封装成ControllerActivator对象。该对象蕴含了controller类型的值以及具体的controller数据类型。其次,通过reflect包获取该controller类型的有哪些办法,并顺次遍历解析这些办法。而后,依据办法名称解析出规范的HTTP申请的办法以及申请门路,即代码中的parseMethord函数。最初,将该申请办法以及申请门路转换成iris惯例的路由,即代码中的c.Handle函数。因为申请办法和申请门路是依据controller中的办法名称解析进去的,所以开发人员在给controller的办法的命名时,须要遵循以下规定: controller中的办法必须采纳驼峰式命名,且首字母必须大小。parseMethod会大写字母为宰割符,将办法名宰割成多个单词。例如。GetHome会宰割成Get、Home两个单词。办法名HOME,则会被宰割成H、O、M、E四个单词。具体实现算法可查看源代码methodLexer.reset函数。办法名的第一个单词必须是http申请的办法名。无效的申请办法为GET、HEAD、POST、PUT、PATCH、DELETE、CONNECT、OPTIONS、TRACE。具体的实现可查看源码申请办法校验逻辑。 从办法名的第二个单词开始,应用 "/" 符号将各个单词连接成对应的申请门路。所以办法名实际上是由申请办法+申请门路模式组成的。例如在testController中有如下GetMyHome办法,那么,对应的申请门路是/my/home。申请http://localhost:8080/my/home就会执行testController的GetMyHome办法的逻辑。 在办法名中能够通过 By能够在路由中减少动静参数。例如在testController中有如下办法GetByHome(username string),则对应的申请门路会被转换成 /{param1:string}/home。申请http://localhost:8080/yufuzi/... 就能拜访到testController中的GetByHome函数的逻辑,并且在该办法中username的变量值就是门路中的yufizi。如下: 若By在办法名的两头地位,一个By只对应一个动静参数;若By在办法名的最初,则办法中的所有输出参数都被解析成门路中的动静参数。示例一:By在办法名两头地位GetByHome办法名中有两个参数,那么,拜访该门路http://localhost:8080/yufuzi/home时就会报panic。如下: func (c *testController) GetByHome(username string, param2 int) { c.Ctx.Writef(c.Ctx.Method() + " " + username + " Home") c.Ctx.Writef("param2:" + param2) //这里param2是对应类型的默认值:0}示例二:By在办法名最初地位GetHomeBy办法名中有两个参数,则会转换成对应的门路 /home/{param1:string}/{param2:int}。如下: ...

February 17, 2023 · 1 min · jiezi

关于go:夭寿啦我的网站被攻击了了735200次还没崩

记得有一个看到鱼皮的网站被攻打,那时候我只是一个小小号,还在调侃,没想到我竟然也有那么一天! 突袭一个风和日丽中午,我正在和共事吃饭,一个内存oom,我的小破站解体了。 尽管天天被攻打吧,给我干oom了多少是不是有点离谱?? 一个小小博客,值得这么攻打吗?我感觉必定是不值得的。必定玩玩就放弃了。洗洗睡了午觉。 暴风雨来领的前夜果然,我只是一台1C2G的服务器,没必要盯着我整,小破站又不赚钱。 终于在15号凌晨3点消停了。我的20块钱也泡汤了。全都是境外流量。 我认为这就完结了,没想到,第二天还来。 攻防 心愿玩是吧!来熊哥陪你玩。 限速搞起来! ip拦挡搞起来!这哥们也是chun,竟然全副都用同一个ip来攻打。 我间接给你禁用了。 cdn平安搞不起来!原本想间接上腾讯云的CDN拦挡,竟然要我一个月 4800 元。。我何德何能。。 简略开个ip彩色名单。 再加个拜访限度。 这下你快不起来了吧! 这一波攻打下来,流量总共12.41GB,申请量 73.52w。至此落下帷幕。 我的博客 coding3min.com 大家温顺一点么么哒么么 一起提高你好,我是小熊,是一个爱技术然而更爱钱的程序员。上进且佛系自律的人。喜爱发小机密/臭屁又爱夸耀。 奋斗的大学,激情的当初。赚了钱买了房,写了书出了名。当过面试官,带过师傅搬过转。 大厂外来务工人员。是我,我是小熊,是不一样的烟火欢送围观。 我的博客 机智的程序员小熊 欢送珍藏

February 16, 2023 · 1 min · jiezi

关于go:不背锅运维Go实现aes加密并带你手撸一个命令行应用程序

什么是AES对于AES更多的常识,请自行脑补,密码学中的高级加密规范(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采纳的一种区块加密规范。go实现aes加密在golang的规范库aes能够实现AES加密,官网规范库aes文档链接:https://pkg.go.dev/crypto/aes小案例需要本篇分享出在理论工作中的理论需要,需要很简略,就是须要实现一个命令行应用程序,能够对传入的明文字符串进行加密,传入密文进行解密。命令行利用叫做passctl,并带有帮忙性能。实现命令行应用程序有很多弱小的第三方库,因为需要过于简略,那么本篇就用规范库中os即可。实战加密代码package mainimport ( "bytes" "crypto/aes" "crypto/cipher" "encoding/base64" "fmt")var EncKey = []byte("QAZWSXEDCRFVTGBY") // 16位明码串func pkcs7Padding(data []byte, blockSize int) []byte { padding := blockSize - len(data)%blockSize padText := bytes.Repeat([]byte{byte(padding)}, padding) return append(data, padText...)}func AesEncrypt(data []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil {  return nil, err } blockSize := block.BlockSize() encryptBytes := pkcs7Padding(data, blockSize) crypted := make([]byte, len(encryptBytes)) blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) blockMode.CryptBlocks(crypted, encryptBytes) return crypted, nil}func EncryptByAes(data []byte) (string, error) { res, err := AesEncrypt(data, EncKey) if err != nil {  return "", err } return base64.StdEncoding.EncodeToString(res), nil}func main() { plaintext := "2wsx$RFV!Qaz" // 假如这是明文明码 p := []byte(plaintext) newp, _ := EncryptByAes(p) // 开始加密 fmt.Println(newp)}解密代码基于上述加密后的明码,对其进行解密。package mainimport ( "crypto/aes" "crypto/cipher" "encoding/base64" "errors" "fmt")var EncKey = []byte("QAZWSXEDCRFVTGBY") // 16位明码串func pkcs7UnPadding(data []byte) ([]byte, error) { length := len(data) if length == 0 {  return nil, errors.New("Sorry, the encryption string is wrong.") } unPadding := int(data[length-1]) return data[:(length - unPadding)], nil}func AesDecrypt(data []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil {  return nil, err } blockSize := block.BlockSize() blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) crypted := make([]byte, len(data)) blockMode.CryptBlocks(crypted, data) crypted, err = pkcs7UnPadding(crypted) if err != nil {  return nil, err } return crypted, nil}func DecryptByAes(data string) ([]byte, error) { dataByte, err := base64.StdEncoding.DecodeString(data) if err != nil {  return nil, err } return AesDecrypt(dataByte, EncKey)}func main() { ciphertext := "+LxjKS8N+Kpy/HNxsSJMIw==" // 密文 pwd, _ := DecryptByAes(ciphertext)       // 开始解密 fmt.Println(string(pwd))}实现passctl命令行利用代码package mainimport ( "bytes" "crypto/aes" "crypto/cipher" "encoding/base64" "errors" "fmt" "os")var EncKey = []byte("QAZWSXEDCRFVTGBY") // 16位明码串func pkcs7Padding(data []byte, blockSize int) []byte { padding := blockSize - len(data)%blockSize padText := bytes.Repeat([]byte{byte(padding)}, padding) return append(data, padText...)}func pkcs7UnPadding(data []byte) ([]byte, error) { length := len(data) if length == 0 {  return nil, errors.New("Sorry, the encryption string is wrong.") } unPadding := int(data[length-1]) return data[:(length - unPadding)], nil}func AesEncrypt(data []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil {  return nil, err } blockSize := block.BlockSize() encryptBytes := pkcs7Padding(data, blockSize) crypted := make([]byte, len(encryptBytes)) blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) blockMode.CryptBlocks(crypted, encryptBytes) return crypted, nil}func AesDecrypt(data []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil {  return nil, err } blockSize := block.BlockSize() blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) crypted := make([]byte, len(data)) blockMode.CryptBlocks(crypted, data) crypted, err = pkcs7UnPadding(crypted) if err != nil {  return nil, err } return crypted, nil}func EncryptByAes(data []byte) (string, error) { res, err := AesEncrypt(data, EncKey) if err != nil {  return "", err } return base64.StdEncoding.EncodeToString(res), nil}func DecryptByAes(data string) ([]byte, error) { dataByte, err := base64.StdEncoding.DecodeString(data) if err != nil {  return nil, err } return AesDecrypt(dataByte, EncKey)}const help = `Help description of encryption and decryption command line application-h --help [Display help]-e --encryption Plaintext string encryption-d --decrypt Ciphertext string decryptionExample:1. encryption example:passctl -e "your plaintext password"2. decryption example:passctl -d "Your ciphertext string"`func main() { args := os.Args[1] if args == "-h" || args == "--help" {  fmt.Print(help) } else if args == "-e" || args == "--encryption" {  plaintext := os.Args[2]  p := []byte(plaintext)  newp, _ := EncryptByAes(p)  fmt.Println(newp) } else if args == "-d" || args == "--decrypt" {  ciphertext := os.Args[2]  a, _ := DecryptByAes(ciphertext)  fmt.Println(string(a)) } else {  fmt.Println("Invalid option") }}编译成二进制后应用# 编译[root@devhost encryptionDecryption]# go build -o passctl main.go# 查看帮忙[root@devhost encryptionDecryption]# ./passctl -hHelp description of encryption and decryption command line application-h --help [Display help]-e --encryption Plaintext string encryption-d --decrypt Ciphertext string decryptionExample:1. encryption example:passctl -e "your plaintext password"2. decryption example:passctl -d "Your ciphertext string"# 加密[root@devhost encryptionDecryption]# ./passctl -e abc123456nGi3ls+2yghdv7o8Ly2Z+A==# 解密[root@devhost encryptionDecryption]# ./passctl -d nGi3ls+2yghdv7o8Ly2Z+A==abc123456[root@devhost encryptionDecryption]#本文转载于(喜爱的盆友关注咱们):https://mp.weixin.qq.com/s/Fs...

February 16, 2023 · 1 min · jiezi

关于go:go语法入门any类型的使用场景与注意

一、介绍在没有any类型之前,go语言中,咱们常常应用interface{},来示意一个类型是未知的,或者有好几种其余根底类型的状况。从 Go1.18开始,go官网定义了一个预申明标识符(Predeclared identifiers):any。 // any is an alias for interface{} and is equivalent to interface{} in all ways.// any 是一个 interface{}的 别名,并且在任何状况下和interface{}雷同。type any = interface{}官网的定义,通知了咱们any就是interface{}的别名,用来代替interface{}的。go源码中当初也大量的应用any比方: func Print(a ...any) (n int, err error) func Println(a ...any) (n int, err error)type Pointer[T any] structvar expunged = new(any)dirty map[any]*entry func Marshal(v any) ([]byte, error) ......太多中央用到了,基本上go官网,用any代替了任何interface{}呈现的中央。

February 15, 2023 · 1 min · jiezi

关于go:go语法入门泛型的两种场景使用

一、介绍go在1.18终于加上了泛型。那么在之前,咱们都没有用10几年了,始终用。加上泛型后,到底益处在哪里呢,解决了那些场景的痛点呢? 泛型次要在于演绎,泛型-代表能够多余一种类型。作为入门的文章,次要介绍两种泛型的应用场景 作为形参作为构造体1.1 泛型作为形参咱们来看一下,在比拟两个数的大小时,没有泛型的时候,仅仅只是传入类型不一样,咱们就要再写一份截然不同的函数。 // maxInt64 获取j,j中的最大值func maxInt64(i, j int64) int64 { if i >= j { return i } return j}// maxInt32 获取j,j中的最大值func maxInt32(i, j int32) int32 { if i >= j { return i } return j}

February 14, 2023 · 1 min · jiezi

关于go:链表反转和删除第N个节点链表golang

package mainimport "fmt"type ListNode struct { val int Next *ListNode}// 链表反转func reverseList(head *ListNode) *ListNode { var pre *ListNode cur := head for cur != nil { temp := cur.Next cur.Next = pre pre = cur cur = temp } return pre}// 删除倒数第N个节点链表func removeNthFromEnd(head *ListNode, n int) *ListNode { nodes := make([]*ListNode, 3) dummy := &ListNode{0, head} for node := dummy; node != nil; node = node.Next { nodes = append(nodes, node) } prev := nodes[len(nodes)-1-n] prev.Next = prev.Next.Next return dummy.Next}func main() { list := &ListNode{ val: 1, Next: &ListNode{ val: 2, Next: &ListNode{ val: 3, Next: nil, }, }, } //fmt.Println(reverseList(list)) result := removeNthFromEnd(list, 1) for result != nil { fmt.Printf("%d->", result.val) result = result.Next }}

February 14, 2023 · 1 min · jiezi

关于go:在-java中调用go

use https://github.com/shangzebei... package mainimport ( "fmt" "unsafe" "gitee.com/aifuturewell/gojni/java")func main() {}func init() { java.OnMainLoad(func(reg java.Register) { reg.WithClass("com.nk.Hello"). BindNative("nice", "void(java.lang.String[])", nice). Done() })}func nice(ss []string) { fmt.Println(ss)}在java 中 package com.nk;public class Hello { static { System.loadLibrary("test"); } public static void main(String[] args) { nice(new String[] { "come", "from", "gojni" }); } public static native void nice(String[] sss);}golang build and run go build -buildmode=c-shared -o libtest.soLD_LIBRARY_PATH=. java com.nk.Hello

February 14, 2023 · 1 min · jiezi

关于go:Go学习二十三JSON编码解析使用

1.Map转JSONpackage mainimport ( "encoding/json" "fmt")func main() { // map外面 map1 := map[string]string { "name":"张三", "age":"18", "home":"北京", } // 将map解析成json json1, err := json.Marshal(map1) if err != nil { fmt.Println(err.Error()) } fmt.Printf("json1: %s 类型: %T \n",json1,json1) map2 := map[string][]string { "fruit": {"香蕉","葡萄"}, "coder":{"PHP","Go","Java"}, "likes":{"打游戏","看动漫"}, } json2, err := json.Marshal(map2) if err != nil { fmt.Println(err.Error()) } fmt.Printf("json2: %s 类型: %T \n",json2,json2)}/**输入json1: {"age":"18","home":"北京","name":"张三"} 类型: []uint8 json2: {"coder":["PHP","Go","Java"],"fruit":["香蕉","葡萄"],"likes":["打游戏","看动漫"]} 类型: []uint8 */2. Json转Mappackage mainimport ( "encoding/json" "fmt")func main() { json1 := `{"age":"18","home":"北京","name":"张三"}` map1 := make(map[string]string) err := json.Unmarshal([]byte(json1), &map1) if err != nil { fmt.Println(err.Error()) } fmt.Printf("map1: %v T: %T\n",map1,map1) json2 := `{"coder":["PHP","Go","Java"],"fruit":["香蕉","葡萄"],"likes":["打游戏","看动漫"]}` map2 := make(map[string][]string) err = json.Unmarshal([]byte(json2), &map2) if err != nil { fmt.Println(err.Error()) } fmt.Printf("map2: %v T: %T\n",map2,map2)}/**输入map1: map[age:18 home:北京 name:张三] T: map[string]stringmap2: map[coder:[PHP Go Java] fruit:[香蕉 葡萄] likes:[打游戏 看动漫]] T: map[string][]string*/3.构造体转JSON3.1 无字段标签构造体转换成JSON在开发中常常会用到。encoding/json包是通过反射机制来实现编解码的,<font color=red>因而构造体必须导出所转换的字段,没有导出的字段不会被encoding/json包解析。</font> ...

February 14, 2023 · 2 min · jiezi

关于go:又花了半个小时将-ChatGPT-接入了钉钉机器人

后面的文章给大家介绍了如何在集体微信中应用 ChatGPT,然而大家都晓得这种操作是有危险的,所以都让大家应用小号,明天再给大家介绍一下如何在钉钉中应用机器人来调戏 AI。 流程注册钉钉开发者平台账号,并创立一个外部组织;在外部组织中创立机器人;注册 OpenAi 账号并获取 Api Key;在服务器中部署程序;在外部群中调戏 AI;创立机器人下面的前两步次要是为了创立一个外部组织的机器人,登录钉钉开发者后盾 https://open-dev.dingtalk.com...,顺次抉择利用开发 > 企业外部开发 > 机器人,点击创立利用。 在这之前须要确保本人的钉钉账号曾经退出了一个组织,如果没有的能够,能够自行创立一个组织。点击创立利用过后,填入相干信息 而后在开发治理菜单外面配置机器人的回调地址,也就是前面要部署代码的服务器的地址和端口,如下图所示。 这里除了间接应用服务器的 IP 之外,还能够配置具体的域名,不过这就须要有域名了,还能够配置 HTTPS,这个依据大家的状况自行应用,过后间接应用 IP 是最简略的一种形式。 不过配置域名的话也比较简单,就是申请一个域名,而后在配置一个 nginx 的反向代理,如果须要 HPPTS 的话再配置一个证书就好了。 接着在版本治理与公布菜单中公布机器人,此时会主动创立一个调试的群,后续能够间接在调试群外面进行调试机器人,在调试没有问题的状况就能够在其余外部群中增加该机器人了。 注册 OpenAiOpenAi 因为某些起因,在国内是无奈失常注册账号的,并且连网站都是无奈失常拜访的,具体的注册流程不在本文的探讨范畴之内,感兴趣的能够在后盾回复关键字【chatgpt】查看注册细节。 这里给大家演示一下注册胜利后,如何获取一个 API key,当咱们注册胜利并且登录当前,能够在链接 https://platform.openai.com/a...,在页面上点击 Create new Secret key 即可生成一个 API key,复制 API key寄存起来,后续备用。 部署服务部署服务还是跟之前一样简略,咱们还是通过 docker 来进行部署,一行命令就能够搞定,这里能够配置自定义的端口和下面获取到的 API Key,以及相应的超时工夫和 Session 状况指令。 docker run -itd --name chatgpt -p 9999:9999 \ -e APIKEY=你的 API key \ -e SESSIONTIMEOUT=60s \ -e MODEL=text-davinci-003 \ -e MAX_TOKENS=512 \ -e TEMPREATURE=0.9 \ -e SESSION_CLEAR_TOKEN=清空会话 \ --rm \ docker.mirrors.sjtug.sjtu.edu.cn/eryajf/chatgpt-dingtalk:latest命令执行胜利过后,咱们通过 docker ps 能够看到服务曾经失常起来了,对应的钉钉开源我的项目地址,我也放到了后盾,感兴趣的能够在公众号后盾回复【chatgpt】自行获取。 ...

February 13, 2023 · 1 min · jiezi

关于go:go-timeflag和正则

timetime.Now() //以后工夫time.Now().UTC //UTCt := t1.Add(time.Hour * 1) //t加一小时,场景:过期工夫jg := t.Sub(t1) //时间差 jg.Hours()小时,jg.Minutes()分。。 package mainimport ( "fmt" "github.com/gin-gonic/gin" "net/http" "time")func main() { r := gin.Default() r.GET("/time", func(c *gin.Context) { // %Y %m %d %H %M %S //2023_01_02-14:04:39 nowtime := time.Now() datanow := nowtime.Format("2006-01-02 15:04:05")//格局不固定,固定内容,不要妄图改内容 fmt.Println(nowtime, datanow) c.JSON(http.StatusOK, gin.H{ "time": nowtime, "转换": datanow, }) }) r.Run("localhost:8082")}os.Args 与 flag相似2 *host是因为拿到的是指针类型 flag 正则 regexp package mainimport ( "fmt" "regexp")func main() { m2, _ := regexp.MatchString("foo.*", "seafood") fmt.Println(m2) reg1 := regexp.MustCompile(`\d{8}`) ll := "12323123sss12323123" ret := reg1.FindString(ll) ret2 := reg1.FindAllString(ll, -1) fmt.Println(ret, ret2)} ...

February 13, 2023 · 1 min · jiezi

关于go:go-语言第一章环境搭建

0、啰嗦两句,千里之行始于足下,不要做旁观者,要做亲历者,一点一滴入手操作,不论天才根底如何,既然决定学习,就踏踏实实,认认真真学起来。1、咱们能够去go 语言中文网下载各个版本的安装包。初学咱们间接下载最新版本就能够。2、装置 go Linux:常见的 linux 发行版本(ubuntu,centos,fedora,arch Linux 等),go 简直都反对。装置办法也大同小异。 a:下载并解压 Go Linux 安装包 tar -C /usr/local -xzf go1.20.linux-amd64.tar.gzb: cd 到 /usr/local 目录下,能够看到一个 go 目录,Go 官网举荐装置到此目录c:配置环境变量:关上 $HOME/.profile 文件,在文件开端增加 export PATH = $PATH:/usr/local/go/bind:source ~/.profile 而后通过 go version验证 输入版本信息,装置胜利windows:采纳图形界面装置,始终点 next 就好 a:默认是装置在 C:\Program Files\Go 下边,也能够本人制订装置目录b:安装程序曾经设置好了环境变量,环境变量中会多出一个 GOPATH 值是 C:\Users\用户名\go,也会未 path 减少一个值 :C:\Program Files\Go\binc:关上 windows 的命令提示符窗口验证 输出go version 输入信息,装置胜利3、多版本问题 理论开发中咱们可能会遇到多版本的问题,下边举荐两个 Go 多版本管理工具gvm:一个简略的 Go 语言版本管理工具,反对 Linux 和 macOS。Goenv:Go 语言版本管理工具,反对 Linux、macOS 和 Windows。大家有须要后续能够查看这两个工具的官网文档配置,初学能够先不必配置,后续教训多了,天然就会配置4、配置 go:go 语言自身是开箱即用的,不配置就能够间接应用,然而为了体验更好,也不便咱们了解 go,下边介绍 go 罕用的配置项 ...

February 13, 2023 · 1 min · jiezi

关于go:go语法入门闭包

备注:本文大量的援用http://c.biancheng.net/view/59.html非原创,写的起因是为了不便本人总结一下应用。 一、介绍go语言中,闭包是援用了自在变量的匿名函数,被援用的自在变量和匿名函数一起存在,即便曾经来到了自在变量的环境也不会被开释或者删除,在闭包中能够持续应用这个自在变量,因而,简略的说: 匿名函数 + 援用环境 = 闭包同一个匿名函数与不同援用环境组合,能够造成不同的实例,如下图所示。图:闭包与函数援用 一个函数类型就像构造体一样,能够被实例化,函数自身不存储任何信息,只有与援用环境联合后造成的闭包才具备”记忆性“,函数是编译期动态的概念,而闭包是运行期动静的概念。 二、闭包的作用域闭包对它作用域上部的变量能够进行批改,批改援用的变量会进行理论批改 // 筹备一个字符串str := "hello world"// 创立一个匿名函数foo := func() { // 匿名函数中拜访str str = "hello hihi"}// 调用匿名函数foo() 输入位 hello hihi起因是 str的 作用域范畴为这里的整个代码块。 foo := func() { // 匿名函数中拜访str str = "hello hihi"}这外面的str,为这个里面的代码块的str,所以程序执行,会批改str的值。

February 13, 2023 · 1 min · jiezi

关于go:go语法入门匿名函数

一、介绍Go语言反对匿名函数,即在须要应用函数时再定义函数,匿名函数没有函数名只有函数体,函数能够作为一种类型被赋值给函数类型的变量,匿名函数也往往以变量形式传递,这与C语言的回调函数比拟相似,不同的是,Go语言反对随时在代码里定义匿名函数。 匿名函数是指不须要定义函数名的一种函数实现形式,由一个不带函数名的函数申明和函数体组成,上面来具体介绍一下匿名函数的定义及应用。 1.1 定义 匿名函数fun(参数)返回{ 函数体}匿名函数的定义就是没有名字的一般函数定义。 1.2 定义 匿名函数的两种形式匿名函数因为没有函数名,所以没方法像一般函数那样调用,所以匿名函数须要保留到某个变量或者作为立刻执行函数: 保留到某个变量立刻执行函数package main import "fmt" func main() { // 定义匿名函数形式一 add := func (x, y int) { fmt.Println(x + y) } add(1, 2) // 通过变量调用匿名函数 // 定义匿名函数形式二 func (x, y int) { fmt.Println(x - y) }(20, 5) //自执行函数:匿名函数定义完加()间接执行}1.3 匿名函数的两种场景(回调函数,闭包)1.3.1 回调函数func callFunc(base int, f func(int, int) int) { num := f(base, 5) // 缩小或者加上5 fmt.Printf("base %d 通过f解决后值是:%d \n", base, num)}func TestCallFunc(t *testing.T) { addNum := func(base, num int) int { return base + num } callFunc(100, addNum)}输入: ...

February 13, 2023 · 1 min · jiezi

关于go:前有-45-亿数据被扒现有-Go-工具链想要我的使用数据

大家好,我是煎鱼。 前两天有读者揭示我关注 Go 大当家 Russ Cox 发动的 Go 工具链中的遥测 telemetry in the Go toolchain 探讨。不看不晓得,一看下一跳。 明天来分享一些认识和信息。 为什么要收集数据Russ Cox(下称 rsc)发动的背景是 Go 开源我的项目的开发者(例如:Go 外围团队)在没有遥测的状况下,会遇到如下的场景: 须要依赖内部的错误报告(例如:GitHub issues)来理解他们本人写的软件如何在意料之外产生异样。考察用户是如何应用本人写的开源我的项目,是否合乎预期,又或是发明出了新的用法。这么一听,如同和咱们平时的认知也差不多。换位思考一下,Go 外围开发者就比拟头疼了。认为这两种形式作用都很无限,达不到他们想要的成果。 被局限的例子错误报告咱们会提交错误报告,个别只会呈现在相似 ”咱们预期这事应该能失常跑起来,但你竟然没有跑起来“ 的场景下。在现有的状况下,如果程序在不影响正确性的状况下呈现了错误行为,用户就不可能留神到。 反之,如果施行了遥测(收集数据),开发者就能够通过 Go 工具链收集上来的统计数据发现异常。(有抓手了) 具体案例,rsc 阐明了在 Go 1.14 版本的公布中,对 macOS Go 发行版的构建形式进行了更改,造成应用 net 包(应用 cgo )编译任何程序都须要装置 Xcode。 这不是他们所预期的,是意外之外的。 但在产生这问题的三年内,没有任何人报告过这个谬误。用户只是简略地承受了这是必要的装置。 直至近期,Go 外围团队排查其余问题,才发现有这个坑。如果有遥测收集应用数据,那这个问题齐全能够被防止。 考察用户Go 开发团队想晓得 ”用户想用 Go 做什么“,或是想基于数据用于做一些新老性能版本的决策。 但当初开源我的项目广泛都很难,他们只能发调查报告,就像咱们每年填的 Go 开发者调查报告,就是这个目标。 然而 rsc 对调查报告也是不满足的。因为只是一个小样本,成果无限。且须要大量的用户选项和回复能力失去绝对精确的测量后果,比拟浪费时间。 具体案例,rsc 举例 go 开源我的项目,常常会在新版本减少开关,再逐渐去掉的做法。像最近发表 Go 1.21 起将不再反对 macOS1.13/1.14,马上就收到了用户的反馈,要求保留。 ...

February 13, 2023 · 1 min · jiezi

关于go:go语法入门将函数作为函数的参数

一、介绍。go语言的函数参数,个别咱们罕用的是,一个理论的值,比方 int,或者struct,map,指针等。但在一些高阶的用法中,常见函数,作为参数,比方net/http库等。明天咱们学一下如何应用函数作为函数的参数。 二、入门应用。其实说白了,函数为参数,被另一个函数应用。就是函数调用函数咱们在一个函数内,调用另外一个函数。 2.1 应用一个 最简略的 无参,无返回函数。在这里,咱们参数为printOne()或printTwo()。咱们在funcNoParam中应用它。 func printOne() { fmt.Println("one")}func printTwo() { fmt.Println("two")}func funcNoParam(f func()) { f()}func TestFuncNoParam(t *testing.T) { funcNoParam(printOne) funcNoParam(printTwo)}在下面的示例中,咱们应用printOne以及printTwo 作为 funcNoParam的参数。执行完后果为: === RUN TestFuncNoParamonetwo--- PASS: TestFuncNoParam (0.00s)能够只有合乎func(),咱们就能够传进到funcNoParam(f func())中。能够执行无数种函数调用。 2.2 调用一个有参函数在这里printOneParam(p int),printOnePlus(p int),都是为一个参数的函数。 printOneParam(p int) 为间接打印出一个p值printOnePlus(p int) 为打印出一个 p++值咱们应用funcParamOne(f func(a int))来把他们作为一个参数传进来,供本人调用。 func printOneParam(p int) { fmt.Println(p)}func printOnePlus(p int) { fmt.Println(p + 1)}func funcParamOne(f func(a int)) { num := 100 f(num)}func TestFuncParamOne(t *testing.T) { funcParamOne(printOneParam) funcParamOne(printOnePlus)}在这里,咱们定义了一个函数funcParamOne(f func(a int))它的参数是一个带有一个int参数的函数,咱们在funcParamOne调用了f。而后咱们咱们调用了两个合乎 func(a int)类型的函数,printOneParam以及printOnePlus。后果为: ...

February 13, 2023 · 1 min · jiezi

关于go:ChatGPT留给知乎小红书的时间不多了

大家好啊,明天我打算给大家整点好活!挑战一下用ChatGPT打入各平台外部。 知乎挑战!首先理解一下“知乎体” 知乎体是以专业知识为根底,以清晰的条理对问题进行论述,并解决该问题的文体格局。对于不能明确给出答案的问答,给出问题相干的思考。从标准上讲,知乎领会给出相干的参考文献并给出相干的援用链接;问题:为什么有人说银行科技岗是程序员最好的待业抉择?请援用文献,列举多条起因阐明此问题。最初对此问题提出质疑,引发思考。 【6】 挑战胜利!!第一段写得挺好的。 认真看看,我怎么不感觉银行科技岗的工作压力比996的程序员压力大? AI 果然是不了解程序员的苦啊。 但这答案。谨严,业余!有知乎范。 小红书挑战!小红书的套路,我过年期间算是摸透了。来我分分钟模仿一篇。 问题:用很吸引人眼球的题目,每个段落都加 emoji 表情,最初加一些tag,举荐一只难看的口红。须要超过200字 这么多益处,看了连我都想来一只了! 惟一的美中不足就是没有配图。 UC题目挑战!问题:再给这条举荐想十个让人很想点击的题目 看着ChatGPT自信的乱答一气。 这几个题目看起来就差不多好吗!在这搪塞我呢? 我感觉得使出大招了。 写代码挑战!问题:请用vue写一个登陆页面,并加上验证码性能。 一波操作猛如虎啊 尽管ChatGPT目前就是把一堆信息拼凑成一篇垃圾 但它总是能够把各种爆款格调信手拈来 熊哥忍不住浑身一颤 他当初曾经砸了各平台文案的饭碗 今天它就能重拳出击,毁了我的键盘 惟一不能超过的货色兴许只有我的帅气 当初代码只写得出demo 等到他能靠形容写出残缺我的项目的时候 大伙趁来得及,快多上下班吧! 一起提高你好,我是小熊,是一个爱技术然而更爱钱的程序员。上进且佛系自律的人。喜爱发小机密/臭屁又爱夸耀。 奋斗的大学,激情的当初。赚了钱买了房,写了书出了名。当过面试官,带过师傅搬过转。 大厂外来务工人员。是我,我是小熊,是不一样的烟火欢送围观。 我的博客 机智的程序员小熊 欢送珍藏

February 12, 2023 · 1 min · jiezi

关于go:Go语言学习笔记08map数据类型

Go语言学习笔记-08map数据类型Go语言中的map,相似于其它语言的映射,字典,哈希表,示意一组无序的键值对 申明var mapVar map[key_type]value_typekey_type为键的类型,不能是函数类型,map类型,切片类型 value_type为值的类型 没有显示初始化的map类型,默认值为nil,是无奈应用的,能够通过以下两种形式初始化 申明同时初始化 var map1 = map[key_type]value_type{}应用make函数初始化,能够指定容量,但不会受限于初始的容量,map会在键值对的个数超过以后长度时,会动静扩容 var map1 = make(map[key_type]value_type, 容量)ch08/main.go package mainimport "fmt"func main() { var map1 map[string]int // 无奈赋值,map1尚未初始化 //map1["a"] = 1 fmt.Printf("map1 %#v\n", map1) map1 = map[string]int{} map1["a"] = 1 fmt.Printf("map1 %v\n", map1) map2 := make(map[string]int, 5) fmt.Printf("map2 %v", map2)}输入 map1 map[string]int(nil)map1 map[a:1]map2 map[] 获取键值对个数应用内置函数len获取map的键值对个数 ch08/getlength/main.go package mainimport "fmt"func main() { map1 := make(map[string]int, 5) fmt.Printf("map1:%v的键值对个数%d\n", map1, len(map1)) map1["a"] = 1 fmt.Printf("map1:%v的键值对个数%d\n", map1, len(map1)) map1["b"] = 2 fmt.Printf("map1:%v的键值对个数%d\n", map1, len(map1))}输入 ...

February 10, 2023 · 2 min · jiezi

关于go:go使用nethttp-创建middleware中间件

在上一节文章 go应用net/http 创立web服务器-高阶篇咱们学会了创立 服务器。在个别的C/S架构中,在多个客户端与服务器进行通信时,咱们常常应用中间件,来进去一些,在业务逻辑之前的,客户端发送给服务器的数据验证。 一、什么是http中间件在互联网的http通信中,如果多个客户端拜访服务器,咱们就会构建一个 client/server 客户端/服务器 架构来实现。服务器用来承受客户端的申请,而后返回特定的资源或者数据给客户端。 中间件是客户端申请达到解决办法之前对其进行预处理的一些逻辑。罕用的利用场景有: 客户端身份校验用户鉴权客户端对服务器提供的特定服务是否有拜访权限验证用户 session,并放弃通信存活客户端申请参数解密客户端参数格式化日志记录器,用于记录每个 REST API 拜访申请跨域配置返回数据格式化Header设置中间件原理在 Go 语言中,中间件 Handler 是封装另一个 http.Handler 以对申请进行预处理或后续解决的 http.Handler。它介于 Go Web 服务器与理论的处理程序之间,因而被称为“中间件”。 原理剖析:咱们先看一个最简略的一个http服务器 package mainimport ( "fmt" "net/http")func index(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello World")}func main() { http.HandleFunc("/index", index) http.ListenAndServe(":8090", nil)}下面的示例代码就实现了一个 http服务器。在路由的解决方面,次要是 通过http.HandleFunc函数,咱们能够看一下官网的源代码 // HandleFunc registers the handler function for the given pattern// HandleFunc 注册handler处理函数到DefaultServeMux,用来绑定给定的url// in the DefaultServeMux.// The documentation for ServeMux explains how patterns are matched.// ServeMux文档有具体的url解析匹配介绍func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}// HandleFunc registers the handler function for the given pattern.func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } mux.Handle(pattern, HandlerFunc(handler))}// The HandlerFunc type is an adapter to allow the use of// ordinary functions as HTTP handlers. If f is a function// with the appropriate signature, HandlerFunc(f) is a// Handler that calls f.type HandlerFunc func(ResponseWriter, *Request)// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}咱们能够看到最初的解决是通过 HandlerFunc 反射。这个正是咱们传过来的index(w http.ResponseWriter, r *http.Request) ...

February 10, 2023 · 1 min · jiezi

关于go:你知道-GO-中什么情况会变量逃逸吗

你晓得 GO 中什么状况会变量逃逸吗?首先咱们先来看看什么是变量逃逸 Go 语言将这个以前咱们写 C/C++ 时候须要做的内存布局和调配,全副整合到了 GO 的编译器中,GO 中将这个称为 变量逃逸 GO 通过编译器剖析代码的特色和代码的生命周期,决定应该应用堆还是栈来进行内存调配 C 代码 和 GO 代码比照哪个会解体?咱们写一个简略的例子,在 C 外面内存调配到栈下面还是堆下面是一个很明确的事件 例如 函数中的变量是调配在栈下面,会随着该函数调用结束后随之销毁掉 程序员本人 malloc 开拓的内存是在堆下面,须要程序员本人去开释 那么问题来了: 如果咱们将某一个函数中的局部变量的地址(全篇以局部变量为例),作为该函数的返回值,最终在函数内部去拜访这个局部变量的地址,是否会出错呢?一起来看看吧 C 程序test.c int * get_res(int num){ int tmp = num + 10; return &tmp;}int main(){ int * res = get_res(80); printf("%d -- %p\n" , *res, res);}下面写了一个简略的 C 代码,获取传入数据并 + 10 失去的后果 # gcc test.c -o testtest.c: In function ‘get_res’:test.c:7:12: warning: function returns address of local variable [-Wreturn-local-addr] return &tmp; ^~~~# ./testSegmentation fault这里能够看出编译程序,报了 warning 了,不过不影响程序的编译 , 这个 warning 报错信息是 因为咱们返回了长期变量的地址,C 编译器检测到了,给咱们抛出了一个 warning ...

February 9, 2023 · 2 min · jiezi

关于go:Go语言学习笔记07数组与切片

Go语言学习笔记-07数组与切片数组Go语言中的数组是一个长度固定,由雷同类型的元素组成 申明只申明,不初始化,默认为数据类型的零值 var arr [个数]数据类型申明并初始化 var arr [5]int = [5]int{} // [0,0,0,0,0]var arr [5]int = [5]{1,2,3,4,5} // [1,2,3,4,5]为某些地位设置值,其余放弃为0 var arr [5]int = [5]int{3:9} // [0,1,3,9,0] 为第4个元素设置值,其余放弃为0应用...主动设置元素个数 var arr = [...]{1,2,3,4,5}应用...设置稠密数组 var arr = [...]{49:50} // 设置第50个元素值为50,其余的为0多维数组申明 var arr [2][3]int // 申明了一个2行3列的数组能够拆开来看,看作2个[3]int 多为数组初始化 // 第2维当前的类型申明可写可不写var arr = [2][3]int{[3]int{1,2,3},[3]int{4,5,6}}var arr = [2][3]int{{1,2,3},{4,5,6}}ch07/main.go package mainimport "fmt"func main() { // 申明不初始化,默认为数据类型的零值 var arr1 [5]int fmt.Println("arr1", arr1) // 申明并初始化 var arr2 [5]int = [5]int{1, 2, 3, 4, 5} fmt.Println("arr2", arr2) // 为某些地位设置值,其余放弃为0 var arr3 [5]int = [5]int{3: 9} fmt.Println("arr4", arr3) // 应用...主动设置元素个数 var arr4 = [...]int{4: 5} fmt.Println("arr4", arr4) // 多维数组 var arr5 = [2][3]int{[3]int{1, 2, 3}, {4, 5, 6}} fmt.Println("arr5", arr5)}输入 ...

February 9, 2023 · 2 min · jiezi

关于go:vim-golang-泛型异常提醒错误问题expected-found-string

开发环境vim + golang + vim-go + ale + Ycm vim-go + ale 提供语言检测, 主动提醒, 主动修复反对 Ycm 提供补全反对 vim 版本: vim9.0golang: go version go1.20 darwin/amd64问题基于以上环境, 开发中定义泛型类型时总是报错, 如下 expected ']', found string踩坑过程这个谬误属于代码检测, 且通过大量查问定位到了gopls版本问题导致的. 低版本的gopls 不反对泛型 因为vim-go和ale 均具备代码检测, 都可能应用gopls, 因而在vim中, :GoInstallBinaries更新了全副golang二进制文件, 且查看gopls的版本曾经是0.11了 ale 中禁用了gopls 作为linter 关上go文件, 该谬误仍存在. 狐疑不是这两个插件导致的, 因而在.vimrc中, 禁用了这两个插件, 再从新关上go文件, 仍有谬误, 由此确认的确不是这两个插件导致的. 最初将Ycm插件禁用后, 该谬误隐没了. 由此确认是Ycm 插件导致的 解决vim 中运行 :YcmDebugInfo 果然发现有gopls 调用 查看执行的gopls门路, 该版本为 与vim-go里更新的gopls 门路不统一且版本不统一 查看官网文档, 配置 let g:ycm_gopls_binary_path = "gopls"即可调用系零碎门路下的gopls ...

February 9, 2023 · 1 min · jiezi

关于go:开源-IM-系统-tinode-部署教程|-WSL-环境

背景咱们的需要是在本地部署一套 IM 零碎,抉择 tinode。为便于后端启动,咱们采纳 WSL 环境,配合 docker 装置数据库,来启动 IM 利用。 解决WSL 启动前端和后盾服务cmd 输出WSL --update,确保 WSL 比拟新cmd 进入 WSL,确保装置了 docker, docker ps -a看看是否启动。 如果提醒 wsl Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?须要启动 docker: sudo service docker start docker 装置 mysql docker run --network="host" --name im-mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest下载 https://github.com/tinode/chat/releases/ linux 包放到 WSL 目录中,解压执行 ./init-db -data=data.json执行 ./tinode 如果要在服务器启动,应用 nohup ./tinode >im.out 2>&1 &查看是否启动胜利 ps aux|grep tinode关上 http://localhost:6060/登陆测试,用户名 bob 明码 bob123,用户 alice 明码 alice123本地部署曾经实现,如果要配合批改前端代码,前端须要从另一个仓库来拉取 ...

February 9, 2023 · 1 min · jiezi

关于go:GO-的-range-如何使用

GO 语言的 for…range 能做什么呢? for…range 如何应用 ? for…range 的返回值有哪些状况,能够对于任何数据结构应用吗? for…range 的返回值如果不须要,能够如何解决? for…range 的数据是如何传递的? 刚学习 golang 的 xdm 会不会有下面的疑难?其实很简略,咱们就一个一个的来分享和实操一遍 GO 语言的 for…range 能做什么呢?golang 的 for…range 是 go 本身的语法,能够用来遍历数据结构,有如下数据结构能够遍历 切片 slice数组 arraymap 哈希表channel 通道for…range 如何应用 ?咱们别离来看看能够如何应用他们,for…range 相当于一个迭代器,能够遍历数据结构的键/索引 和值 数组 array初始化一个数组应用 for…range 遍历 , 对应的是 索引 和 值func main() { myArray := [5]int{1, 2, 3, 4, 5} for i, v := range myArray { fmt.Printf("%d -- %d -- %p\n", i, v, &v) }}切片 slice初始化一个切片应用 for…range 遍历 , 对应的是 索引 和 值mySlice := []int{1, 2, 3, 4, 5} for i, v := range mySlice { fmt.Printf("%d -- %d -- %p\n", i, v, &v)}map 哈希表初始化一个 map 哈希表应用 for…range 遍历 , map 对应的 键值对myMap := map[string]string{ "name": "xmt", "hobby": "program", "addr": "mengli",}for k, v := range myMap { fmt.Printf("%s -- %s -- %p\n", k, v, &v)}channel 通道创立一个能够缓冲 10 个 int 类型数据的通道创立一个协程专门向通道中写入数据主协程遍历通道,读取数据package mainimport "fmt"var myCh = make(chan int, 10)func writeCh() { for i := 0; i < 5; i++ { myCh <- i } close(myCh)}func main() { go writeCh() for { for data := range myCh { fmt.Println(data) } break }}for…range 的返回值有哪些状况,能够对于任何数据结构应用吗?并不是所有数据结构都能够应用 for…range 的,如下构造能够应用这个办法 ...

February 8, 2023 · 2 min · jiezi

关于go:使用-go-kit进行微服务开发

go-kit的根本介绍go-kit 介绍go-kit 是一个 Golang 编写的开发框架,能够帮忙开发者更快捷地构建可伸缩的微服务架构。它提供了一系列模块化的组件,能够帮忙开发者更轻松地构建和保护微服务。go-kit的设计理念是可组合的,它能够与各种服务发现零碎进行集成,如etcd、consul和zookeeper等,并且能够轻松实现服务熔断和负载平衡。 另外,go-kit也提供了诸如监控、日志和链路追踪的性能,能够帮忙开发者更好地了解和管制微服务架构。 go-kit 还提供了指标收集和剖析性能,能够帮忙开发者进行性能优化和故障诊断。它还容许用户应用自定义的协定,比方REST、gRPC和GraphQL等,来实现不同服务之间的通信。 设计哲学go-kit 是一个合乎 KISS 准则的框架,通过应用关注点拆散,让开发者优先集中于业务逻辑的开发。在业务逻辑实现之后,再通过组合疾速接入微服务的各种能力。 go-kit 次要能够划分为: Service Layer —— 专一于业务逻辑,解决 request,返回 response。Endpoint Layer —— 是 Service 的入口,对 Service 进行 wrapper,能够附加各种 rate-limit metrics 的 middleware,从而加强 Service。Transport Layer —— 定义客户端和服务端应该如何通信,负责网络协议转换等,例如 gRPC、HTTP 等协定的解决。在 go-kit 中,整个我的项目就像是一个洋葱,最内核是 Service,也就是业务逻辑。而后通过一层层middleware 进行包裹,为我的项目增加各种能力。 入手实际Service既然是业务优先,那么开发的程序天然是应该从 Service 业务逻辑开始。 让咱们从一个简略的用户服务开始吧!假如咱们须要实现一个user-service,它须要解决用户的注册、登录的逻辑。基于面向接口编程的准则,咱们能够设计一个Service如下: type HelloRequest struct { Name string `json:"name"`}type HelloResponse struct { Message string `json:"message"`}type HelloService interface { Hello(ctx context.Context, name string) (HelloResponse, error)}type helloService struct{}func (s *helloService) Hello(ctx context.Context, name string) (HelloResponse, error) { return HelloResponse{Message: "Hello, " + name}, nil}Endpoint写完业务逻辑之后,咱们须要对外提供这个接口,能够用Endpoint来包裹这个Service。在 go-kit 中,Endpoint 就是一个interface: ...

February 8, 2023 · 3 min · jiezi

关于go:Go120新版本正式发布新特性值得一看

Go1.20新版本正式公布,新个性值得一看 该版本仍然放弃 Go1 兼容性,能够降级到 Go1.20,而不须要做任何代码改变。 能够应用你任何喜爱的形式降级: 比方:go install golang.org/dl/go1.20@latest 具体的能够参考官网教程:https://go.dev/doc/go1.20 或者关注我: 王中阳Go的主页 Go 1.20 简介最新的 Go 版本 1.20 在Go 1.19 公布六个月后公布。它的大部分更改都在工具链、运行时和库的实现中。 判若两人,该版本放弃了 Go 1的兼容性承诺。咱们冀望简直所有的 Go 程序都能像以前一样持续编译和运行。 内容很长,倡议先珍藏,再转发给gopher小伙伴们,缓缓看。 语言的变动Go 1.20 包含对语言的四个更改。 Go 1.17 增加了从切片到数组指针的转换。Go 1.20 扩大了它以容许从切片到数组的转换:给定一个切片x,[4]byte(x)当初能够写成*(*[4]byte)(x). 该unsafe包定义了三个新函数SliceData、String和StringData。与 Go 1.17 一起Slice,这些函数当初提供了构建和解构切片和字符串值的残缺能力,而不依赖于它们的确切示意。 该标准当初定义构造值一次比拟一个字段,依照它们在构造类型定义中呈现的程序思考字段,并在第一个不匹配时进行。之前能够浏览标准,就如同所有字段都须要比拟第一个不匹配之外的字段。相似地,该标准当初定义数组值按递增索引程序一次比拟一个元素。在这两种状况下,差别会影响某些比拟是否必须恐慌。现有程序没有扭转:新的标准措辞形容了实现始终所做的事件。 可比拟的类型(例如一般接口)当初能够满足comparable束缚,即便类型参数不是严格可比拟的(比拟可能会在运行时解体)。这使得实例化受约束的类型参数comparable (例如,用户定义的通用映射键的类型参数)与非严格可比拟的类型参数(例如接口类型或蕴含接口类型的复合类型)成为可能。 本文内容来自golang官网,在机器翻译的根底上进行了优化,不便大家了解。端口WindowsGo 1.20 是将在 Windows 7、8、Server 2008 和 Server 2012 的任何版本上运行的最初一个版本。Go 1.21 将至多须要 Windows 10 或 Server 2016。 Darwin and iOSGo 1.20 是将在 macOS 10.13 High Sierra 或 10.14 Mojave 上运行的最初一个版本。Go 1.21 将须要 macOS 10.15 Catalina 或更高版本。 ...

February 8, 2023 · 4 min · jiezi

关于go:使用go的gin-web框架-实现简单上传文件

install gin在终端中执行 呦 % go env -w GOPROXY=https://goproxy.cn,direct$ go get -u github.com/gin-gonic/gin在 golang的idea 中搞% go mod init awesomeProject 得呈现 go.mod 才行codepackage mainimport ( "fmt" "github.com/gin-gonic/gin" "log" "net/http" "time")func m1(c *gin.Context) { fmt.Println("m1 in..") start := time.Now() c.Abort() cost := time.Since(start) fmt.Printf("cost: %v", cost)}func index(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "msg": "ok", })}type Userinfo struct { Username string `form:"username"` Password string `form:"password"`}func main() { r := gin.Default() r.LoadHTMLGlob("./templates/*") //定义路由的GET办法及响应处理函数 r.GET("/hello", func(c *gin.Context) { //将发送的信息封装成JSON发送给浏览器 c.JSON(http.StatusOK, gin.H{ //这是咱们定义的数据 "message": "疾速入门", }) }) r.NoRoute(func(c *gin.Context) { c.HTML(http.StatusNotFound,"templates/404.html",nil) }) r.GET("/student", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "cha xun student mes success", }) }) r.POST("/create_student", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "create student success", }) }) r.PUT("/updata_student", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "更新学生信息胜利", }) }) r.DELETE("/delete_student", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "删除学生信息胜利", }) }) r.GET("/demo", func(c *gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "name": "admin", "pwd": "123456", }) }) r.GET("/user/:username", func(c *gin.Context) { username := c.Param("username") c.JSON(http.StatusOK, gin.H{ "username": username, }) }) user := r.Group("/user") user.GET("/index", func(c *gin.Context) { }) user.POST("/login", func(c *gin.Context) { }) //r.GET("/", func(c *gin.Context) { // name := c.Query("name") // pwd := c.Query("pwd") // c.JSON(http.StatusOK, gin.H{ // "name": name, // "pwd": pwd, // }) //}) r.GET("/", m1, index) r.GET("/long_async", func(c *gin.Context) { // 创立在 goroutine 中应用的正本 tmp := c.Copy() go func() { // 用 time.Sleep() 模仿一个长工作。 time.Sleep(5 * time.Second) // 请留神您应用的是复制的上下文 "tmp",这一点很重要 log.Println("Done! in path " + tmp.Request.URL.Path) }() }) r.GET("/long_sync", func(c *gin.Context) { // 用 time.Sleep() 模仿一个长工作。 time.Sleep(5 * time.Second) // 因为没有应用 goroutine,不须要拷贝上下文 log.Println("Done! in path " + c.Request.URL.Path) }) r.GET("/user", func(c *gin.Context) { var u Userinfo err := c.ShouldBind(&u) if err != nil { c.JSON(http.StatusBadGateway, gin.H{ "error": err.Error(), }) }else { c.JSON(http.StatusOK, gin.H{ "status": "ok", }) } fmt.Printf("%#v\n", u) }) r.GET("/upload", func(c *gin.Context) { c.HTML(http.StatusOK, "upload.html", gin.H{ "mess": "mess", }) }) r.POST("/upload", func(c *gin.Context) { file, err := c.FormFile("f1") if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "message": err.Error(), }) return } log.Println(file.Filename) dst := fmt.Sprintf("./dst/%s", file.Filename) c.SaveUploadedFile(file, dst) c.JSON(http.StatusOK, gin.H{ "message": fmt.Sprintf("'%s' uploaded!", file.Filename), }) }) r.Run() //默认在本地8080端口启动服务}test申明并创立前端模版文件夹和upload目录 ...

February 8, 2023 · 2 min · jiezi

关于go:go语言syncmap源码阅读基于120

一、什么是 sync.Mapsync.Map,是一种能够像Go语言中的Map那样以Key/Value格局将值存储在内存中。sync通用Mutex,能够在多个goroutine并发执行上也能够平安应用。咱们在命令行中输出:go doc sync.map基于go1.20版本,能够应用的性能如下。 type Map struct {}// 罕用func (m *Map) Store(key, value any)func (m *Map) Delete(key any)func (m *Map) Load(key any) (value any, ok bool)func (m *Map) Range(f func(key, value any) bool)// 其余func (m *Map) LoadAndDelete(key any) (value any, loaded bool)func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool)func (m *Map) Swap(key, value any) (previous any, loaded bool)func (m *Map) CompareAndDelete(key, old any) (deleted bool)func (m *Map) CompareAndSwap(key, old, new any) bool当初咱们来一边应用,一边学习。 ...

February 8, 2023 · 1 min · jiezi