关于golang:聊聊golang的tunny

序本文次要钻研一下tunny Workertype Worker interface { // Process will synchronously perform a job and return the result. Process(interface{}) interface{} // BlockUntilReady is called before each job is processed and must block the // calling goroutine until the Worker is ready to process the next job. BlockUntilReady() // Interrupt is called when a job is cancelled. The worker is responsible // for unblocking the Process implementation. Interrupt() // Terminate is called when a Worker is removed from the processing pool // and is responsible for cleaning up any held resources. Terminate()}Worker接口定义了Process、BlockUntilReady、Interrupt、Terminate办法closureWorkertype closureWorker struct { processor func(interface{}) interface{}}func (w *closureWorker) Process(payload interface{}) interface{} { return w.processor(payload)}func (w *closureWorker) BlockUntilReady() {}func (w *closureWorker) Interrupt() {}func (w *closureWorker) Terminate() {}closureWorker定义了processor属性,它实现了Worker接口的Process、BlockUntilReady、Interrupt、Terminate办法,其中Process办法委托给processorcallbackWorkertype callbackWorker struct{}func (w *callbackWorker) Process(payload interface{}) interface{} { f, ok := payload.(func()) if !ok { return ErrJobNotFunc } f() return nil}func (w *callbackWorker) BlockUntilReady() {}func (w *callbackWorker) Interrupt() {}func (w *callbackWorker) Terminate() {}callbackWorker定义了processor属性,它实现了Worker接口的Process、BlockUntilReady、Interrupt、Terminate办法,其中Process办法执行的是payload函数Pooltype Pool struct { queuedJobs int64 ctor func() Worker workers []*workerWrapper reqChan chan workRequest workerMut sync.Mutex}func New(n int, ctor func() Worker) *Pool { p := &Pool{ ctor: ctor, reqChan: make(chan workRequest), } p.SetSize(n) return p}func NewFunc(n int, f func(interface{}) interface{}) *Pool { return New(n, func() Worker { return &closureWorker{ processor: f, } })}func NewCallback(n int) *Pool { return New(n, func() Worker { return &callbackWorker{} })}Pool定义了queuedJobs、ctor、workers、reqChan、workerMut属性;New办法依据n和ctor创立Pool;NewFunc办法依据n和f来创立closureWorker;NewCallback办法创立callbackWorkerProcessfunc (p *Pool) Process(payload interface{}) interface{} { atomic.AddInt64(&p.queuedJobs, 1) request, open := <-p.reqChan if !open { panic(ErrPoolNotRunning) } request.jobChan <- payload payload, open = <-request.retChan if !open { panic(ErrWorkerClosed) } atomic.AddInt64(&p.queuedJobs, -1) return payload}Process办法首先递增queuedJobs,而后从reqChan读取request,而后往jobChan写入payload,之后再期待retChan,最初递加queuedJobsSetSizefunc (p *Pool) SetSize(n int) { p.workerMut.Lock() defer p.workerMut.Unlock() lWorkers := len(p.workers) if lWorkers == n { return } // Add extra workers if N > len(workers) for i := lWorkers; i < n; i++ { p.workers = append(p.workers, newWorkerWrapper(p.reqChan, p.ctor())) } // Asynchronously stop all workers > N for i := n; i < lWorkers; i++ { p.workers[i].stop() } // Synchronously wait for all workers > N to stop for i := n; i < lWorkers; i++ { p.workers[i].join() } // Remove stopped workers from slice p.workers = p.workers[:n]}SetSize办法首先通过workerMut加锁,而后依据lWorkers创立newWorkerWrapper,之后执行worker.stop,再执行worker.join(),而后清空workersClosefunc (p *Pool) Close() { p.SetSize(0) close(p.reqChan)}Close办法执行SetSize(0)及close(p.reqChan)实例func TestFuncJob(t *testing.T) { pool := NewFunc(10, func(in interface{}) interface{} { intVal := in.(int) return intVal * 2 }) defer pool.Close() for i := 0; i < 10; i++ { ret := pool.Process(10) if exp, act := 20, ret.(int); exp != act { t.Errorf("Wrong result: %v != %v", act, exp) } }}TestFuncJob通过NewFunc创立pool,小结tunny的Worker接口定义了Process、BlockUntilReady、Interrupt、Terminate办法;NewFunc办法创立的是closureWorker,NewCallback办法创立的是callbackWorker。 ...

April 27, 2021 · 2 min · jiezi

关于golang:golang逻辑运算

短路或与短路与 a == b || a == c 如果a等于b,就不会检测a是否等于c,间接返回true 同理 a == b && a == c 如果a不等于b,也不会检测a是否等于c,间接返回false 除运算- 1fmt.Println("9 / 4:",9/4)fmt.Println("9.0 / 4:",9.0/4)9 / 4: 29.0 / 4: 2.25-2 不应用两头变量替换两个变量a = a + bb = a - ba = a - b-3数组中存在一个独自值,其余都是成对的,怎么找到arr := []int {1,1,2,2,3,3,4}temp := 0for _,v := range arr { temp ^= v}fmt.Println("temp:",temp)temp: 4

April 27, 2021 · 1 min · jiezi

关于golang:Go-语言基础数据类型及变量赋值

根底数据类型布尔型:bool整型:int、int8、int16、int32、int64、uint8、uint16、uint32、uint64浮点型:float32、float64字符串:string变量赋值先定义变量再赋值: package main// 导入fmt规范包,用于格式化输入import "fmt"// 主函数,花括号必须与函数名同行func main() { var name string name = "wu" var age int age = 20 fmt.Println("name:", name) fmt.Println("name: %s, age: %d\n", name, age)}定义同时赋值: package main// 导入fmt规范包,用于格式化输入import "fmt"// 主函数,花括号必须与函数名同行func main() { var gender = "男" fmt.Println("gender:", gender)}主动推导赋值(罕用): package main// 导入fmt规范包,用于格式化输入import "fmt"// 主函数,花括号必须与函数名同行func main() { address := "广东" fmt.Println("address:", address)}平行赋值: package main// 导入fmt规范包,用于格式化输入import "fmt"// 主函数,花括号必须与函数名同行func main() { i, j := 10, 20 i, j := j, i fmt.Println("i:", i, " j:", j)}

April 27, 2021 · 1 min · jiezi

关于golang:golang-网络连接库-cellnet1

cellnet是一个组件化、高扩展性、高性能的开源服务器网络库。它的次要概念如下 队列队列应用NewEventQueue创立,应用.StartLoop()开启队列事件处理循环,所有投递到队列中的函数回调会在队列自在的goroutine中被调用,逻辑在此时被解决 个别在main goroutine中调用queue.Wait阻塞期待队列完结。 队列在cellnet中应用cellnet.Queue接口, 底层由带缓冲的channel实现 端(Peer)peer是客户端和服务端的封装cellnet应用Acceptor接管多个连贯,Acceptor是一种Peer(端),连贯到Acceptor的Peer叫做Connector。 一个Peer领有很多属性(名称,地址,队列),peer.NewGenericPeer函数封装了属性的设置过程。 peer.NewGenericPeer创立好的Peer不会产生任何socket操作,对于Acceptor来说,调用Acceptor的Start办法后,才会真正开始socket的侦听 应用如下代码创立一个接受器(Acceptor): queue := cellnet.NewEventQueue() // NewGenericPeer参数顺次是: peer类型, peer名称(日志中不便查看), 侦听地址,事件队列 peerIns := peer.NewGenericPeer("tcp.Acceptor", "server", "127.0.0.1:8801", queue) peerIns.Start()Connector也是一种Peer,与Acceptor很很多相似的中央,因而创立过程也是相似的。 应用如下代码创立一个连接器(Connector): queue := cellnet.NewEventQueue() peerIns := peer.NewGenericPeer("tcp.Connector", "client", "127.0.0.1:8801", queue) peerIns.Start("127.0.0.1:8801")处理器(proc)cellnet应用Processor解决音讯的收发过程。 应用proc.BindProcessorHandler函数,将一个Peer绑定到某个Processor上,且设置用户音讯解决回调。 上面代码尝试将peerIns的Peer,绑定"tcp.ltv"处理器,回调函数为func(ev cellnet.Event) { ... } proc.BindProcessorHandler(peerIns, "tcp.ltv", func(ev cellnet.Event) { switch msg := ev.Message().(type) { // 有新的连贯连到8801端口 case *cellnet.SessionAccepted: log.Debugln("server accepted") // 有连贯从8801端口断开 case *cellnet.SessionClosed: log.Debugln("session closed: ", ev.Session().ID()) // 收到某个连贯的ChatREQ音讯 case *proto.ChatREQ: // 筹备回应的音讯 ack := proto.ChatACK{ Content: msg.Content, // 聊天内容 Id: ev.Session().ID(), // 应用会话ID作为发送内容的ID } // 在Peer上查问SessionAccessor接口,并遍历Peer上的所有连贯,并发送回应音讯(即播送音讯) p.(cellnet.SessionAccessor).VisitSession(func(ses cellnet.Session) bool { ses.Send(&ack) return true }) }})callnet 通过这三种封装将网络连接和解决音讯解耦开,下篇文章将要介绍callnet对于peer源码的解析,不同的协定(http,tcp, protobuf)都有对应的peer我将取其中(tcp)解析,其它的协定下的内容都差不多。 ...

April 27, 2021 · 1 min · jiezi

关于golang:golang-第三方库1goid获取goroutine-id

一、参考golang学习系列目录——更新ing goid 二、根本应用获取以后运行 goroutine的id 2.1 装置GO111MODULE=on go get -u github.com/petermattis/goid 2.2 示例 1 package sync1 2 3 import ( 4 "fmt" 5 "runtime" 6 "strconv" 7 "strings" 8 "testing" 9 10 "github.com/petermattis/goid" 11 ) 12 13 func TestGetGoID(t *testing.T) { 14 15 var buf [64]byte 16 n := runtime.Stack(buf[:], false) 17 idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] 18 id, err := strconv.Atoi(idField) 19 if err != nil { 20 panic(fmt.Sprintf("can not get goroutine id: %v", err)) 21 } 22 fmt.Println("get goroutine id: ", id) 23 } 24 25 func TestGetGoID2(t *testing.T) { 26 gid := goid.Get() 27 fmt.Println("get goroutine id is ", gid) 28 }

April 27, 2021 · 1 min · jiezi

关于golang:zap源码阅读3

zap 和 lumberjack 联合应用本篇内容次要是联合这两个开源库应用。 package loggerimport ( "os" "sync" "time" "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore")var Log *ZapLoggertype ZapLogger struct { *zap.SugaredLogger logLevel zap.AtomicLevel}const defaultloglevel = zapcore.DebugLevel // 默认日志等级为debug//设置日志// Param:// logPath 日志文件门路// logLevel 日志级别 debug/info/warn/error// maxSize 单个文件大小,MB// maxBackups 保留的文件个数// compress 压缩// jsonFormat 是否输入为json格局// shoowLine 显示代码行// logInConsole 是否同时输入到控制台func (l *ZapLogger) SetLogger(logLevel, logPath string, maxSizeMb, maxBackups int, jsonFormat, logInConsole bool) { hook := lumberjack.Logger{ Filename: logPath, // 日志文件门路 MaxSize: maxSizeMb, // megabytes MaxBackups: maxBackups, // 最多保留300个备份 Compress: false, // 是否压缩 disabled by default } var syncer zapcore.WriteSyncer if !logInConsole && "" != logPath { syncer = zapcore.NewMultiWriteSyncer(zapcore.AddSync(&hook)) } else { syncer = zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout)) } formatEncodeTime := func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05.000")) } encoderConfig := zapcore.EncoderConfig{ TimeKey: "time", LevelKey: "level", NameKey: "logger", CallerKey: "line", MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: formatEncodeTime, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, EncodeName: zapcore.FullNameEncoder, } var encoder zapcore.Encoder if jsonFormat { encoder = zapcore.NewJSONEncoder(encoderConfig) } else { encoder = zapcore.NewConsoleEncoder(encoderConfig) } // debug->info->warn->error var level zapcore.Level if level.UnmarshalText([]byte(logLevel)) != nil { level = defaultloglevel } atomicLevel := zap.NewAtomicLevelAt(level) core := zapcore.NewCore( encoder, syncer, atomicLevel, ) logger := zap.New(core) logger = logger.WithOptions(zap.AddCaller()) //增加代码行号 l.SugaredLogger = logger.Sugar() l.logLevel = atomicLevel}var loggers sync.Mapfunc GetLogger(name string) *ZapLogger { val, ok := loggers.Load(name) if ok { log, ok := val.(*ZapLogger) if !ok { return nil } return log } log := &ZapLogger{} if "" != name { log.SetLogger( "DEBUG", "test_"+name+".log", 40, 100, false, false, ) } else { //输入到控制台 log.SetLogger( "DEBUG", "", 40, 100, false, true, ) } loggers.Store(name, log) return log}

April 27, 2021 · 2 min · jiezi

关于golang:zap源码阅读2

zap源码浏览(2)上篇文章次要是介绍了zap库的构造和性能劣势,然而其提供的只是根底的日志写入性能,理论工作须要日志具备大小切割,日志压缩,保留指定天数等性能,此时就须要应用另外一个开源库lumberjack(https://github.com/natefinch/... 1. lumberjack 库介绍Logger构造体 type Logger struct { Filename string `json:"filename" yaml:"filename"` //文件名 MaxSize int `json:"maxsize" yaml:"maxsize"` // 最大保留文件大小,默认100M MaxAge int `json:"maxage" yaml:"maxage"` // 最大保留天数 MaxBackups int `json:"maxbackups" yaml:"maxbackups"` // 最大保留个数 LocalTime bool `json:"localtime" yaml:"localtime"` //文件以工夫命名的格局 Compress bool `json:"compress" yaml:"compress"` // 是否压缩 size int64 // 以后文件大小 file *os.File mu sync.Mutex millCh chan bool startMill sync.Once}该库对外提供的接口比较简单,次要是Write函数。 func (l *Logger) Write(p []byte) (n int, err error) { l.mu.Lock() defer l.mu.Unlock() writeLen := int64(len(p)) if writeLen > l.max() { // 字节长度超过单个文件最大值 return 0, fmt.Errorf( "write length %d exceeds maximum file size %d", writeLen, l.max(), ) } if l.file == nil { // 没有指定保留文件名, 创立一个 if err = l.openExistingOrNew(len(p)); err != nil { return 0, err } } if l.size+writeLen > l.max() { // 单个文件大于指定的最大值,须要拆分 if err := l.rotate(); err != nil { return 0, err } } n, err = l.file.Write(p) // 失常的文件写入内容 l.size += int64(n) return n, err}openExistingOrNew 函数 ...

April 27, 2021 · 5 min · jiezi

关于golang:golang专家编程读书笔记

channelnil和close channel读写nil均会阻塞。敞开的管道依然能够读取数据(包含select也会选中),向敞开的管道写数据会处触发panic slicemake slice s := make([]int, 10, 100) a := new([]int) fmt.Println(*a == nil) // true b := make([]int, 0) fmt.Println(b == nil) // true var c []int fmt.Println(c == nil) // false切取有两种表达方式: a[low:hight]a[low:high:max] // 其中max示意容量a是数组和slice都会生成slice,如果a是字符串那么会生成字符串 数据结构var a []inttype SliceHeader struct { Data uintptr // 0 Len int // 0 Cap int // 0}var a = make([]int, 0)var b = []int{}type SliceHeader struct { Data uintptr // 指向8字节内容 Len int // 0 Cap int // 0}stringutf-8func testString() { a := "你好" fmt.Println(len(a)) // 6 for i, c := range a { fmt.Println(i, c, string(c)) // c是int32 // 0 20320 你 // 3 22909 好 }}

April 27, 2021 · 1 min · jiezi

关于golang:Go-语言常用命令

# 编译目录下的所有go文件go build *.go# 编译指定的go文件go build -o test.exe main.go# 不编译,间接运行go文件go run hello.go# 编译并将后果放在 GOPATH 的 bin 目录下go install -x hello.go# 查看go环境变量go env延长浏览:GO 命令教程

April 26, 2021 · 1 min · jiezi

关于golang:聊聊xxljobexecutorgo的Task

序本文次要钻研一下xxl-job-executor-go的Task Task//工作type Task struct { Id int64 Name string Ext context.Context Param *RunReq fn TaskFunc Cancel context.CancelFunc StartTime int64 EndTime int64 //日志 log Logger}type TaskFunc func(cxt context.Context, param *RunReq) stringTask定义了Id、Name、Ext、Param、fn、Cancel、StartTime、EndTime、log属性Runfunc (t *Task) Run(callback func(code int64, msg string)) { defer func(cancel func()) { if err := recover(); err != nil { t.log.Info(t.Info()+" panic: %v", err) debug.PrintStack() //堆栈跟踪 callback(500, "task panic:"+fmt.Sprintf("%v", err)) cancel() } }(t.Cancel) msg := t.fn(t.Ext, t.Param) callback(200, msg) return}Run办法注册了defer在panic的时候执行callback和cancel,而后执行t.fn(t.Ext, t.Param)以及callback(200, msg)Infofunc (t *Task) Info() string { return "工作ID[" + Int64ToStr(t.Id) + "]工作名称[" + t.Name + "]参数:" + t.Param.ExecutorParams}Info办法返回了工作ID、工作名称、参数信息小结xxl-job-executor-go的Task定义了Id、Name、Ext、Param、fn、Cancel、StartTime、EndTime、log属性;它提供了Run、Info办法。 ...

April 26, 2021 · 1 min · jiezi

关于golang:使用-Go-实现一个简单的-kv-数据库

大家好,我是 roseduan,明天我向大家举荐一下我写的一个 Go 语言实战我的项目—rosedb。 rosedb 是一个简略、内嵌的 k-v 数据库,应用 Golang 实现,反对多种数据结构,蕴含 String、List、Hash、Set、Sorted Set,接口名称格调和 Redis 相似。 如果你曾经比拟相熟 Go 语言的基础知识了,然而又苦于没有我的项目实战,无奈失去进一步的晋升,那么这个我的项目肯定是帮忙你坚固和进阶 Go 常识的绝佳素材。 我将这个我的项目开源到了我的 GitHub 下面,你能够在 GitHub 搜寻 rosedb,或者点开这个链接查看: https://github.com/roseduan/rosedb 大略去年 6 月份,我刚开始学习 Go 语言,因为之前有 Java 的教训,加上 Go 的根本语法较简略,上手还是很快,然而学完根底的语法常识之后,就不晓得下一步应该做什么了,置信有很多小伙伴跟我有一样的感觉。 一个偶尔的机会,我在网上看到了一篇介绍数据库模型的文章(文章地址在我的项目的 Issue 中),文章很简略,了解起来也很容易,并且我始终以来对数据库还是比拟感兴趣的。 因而我想着能够本人实现一个 k-v 数据库,造个简略的轮子,借此坚固本人的一些基础知识,通过实际这个我的项目,至多能够学习到: Go 语言大多数根底语法,以及一些高级个性比方 goroutine、chan、mutex数据结构及算法相干常识,链表,哈希表,跳表,有序汇合等等操作系统的一些常识,特地是对文件系统,内存映射相干的内容今年初,我在找工作的时候,其实这个我的项目对我的帮忙还是挺大的,在这之前我没有任何 Go 相干工作教训,仅自学了 Go 语言的基础知识,但还是顺利找到了 Go 开发的工作职位。 在面试的过程当中,一些面试官也对这个我的项目比拟的感兴趣,都让我介绍一下,这也从某些水平阐明,领有一个开源我的项目,对于找工作是一个亮点,不管我的项目的 star 数量多不多,这至多可能阐明你的入手能力,学习能力都还是不错的。 很多同学可能感觉是写数据库,就想到可能有点艰难。但其实我想说这种担心是多余的,因为我的项目整体来说还是挺简略的,我在 B 站也录制了几期视频,专门来解说这个我的项目,你能够跟着视频来了解,就不会感觉很艰难了。 你能够在 B 站搜寻 roseduanV 或者点上面的链接拜访: 应用 Go 写一个数据库—1 根本构造应用 Go 写一个数据库—2 数据操作应用 Go 写一个数据库—3 数据库操作应用 Go 写一个数据库—4 数据结构目前为止,其实这个我的项目还十分的不欠缺,我构想的很多性能都还没有实现,在我的项目的 TODO 列表中,你能够看到很多待实现的性能。 ...

April 26, 2021 · 1 min · jiezi

关于golang:Mix-XWP-V11-Go-通用动态协程池-WorkerPool

OpenMix 出品:https://openmix.orgMix XWP通用的工作池 A common worker pool Githubhttps://github.com/mix-go/xwp Installationgo get github.com/mix-go/xwpUsage先创立一个构造体用来解决工作,应用类型断言转换工作数据类型,例如:i := data.(int) type Foo struct {}func (t *Foo) Do(data interface{}) { // do something}调度工作 也能够应用 RunF 采纳闭包来解决工作如果不想阻塞执行,能够应用 p.Start() 启动jobQueue := make(chan interface{}, 200)p := &xwp.WorkerPool{ JobQueue: jobQueue, MaxWorkers: 1000, InitWorkers: 100, MaxIdleWorkers: 100, RunI: &Foo{},}go func() { // 投放工作 for i := 0; i < 10000; i++ { jobQueue <- i } // 投放完进行调度 p.Stop()}()p.Run() // 阻塞期待异样解决:Do 办法中执行的代码,可能会呈现 panic 异样,咱们能够通过 recover 获取异样信息记录到日志或者执行其余解决 ...

April 26, 2021 · 1 min · jiezi

关于golang:聊聊xxljobexecutorgo

序本文次要钻研一下xxl-job-executor-go Executor//执行器type Executor interface { //初始化 Init(...Option) //日志查问 LogHandler(handler LogHandler) //注册工作 RegTask(pattern string, task TaskFunc) //运行工作 RunTask(writer http.ResponseWriter, request *http.Request) //杀死工作 KillTask(writer http.ResponseWriter, request *http.Request) //工作日志 TaskLog(writer http.ResponseWriter, request *http.Request) //运行服务 Run() error}Executor定义了Init、LogHandler、RegTask、RunTask、KillTask、TaskLog、Run办法executortype executor struct { opts Options address string regList *taskList //注册工作列表 runList *taskList //正在执行工作列表 mu sync.RWMutex log Logger logHandler LogHandler //日志查问handler}executor定义了opts、address、regList、runList、mu、log、logHandler属性Initfunc (e *executor) Init(opts ...Option) { for _, o := range opts { o(&e.opts) } e.log = e.opts.l e.regList = &taskList{ data: make(map[string]*Task), } e.runList = &taskList{ data: make(map[string]*Task), } e.address = e.opts.ExecutorIp + ":" + e.opts.ExecutorPort go e.registry()}Init办法遍历opts利用opt,而后初始化regList、runList、address,最初异步e.registry()RegTask//注册工作func (e *executor) RegTask(pattern string, task TaskFunc) { var t = &Task{} t.fn = task e.regList.Set(pattern, t) return}RegTask办法往regList增加指定pattern的taskrunTaskfunc (e *executor) runTask(writer http.ResponseWriter, request *http.Request) { e.mu.Lock() defer e.mu.Unlock() req, _ := ioutil.ReadAll(request.Body) param := &RunReq{} err := json.Unmarshal(req, &param) if err != nil { _, _ = writer.Write(returnCall(param, 500, "params err")) e.log.Error("参数解析谬误:" + string(req)) return } e.log.Info("工作参数:%v", param) if !e.regList.Exists(param.ExecutorHandler) { _, _ = writer.Write(returnCall(param, 500, "Task not registered")) e.log.Error("工作[" + Int64ToStr(param.JobID) + "]没有注册:" + param.ExecutorHandler) return } //阻塞策略解决 if e.runList.Exists(Int64ToStr(param.JobID)) { if param.ExecutorBlockStrategy == coverEarly { //笼罩之前调度 oldTask := e.runList.Get(Int64ToStr(param.JobID)) if oldTask != nil { oldTask.Cancel() e.runList.Del(Int64ToStr(oldTask.Id)) } } else { //单机串行,抛弃后续调度 都进行阻塞 _, _ = writer.Write(returnCall(param, 500, "There are tasks running")) e.log.Error("工作[" + Int64ToStr(param.JobID) + "]曾经在运行了:" + param.ExecutorHandler) return } } cxt := context.Background() task := e.regList.Get(param.ExecutorHandler) if param.ExecutorTimeout > 0 { task.Ext, task.Cancel = context.WithTimeout(cxt, time.Duration(param.ExecutorTimeout)*time.Second) } else { task.Ext, task.Cancel = context.WithCancel(cxt) } task.Id = param.JobID task.Name = param.ExecutorHandler task.Param = param task.log = e.log e.runList.Set(Int64ToStr(task.Id), task) go task.Run(func(code int64, msg string) { e.callback(task, code, msg) }) e.log.Info("工作[" + Int64ToStr(param.JobID) + "]开始执行:" + param.ExecutorHandler) _, _ = writer.Write(returnGeneral())}runTask办法先判断task是否曾经注册了,则依据ExecutorBlockStrategy做不同解决,若是coverEarly则cancel掉已有的task;最初通过task.Run来异步执行工作killTaskfunc (e *executor) killTask(writer http.ResponseWriter, request *http.Request) { e.mu.Lock() defer e.mu.Unlock() req, _ := ioutil.ReadAll(request.Body) param := &killReq{} _ = json.Unmarshal(req, &param) if !e.runList.Exists(Int64ToStr(param.JobID)) { _, _ = writer.Write(returnKill(param, 500)) e.log.Error("工作[" + Int64ToStr(param.JobID) + "]没有运行") return } task := e.runList.Get(Int64ToStr(param.JobID)) task.Cancel() e.runList.Del(Int64ToStr(param.JobID)) _, _ = writer.Write(returnGeneral())}killTask办法则执行task.Cancel(),同时将其从runList移除taskLogfunc (e *executor) taskLog(writer http.ResponseWriter, request *http.Request) { var res *LogRes data, err := ioutil.ReadAll(request.Body) req := &LogReq{} if err != nil { e.log.Error("日志申请失败:" + err.Error()) reqErrLogHandler(writer, req, err) return } err = json.Unmarshal(data, &req) if err != nil { e.log.Error("日志申请解析失败:" + err.Error()) reqErrLogHandler(writer, req, err) return } e.log.Info("日志申请参数:%+v", req) if e.logHandler != nil { res = e.logHandler(req) } else { res = defaultLogHandler(req) } str, _ := json.Marshal(res) _, _ = writer.Write(str)}taskLog办法通过e.logHandler(req)或者defaultLogHandler(req)来获取日志小结xxl-job-executor-go的Executor定义了Init、LogHandler、RegTask、RunTask、KillTask、TaskLog、Run办法;executor实现了Executor接口,并提供了http的api接口。 ...

April 25, 2021 · 2 min · jiezi

关于golang:微服务架构学习与思考02微服务实施的前提条件有哪些问题需要思考

一、前言前一篇文章简略剖析了微服务的益处,以及会带来的问题。 遇到问题并不可怕,可怕的是咱们不去面对它,不去想方法解决它,回避问题是不可能有任何提高。所以踊跃想方法应答问题并解决问题,能力一直的提高。 后面讲了,微服务个别都是由单体演进而来,很少有业务从0就开始进行微服务开发。如果能从0就开始用微服务开发,的确是一件很好的事件,前提是你的确思考分明了用微服务开发适宜以后的业务以及业务的倒退需要。 那么问题来了,企业什么时候引入微服务呢? 二、企业什么时候引入微服务?引入起因: 单体利用无奈满足业务增长的需要,业务的交付、业务的可靠性、稳定性要求,随着时间推移问题会越来越多。-- 也就是后面遇到的一些问题 不过也有人,不是从本公司业务倒退,开发成本,开发效率来思考问题,而是什么开发大会上看到很多公司微服务的演讲,或者据说很多公司在用微服务,从这些方面来思考,也就是随大流,这种思考形式显然不是正确思考形式。不要为了微服务而微服务。采纳微服务收益肯定要大于单体利用,要能解决遇到的问题。 从单体架构降级到微服务架构,必定心愿进步研发效率,缩短工期,放慢产品交付速度。 那他们在具体生产效率上有什么区别? 依据马丁·福勒(Martin Fowler)的这篇文章,揭示了生产率和复杂度的关系。在复杂度较小时,单体利用的生产率更高,微服务架构反而升高了生产率。然而,当复杂度到了肯定规模,无论采纳单体利用还是微服务架构,都会升高零碎的生产率。区别是:单体利用生产率开始急剧下降,而微服务架构则能缓解生产率降落的水平。如下图:  x 轴是零碎复杂度,y 轴是开发的生产力 绿色示意单体利用蓝色示意微服务架构单体利用和微服务有一个相交的点,这个点是单体利用生产率急剧下降,微服务平缓降落的交叉点,他们的生产效率开始呈现不同。 这个点就是把单体利用切换到微服务的工夫点。 但问题是,这个工夫点,文章并没有具体阐明什么时候会呈现,怎么掂量这个工夫点。所以只能各个公司具体问题具体分析,技术领导者要思考判断这个工夫点。这也是思考技术领导力的时候。不过有些因素能够参考: 业务角度 业务需要开发是否常常提早产品交付是否能跟上业务倒退研发品质 代码是否因为批改而经常出现bug代码臃肿宏大技术人员 有技术,有志愿团队人数 等等一些思考因素,在加上前一篇单体利用呈现的一些问题。 在思考分明之后,决定引入微服务,那么,又会遇到什么问题? 三、组织架构如何变动?康威定律康威定律 (康威法令 , Conway's Law) 是马尔文·康威1967年提出的:设计零碎的架构受制于产生这些设计的组织的沟通构造。康威定律通知咱们,如果咱们施行了微服务,那么组织架构的变动也要跟着施行微服务架构而做出相应的调整。这样才有可能适应微服务的倒退。 单体架构和微服务架构先看看传统单体架构和微服务架构,如下图: 图片来自:https://martinfowler.com/arti...左半局部的单体架构图:单体利用将所有性能放到一个过程中扩大:通过将整个利用复制到多态服务器实现扩大 右半局部的微服务架构图:微服务架构将性能拆散,放到多个不同的过程中扩大:通过将不同的服务散布于不同的服务器上,并按须要复制形式进行扩大 组织架构单体利用的组织架构:图片来自:https://martinfowler.com/arti...它是一个整体式的利用团队,每个团队依照职能来进行划分(图片左半局部),比方:UI团队,中间件团队,DBA团队。 不同职能的人属于不同的团队。做我的项目的时候就从不同职能部门选出一些人来负责我的项目。这样的组织架构有一个问题就是:跨职能部门沟通协调问题。这种团队组织模式不能适应微服务架构的特点。 微服务利用组织架构图片来自:https://martinfowler.com/arti...微服务架构特点:每个微服务是独立的,团队能够独立开发,独立测试,独立部署,服务是自治的。相应的团队组成人员也有产品,技术,测试,团队成员在本人外部就能够残缺的进行微服务各种性能开发。 这就要突破原先传统的那种按职能划分的组织团队模式,而要把不同职能的人组织在一个团队内,组成一个跨职能的产品组织架构。这样能力把一个微服务性能架构、设计、开发、测试、部署、上线运行,在一个组织外部实现,从而造成残缺的业务、开发、交付闭环。 团队组织的变动一图胜千言: 图片来自:https://insights.thoughtworks...原先那种职能型的团队,变成了跨职能的小团队,这种团队和微服务架构对齐。 每个团队组织成员多少适合呢?亚马逊的“两个披萨团队”,6-10人的规模。这个只是一种参考,毕竟每个公司规模、业务、行业、成员等不一样,找到适宜本人的团队形成,就是最好的团队组织。 阐明: 以上图片侵删,请分割主页邮箱!四、怎么构建第一个微服务项目第一种:有新我的项目,能够从0开始设计微服务架构。第二种:革新旧有的老我的项目这种也能够划分2类: 从我的项目小范畴开始试水,进行革新。齐全重构我的项目 - 个别不举荐这种形式。因为不仅老我的项目须要保护,而且来了新需要咋办?是老我的项目进行需要开发,还是新旧一起加,一起加又节约人力,不加技术跟不上业务倒退。这些危险都是须要思考掂量。第三种:从边缘不重要的小我的项目开始。 这种我的项目需要开发个别不紧迫,我的项目又小,绝对独立,与现有零碎耦合较小,能够齐全重构。从这种小我的项目开始施行微服务,一步一步来构建,升高危险。 经验丰富后,在逐渐将其余我的项目进行革新。 这种是折中的方法,不是那种“休克疗法”。五、参考https://martinfowler.com/articles/microservices.html Microserviceshttps://martinfowler.com/bliki/MicroservicePremium.htmlhttps://insights.thoughtworks.cn/management-of-microservices-team/ 微服务化小团队集群的组织和治理

April 25, 2021 · 1 min · jiezi

关于云原生:Whats-new-in-dubbogo-v156

作者 | 铁城  dubbo-go 社区 committer起源 | 阿里巴巴云原生公众号 dubbogo 社区近期公布了 dubbogo v1.5.6。该版本和 dubbo 2.7.8 对齐,提供了命令行工具,并提供了多种加载配置的形式。 相干改良切实太多,本文只列出相干重大 feature 和 性能晋升项。 1. 命令行工具相熟dubbo 的敌人可能晓得 dubbo 反对 telnet 命令行在线调试。 本次公布也减少了 dubbo-go 的 cli 命令行工具,能够不便用户直连特定服务,通过编写 json 文件来定义传输构造和数据,发动调用进行在线调试,打印返回数据和耗时状况。 目前反对嵌套 struct,然而只反对单个参数的申请包和回包。数据类型因为须要在 json 中定义,只反对 golang 根本数据类型:字符串、整形、浮点。 社区后续会再发一篇文章,着重解说其原理和实现。相干 pr 为 https://github.com/apache/dubbo-go/pull/818,由 dubbogo 最年老的 00 后 apache committer 李志信同学实现。 2. 代理实现扩大重构 Proxy,增加 ImplementFunc 函数,容许我的项目对 Proxy 的代理进行从新实现。在应用 ProxyFactory 自定义注册的场景下,创立的 proxy.Proxy 也自定义实现,能够对返回数据进行批改。 次要利用场景为在网关泛化调用场景下。懂得的人天然懂。 相干 pr  https://github.com/apache/dubbo-go/pull/1019,由本文作者亲自操刀。 3. 启动时指定配置文件的门路用户应用之前版本的 dubbogo 时,始终吐槽其只提供环境变量的形式,加载指定的配置文件。 export CONF_PROVIDER_FILE_PATH="../profiles/dev/server.yml"export CONF_CONSUMER_FILE_PATH="../profiles/dev/server.yml"export APP_LOG_CONF_FILE="../profiles/dev/log.yml"v1.5.6 提供了新的配置文件加载接口:在启动命令行通过  proConf、conConf、logConf三个 flag 设定配置文件门路。 ...

April 25, 2021 · 2 min · jiezi

关于golang:有趣的面试题Go语言中nil的比较结果

原文链接:面试官:两个nil比拟后果是什么?背景哈喽,大家好,我是asong。前几天在一个交换群里看到了一道十分有意思的面试题,明天把它分享进去,咱们先来看一下这个道题:fmt.Println(nil== nil)两个nil的比拟后果是什么? true、false、还是无奈编译? 大家先思考着,文中揭晓答案。 写在开始:倡议你们看一下这个视频:https://www.youtube.com/watch... 须要翻墙哈,看完这个你对nil会有一个新的了解。Go中nil的定义在Go官网文档中,对nil的定义如下: // nil is a predeclared identifier representing the zero value for a// pointer, channel, func, interface, map, or slice type.var nil Type // Type must be a pointer, channel, func, interface, map, or slice typenil是一个事后申明的标识符,代表指针(pointer)、通道(channel)、函数(func)、接口(interface)、map、切片(slice)。也能够这么了解:指针、通道、函数、接口、map、切片的零值就是nil,就像布尔类型的零值是false、整型的零值是0。 深刻了解nilnil基本不是关键字咱们先来看一段代码: func main() { nil := "this is nil" fmt.Println(nil)}// 运行后果this is nil那再改成这样呢? func main() { nil := "this is nil" fmt.Println(nil) var slice []string = nil fmt.Println(slice)}// 运行后果# command-line-arguments./nil.go:10:6: cannot use nil (type string) as type []string in assignment编译的时候间接报错了,因为这个nil是一个string类型,所以从这里确定nil在Go语言中并不是关键字,咱们能够随便定义变量名为nil(不过不倡议这么用)。 ...

April 25, 2021 · 3 min · jiezi

关于golang:Golang-interface的类型断言是如何实现

前言哈喽,everyBody,我是asong,明天咱们一起来摸索一下interface的类型断言是如何实现的。咱们通常应用interface有两种形式,一种是带办法的interface,一种是空的interface。因为Go中是没有泛型,所以咱们能够用空的interface{}来作为一种伪泛型应用,当咱们应用到空的interface{}作为入参或返回值时,就会应用到类型断言,来获取咱们所须要的类型,所以平时咱们会在代码中看到大量的类型断言应用,你就不好奇它是怎么实现的嘛?你就不好奇它的性能损耗是多少嘛?反正我很好奇,略~。类型断言的根本应用Type Assertion(断言)是用于interface value的一种操作,语法是x.(T),x是interface type的表达式,而T是asserted type,被断言的类型。举个例子看一下根本应用: func main() { var demo interface{} = "Golang梦工厂" str := demo.(string) fmt.Printf("value: %v", str)}下面咱们申明了一个接口对象demo,通过类型断言的形式断言一个接口对象demo是不是nil,并判断接口对象demo存储的值的类型是T,如果断言胜利,就会返回值给str,如果断言失败,就会触发panic。这段代码加上如果这样写,就会触发panic: number := demo.(int64)fmt.Printf("value: %v\n", number)所以为了平安起见,咱们还能够这样应用: func main() { var demo interface{} = "Golang梦工厂" number, ok := demo.(int64) if !ok { fmt.Printf("assert failed") return } fmt.Printf("value: %v\n", number)}运行后果:assert failed这里应用的表达式是t,ok:=i.(T),这个表达式也是能够断言一个接口对象(i)里不是 nil,并且接口对象(i)存储的值的类型是 T,如果断言胜利,就会返回其类型给 t,并且此时 ok 的值 为 true,示意断言胜利。如果接口值的类型,并不是咱们所断言的 T,就会断言失败,但和第一种表达式不同的是这个不会触发 panic,而是将 ok 的值设为 false ,示意断言失败,此时t 为 T 的零值。所以举荐应用这种形式,能够保障代码的健壮性。 如果咱们想要辨别多种类型,能够应用type switch断言,应用这种办法就不须要咱们按下面的形式去一个一个的进行类型断言了,更简略,更高效。下面的代码咱们能够改成这样: func main() { var demo interface{} = "Golang梦工厂" switch demo.(type) { case nil: fmt.Printf("demo type is nil\n") case int64: fmt.Printf("demo type is int64\n") case bool: fmt.Printf("demo type is bool\n") case string: fmt.Printf("demo type is string\n") default: fmt.Printf("demo type unkonwn\n") }}type switch的一个典型利用是在go.uber.org/zap库中的zap.Any()办法,外面就用到了类型断言,把所有的类型的case都列举进去了,default分支应用的是Reflect,也就是当所有类型都不匹配时应用反射获取相应的值,具体大家能够去看一下源码。 ...

April 25, 2021 · 6 min · jiezi

关于golang:Go-一个对新手很友好的项目带界面

这个开源我的项目就是:go-gin-api ,目前 800+ Star。 go-gin-api 是基于 Gin 进行模块化设计的 API 框架,封装了罕用的性能,应用简略,致力于进行疾速的业务研发,同时减少了更多限度,束缚项目组开发成员,躲避凌乱无序及自在随便的编码。 下载后可间接运行$ git clone https://github.com/xinliangnote/go-gin-api$ cd go-gin-api$ go run main.go -env fat // -env fat 设置为测试环境首先进入到服务初始化界面。 接下来填写 MySQL、Redis 配置信息,填写实现后,点击初始化按钮。 如上图所示,示意服务初始化胜利,须要再重新启动服务,服务启动后就会看到登录界面。 应用默认用户信息: admin,admin 登录即可,登录胜利后就会看到仪表盘界面。 已集成哪些性能?Panic 时邮件告警告诉在后盾可配置邮件发件人信息,如下图所示: 邮件模板如下: 接口鉴权在后盾可设置调用方 KEY、调用方 SECRET、调用方对接人、备注等信息。 将创立的调用方的 KEY、SECRET 发给调用方即可,能够对调用方进行 启用/禁用/删除 等操作,还能够对其受权可拜访的接口。 接口鉴权是基于 HTTP Header 中的两个参数 Authorization、Authorization-Date 存储签名信息,代码中提供了 3 种语言的加密算法:Go、PHP、JS。 代码生成工具 gormgen,基于数据表生成三个文件,以 admin 表为例会生成:gen_table.md 表正文的 MD 文档、gen_model.go 表字段的构造体、gen_admin.go 表 CURD 操作代码。 ...

April 24, 2021 · 2 min · jiezi

关于golang:聊聊dbsync的jobs

序本文次要钻研一下dbsync的jobs Job//Job represents db sync jobtype Job struct { ID string Error string Status string Progress Progress Items []*Transferable Chunked bool mutex *sync.Mutex StartTime time.Time EndTime *time.Time}//NewJob creates a new jobfunc NewJob(id string) *Job { return &Job{ ID: id, StartTime: time.Now(), mutex: &sync.Mutex{}, Items: make([]*Transferable, 0), }}Job办法定义了ID、Error、Status、Progress、Items、Chunked、mutex、StartTime、EndTimeUpdate//Update updates job progressfunc (j *Job) Update() { if len(j.Items) == 0 { return } sourceCount := 0 destCount := 0 transferred := 0 for i := range j.Items { if j.Items[i].Status == nil { continue } sourceCount += j.Items[i].Source.Count() destCount += j.Items[i].Dest.Count() transferred += int(atomic.LoadUint32(&j.Items[i].Transferred)) } j.Progress.Transferred = transferred j.Progress.SourceCount = sourceCount j.Progress.DestCount = destCount if sourceCount > 0 { j.Progress.Pct = transferred / sourceCount }}Update办法遍历Items,统计transferred、sourceCount、destCountDone//Done flag job as donefunc (j *Job) Done(now time.Time) { if j.Status != shared.StatusError { j.Status = shared.StatusDone } j.EndTime = &now}Done办法更新Status和EndTimeAdd//Add add transferablefunc (j *Job) Add(transferable *Transferable) { j.mutex.Lock() defer j.mutex.Unlock() j.Items = append(j.Items, transferable)}Add办法往transferable增加ItemsIsRunning//IsRunning returns true if jos has running statusfunc (j *Job) IsRunning() bool { return j.Status == shared.StatusRunning || j.EndTime == nil}IsRunning办法通过status和EndTime来判断是否是runningService//Service represents a job servicetype Service interface { //List lists all active or recently active jobs List(request *ListRequest) *ListResponse //Create creates a new job Create(ID string) *core.Job //Get returns a job for supplied ID or nil Get(ID string) *core.Job}type service struct { registry *registry}//New create a job servicefunc New() Service { return &service{ registry: newRegistry(), }}Service接口定义了List、Create、GetGet//Get returns job by ID or nilfunc (s *service) Get(ID string) *core.Job { jobs := s.registry.list() for i := range jobs { if jobs[i].ID == ID { jobs[i].Update() return jobs[i] } } return nil}Get办法先执行registry.list(),而后遍历list找到ID对应的job,而后执行UpdateList//List lists all jobsfunc (s *service) List(request *ListRequest) *ListResponse { jobs := s.registry.list() if len(request.IDs) == 0 { return &ListResponse{ Jobs: jobs, } } var requestedIDs = make(map[string]bool) for i := range request.IDs { requestedIDs[request.IDs[i]] = true } var filtered = make([]*core.Job, 0) for i := range jobs { if _, has := requestedIDs[jobs[i].ID]; !has { continue } jobs[i].Update() filtered = append(filtered, jobs[i]) } return &ListResponse{ Jobs: filtered, }}List办法先执行registry.list(),之后依据requestedIDs找出对应的job,执行Update,最初返回Create//Create creates a new jobfunc (s *service) Create(ID string) *core.Job { job := core.NewJob(ID) s.registry.add(job) return job}Create办法通过core.NewJob(ID)创立job,而后执行registry.add(job)小结dbsync的Schedulable定义了URL、ID、*contract.Sync、Schedule、Status、status属性,它提供了Clone、Done、IsRunning、ScheduleNexRun、Init、Validate办法。 ...

April 23, 2021 · 2 min · jiezi

关于golang:go语言的31个坑

[TOC] go语言的31个坑资源来自于如下链接: http://devs.cloudimmunity.com...关上之后他是长这个样子的: 一一了解并操作之后,筛选出如下31个GOLANG的坑,与大家分享分享 1.左大括号不能独自放一行 {在其余大多数语言中,{ 的地位你自行决定。Go比拟特地,恪守分号注入规定(automatic semicolon injection):编译器会在每行代码尾部特定分隔符后加;来分隔多条语句,比方会在 ) 后加分号: // 谬误示例func main() { println("www.topgoer.com是个不错的go语言中文文档")}// 等效于func main(); // 无函数体 { println("hello world")}// 正确示例func main() { println("Golang老手可能会踩的50个坑")}上述谬误示例编译报错如下: 2.不能应用简短申明来设置字段的值struct 的变量字段不能应用 := 来赋值以应用预约义的变量来防止解决: // 谬误示例package mainimport "fmt"type info struct { result int}func work() (int, error) { return 3, nil}func main() { var data info data.result, err := work() // error: non-name data.result on left side of := if err != nil{ fmt.Println(err) return } fmt.Printf("info: %+v\n", data)}// 正确示例func work() (int, error) { return 3, nil}func main() { tmp, err := work() // error: non-name data.result on left side of := if err != nil { fmt.Println(err) return } fmt.Printf("info: %+v\n", tmp)}上述谬误示例 谬误提醒如下: ...

April 23, 2021 · 13 min · jiezi

关于golang:遍历Map源码分析-GO1162

代码func MapTest() { var aMap = map[int64]int64{ 1: 10, 2: 20, 3: 30, } for k, v := range aMap { fmt.Println(k, v) }}编译 go tool compile -N -l -S ./test_map.go > ./test_map.s汇编后果 "".MapTest STEXT size=912 args=0x0 locals=0x208 funcid=0x0 0x0000 00000 (./test_map.go:13) TEXT "".MapTest(SB), ABIInternal, $520-0 0x0000 00000 (./test_map.go:13) MOVQ (TLS), CX 0x0009 00009 (./test_map.go:13) LEAQ -392(SP), AX 0x0011 00017 (./test_map.go:13) CMPQ AX, 16(CX) 0x0015 00021 (./test_map.go:13) PCDATA $0, $-2 0x0015 00021 (./test_map.go:13) JLS 902 0x001b 00027 (./test_map.go:13) PCDATA $0, $-1 0x001b 00027 (./test_map.go:13) SUBQ $520, SP 0x0022 00034 (./test_map.go:13) MOVQ BP, 512(SP) 0x002a 00042 (./test_map.go:13) LEAQ 512(SP), BP 0x0032 00050 (./test_map.go:13) FUNCDATA $0, gclocals·3e27b3aa6b89137cce48b3379a2a6610(SB) 0x0032 00050 (./test_map.go:13) FUNCDATA $1, gclocals·4b48a54b979a44c0a517bad5b8d15a09(SB) 0x0032 00050 (./test_map.go:13) FUNCDATA $2, "".MapTest.stkobj(SB) 0x0032 00050 (./test_map.go:14) MOVQ $0, "".b+224(SP) 0x003e 00062 (./test_map.go:15) XORPS X1, X1 0x0041 00065 (./test_map.go:15) MOVUPS X1, ""..autotmp_7+368(SP) 0x0049 00073 (./test_map.go:15) MOVUPS X1, ""..autotmp_7+384(SP) 0x0051 00081 (./test_map.go:15) MOVUPS X1, ""..autotmp_7+400(SP) 0x0059 00089 (./test_map.go:15) LEAQ ""..autotmp_8+80(SP), DI 0x005e 00094 (./test_map.go:15) XORPS X0, X0 0x0061 00097 (./test_map.go:15) PCDATA $0, $-2 0x0061 00097 (./test_map.go:15) LEAQ -48(DI), DI 0x0065 00101 (./test_map.go:15) DUFFZERO $258 0x0078 00120 (./test_map.go:15) PCDATA $0, $-1 0x0078 00120 (./test_map.go:15) LEAQ ""..autotmp_7+368(SP), AX 0x0080 00128 (./test_map.go:15) MOVQ AX, ""..autotmp_9+240(SP) 0x0088 00136 (./test_map.go:15) TESTB AL, (AX) 0x008a 00138 (./test_map.go:15) LEAQ ""..autotmp_8+80(SP), AX 0x008f 00143 (./test_map.go:15) MOVQ AX, ""..autotmp_7+384(SP) 0x0097 00151 (./test_map.go:15) LEAQ ""..autotmp_7+368(SP), AX 0x009f 00159 (./test_map.go:15) MOVQ AX, ""..autotmp_10+304(SP) 0x00a7 00167 (./test_map.go:15) PCDATA $1, $1 0x00a7 00167 (./test_map.go:15) CALL runtime.fastrand(SB) 0x00ac 00172 (./test_map.go:15) MOVQ ""..autotmp_10+304(SP), AX 0x00b4 00180 (./test_map.go:15) TESTB AL, (AX) 0x00b6 00182 (./test_map.go:15) MOVL (SP), CX 0x00b9 00185 (./test_map.go:15) MOVL CX, 12(AX) 0x00bc 00188 (./test_map.go:15) LEAQ ""..autotmp_7+368(SP), AX 0x00c4 00196 (./test_map.go:15) MOVQ AX, "".aMap+232(SP) 0x00cc 00204 (./test_map.go:16) MOVQ $1, ""..autotmp_11+72(SP) 0x00d5 00213 (./test_map.go:16) MOVQ $10, ""..autotmp_12+64(SP) 0x00de 00222 (./test_map.go:16) MOVQ ""..autotmp_11+72(SP), AX 0x00e3 00227 (./test_map.go:16) MOVQ "".aMap+232(SP), CX 0x00eb 00235 (./test_map.go:16) LEAQ type.map[int64]int64(SB), DX 0x00f2 00242 (./test_map.go:16) MOVQ DX, (SP) 0x00f6 00246 (./test_map.go:16) MOVQ CX, 8(SP) 0x00fb 00251 (./test_map.go:16) MOVQ AX, 16(SP) 0x0100 00256 (./test_map.go:16) PCDATA $1, $2 0x0100 00256 (./test_map.go:16) CALL runtime.mapassign_fast64(SB) 0x0105 00261 (./test_map.go:16) MOVQ 24(SP), AX 0x010a 00266 (./test_map.go:16) MOVQ AX, ""..autotmp_13+296(SP) 0x0112 00274 (./test_map.go:16) TESTB AL, (AX) 0x0114 00276 (./test_map.go:16) MOVQ ""..autotmp_12+64(SP), CX 0x0119 00281 (./test_map.go:16) MOVQ CX, (AX) 0x011c 00284 (./test_map.go:17) MOVQ $2, ""..autotmp_11+72(SP) 0x0125 00293 (./test_map.go:17) MOVQ $20, ""..autotmp_12+64(SP) 0x012e 00302 (./test_map.go:17) MOVQ ""..autotmp_11+72(SP), AX 0x0133 00307 (./test_map.go:17) MOVQ "".aMap+232(SP), CX 0x013b 00315 (./test_map.go:17) LEAQ type.map[int64]int64(SB), DX 0x0142 00322 (./test_map.go:17) MOVQ DX, (SP) 0x0146 00326 (./test_map.go:17) MOVQ CX, 8(SP) 0x014b 00331 (./test_map.go:17) MOVQ AX, 16(SP) 0x0150 00336 (./test_map.go:17) CALL runtime.mapassign_fast64(SB) 0x0155 00341 (./test_map.go:17) MOVQ 24(SP), AX 0x015a 00346 (./test_map.go:17) MOVQ AX, ""..autotmp_14+288(SP) 0x0162 00354 (./test_map.go:17) TESTB AL, (AX) 0x0164 00356 (./test_map.go:17) MOVQ ""..autotmp_12+64(SP), CX 0x0169 00361 (./test_map.go:17) MOVQ CX, (AX) 0x016c 00364 (./test_map.go:18) MOVQ $3, ""..autotmp_11+72(SP) 0x0175 00373 (./test_map.go:18) MOVQ $30, ""..autotmp_12+64(SP) 0x017e 00382 (./test_map.go:18) MOVQ ""..autotmp_11+72(SP), AX 0x0183 00387 (./test_map.go:18) MOVQ "".aMap+232(SP), CX 0x018b 00395 (./test_map.go:18) LEAQ type.map[int64]int64(SB), DX 0x0192 00402 (./test_map.go:18) MOVQ DX, (SP) 0x0196 00406 (./test_map.go:18) MOVQ CX, 8(SP) 0x019b 00411 (./test_map.go:18) MOVQ AX, 16(SP) 0x01a0 00416 (./test_map.go:18) CALL runtime.mapassign_fast64(SB) 0x01a5 00421 (./test_map.go:18) MOVQ 24(SP), AX 0x01aa 00426 (./test_map.go:18) MOVQ AX, ""..autotmp_15+280(SP) 0x01b2 00434 (./test_map.go:18) TESTB AL, (AX) 0x01b4 00436 (./test_map.go:18) MOVQ ""..autotmp_12+64(SP), CX 0x01b9 00441 (./test_map.go:18) MOVQ CX, (AX) 0x01bc 00444 (./test_map.go:20) MOVQ "".aMap+232(SP), AX 0x01c4 00452 (./test_map.go:20) MOVQ AX, ""..autotmp_4+248(SP) 0x01cc 00460 (./test_map.go:20) LEAQ ""..autotmp_5+416(SP), DI 0x01d4 00468 (./test_map.go:20) XORPS X0, X0 0x01d7 00471 (./test_map.go:20) PCDATA $0, $-2 0x01d7 00471 (./test_map.go:20) LEAQ -32(DI), DI 0x01db 00475 (./test_map.go:20) NOP 0x01e0 00480 (./test_map.go:20) DUFFZERO $273 0x01f3 00499 (./test_map.go:20) PCDATA $0, $-1 0x01f3 00499 (./test_map.go:20) MOVQ ""..autotmp_4+248(SP), AX 0x01fb 00507 (./test_map.go:20) LEAQ type.map[int64]int64(SB), CX 0x0202 00514 (./test_map.go:20) MOVQ CX, (SP) 0x0206 00518 (./test_map.go:20) MOVQ AX, 8(SP) 0x020b 00523 (./test_map.go:20) LEAQ ""..autotmp_5+416(SP), AX 0x0213 00531 (./test_map.go:20) MOVQ AX, 16(SP) 0x0218 00536 (./test_map.go:20) PCDATA $1, $3 0x0218 00536 (./test_map.go:20) CALL runtime.mapiterinit(SB) 0x021d 00541 (./test_map.go:20) JMP 543 0x021f 00543 (./test_map.go:20) CMPQ ""..autotmp_5+416(SP), $0 0x0228 00552 (./test_map.go:20) JNE 559 0x022a 00554 (./test_map.go:20) JMP 886 0x022f 00559 (./test_map.go:20) MOVQ ""..autotmp_5+416(SP), AX 0x0237 00567 (./test_map.go:20) TESTB AL, (AX) 0x0239 00569 (./test_map.go:20) MOVQ (AX), AX 0x023c 00572 (./test_map.go:20) MOVQ AX, "".k+56(SP) 0x0241 00577 (./test_map.go:20) MOVQ ""..autotmp_5+424(SP), AX 0x0249 00585 (./test_map.go:20) TESTB AL, (AX) 0x024b 00587 (./test_map.go:20) MOVQ (AX), AX 0x024e 00590 (./test_map.go:20) MOVQ AX, "".v+48(SP) 0x0253 00595 (./test_map.go:21) XORPS X0, X0 0x0256 00598 (./test_map.go:21) MOVUPS X0, ""..autotmp_6+336(SP) 0x025e 00606 (./test_map.go:21) MOVUPS X0, ""..autotmp_6+352(SP) 0x0266 00614 (./test_map.go:21) LEAQ ""..autotmp_6+336(SP), AX 0x026e 00622 (./test_map.go:21) MOVQ AX, ""..autotmp_17+272(SP) 0x0276 00630 (./test_map.go:21) MOVQ "".k+56(SP), AX 0x027b 00635 (./test_map.go:21) MOVQ AX, (SP) 0x027f 00639 (./test_map.go:21) PCDATA $1, $4 0x027f 00639 (./test_map.go:21) NOP 0x0280 00640 (./test_map.go:21) CALL runtime.convT64(SB) 0x0285 00645 (./test_map.go:21) MOVQ 8(SP), AX 0x028a 00650 (./test_map.go:21) MOVQ AX, ""..autotmp_18+264(SP) 0x0292 00658 (./test_map.go:21) MOVQ ""..autotmp_17+272(SP), CX 0x029a 00666 (./test_map.go:21) TESTB AL, (CX) 0x029c 00668 (./test_map.go:21) LEAQ type.int64(SB), DX 0x02a3 00675 (./test_map.go:21) MOVQ DX, (CX) 0x02a6 00678 (./test_map.go:21) LEAQ 8(CX), DI 0x02aa 00682 (./test_map.go:21) PCDATA $0, $-2 0x02aa 00682 (./test_map.go:21) CMPL runtime.writeBarrier(SB), $0 0x02b1 00689 (./test_map.go:21) JEQ 696 0x02b3 00691 (./test_map.go:21) JMP 876 0x02b8 00696 (./test_map.go:21) MOVQ AX, 8(CX) 0x02bc 00700 (./test_map.go:21) JMP 702 0x02be 00702 (./test_map.go:21) PCDATA $0, $-1 0x02be 00702 (./test_map.go:21) MOVQ "".v+48(SP), AX 0x02c3 00707 (./test_map.go:21) MOVQ AX, (SP) 0x02c7 00711 (./test_map.go:21) CALL runtime.convT64(SB) 0x02cc 00716 (./test_map.go:21) MOVQ 8(SP), AX 0x02d1 00721 (./test_map.go:21) MOVQ AX, ""..autotmp_19+256(SP) 0x02d9 00729 (./test_map.go:21) MOVQ ""..autotmp_17+272(SP), CX 0x02e1 00737 (./test_map.go:21) TESTB AL, (CX) 0x02e3 00739 (./test_map.go:21) LEAQ type.int64(SB), DX 0x02ea 00746 (./test_map.go:21) MOVQ DX, 16(CX) 0x02ee 00750 (./test_map.go:21) LEAQ 24(CX), DI 0x02f2 00754 (./test_map.go:21) PCDATA $0, $-2 0x02f2 00754 (./test_map.go:21) CMPL runtime.writeBarrier(SB), $0 0x02f9 00761 (./test_map.go:21) JEQ 765 0x02fb 00763 (./test_map.go:21) JMP 869 0x02fd 00765 (./test_map.go:21) MOVQ AX, 24(CX) 0x0301 00769 (./test_map.go:21) JMP 771 0x0303 00771 (./test_map.go:21) PCDATA $0, $-1 0x0303 00771 (./test_map.go:21) MOVQ ""..autotmp_17+272(SP), AX 0x030b 00779 (./test_map.go:21) TESTB AL, (AX) 0x030d 00781 (./test_map.go:21) JMP 783 0x030f 00783 (./test_map.go:21) MOVQ AX, ""..autotmp_16+312(SP) 0x0317 00791 (./test_map.go:21) MOVQ $2, ""..autotmp_16+320(SP) 0x0323 00803 (./test_map.go:21) MOVQ $2, ""..autotmp_16+328(SP) 0x032f 00815 (./test_map.go:21) MOVQ AX, (SP) 0x0333 00819 (./test_map.go:21) MOVQ $2, 8(SP) 0x033c 00828 (./test_map.go:21) MOVQ $2, 16(SP) 0x0345 00837 (./test_map.go:21) PCDATA $1, $3 0x0345 00837 (./test_map.go:21) CALL fmt.Println(SB) 0x034a 00842 (./test_map.go:21) JMP 844 0x034c 00844 (./test_map.go:20) LEAQ ""..autotmp_5+416(SP), AX 0x0354 00852 (./test_map.go:20) MOVQ AX, (SP) 0x0358 00856 (./test_map.go:20) CALL runtime.mapiternext(SB) 0x035d 00861 (./test_map.go:20) NOP 0x0360 00864 (./test_map.go:20) JMP 543 0x0365 00869 (./test_map.go:21) PCDATA $0, $-2 0x0365 00869 (./test_map.go:21) CALL runtime.gcWriteBarrier(SB) 0x036a 00874 (./test_map.go:21) JMP 771 0x036c 00876 (./test_map.go:21) CALL runtime.gcWriteBarrier(SB) 0x0371 00881 (./test_map.go:21) JMP 702 0x0376 00886 (./test_map.go:24) PCDATA $0, $-1 0x0376 00886 (./test_map.go:24) PCDATA $1, $-1 0x0376 00886 (./test_map.go:24) MOVQ 512(SP), BP 0x037e 00894 (./test_map.go:24) ADDQ $520, SP 0x0385 00901 (./test_map.go:24) RET 0x0386 00902 (./test_map.go:24) NOP 0x0386 00902 (./test_map.go:13) PCDATA $1, $-1 0x0386 00902 (./test_map.go:13) PCDATA $0, $-2 0x0386 00902 (./test_map.go:13) CALL runtime.morestack_noctxt(SB) 0x038b 00907 (./test_map.go:13) PCDATA $0, $-1 0x038b 00907 (./test_map.go:13) JMP 0上面会对这段代码进行精简分段剖析 ...

April 23, 2021 · 10 min · jiezi

关于golang:这一次彻底搞懂-Go-Cond

hi,大家好,我是 haohongfan。 本篇文章会从源码角度去深刻分析下 sync.Cond。Go 日常开发中 sync.Cond 可能是咱们用的较少的管制并发的伎俩,因为大部分场景下都被 Channel 代替了。还有就是 sync.Cond 应用的确也蛮简单的。 比方上面这段代码: package mainimport ( "fmt" "time")func main() { done := make(chan int, 1) go func() { time.Sleep(5 * time.Second) done <- 1 }() fmt.Println("waiting") <-done fmt.Println("done")}同样能够应用 sync.Cond 来实现 package mainimport ( "fmt" "sync" "time")func main() { cond := sync.NewCond(&sync.Mutex{}) var flag bool go func() { time.Sleep(time.Second * 5) cond.L.Lock() flag = true cond.Signal() cond.L.Unlock() }() fmt.Println("waiting") cond.L.Lock() for !flag { cond.Wait() } cond.L.Unlock() fmt.Println("done")}大部分场景下应用 channel 是比 sync.Cond不便的。不过咱们要留神到,sync.Cond 提供了 Broadcast 办法,能够告诉所有的期待者。想利用 channel 实现这个办法还是不容易的。我想这应该是 sync.Cond 惟一有用武之地的中央。 ...

April 23, 2021 · 2 min · jiezi

关于golang:go语言入门helloworld

新建工程---新建go文件---依照如下编写hello world---执行用run或者go run go文件形式 package main示意一个可独立执行的程序,每个 Go 应用程序都蕴含一个名为 main 的包。func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须蕴含的

April 22, 2021 · 1 min · jiezi

关于golang:go语言入门环境搭建

1.安装包下载,依据操作系统抉择对应的安装包,下载好后,点击下一步下一步即可https://golang.org/dl/ 2.查看是否装置胜利以及版本 3.ide下载,有vscode和goland,因为python应用的pycharm,所以更喜爱golandhttps://golang.google.cn/ 4.下载后,点击下一步下一步即可,选试用三十天,而后依照如下破解链接进行操作http://www.520xiazai.com/soft...

April 22, 2021 · 1 min · jiezi

关于golang:70-Go-中不常注意的各种细节集锦-Go-夜读

原视频: https://www.bilibili.com/vide... 1. 打印true还是false?func f() bool { return false}func main() { switch f() { case true: fmt.Println("true") case false: fmt.Println("false") }}等价于 func f() bool { return false}func main() { switch f(); false { case true: fmt.Println("true") case false: fmt.Println("false") }}2. os.Exit(包含log.Fatal等)的时候不会调用defer3. runtime.Goexit调用后会调用defer4. 估值后果因是否为变量或者常量而异例1: func main() { var n uint = 10 const N uint = 10 var x byte = (1 << n) / 100 // 1被推断出byte,溢出 var y byte = (1 << N) / 100 // 1被推断不确定数 fmt.Println(x) // 0 fmt.Println(y) // 100}例2: ...

April 22, 2021 · 3 min · jiezi

关于golang:Mix-XCLI-V11-Go-命令行交互开发框架

OpenMix 出品:https://openmix.orgMix XCLI命令行交互开发框架 CLI Interactive Commander Overview一个命令行交互开发库,它能够让单个 CLI 程序可执行多个命令,同时它还包含命令行参数获取、全局 panic 捕捉与解决、程序后盾执行等命令行开发罕用性能。 Githubhttps://github.com/mix-go/xcli Installationgo get github.com/mix-go/xcliQuick startpackage mainimport ( "github.com/mix-go/xcli" "github.com/mix-go/xcli/flag")func main() { xcli.SetName("app").SetVersion("0.0.0-alpha") cmd := &xcli.Command{ Name: "hello", Short: "Echo demo", Run: func() { name := flag.Match("n", "name").String("default") // do something }, } opt := &xcli.Option{ Names: []string{"n", "name"}, Usage: "Your name", } cmd.AddOption(opt) xcli.AddCommand(cmd).Run()}编译后,查看整个命令行程序的帮忙 $ ./go_build_main_go Usage: ./go_build_main_go [OPTIONS] COMMAND [opt...]Commands: hello Echo demoGlobal Options: -h, --help Print usage -v, --version Print version informationRun './go_build_main_go COMMAND --help' for more information on a command.Developed with Mix Go framework. (openmix.org/mix-go)查看命令行程序的版本信息 ...

April 22, 2021 · 2 min · jiezi

关于golang:聊聊dbsync的Criterion

序本文次要钻研一下dbsync的Criterion Criterion//Criterion represents criteriontype Criterion fmt.StringerCriterion是一个fmt.Stringer类型betweentype between struct { from int to int}func (b between) String() string { return fmt.Sprintf("BETWEEN %v AND %v", b.from, b.to)}//NewBetween creates new betwee criterionfunc NewBetween(from, to int) Criterion { return &between{from: from, to: to}}between定义from、to两个属性lessOrEqualtype lessOrEqual struct { value int}func (c lessOrEqual) String() string { return fmt.Sprintf(" <= %v", c.value)}//NewLessOrEqual creates less of equal criterionfunc NewLessOrEqual(value int) Criterion { return &lessOrEqual{value}}lessOrEqual定义了value属性,表达式为<=greaterThantype greaterThan struct { value int}func (c greaterThan) String() string { return fmt.Sprintf(" > %v", c.value)}//NewGraterThan creates grater than criterionfunc NewGraterThan(value int) Criterion { return &greaterThan{value}}greaterThan定义了value属性,表达式为>greaterOrEqualtype greaterOrEqual struct { value int}func (c greaterOrEqual) String() string { return fmt.Sprintf(" >= %v", c.value)}//NewGraterOrEqual creates grater or equal criterionfunc NewGraterOrEqual(value int) Criterion { return &greaterOrEqual{value}}greaterOrEqual定义了value属性,表达式为>=ToCriterion//ToCriterion converts a kv pair to a criterionfunc ToCriterion(k string, v interface{}) string { if greaterOrEqual, ok := v.(*greaterOrEqual); ok { return fmt.Sprintf("%v >= %v", k, greaterOrEqual.value) } else if greaterThan, ok := v.(*greaterThan); ok { return fmt.Sprintf("%v > %v", k, greaterThan.value) } else if lessOrEqual, ok := v.(*lessOrEqual); ok { return fmt.Sprintf("%v <= %v", k, lessOrEqual.value) } else if between, ok := v.(*between); ok { return fmt.Sprintf("%v BETWEEN %v AND %v", k, between.from, between.to) } else if toolbox.IsSlice(v) { aSlice := toolbox.AsSlice(v) var whereValues = make([]string, 0) for _, item := range aSlice { if intValue, err := toolbox.ToInt(item); err == nil { whereValues = append(whereValues, fmt.Sprintf(`%v`, intValue)) } else { itemLiteral := toolbox.AsString(item) if strings.HasPrefix(itemLiteral, "(") && strings.HasSuffix(itemLiteral, ")") { whereValues = append(whereValues, fmt.Sprintf(`%v`, item)) } else { whereValues = append(whereValues, fmt.Sprintf(`'%v'`, item)) } } } return fmt.Sprintf("%v IN(%v)", k, strings.Join(whereValues, ",")) } else if _, err := toolbox.ToInt(v); err == nil { return fmt.Sprintf("%v = %v", k, v) } else { literal := strings.TrimSpace(toolbox.AsString(v)) lowerLiteral := strings.ToLower(literal) if strings.Contains(literal, ">") || strings.Contains(literal, "<") || strings.Contains(lowerLiteral, " null") { return fmt.Sprintf("%v %v", k, v) } return fmt.Sprintf("%v = '%v'", k, v) }}ToCriterion办法判断v是否是greaterOrEqual、greaterThan、lessOrEqual、between类型做对应的转换,针对IsSlice的做专门的拼接解决小结dbsync的Criterion是一个fmt.Stringer类型,它内置了greaterOrEqual、greaterThan、lessOrEqual、between类型,并提供ToCriterion办法进行转换。 ...

April 21, 2021 · 2 min · jiezi

关于golang:Go语言GC实现原理及源码分析-go11572

Posted on 2021年3月20日 by luozhiyun转载请申明出处哦~,本篇文章公布于luozhiyun的博客: https://www.luozhiyun.com/arc... 本文应用的 Go 的源码1.15.7

April 21, 2021 · 1 min · jiezi

关于golang:Go-使用记录

for range 鲜为人知的坑,其实以前用 php 的时候遇到过,Java 没有这个问题 // 因为 range 的时候,会创立一个长期变量 v 来承受 data 的遍历值// 而不是你想的 append(&data[0], $data[1], ...)data := []int{1,2,3}result := []*int{}for _, v := range data {result = append(result, &v)fmt.Println(&v) // 0xc0000ac008,0xc0000ac008,0xc0000ac008}for _, v := range result {fmt.Println(*v) // [3,3,3]}// 正确处理形式data := []int{1,2,3}result := []*int{}for i, v := range data {result = append(result, &data[i])}for _, v := range result {fmt.Println(*v)}我的项目中函数返回值更多用的是构造体,而不是构造体指针。尽管这会进步内存拷贝开销,但另一方面能够升高 gc 压力。因为返回值如果用指针,那么变量会存储到堆,而不是栈,也就是产生了变量逃逸。变量逃逸状况:(1)函数中 new 或者字面量创立进去的变量,如果取变量指针作为函数返回值,那么该变量产生逃逸(2)逃逸变量援用的指针逃逸(3)被指针类型的 slice,map,chan 饮用的变量产生逃逸

April 21, 2021 · 1 min · jiezi

关于golang:Go语言GC实现原理及源码分析-go1157

转载请申明出处哦~,本篇文章公布于luozhiyun的博客: https://www.luozhiyun.com/arc... 本文应用的 Go 的源码1.15.7 介绍三色标记法三色标记法将对象的色彩分为了黑、灰、白,三种色彩。 彩色:该对象曾经被标记过了,且该对象下的属性也全副都被标记过了(程序所须要的对象);灰色:该对象曾经被标记过了,但该对象下的属性没有全被标记完(GC须要从此对象中去寻找垃圾);红色:该对象没有被标记过(对象垃圾)在垃圾收集器开始工作时,从 GC Roots 开始进行遍历拜访,拜访步骤能够分为上面几步: GC Roots 根对象会被标记成灰色;而后从灰色汇合中获取对象,将其标记为彩色,将该对象援用到的对象标记为灰色;反复步骤2,直到没有灰色汇合能够标记为止;完结后,剩下的没有被标记的红色对象即为 GC Roots 不可达,能够进行回收。流程大略如下:三色标记法所存在问题多标-浮动垃圾问题假如 E 曾经被标记过了(变成灰色了),此时 D 和 E 断开了援用,按理来说对象 E/F/G 应该被回收的,然而因为 E 曾经变为灰色了,其仍会被当作存活对象持续遍历上来。最终的后果是:这部分对象仍会被标记为存活,即本轮 GC 不会回收这部分内存。 这部分本应该回收 然而没有回收到的内存,被称之为“浮动垃圾”。过程如下图所示: 漏标-悬挂指针问题除了下面多标的问题,还有就是漏标问题。当 GC 线程曾经遍历到 E 变成灰色,D变成彩色时,灰色 E 断开援用红色 G ,彩色 D 援用了红色 G。此时切回 GC 线程持续跑,因为 E 曾经没有对 G 的援用了,所以不会将 G 放到灰色汇合。只管因为 D 从新援用了 G,但因为 D 曾经是彩色了,不会再从新做遍历解决。 最终导致的后果是:G 会始终停留在红色汇合中,最初被当作垃圾进行革除。这间接影响到了应用程序的正确性,是不可承受的,这也是 Go 须要在 GC 时解决的问题。 内存屏障为了解决下面的悬挂指针问题,咱们须要引入屏障技术来保障数据的一致性。 A memory barrier, is a type of barrier instruction that causes a central processing unit (CPU) or compiler to enforce an ordering constraint on memory operations issued before and after the barrier instruction. This typically means that operations issued prior to the barrier are guaranteed to be performed before operations issued after the barrier.内存屏障,是一种屏障指令,它能使CPU或编译器对在该屏障指令之前和之后收回的内存操作强制执行排序束缚,在内存屏障前执行的操作肯定会先于内存屏障后执行的操作。 ...

April 20, 2021 · 4 min · jiezi

关于golang:从源码剖析Go语言基于信号抢占式调度go-157

转载自(https://www.luozhiyun.com/arc... 介绍在 Go 的 1.14 版本之前抢占试调度都是基于合作的,须要本人被动的让出执行,然而这样是无奈解决一些无奈被抢占的边缘状况。例如:for 循环或者垃圾回收长时间占用线程,这些问题中的一部分直到 1.14 才被基于信号的抢占式调度解决。 上面咱们通过一个例子来验证一下1.14 版本和 1.13 版本之间的抢占差别: package mainimport ( "fmt" "os" "runtime" "runtime/trace" "sync")func main() { runtime.GOMAXPROCS(1) f, _ := os.Create("trace.output") defer f.Close() _ = trace.Start(f) defer trace.Stop() var wg sync.WaitGroup for i := 0; i < 30; i++ { wg.Add(1) go func() { defer wg.Done() t := 0 for i:=0;i<1e8;i++ { t+=2 } fmt.Println("total:", t) }() } wg.Wait()}这个例子中会通过 go trace 来进行执行过程的调用跟踪。在代码中指定 runtime.GOMAXPROCS(1)设置最大的可同时应用的 CPU 核数为1,只用一个 P(处理器),这样就确保是单处理器的场景。而后调用一个 for 循环开启 10 个 goroutines 来执行 func 函数,这是一个纯计算且耗时的函数,避免 goroutines 闲暇让出执行。 ...

April 20, 2021 · 9 min · jiezi

关于golang:golang-gc阅读笔记

援用[1]. Go GC 20 问

April 20, 2021 · 1 min · jiezi

关于golang:golang-runtimemain解析

mainfunc main() { g := getg() g.m.g0.racectx = 0 if sys.PtrSize == 8 { maxstacksize = 1000000000 } else { maxstacksize = 250000000 } maxstackceiling = 2 * maxstacksize // Allow newproc to start new Ms. mainStarted = true if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon // For runtime_syscall_doAllThreadsSyscall, we // register sysmon is not ready for the world to be // stopped. atomic.Store(&sched.sysmonStarting, 1) systemstack(func() { newm(sysmon, nil, -1) }) } // Lock the main goroutine onto this, the main OS thread, // during initialization. Most programs won't care, but a few // do require certain calls to be made by the main thread. // Those can arrange for main.main to run in the main thread // by calling runtime.LockOSThread during initialization // to preserve the lock. lockOSThread() if g.m != &m0 { throw("runtime.main not on m0") } m0.doesPark = true // Record when the world started. // Must be before doInit for tracing init. runtimeInitTime = nanotime() if runtimeInitTime == 0 { throw("nanotime returning zero") } if debug.inittrace != 0 { inittrace.id = getg().goid inittrace.active = true } doInit(&runtime_inittask) // Must be before defer. // Defer unlock so that runtime.Goexit during init does the unlock too. needUnlock := true defer func() { if needUnlock { unlockOSThread() } }() gcenable() main_init_done = make(chan bool) if iscgo { if _cgo_thread_start == nil { throw("_cgo_thread_start missing") } if GOOS != "windows" { if _cgo_setenv == nil { throw("_cgo_setenv missing") } if _cgo_unsetenv == nil { throw("_cgo_unsetenv missing") } } if _cgo_notify_runtime_init_done == nil { throw("_cgo_notify_runtime_init_done missing") } // Start the template thread in case we enter Go from // a C-created thread and need to create a new thread. startTemplateThread() cgocall(_cgo_notify_runtime_init_done, nil) } doInit(&main_inittask) // Disable init tracing after main init done to avoid overhead // of collecting statistics in malloc and newproc inittrace.active = false close(main_init_done) needUnlock = false unlockOSThread() if isarchive || islibrary { // A program compiled with -buildmode=c-archive or c-shared // has a main, but it is not executed. return } fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime fn() if raceenabled { racefini() } // Make racy client program work: if panicking on // another goroutine at the same time as main returns, // let the other goroutine finish printing the panic trace. // Once it does, it will exit. See issues 3934 and 20018. if atomic.Load(&runningPanicDefers) != 0 { // Running deferred functions should not take long. for c := 0; c < 1000; c++ { if atomic.Load(&runningPanicDefers) == 0 { break } Gosched() } } if atomic.Load(&panicking) != 0 { gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1) } exit(0) for { var x *int32 *x = 0 }}执行runtime init执行main init执行main main

April 20, 2021 · 3 min · jiezi

关于golang:你真的懂-golang-reslice-吗

package mainfunc a() []int { a1 := []int{3} a2 := a1[1:] return a2}func main() { a()}看到这个题, 你的第一反馈是啥? (A) 编译失败(B) panic: runtime error: index out of range [1] with length 1(C) [](D) 其余第一感觉: 必定能编译过, 然而运行时肯定会panic的. 但大失所望居然可能失常运行, 后果是:[] 疑难a1 := []int{3}a2 := a1[1:]fmt.Println("a[1:]", a2)a1 和 a2 共享同样的底层数组, len(a1) = 1, a1[1]相对会panic, 然而a[1:]却能失常输入, 这是为何? 从外表动手整体上看下整体的状况 a1 := []int{3}fmt.Printf("len:%d, cap:%d", len(a1), cap(a1))fmt.Println("a[0:]", a1[0:])fmt.Println("a[1:]", a1[1:])fmt.Println("a[2:]", a1[2:])后果: len:1, cap:1a[0:]: [1]a[1:] []panic: runtime error: slice bounds out of range [2:1]从外表来看, 从a[2:]才开始panic, 到底是谁一手造成这样的后果呢? ...

April 20, 2021 · 2 min · jiezi

关于golang:疑惑-Go-const-会导致程序结果错乱

const 是 Go 外面咱们常常应用的关键字, 基本上很难玩出花来. 不过某些非凡状况下 const 会呈现你意想不到的后果 场景模仿某公司某次营销流动中, 会依据用户 VIP 级别送用户一些优惠券, 最大面值520. 某用户发现自己购买的 500 元钱的商品, 应用 520 的优惠券来领取, 实践上能 0 元购买的商品, 最初却须要领取一个天文数字. 这个场景是我本人轻易想的, 如果过于夸大, 请原谅我. ^^ 上面咱们用代码大略模仿下这个场景: func main() { var totalPrice uint32 = 500 const couponPrice = 550 fmt.Println("用户须要领取金额: ", totalPrice-couponPrice)}先别运行程序, 你感觉应该返回的后果是多少? A. 程序无奈编译B. -50C. 50D. 4294967246后果是 D, 你会不会感觉很意外? 一些疑难: 500 - 550 的后果为什么不是 -50 ?你是否留神过 const 的类型 ?如果你留神过 const 类型, 为什么程序能失常编译 ?500 - 550 的后果为什么不是 -50 ?重写再写一段新的代码, 咱们把数值放大一点, 不便前面的论述 ...

April 20, 2021 · 3 min · jiezi

关于golang:golang调度学习调度流程-六-抢占调度

golang调度高效秘诀之一是它的抢占式调度。当工作函数执行的工夫超过了肯定的工夫,sysmon办法会一直的检测所有p上工作的执行状况,当有超过预约执行工夫的g时,会发动抢占。这所有也是在retake函数中实现的,上文形容了该函数在零碎调用中的性能,这里讲下该函数如何执行抢占。 retakeretake()函数会遍历所有的P,如果一个P处于执行状态, 且曾经间断执行了较长时间,就会被抢占。retake()调用preemptone()将P的stackguard0设为 stackPreempt(对于stackguard的具体内容,能够参考 Split Stacks),这将导致该P中正在执行的G进行下一次函数调用时,导致栈空间查看失败。进而触发morestack()(汇编代码,位于asm_XXX.s中)而后进行一连串的函数调用,次要的调用过程如下:morestack()(汇编代码)-> newstack() -> gopreempt_m() -> goschedImpl() -> schedule()http://ga0.github.io/golang/2... func retake(now int64) uint32 { n := 0 // Prevent allp slice changes. This lock will be completely // uncontended unless we're already stopping the world. lock(&allpLock) // We can't use a range loop over allp because we may // temporarily drop the allpLock. Hence, we need to re-fetch // allp each time around the loop. for i := 0; i < len(allp); i++ { _p_ := allp[i] if _p_ == nil { // This can happen if procresize has grown // allp but not yet created new Ps. continue } pd := &_p_.sysmontick s := _p_.status sysretake := false if s == _Prunning || s == _Psyscall { // Preempt G if it's running for too long. t := int64(_p_.schedtick) if int64(pd.schedtick) != t { pd.schedtick = uint32(t) pd.schedwhen = now } else if pd.schedwhen+forcePreemptNS <= now { // 超时抢占 preemptone(_p_) // In case of syscall, preemptone() doesn't // work, because there is no M wired to P. sysretake = true } } //p在零碎调用中或者被调用 if s == _Psyscall { // Retake P from syscall if it's there for more than 1 sysmon tick (at least 20us). t := int64(_p_.syscalltick) if !sysretake && int64(pd.syscalltick) != t { pd.syscalltick = uint32(t) pd.syscallwhen = now continue } // On the one hand we don't want to retake Ps if there is no other work to do, // but on the other hand we want to retake them eventually // because they can prevent the sysmon thread from deep sleep. //没有能够调度的工作且工夫阻塞工夫未到阀值,间接跳过 if runqempty(_p_) && atomic.Load(&sched.nmspinning)+atomic.Load(&sched.npidle) > 0 && pd.syscallwhen+10*1000*1000 > now { continue } // Drop allpLock so we can take sched.lock. // 这里登程了零碎调用长时间阻塞的调度 unlock(&allpLock) // Need to decrement number of idle locked M's // (pretending that one more is running) before the CAS. // Otherwise the M from which we retake can exit the syscall, // increment nmidle and report deadlock. incidlelocked(-1) if atomic.Cas(&_p_.status, s, _Pidle) { if trace.enabled { traceGoSysBlock(_p_) traceProcStop(_p_) } n++ _p_.syscalltick++ //要害办法,将对长时间阻塞的p进行从新调度 handoffp(_p_) } incidlelocked(1) lock(&allpLock) } } unlock(&allpLock) return uint32(n)}handoffp当零碎调用工夫过长的时候,会调用handoffp()办法: ...

April 20, 2021 · 3 min · jiezi

关于golang:当-Go-struct-遇上-Mutex

struct 是咱们写 Go 必然会用到的关键字, 不过当 struct 遇上一些比拟非凡类型的时候, 你留神过你的程序是否失常吗 ? 一段代码type URL struct { Ip string Port string mux sync.RWMutex params url.Values}func (c *URL) Clone() URL { newUrl := URL{} newUrl.Ip = c.Ip newUrl.params = url.Values{} return newUrl}这段代码你能看进去问题所在吗 ? A: 程序失常B: 编译失败C: panicD: 有可能产生 data raceE: 有可能产生死锁如果你看进去问题在哪里的话, 那我再轻轻通知你, 这段代码是 github 某 3k star Go 框架的底层外围代码, 那你是不是就感觉这个话题开始有意思了 ? 先说论断下面那段代码的问题是 sync.RWMutex 引起的. 如果你看过无关 sync 相干类型的介绍或者相干源码时, 在 sync 包外面的所有类型都有句这样的正文: must not be copied after first use, 可能很多人却并不知道这句话有什么作用, 顶多看到相干介绍时还记得 sync 相干类型的变量不能复制, 可能真正应用 Mutex, WaitGroup, Cond时, 早把这个正文忘的一尘不染. ...

April 20, 2021 · 3 min · jiezi

关于golang:一文完全掌握-Go-mathrand

Go 获取随机数是开发中常常会用到的性能, 不过这个外面还是有一些坑存在的, 本文将齐全分析 Go math/rand, 让你轻松应用 Go Rand. 开篇一问: 你感觉 rand 会 panic 吗 ? 源码分析math/rand 源码其实很简略, 就两个比拟重要的函数 func (rng *rngSource) Seed(seed int64) { rng.tap = 0 rng.feed = rngLen - rngTap //... x := int32(seed) for i := -20; i < rngLen; i++ { x = seedrand(x) if i >= 0 { var u int64 u = int64(x) << 40 x = seedrand(x) u ^= int64(x) << 20 x = seedrand(x) u ^= int64(x) u ^= rngCooked[i] rng.vec[i] = u } }}这个函数就是在设置 seed, 其实就是对 rng.vec 各个地位设置对应的值. rng.vec 的大小是 607. ...

April 20, 2021 · 3 min · jiezi

关于golang:最清晰易懂的-Go-WaitGroup-源码剖析

hi,大家好,我是haohongfan。 本篇次要介绍 WaitGroup 的一些个性,让咱们从实质下来理解 WaitGroup。对于 WaitGroup 的根本用法这里就不做过多介绍了。绝对于《这可能是最容易了解的 Go Mutex 源码分析》来说,WaitGroup 就简略的太多了。 源码分析Add() Wait() type WaitGroup struct { noCopy noCopy state1 [3]uint32}WaitGroup 底层构造看起来简略,但 WaitGroup.state1 其实代表三个字段:counter,waiter,sema。 counter :能够了解为一个计数器,计算通过 wg.Add(N), wg.Done() 后的值。waiter :以后期待 WaitGroup 工作完结的期待者数量。其实就是调用 wg.Wait() 的次数,所以通常这个值是 1 。sema : 信号量,用来唤醒 Wait() 函数。为什么要将 counter 和 waiter 放在一起 ?其实是为了保障 WaitGroup 状态的完整性。举个例子,看上面的一段源码 // sync/waitgroup.go:L79 --> Add()if v > 0 || w == 0 { // v => counter, w => waiter return}// ...*statep = 0for ; w != 0; w-- { runtime_Semrelease(semap, false, 0)}当同时发现 wg.counter <= 0 && wg.waiter != 0 时,才会去唤醒期待的 waiters,让期待的协程持续运行。然而应用 WaitGroup 的调用方个别都是并发操作,如果不同时获取的 counter 和 waiter 的话,就会造成获取到的 counter 和 waiter 可能不匹配,造成程序 deadlock 或者程序提前结束期待。 ...

April 20, 2021 · 2 min · jiezi

关于golang:这可能是最容易理解的-Go-Mutex-源码剖析

Hi,大家好,我是 haohongfan。 上一篇文章《一文齐全把握 Go math/rand》,咱们晓得 math/rand 的 global rand 有一个全局锁,我的文章外面有一句话:“修复计划: 就是把 rrRand 换成了 globalRand, 在线上高并发场景下, 发现全局锁影响并不大.”, 有同学私聊我“他们遇到线上服务的锁竞争特地强烈”。的确我这句话说的并不谨严。然而也让我有了一个思考:到底多高的 QPS 能力让 Mutex 产生强烈的锁竞争 ? 到底加锁的代码会不会产生线上问题? 到底该不该应用锁来实现这个性能?线上的问题是不是因为应用了锁造成的?针对这些问题,本文就从源码角度分析 Go Mutex, 揭开 Mutex 的迷雾。 源码剖析Go mutex 源码只有短短的 228 行,然而却蕴含了很多的状态转变在外面,很不容易看懂,具体能够参见上面的流程图。Mutex 的实现次要借助了 CAS 指令 + 自旋 + 信号量来实现,具体代码我就不再每一行做剖析了,有趣味的能够依据上面流程图配合源码浏览一番。 Lock Unlock 一些例子1. 一个 goroutine 加锁解锁过程 2. 没有加锁,间接解锁问题 3. 两个 Goroutine,相互加锁解锁 4. 三个 Goroutine 期待加锁过程 整篇源码其实波及比拟难以了解的就是 Mutex 状态(mutexLocked,mutexWoken,mutexStarving,mutexWaiterShift) 与 Goroutine 之间的状态(starving,awoke)扭转, 咱们上面将逐个阐明。 什么是 Goroutine 排队? 如果 Mutex 曾经被一个 Goroutine 获取了锁, 其它期待中的 Goroutine 们只能始终期待。那么等这个锁开释后,期待中的 Goroutine 中哪一个会优先获取 Mutex 呢? ...

April 20, 2021 · 4 min · jiezi

关于golang:golang调度学习调度流程-五-Syscall

syscall函数Syscall函数的定义如下,传入4个参数,返回3个参数。 func syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) syscall函数的作用是传入零碎调用的地址和参数,执行实现后返回。流程次要是零碎调用前执行entersyscall,设置g p的状态,而后入参,执行后,写返回值而后执行exitsyscall设置g p的状态。entersyscall和exitsyscall在g的调用中细讲。 // func Syscall(trap int64, a1, a2, a3 uintptr) (r1, r2, err uintptr);// Trap # in AX, args in DI SI DX R10 R8 R9, return in AX DX// Note that this differs from "standard" ABI convention, which// would pass 4th arg in CX, not R10.// 4个入参:PC param1 param2 param3TEXT ·Syscall(SB),NOSPLIT,$0-56 // 调用entersyscall 判断是执行条件是否满足 记录调度信息 切换g p的状态 CALL runtime·entersyscall(SB) // 将参数存入寄存器中 MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI MOVQ a3+24(FP), DX MOVQ trap+0(FP), AX // syscall entry SYSCALL CMPQ AX, $0xfffffffffffff001 JLS ok // 执行失败时 写返回值 MOVQ $-1, r1+32(FP) MOVQ $0, r2+40(FP) NEGQ AX MOVQ AX, err+48(FP) // 调用exitsyscall 记录调度信息 CALL runtime·exitsyscall(SB) RETok: // 执行胜利时 写返回值 MOVQ AX, r1+32(FP) MOVQ DX, r2+40(FP) MOVQ $0, err+48(FP) CALL runtime·exitsyscall(SB) RET TEXT ·RawSyscall(SB),NOSPLIT,$0-56 MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI MOVQ a3+24(FP), DX MOVQ trap+0(FP), AX // syscall entry SYSCALL JCC ok1 MOVQ $-1, r1+32(FP) // r1 MOVQ $0, r2+40(FP) // r2 MOVQ AX, err+48(FP) // errno RETok1: MOVQ AX, r1+32(FP) // r1 MOVQ DX, r2+40(FP) // r2 MOVQ $0, err+48(FP) // errno RET显著SysCall比RawSyscall多调用了两个办法,entersyscall和exitsyscall,减少这两个函数的调用,让调度器有机会去对行将要进入零碎调用的goroutine进行调整,不便调度。 ...

April 20, 2021 · 5 min · jiezi

关于golang:go-runtime其他函数-gogo-goexit

gogogogo由汇编实现,次要是由g0切换到g栈,而后执行函数。 // func gogo(buf *gobuf)// restore state from Gobuf; longjmpTEXT runtime·gogo(SB), NOSPLIT, $16-8 MOVQ buf+0(FP), BX // gobuf MOVQ gobuf_g(BX), DX MOVQ 0(DX), CX // make sure g != nil get_tls(CX) MOVQ DX, g(CX) MOVQ gobuf_sp(BX), SP // restore SP, 复原sp寄存器值切换到g栈 MOVQ gobuf_ret(BX), AX MOVQ gobuf_ctxt(BX), DX MOVQ gobuf_bp(BX), BP MOVQ $0, gobuf_sp(BX) // clear to help garbage collector MOVQ $0, gobuf_ret(BX) MOVQ $0, gobuf_ctxt(BX) MOVQ $0, gobuf_bp(BX) MOVQ gobuf_pc(BX), BX // 获取G工作函数的地址 JMP BX // 转到工作函数执行goexit当调用工作函数完结返回的时候,会执行到咱们在创立g流程中就初始化好的指令:goexit ...

April 19, 2021 · 2 min · jiezi

关于golang:Go-语言环境搭建及测试构建

开发工具:goland 规范库SDK:https://studygolang.com/dl 目录构造: C:\project src # 源代码目录 bin # 编译后的可执行文件目录 pkg # 扩大包目录配置环境变量: # 指标可执行程序运行操作系统,反对 darwin,freebsd,linux,windowsGOOS:windows# 指标可执行程序操作系统构架,包含 386,amd64,armGOARCH:amd64# 工作空间,保留go我的项目代码和第三方依赖包GOPATH:C:\project# go规范库目录,即装置门路GOROOT:C:\Go# 查看go环境变量> go env配置goland环境变量,并关上工程C:\project\src。 编写测试程序: # \project\src\hello.go// 每个程序都必须有一个包名package main// 导入fmt规范包,用于格式化输入import "fmt"// 主函数,花括号必须与函数名同行func main() { // 不须要分号结尾 fmt.Println("hello world")}构建我的项目: # Windowsgo build -o hello.exe hello.go./hello.exe# LinuxGOOS=linuxgo build -o hello-linux hello.gochmod +x hello-linux./hello-linux

April 19, 2021 · 1 min · jiezi

关于golang:Excelize-240-正式版发布-新增-152-项公式函数支持

Excelize 是 Go 语言编写的用于操作 Office Excel 文档根底库,基于 ECMA-376,ISO/IEC 29500 国际标准。能够应用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创立的电子表格文档。反对 XLSX / XLSM / XLTM 等多种文档格局,高度兼容带有款式、图片(表)、透视表、切片器等简单组件的文档,并提供流式读写 API,用于解决蕴含大规模数据的工作簿。可利用于各类报表平台、云计算、边缘计算等零碎。入选 2020 Gopher China - Go 畛域明星开源我的项目 (GSP)、 2018 开源中国码云 Gitee 最有价值开源我的项目 GVP,目前已成为 Go 语言最受欢迎的 Excel 文档根底库。 开源代码GitHub: github.com/xuri/excelize Gitee: gitee.com/xurime/excelize 中文文档: xuri.me/excelize/zh-hans 2021年4月19日,社区正式公布了 2.4.0 版本,该版本蕴含了多项新增性能、谬误修复和兼容性晋升优化。上面是无关该版本更新内容的摘要,残缺的更改列表可查看 changelog。 Release Notes此版本中最显著的变动包含: 兼容性提醒降级至该版本须要您应用的 Go 语言为 1.15 或更高版本。 新增性能新增 GetCellRichText API,反对依据给定的工作表名称获取富文本SetPageLayout 和 GetPageLayout 现已反对设置和获取页面打印缩放比例、指定单色打印、起始页码SetSheetPrOptions 和 GetSheetPrOptions 现已反对设置和获取工作表标签页色彩SetCellHyperLink 反对设置屏幕提示文字与显示文字,相干 issue #790创立数据透视表时新增反对 ShowError 选项流式 API 反对设置单元格公式, 相干 issue #625公式计算引擎反对不等于运算符嵌套公式函数当初反对将单元格援用用作参数反对指定是否设置图表数据系列格局为主动填充色彩新增 152 项公式函数反对: ATAN, AVERAGE, AVERAGEA, BESSELI, BESSELJ, BIN2DEC, BIN2HEX, BIN2OCT, BITAND, BITLSHIFT, BITOR, BITRSHIFT, BITXOR, CHAR, CHOOSE, CLEAN, CODE, COLUMN, COLUMNS, COMPLEX, CONCAT, CONCATENATE, COUNT, COUNTBLANK, CUMIPMT, CUMPRINC, DATE, DATEDIF, DB, DDB, DEC2BIN, DEC2HEX, DEC2OCT, DOLLARDE, DOLLARFR, EFFECT, ENCODEURL, EXACT, FALSE, FIND, FINDB, FISHER, FISHERINV, FIXED, FV, FVSCHEDULE, GAMMA, GAMMALN, HARMEAN, HEX2BIN, HEX2DEC, HEX2OCT, HLOOKUP, IF, IFERROR, IMABS, IMAGINARY, IMARGUMENT, IMCONJUGATE, IMCOS, IMCOSH, IMCOT, IMCSC, IMCSCH, IMDIV, IMEXP, IMLN, IMLOG10, IMLOG2, IMPOWER, IMPRODUCT, IMREAL, IMSEC, IMSECH, IMSIN, IMSINH, IMSQRT, IMSUB, IMSUM, IMTAN, IPMT, IRR, ISTEXT, ISPMT, KURT, LARGE, LEFT, LEFTB, LEN, LENB, LOOKUP, LOWER, MAX, MID, MIDB, MIN, MINA, MIRR, N, NOMINAL, NORM.DIST, NORMDIST, NORM.INV, NORMINV, NORM.S.DIST, NORMSDIST, NORM.S.INV, NORMSINV, NOT, NOW, NPER, NPV, OCT2BIN, OCT2DEC, OCT2HEX, PDURATION, PERCENTILE.INC, PERCENTILE, PERMUT, PERMUTATIONA, PMT, POISSON.DIST, POISSON, PPMT, PROPER, QUARTILE, QUARTILE.INC, REPLACE, REPLACEB, REPT, RIGHT, RIGHTB, ROMAN, ROW, ROWS, SHEET, SKEW, SMALL, STDEV, STDEV.S, STDEVA, SUBSTITUTE, T, TODAY, TRIM, TRUE, UNICHAR, UNICODE, UPPER, VAR.P, VARP, VLOOKUP兼容性晋升当以 nil 作为值调用 SetCellValue 设置单元格的值时,不再为单元格设置空白字符串而以空值代替,解决 issue #756移除外部处理单元格填充色彩款式时冗余的 XML 可选空值解析标识晋升与 Google Sheets 离线浏览器扩大应用程序的兼容性,相干 issue #769在筛选器对应的名称中应用相对援用以晋升与 Apache OpenOffice 应用程序的兼容性,解决 issue #776在流式解析工作表过程中减少 XML 标签敞开事件的解决,晋升行/列迭代器读取性能,修复局部状况下读取行数有误的问题进步工作簿内工作表文档应用相对路径的兼容性防止创立反复的富文本款式,解决 issue #787进步工作簿内工作表文档应用绝对路径与 Windows 目录分隔符的兼容性问题修复修复数值舍入精度问题 #764增加并调整工作表字段的解析程序,修复局部状况下生成的文档损坏的问题 #766修复 COTH 双曲余切三角函数计算有误的问题公式计算链减少对工作表的关联解决,修复局部状况下复制行导致的文档损坏问题,解决 issue #774删除工作表时减少对名称的解决,解决 issue #775修复外部函数 newFills 和 parseToken 圈简单度过高的问题修复对工作表默认自定义行高的查看修复勾销工作表中全副合并单元格时导致文档损坏的问题, 解决 issue #782修复局部状况下筛选条件局部失落的问题修复当工作簿蕴含图表工作表、对话工作表时,UpdateLinkedValue 产生谬误的问题修复局部状况下 GetColWidth 返回默认列宽谬误的问题修复无奈通过 Excel 电子表格应用程序向创立的数据透视表中增加时间轴与切片器的问题,解决 issue #804设置名称时外部的 localSheetId 属性将应用 sheetIndex,修改谬误的工作表索引应用修复局部状况下保留后的文档单元格锁定或暗藏属性可能失落问题,解决 issue #809修复流式写入数据后调用一般 API 将导致流式写入的失落问题,解决 issue #813修复负值图表数据系列填充色彩失落问题性能优化进步了数值精度处理速度其余Go Modules 依赖模块更新单元测试与文档更新蕴含简体中文、英语、法语、俄语、日语、韩语、阿拉伯语、德语和西班牙语的多国语言文档网站更新

April 19, 2021 · 2 min · jiezi

关于golang:一文带你更方便的控制-goroutine

上一篇咱们讲了 go-zero 中的并发工具包 core/syncx。 从整体剖析来看,并发组件次要通过 channel + mutex 控制程序中协程之间沟通。 Do not communicate by sharing memory; instead, share memory by communicating. 不要通过共享内存来通信,而应通过通信来共享内存。 本篇来聊 go-zero 对 Go 中 goroutine 反对的并发组件。 咱们回顾一下,go原生反对的 goroutine 管制的工具有哪些? go func() 开启一个协程sync.WaitGroup 管制多个协程工作编排sync.Cond 协程唤醒或者是协程期待那可能会问 go-zero 为什么还要拿出来讲这些?回到 go-zero 的设计理念:工具大于约定和文档。 那么就来看看,go-zero 提供哪些工具? threading尽管 go func() 曾经很不便,然而有几个问题: 如果协程异样退出,无奈追踪异样栈某个异样申请触发panic,应该做故障隔离,而不是整个过程退出,容易被攻打咱们看看 core/threading 包提供了哪些额定抉择: func GoSafe(fn func()) { go RunSafe(fn)}func RunSafe(fn func()) { defer rescue.Recover() fn()}func Recover(cleanups ...func()) { for _, cleanup := range cleanups { cleanup() } if p := recover(); p != nil { logx.ErrorStack(p) }}GoSafethreading.GoSafe() 就帮你解决了这个问题。开发者能够将本人在协程中须要实现逻辑,以闭包的形式传入,由 GoSafe() 外部 go func(); ...

April 19, 2021 · 2 min · jiezi

关于golang:golang-close-函数的行为

close 函数官网定义如下close函数是一个内建函数, 用来敞开channel,这个channel要么是双向的, 要么是只写的(chan<- Type)。 这个办法应该只由发送者调用, 而不是接收者。 当最初一个发送的值都被接收者从敞开的channel(下简称为c)中接管时, 接下来所有接管的值都会非阻塞间接胜利,返回channel元素的零值。 如下的代码: 如果c曾经敞开(c中所有值都被接管), x, ok := <- c, 读取ok将会失去false。close函数再执行后chan会接管到一次, x, ok := <- c,ok失去false。 代码如下 package mainimport ( "fmt" "github.com/gin-gonic/gin")func cWait(c <-chan int) { v, ok := <-c if ok { fmt.Printf("read a int is %d\n", v) } else { fmt.Printf("read a error a int \n") }}func main() { c := make(chan int) go cWait(c) close(c) router := gin.Default() router.Run()}输入会蕴含有一次 read a error a int ...

April 18, 2021 · 1 min · jiezi

关于golang:一个-Lock-Free-的-Go-语言雪花算法实现

An Lock Free ID Generator for Golang implementation View on GitHub. Snowflake is a network service for generating unique ID numbers at high scale with some simple guarantees. The first bit is unused sign bit.The second part consists of a 41-bit timestamp (milliseconds) whose value is the offset of the current time relative to a certain time.The 10 bits machineID(5 bit workid + 5 bit datacenter id), max value is 2^10 -1 = 1023.The last part consists of 12 bits, its means the length of the serial number generated per millisecond per working node, a maximum of 2^12 -1 = 4095 IDs can be generated in the same millisecond.The binary length of 41 bits is at most 2^41 -1 millisecond = 69 years. So the snowflake algorithm can be used for up to 69 years, In order to maximize the use of the algorithm, you should specify a start time for it.The ID generated by the snowflake algorithm is not guaranteed to be unique. For example, when two different requests enter the same machine at the same time, and the sequence generated by the node is the same, the generated ID will be duplicated. ...

April 18, 2021 · 3 min · jiezi

关于golang:Java日志框架中需要判断logisDebugEnabled吗

Java日志框架中须要判断log.isDebugEnabled()吗?背景在日常开发中,我的项目会应用形象日志接口slf4j来打印日志。如下是一段典型的打印日志代码: logger.debug("hello, world");然而在一些我的项目或第三方开源框架中,也会发现有些代码在输入日志时,在后面增加if判断,代码如下: if(logger.isDebugEnabled()){ logger.debug("hello, world");}简略来说,先应用isDebufEnabled来判断日志级别。这么写的目标是什么呢?而且网上有些文档指出这种判断其实是不须要的。那理论开发中,咱们到底要不要判断isDebugEnabled呢?心愿通过这篇文章来分享一些我对日志打印的思考。 简略的源码分析有些人不明确为什么要增加if判断,会认为这样是为了管制日志的输入。其实这是不对的。对于上面的两段代码: logger.debug("hello, world")if(logger.isDebugEnabled()){ logger.debug("hello, world");}如果利用的日志级别大于debug,比方为info。那么这两段代码,最终都不会输入日志。在debug办法外部,会判断日志级别,如果利用级别大于日志级别,就不会输入日志。以下是isDebugEnabled和debug的外围代码(我删除了一些无关代码): isDebugEnabled办法: public boolean isDebugEnabled(Marker marker) { final FilterReply decision = callTurboFilters(marker, Level.DEBUG); if (decision == FilterReply.NEUTRAL) { return effectiveLevelInt <= Level.DEBUG_INT; } else { throw new IllegalStateException("Unknown FilterReply value: " + decision); } }debug办法: private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params, final Throwable t) { final FilterReply decision = loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, msg, params, t); if (decision == FilterReply.NEUTRAL) { if (effectiveLevelInt > level.levelInt) { return; } } else if (decision == FilterReply.DENY) { return; } buildLoggingEventAndAppend(localFQCN, marker, level, msg, params, t); }那么问题来了,既然isDebugEnabled不会影响日志是否输入,那为什么要增加这个判断呢? ...

April 18, 2021 · 1 min · jiezi

关于golang:golang学习系列目录更新ing

一、根底二、模块golang 模块(1)——包治理 三、开发教训golang理论总结(1) ——golang我的项目构造(模块拆分)

April 15, 2021 · 1 min · jiezi

关于云原生:KubeVela-10-开启可编程式应用平台的未来

作者 | KubeVela 我的项目维护者起源 | 阿里巴巴云原生公众号 作为 OAM(Open Application Model)在 Kubernetes 上的实现,KubeVela 我的项目从 oam-kubernetes-runtime 演进至今不过半年多工夫,但发展势头十分迅猛,不仅间断登上 GitHub Go 语言趋势榜首和 HackerNews 首页,更是迅速播种了包含 MasterCard、Springer Nature、第四范式、SILOT、Upbound 等来自世界各地、不同行业的终端用户,甚至还呈现了像 Oracle Cloud、Napptive 等基于它构建的商业化产品。就在 2021年3月底,KubeVela 社区发表蕴含所有稳定版 API 的 v1.0 版本公布,正式开始向企业级生产可用迈进。 不过,如果你对云原生畛域不太关注,可能对 KubeVela 还没有做过太深刻的理解。别着急,本文就借着 v1.0 公布之际,为你具体的梳理一次 KubeVela 我的项目的倒退脉络,解读它的核心思想和愿景,领悟这个正冉冉升起的云原生利用治理平台之星背地的“道之所在”。 首先,什么是 KubeVela? 一言以蔽之,KubeVela 是一个“可编程式”的云原生利用治理与交付平台。 可是,什么是“可编程”呢?它跟 Kubernetes 又是什么关系?它能帮忙咱们解决什么问题? PaaS 零碎的“能力窘境”PaaS 零碎(比方 Cloud Foundry、Heroku 等)自诞生以来,就以其简略、高效的利用部署体验而被所有人津津有味。然而,大家也晓得,咱们明天的“云原生”,却是一个 Kubernetes 大行其道的世界,已经的 PaaS(包含 Docker)到底遇到了什么问题呢? 其实任何一个尝试应用过 PaaS 的人,都会对这种零碎的一个实质缺点感触颇深,那就是 PaaS 零碎的“能力窘境”。 图 1 - PaaS零碎的能力窘境 如图 1 所示,PaaS 零碎在最开始应用的时候,往往体验十分好,也总能恰到好处地解决问题。但随着应用工夫的推移,一个十分厌恶的状况就会呈现:利用的诉求,开始超过 PaaS 零碎可能提供的能力。而更可怕的是,一旦这个问题呈现,用户对 PaaS 零碎的满意度就会断崖式上涨,这是因为无论是从新开发平台减少性能,还是批改利用去适配平台,都是一项投入微小但收益很低的事件。更何况所有人这时候都会开始对平台失去信念:谁晓得下一次零碎或者利用大改,是不是很快又要产生了? ...

April 14, 2021 · 3 min · jiezi

关于golang:Golang-内存组件之mspanmcachemcentral-和-mheap-数据结构

最新版本请查看原文:https://blog.haohtml.com/arch... Golang中的内存部件组成关系如下图所示 golang 内存调配组件 在学习golang 内存时,常常会波及几个重要的数据结构,如果不相熟它们的状况下,了解它们就显得分外的吃力,所以本篇次要对相干的几个内存组件做下数据结构的介绍。 在 Golang 中,mcache、mcentral 和 mheap 是内存治理的三大组件,mcache 治理线程在本地缓存的 mspan,页 mcentral 治理着全局的 mspan 为所有 mcache 提供所有线程。 依据调配对象的大小,外部会应用不同的内存分配机制,具体参考函数 mallocgo() <16b 会应用渺小对象内存分配器,次要应用 mcache.tinyXXX 这类的字段16-32b 从P上面的 mcache 中调配>32b 间接从 mheap 中调配对于golang中的内存申请流程,大家应该都十分相熟了,这里不再进行详细描述。 mcache在GPM关系中,会在每个 P 下都有一个 mcache 字段,用来示意内存信息。 在 Go 1.2 版本前调度器应用的是 GM 模型,将mcache 放在了 M 里,但发现存在诸多问题,期中对于内存这一块存在着微小的节约。每个M 都持有 mcache 和 stack alloc,但只有在 M 运行 Go 代码时才须要应用的内存(每个 mcache 能够高达2mb),当 M 在处于 syscall 或 网络申请 的时候是不须要的,再加上 M 又是容许创立多个的,这就造成了很大的节约。所以从go 1.3版本开始应用了GPM模型,这样在高并发状态下,每个G只有在运行的时候才会应用到内存,而每个 G 会绑定一个P,所以它们在运行只占用一份 mcache,对于 mcache 的数量就是P 的数量,同时并发拜访时也不会产生锁。 ...

April 13, 2021 · 6 min · jiezi

关于golang:MixGo-v11发布-Go-快速开发脚手架工具

Mix Go 是一个基于 Go 进行疾速开发的残缺零碎,相似前端的 Vue CLI,提供: 通过 mix-go/mixcli 实现的交互式我的项目脚手架: 能够生成 cli, api, web, grpc 多种我的项目代码生成的代码开箱即用可抉择是否须要 .env 环境配置可抉择是否须要 .yml, .json, .toml 等独立配置可抉择应用 gorm, xorm 的数据库可抉择应用 logrus, zap 的日志库通过 mix-go/xcli 实现的命令行原型开发。基于 mix-go/xdi 的 DI, IoC 容器。Githubhttps://github.com/mix-go/mix疾速开始装置 go get github.com/mix-go/mixcli创立我的项目 $ mixcli new helloUse the arrow keys to navigate: ↓ ↑ → ← ? Select project type: ▸ CLI API Web (contains the websocket) gRPC技术交换知乎:https://www.zhihu.com/people/... 微博:http://weibo.com/onanying 官网QQ群:284806582, 825122875,敲门暗号:goer 编写一个 CLI 程序首先咱们应用 mixcli 命令创立一个我的项目骨架: ...

April 13, 2021 · 10 min · jiezi

关于golang:聊聊dddsamplecore的model

序本文次要钻研一下dddsample-core的model Entitypublic interface Entity<T> { /** * Entities compare by identity, not by attributes. * * @param other The other entity. * @return true if the identities are the same, regardless of other attributes. */ boolean sameIdentityAs(T other);}Entity接口定义了sameIdentityAs办法ValueObjectpublic interface ValueObject<T> extends Serializable { /** * Value objects compare by the values of their attributes, they don't have an identity. * * @param other The other value object. * @return <code>true</code> if the given value object's and this value object's attributes are the same. */ boolean sameValueAs(T other);}ValueObject接口定义了sameValueAs办法TrackingIdpublic final class TrackingId implements ValueObject<TrackingId> { private String id; /** * Constructor. * * @param id Id string. */ public TrackingId(final String id) { Validate.notNull(id); this.id = id; } /** * @return String representation of this tracking id. */ public String idString() { return id; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TrackingId other = (TrackingId) o; return sameValueAs(other); } @Override public int hashCode() { return id.hashCode(); } @Override public boolean sameValueAs(TrackingId other) { return other != null && this.id.equals(other.id); } @Override public String toString() { return id; } TrackingId() { // Needed by Hibernate }}TrackingId实现了ValueObject接口,sameValueAs办法通过equals办法判断Type public enum Type implements ValueObject<Type> { LOAD(true), UNLOAD(true), RECEIVE(false), CLAIM(false), CUSTOMS(false); private final boolean voyageRequired; /** * Private enum constructor. * * @param voyageRequired whether or not a voyage is associated with this event type */ private Type(final boolean voyageRequired) { this.voyageRequired = voyageRequired; } /** * @return True if a voyage association is required for this event type. */ public boolean requiresVoyage() { return voyageRequired; } /** * @return True if a voyage association is prohibited for this event type. */ public boolean prohibitsVoyage() { return !requiresVoyage(); } @Override public boolean sameValueAs(Type other) { return other != null && this.equals(other); } }Type枚举实现了ValueObject接口,其sameValueAs办法通过equals判断DomainEventpublic interface DomainEvent<T> { /** * @param other The other domain event. * @return <code>true</code> if the given domain event and this event are regarded as being the same event. */ boolean sameEventAs(T other);}DomainEvent接口定义了sameEventAs办法小结dddsample-core定义了Entity、ValueObject、DomainEvent接口,它们别离定义了sameIdentityAs、sameValueAs、sameEventAs办法。 ...

April 12, 2021 · 2 min · jiezi

关于golang:emacs-配置golang开发环境

一、参考Becoming an emacs gopher Emacs Golang开发环境配置指南 Stop debugging Go with Println and use Delve instead Delve调试器 二、装置golang包2.1 goimportsCommand goimports updates your Go import lines, adding missing ones and removing unreferenced ones. 对于代码中引入的第三方模块,自动更新 goimports命令能主动格式化代码,主动增加、移除imports,而且与Emacs集成良好。能够代替官网gofmt命令。 GO111MODULE=on go get -u golang.org/x/tools/cmd/goimports2.2 godefGodef, given an expression or a location in a source file, prints the location of the definition of the symbol referred to. 对于代码中的函数援用,能够查看原始定义地位godef命令能在Go源码变量、函数定义间跳转,是查看变量、函数、文件定义的好助手 GO111MODULE=on go get -u github.com/rogpeppe/godef2.3 gocodeAn autocompletion daemon for the Go programming language ...

April 12, 2021 · 2 min · jiezi

关于golang:更简的并发代码更强的并发控制

有没感觉 Go 的 sync 包不够用?有没遇到类型没有 sync/atomic 反对? 咱们一起看看 go-zero 的 syncx 包对规范库的一些增值补充。 https://github.com/tal-tech/g... name作用AtomicBoolbool类型 原子类AtomicDurationDuration无关 原子类AtomicFloat64float64类型 原子类Barrier栏栅【将加锁解锁包装】Cond条件变量DoneChan优雅告诉敞开ImmutableResource创立后不会批改的资源Limit管制申请数LockedCalls确保办法的串行调用ManagedResource资源管理Once提供 once funcOnceGuard一次性应用的资源管理Poolpool,简略的池RefResource援用计数的资源ResourceManager资源管理器SharedCalls相似 singflight 的性能SpinLock自旋锁:自旋+CASTimeoutLimitLimit + timeout 管制上面开始对以上库组件做别离介绍。 atomic因为没有 泛型 反对,所以才会呈现多种类型的原子类反对。以下采纳 float64 作为例子: func (f *AtomicFloat64) Add(val float64) float64 { for { old := f.Load() nv := old + val if f.CompareAndSwap(old, nv) { return nv } }}func (f *AtomicFloat64) CompareAndSwap(old, val float64) bool { return atomic.CompareAndSwapUint64((*uint64)(f), math.Float64bits(old), math.Float64bits(val))}func (f *AtomicFloat64) Load() float64 { return math.Float64frombits(atomic.LoadUint64((*uint64)(f)))}func (f *AtomicFloat64) Set(val float64) { atomic.StoreUint64((*uint64)(f), math.Float64bits(val))}Add(val):如果 CAS 失败,一直for循环重试,获取 old val,并set old+val;CompareAndSwap(old, new):调用底层 atomic 的 CAS;Load():调用 atomic.LoadUint64 ,而后转换Set(val):调用 atomic.StoreUint64至于其余类型,开发者想本人扩大本人想要的类型,能够按照上述,基本上调用原始 atomic 操作,而后转换为须要的类型,比方:遇到 bool 能够借助 0, 1 来分辨对应的 false, true。 ...

April 12, 2021 · 3 min · jiezi

关于golang:Go徒手实现web的session管理器

如果对你有帮忙请你点个关注,按赞,谢谢,你们反对就是我更新的能源! 概 述大家都晓得 session 是web利用在服务器端实现的一种用户和服务器之间认证的解决方案,目前 Go 规范包没有为 session 提供任何反对,本文我将解说session的实现原理,和一些常见基于session平安产生的进攻问题。 当然有人可能看了会抬杠,说当初大部分不是前后端拆散架构吗?对,你能够应用JWT解决你的问题。然而也有一些一体化web利用须要session,所以我筹备造个轮子。本人造的轮子哪里出问题了,比他人更相熟,有bug了,还不必求着他人修bug,本人修就好了,呵呵哈哈哈,当然这几句话有点皮。 需 求我感觉一名好的程序员,在写程序之前应该列一下需要剖析,整顿一下思路,而后再去写代码。 反对内存存储会话数据反对分布式redis会话存储会话如果有心跳就主动续命30分钟(生命周期)提供进攻:中间人,会话劫持,会话重放等攻打工作原理首先必须理解工作原理能力写代码,这里我就略微说一下,session是基于cookie实现的,一个session对应一个uuid也是sessionid,在服务器创立一个相干的数据结构,而后把这个sessionid通过cookie让浏览器保留着,下次浏览器申请过去了就会有sessionid,而后通过sessionid获取这个会话的数据。 代码实现都是说着容易,理论写起来就是各种坑,不过我还是实现了。 少说废话,还是间接干代码吧。 依赖关系 下面是设计的相干依赖关系图,session是一个独立的构造体,GlobalManager是整体的会话管理器负责数据长久化,过期会话垃圾回收工作♻️,storage是存储器接口,因为咱们要实现两种形式存储会话数据或者当前要减少其余长久化存储,所以必须须要接口形象反对,memory和redis是存储的具体实现。 storage接口package sessionx// session storage interfacetype storage interface { Read(s *Session) error Create(s *Session) error Update(s *Session) error Remove(s *Session) error}storage就9行代码,是具体的会话数据操作动作的形象,全副参数应用的是session这个构造的指针,如果解决异样了就即错即返回。 为什么把函数签名的形参应用指针类型的,这个我想看的懂人应该晓得这是为什么了 memoryStore构造体type memoryStore struct { sync.Map}memoryStore构造体外面就嵌入sync.Map构造体,一开始是应用的map这种,然而前面发现在并发读写而后加sync.Mutex锁,性能还不如间接应用sync.Map速度快。sync.Map用来做K:V存储的,也就是sessionid对应session data的。 实现storage具体方法如下: func (m *memoryStore) Read(s *Session) error { if ele, ok := m.Load(s.ID); ok { // bug 这个不能间接 s = ele s.Data = ele.(*Session).Data return nil } // s = nil return fmt.Errorf("id `%s` not exist session data", s.ID)}读取数据的时候先将长久化的数据读出来而后赋值给本次会话的session。 ...

April 11, 2021 · 5 min · jiezi

关于golang:许式伟相比-Python我们可能更需要-Go

ECUG(Effective Cloud User Group,实效云计算用户组)主办的 2021 ECUG Con 于 2021 年 4 月 10 日 - 11 日在上海举办。会上,七牛云 CEO 许式伟以 “数据迷信与 Go+” 为主题发表了主题分享,讲述了对数据迷信变迁的了解,对新语言 Go+ 的构想和布局,并大胆指出数据迷信正迎来暴发期,像字节跳动一样的新型公司只会越来越多。以下为演讲内容整顿。 方才在闲聊说 ECUG 变得越来越高大上,其实我也变得越来越像一个单纯的讲师。往年是 ECUG 社区的第 14 个年头,这场流动也是第 14 届 ECUG Con。其实这一届原本应该在去年办,但因为疫情延后了。 其实,我在 ECUG 始终贯彻的理念有两个: 第一,让本人继续地写代码。因为每一次来 ECUG 我都很缓和,不能什么都没有呀。所以这也是挺好的机会,能让本人继续留在技术一线; 另外,我每年分享的主题都有肯定的延续性,出现了我本人对将来思考的脉络。 去年开始,我在聊数据迷信,后面有三年是聊在端上的一些实际。起因是我认为云计算的第一个时代应该是属于机器计算,也就是虚拟机;第二代就是云原生,我认为这是一场被称为“基础架构”的反动。也就是说,第一阶段是资源,第二阶段是基础架构。 第三个阶段,我的判断为利用计算,这会波及前端和后端的协同。 从去年开始,我的分享转向了数据迷信,一个很重要的因素和趋势是,数据时代的到来。尤其是 2017 年之后,数据大量地被数字化当前,在各行各业都会有波及数据迷信的广泛应用。 去年也是蛮巧的,我脑子一热就搞了一个语言进去。我以前搞过蛮多语言,受众也有一些。然而那个都很明确,素来没有想过有一天可能商业化。兴许碰巧有一些公司用它来做商业化,然而基本上从出世那一刻开始,就不是冲着商业化去的。 2012 年我花了很多精力在布道 Go,因为过后作为一个初创公司,招人太难。一个比拟好的招人逻辑就是让他人感觉你乏味,公司技术气氛很不错。Go+ 是我第一个认认真真心愿可能把它商业化的语言,但目前宣传得还不多,1.0 还没公布。我想讲讲我本人对 Go+ 和数据迷信的一个思考,为什么认为 Go+ 有商业化的机会。 我明天聊的话题大略有四个方面: 语言的倒退数据迷信的倒退Go+ 的设计理念Go+ 实现的迭代语言的倒退首先,咱们讲讲语言的倒退,程序员对这个话题十分感兴趣。我把语言的发展史分为三个局部来说。 第一,动态语言的发展史。我选的是 TOP20 的语言,这个是依据当初最火的语言排行榜排名选的,前 20 名的语言我排了一下大略是这样的,最早公布的是 C,到当初其实还在排行榜前三的地位。第二是 C++,Objective-C、Java、C#、Go、Swift、Go+。咱们能够看到一个比拟乏味的景象,差不多每 6-8 年会呈现一轮新的、具备影响力的动态语言,这是生产力迭代的表征。 ...

April 11, 2021 · 2 min · jiezi

关于golang:聊聊gocqrs的EventHandler

序本文次要钻研一下go.cqrs的EventHandler EventHandlertype EventHandler interface { Handle(EventMessage)}EventHandler定义了Handle办法EventMessage// EventMessage is the interface that a command must implement.type EventMessage interface { // AggregateID returns the ID of the Aggregate that the event relates to AggregateID() string // GetHeaders returns the key value collection of headers for the event. // // Headers are metadata about the event that do not form part of the // actual event but are still required to be persisted alongside the event. GetHeaders() map[string]interface{} // SetHeader sets the value of the header specified by the key SetHeader(string, interface{}) // Returns the actual event which is the payload of the event message. Event() interface{} // EventType returns a string descriptor of the command name EventType() string // Version returns the version of the event Version() *int}EventMessage接口定义了AggregateID、GetHeaders、SetHeader、Event、EventType、Version办法EventDescriptor// EventDescriptor is an implementation of the event message interface.type EventDescriptor struct { id string event interface{} headers map[string]interface{} version *int}// NewEventMessage returns a new event descriptorfunc NewEventMessage(aggregateID string, event interface{}, version *int) *EventDescriptor { return &EventDescriptor{ id: aggregateID, event: event, headers: make(map[string]interface{}), version: version, }}// EventType returns the name of the event type as a string.func (c *EventDescriptor) EventType() string { return typeOf(c.event)}// AggregateID returns the ID of the Aggregate that the event relates to.func (c *EventDescriptor) AggregateID() string { return c.id}// GetHeaders returns the headers for the event.func (c *EventDescriptor) GetHeaders() map[string]interface{} { return c.headers}// SetHeader sets the value of the header specified by the keyfunc (c *EventDescriptor) SetHeader(key string, value interface{}) { c.headers[key] = value}// Event the event payload of the event messagefunc (c *EventDescriptor) Event() interface{} { return c.event}// Version returns the version of the eventfunc (c *EventDescriptor) Version() *int { return c.version}EventDescriptor定义了id、event、headers、version属性,它实现了EventMessage接口PublishEvent// PublishEvent publishes events to all registered event handlersfunc (b *InternalEventBus) PublishEvent(event EventMessage) { if handlers, ok := b.eventHandlers[event.EventType()]; ok { for handler := range handlers { handler.Handle(event) } }}InternalEventBus的PublishEvent办法会遍历指定event.EventType()的handlers,挨个执行handler.Handle(event)办法小结go.cqrs的EventHandler定义了Handle办法;InternalEventBus的PublishEvent办法会遍历指定event.EventType()的handlers,挨个执行handler.Handle(event)办法。 ...

April 10, 2021 · 2 min · jiezi

关于golang:聊聊gocqrs的Dispatcher

序本文次要钻研一下go.cqrs的Dispatcher Dispatchertype Dispatcher interface { Dispatch(CommandMessage) error RegisterHandler(CommandHandler, ...interface{}) error}Dispatcher接口定义了Dispatch、RegisterHandler办法InMemoryDispatchertype InMemoryDispatcher struct { handlers map[string]CommandHandler}//NewInMemoryDispatcher constructs a new in memory dispatcherfunc NewInMemoryDispatcher() *InMemoryDispatcher { b := &InMemoryDispatcher{ handlers: make(map[string]CommandHandler), } return b}//Dispatch passes the CommandMessage on to all registered command handlers.func (b *InMemoryDispatcher) Dispatch(command CommandMessage) error { if handler, ok := b.handlers[command.CommandType()]; ok { return handler.Handle(command) } return fmt.Errorf("The command bus does not have a handler for commands of type: %s", command.CommandType())}//RegisterHandler registers a command handler for the command types specified by the//variadic commands parameter.func (b *InMemoryDispatcher) RegisterHandler(handler CommandHandler, commands ...interface{}) error { for _, command := range commands { typeName := typeOf(command) if _, ok := b.handlers[typeName]; ok { return fmt.Errorf("Duplicate command handler registration with command bus for command of type: %s", typeName) } b.handlers[typeName] = handler } return nil}InMemoryDispatcher定义了map[string]CommandHandler属性,其Dispatch办法依据command.CommandType()获取handler,而后执行handler.Handle(command);其RegisterHandler办法遍历commands,而后获取command的type,挨个注册到map[string]CommandHandler中CommandHandler// CommandHandler is the interface that all command handlers should implement.type CommandHandler interface { Handle(CommandMessage) error}// CommandMessage is the interface that a command message must implement.type CommandMessage interface { // AggregateID returns the ID of the Aggregate that the command relates to AggregateID() string // Headers returns the key value collection of headers for the command. Headers() map[string]interface{} // SetHeader sets the value of the header specified by the key SetHeader(string, interface{}) // Command returns the actual command which is the payload of the command message. Command() interface{} // CommandType returns a string descriptor of the command name CommandType() string}CommandHandler接口定义了Handle办法;CommandMessage接口定义了AggregateID、Headers、SetHeader、Command、CommandType办法小结go.cqrs的Dispatcher接口定义了Dispatch、RegisterHandler办法;InMemoryDispatcher定义了map[string]CommandHandler属性,其Dispatch办法依据command.CommandType()获取handler,而后执行handler.Handle(command);其RegisterHandler办法遍历commands,而后获取command的type,挨个注册到map[string]CommandHandler中。 ...

April 9, 2021 · 2 min · jiezi

关于golang:Nginx负载均衡算法加权轮询golang-实现

Nginx 负载平衡算法--加权轮询最近在看一些 getway 相干的材料,发现无关 Nginx 负载平衡的算法有点多,然而有点乱,所以整顿下。。。如有不对中央请指出。一,Nginx 负载平衡的轮询 (round-robin)在说加权轮询之前咱们先来简略的说一下轮询 1. nginx 中的配置 upstream cluster { server 192.168.0.14; server 192.168.0.15;}location / { proxy_set_header X-Real-IP $remote_addr; //返回实在IP proxy_pass http://cluster; //代理指向cluster }2. 简略介绍 轮询 作为负载平衡中较为根底的算法,他的实现不须要配置额定的参数。简略了解:配置文件中一共配置了 N 台服务器,轮询 算法会遍历服务的节点列表,并依照节点程序每轮抉择一台服务器解决申请,当所有节点遍历一遍后,从新开始 3. 特点 轮询 算法中咱们不难看出,每台服务器解决申请的数量根本持平,依照申请工夫逐个调配,因而只能实用于集群服务器性能相近的状况,平均分配让每台服务器承载量根本持平。然而如果集群服务器性能参差不齐,这样的算法会导致资源分配不合理,造成局部申请阻塞,局部服务器资源节约。为了解决上述问题,咱们将 轮询 算法降级了,引入了 加权轮询 算法,让集群中性能差别较大的服务器也能正当分配资源。达到资源尽量最大化正当利用 4. 实现 (这里应用golang模仿实现) type RoundRobinBalance struct { curIndex int rss []string}/** * @Author: yang * @Description:增加服务 * @Date: 2021/4/7 15:36 */func (r *RoundRobinBalance) Add (params ...string) error{ if len(params) == 0 { return errors.New("params len 1 at least") } addr := params[0] r.rss = append(r.rss, addr) return nil}/** * @Author: yang * @Description:轮询获取服务 * @Date: 2021/4/7 15:36 */func (r *RoundRobinBalance) Next () string { if len(r.rss) == 0 { return "" } lens := len(r.rss) if r.curIndex >= lens { r.curIndex = 0 } curAdd := r.rss[r.curIndex ] r.curIndex = (r.curIndex + 1) % lens return curAdd}5. 测试 ...

April 9, 2021 · 3 min · jiezi

关于golang:golang-的包管理

一、参考

April 9, 2021 · 1 min · jiezi

关于golang:聊聊gocqrs的DomainRepository

序本文次要钻研一下go.cqrs的DomainRepository DomainRepository// DomainRepository is the interface that all domain repositories should implement.type DomainRepository interface { //Loads an aggregate of the given type and ID Load(aggregateTypeName string, aggregateID string) (AggregateRoot, error) //Saves the aggregate. Save(aggregate AggregateRoot, expectedVersion *int) error}DomainRepository定义了Load、Save办法GetEventStoreCommonDomainRepo// GetEventStoreCommonDomainRepo is an implementation of the DomainRepository// that uses GetEventStore for persistencetype GetEventStoreCommonDomainRepo struct { eventStore *goes.Client eventBus EventBus streamNameDelegate StreamNamer aggregateFactory AggregateFactory eventFactory EventFactory}// Load will load all events from a stream and apply those events to an aggregate// of the type specified.//// The aggregate type and id will be passed to the configured StreamNamer to// get the stream name.func (r *GetEventStoreCommonDomainRepo) Load(aggregateType, id string) (AggregateRoot, error) { if r.aggregateFactory == nil { return nil, fmt.Errorf("The common domain repository has no Aggregate Factory.") } if r.streamNameDelegate == nil { return nil, fmt.Errorf("The common domain repository has no stream name delegate.") } if r.eventFactory == nil { return nil, fmt.Errorf("The common domain has no Event Factory.") } aggregate := r.aggregateFactory.GetAggregate(aggregateType, id) if aggregate == nil { return nil, fmt.Errorf("The repository has no aggregate factory registered for aggregate type: %s", aggregateType) } streamName, err := r.streamNameDelegate.GetStreamName(aggregateType, id) if err != nil { return nil, err } stream := r.eventStore.NewStreamReader(streamName) for stream.Next() { switch err := stream.Err().(type) { case nil: break case *url.Error, *goes.ErrTemporarilyUnavailable: return nil, &ErrRepositoryUnavailable{} case *goes.ErrNoMoreEvents: return aggregate, nil case *goes.ErrUnauthorized: return nil, &ErrUnauthorized{} case *goes.ErrNotFound: return nil, &ErrAggregateNotFound{AggregateType: aggregateType, AggregateID: id} default: return nil, &ErrUnexpected{Err: err} } event := r.eventFactory.GetEvent(stream.EventResponse().Event.EventType) //TODO: No test for meta meta := make(map[string]string) stream.Scan(event, &meta) if stream.Err() != nil { return nil, stream.Err() } em := NewEventMessage(id, event, Int(stream.EventResponse().Event.EventNumber)) for k, v := range meta { em.SetHeader(k, v) } aggregate.Apply(em, false) aggregate.IncrementVersion() } return aggregate, nil}// Save persists an aggregatefunc (r *GetEventStoreCommonDomainRepo) Save(aggregate AggregateRoot, expectedVersion *int) error { if r.streamNameDelegate == nil { return fmt.Errorf("The common domain repository has no stream name delagate.") } resultEvents := aggregate.GetChanges() streamName, err := r.streamNameDelegate.GetStreamName(typeOf(aggregate), aggregate.AggregateID()) if err != nil { return err } if len(resultEvents) > 0 { evs := make([]*goes.Event, len(resultEvents)) for k, v := range resultEvents { //TODO: There is no test for this code v.SetHeader("AggregateID", aggregate.AggregateID()) evs[k] = goes.NewEvent("", v.EventType(), v.Event(), v.GetHeaders()) } streamWriter := r.eventStore.NewStreamWriter(streamName) err := streamWriter.Append(expectedVersion, evs...) switch e := err.(type) { case nil: break case *goes.ErrConcurrencyViolation: return &ErrConcurrencyViolation{Aggregate: aggregate, ExpectedVersion: expectedVersion, StreamName: streamName} case *goes.ErrUnauthorized: return &ErrUnauthorized{} case *goes.ErrTemporarilyUnavailable: return &ErrRepositoryUnavailable{} default: return &ErrUnexpected{Err: e} } } aggregate.ClearChanges() for k, v := range resultEvents { if expectedVersion == nil { r.eventBus.PublishEvent(v) } else { em := NewEventMessage(v.AggregateID(), v.Event(), Int(*expectedVersion+k+1)) r.eventBus.PublishEvent(em) } } return nil}GetEventStoreCommonDomainRepo定义了eventStore、eventBus、streamNameDelegate、aggregateFactory、eventFactory属性,其Load办法先通过r.aggregateFactory.GetAggregate获取aggregate,再通过r.streamNameDelegate.GetStreamName(aggregateType, id)获取streamName,而后通过r.eventStore.NewStreamReader去遍历event,挨个执行aggregate.Apply(em, false)及aggregate.IncrementVersion();其Save办法先通过aggregate.GetChanges()获取resultEvents,再遍历resultEvents结构goes.Event,之后通过streamWriter.Append写入,而后执行aggregate.ClearChanges(),最初执行r.eventBus.PublishEvent小结go.cqrs的DomainRepository定义了Load、Save办法;GetEventStoreCommonDomainRepo实现了DomainRepository接口,其Load办法次要是读取event,而后挨个执行aggregate.Apply;其Save办法次要是将aggregate.GetChanges()转换为event,而后通过streamWriter.Append写入,而后执行aggregate.ClearChanges(),最初执行r.eventBus.PublishEvent。 ...

April 8, 2021 · 2 min · jiezi

关于golang:手撸golang-学ectd-手写raft协议13-小结

手撸golang 学ectd 手写raft协定13 小结 缘起最近浏览 [云原生分布式存储基石:etcd深刻解析] (杜军 , 2019.1)本系列笔记拟采纳golang练习之 raft分布式一致性算法分布式存储系统通常会通过保护多个副原本进行容错,以进步零碎的可用性。这就引出了分布式存储系统的外围问题——如何保障多个正本的一致性?Raft算法把问题分解成了四个子问题:1. 首领选举(leader election)、2. 日志复制(log replication)、3. 安全性(safety)4. 成员关系变动(membership changes)这几个子问题。源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接:https://my.oschina.net/ioly指标依据raft协定,实现高可用分布式强统一的kv存储小结累计投入业余时间约30工时,勉强写出一个可运行的"raft非残缺玩具实现"所得: 根本理解raft协定及其外延细节翻新应用”事件驱动+读写拆散“模式组织简单状态对象的逻辑编排已实现的个性: Raft状态机Leader,Candidate,Follower三种角色的转换Leader的心跳,选举,再选举Follower角色下,Proxy业务申请(kv操作)到Followerkv日志的两阶段(append+commit)流传和长久化,间接用boltdbkv数据的长久化,基于boltdb未实现的个性: 应用WAL机制写kv日志(高性能)kv日志两阶段流传的异步并发(高性能)Leader为每个Follower保护一个日志队列(异步并发+加重慢Follower的影响)思考: 尽管raft自身好了解,但落到代码,还是可能因为细节问题,导致产出是”未经实践验证的近似品“依照分层思维,划分三层来实现: 先实现一个高可用的网格虚拟机,确保输出网格的每条指令,是严格有序序,且仅执行一次的基于该网格虚拟机,设计一套raft dsl,满足一致性的要求基于该raft状态机,增加业务解决接口嗯,这么说该”网格虚拟机+网格DSL“是分布式计算的终极杀器了(raft系列 end)

April 8, 2021 · 1 min · jiezi

关于golang:聊聊gocqrs的AggregateRoot

序本文次要钻研一下go.cqrs的AggregateRoot AggregateRoot//AggregateRoot is the interface that all aggregates should implementtype AggregateRoot interface { AggregateID() string OriginalVersion() int CurrentVersion() int IncrementVersion() Apply(events EventMessage, isNew bool) TrackChange(EventMessage) GetChanges() []EventMessage ClearChanges()}AggregateRoot接口定义了AggregateID、OriginalVersion、CurrentVersion、IncrementVersion、Apply、TrackChange、GetChanges、ClearChanges办法AggregateBase// AggregateBase is a type that can be embedded in an AggregateRoot// implementation to handle common aggragate behaviour//// All required methods to implement an aggregate are here, to implement the// Aggregate root interface your aggregate will need to implement the Apply// method that will contain behaviour specific to your aggregate.type AggregateBase struct { id string version int changes []EventMessage}// NewAggregateBase contructs a new AggregateBase.func NewAggregateBase(id string) *AggregateBase { return &AggregateBase{ id: id, changes: []EventMessage{}, version: -1, }}// AggregateID returns the AggregateIDfunc (a *AggregateBase) AggregateID() string { return a.id}// OriginalVersion returns the version of the aggregate as it was when it was// instantiated or loaded from the repository.//// Importantly an aggregate with one event applied will be at version 0// this allows the aggregates to match the version in the eventstore where// the first event will be version 0.func (a *AggregateBase) OriginalVersion() int { return a.version}// CurrentVersion returns the version of the aggregate as it was when it was// instantiated or loaded from the repository.//// Importantly an aggregate with one event applied will be at version 0// this allows the aggregates to match the version in the eventstore where// the first event will be version 0.func (a *AggregateBase) CurrentVersion() int { return a.version + len(a.changes)}// IncrementVersion increments the aggregate version number by one.func (a *AggregateBase) IncrementVersion() { a.version++}// TrackChange stores the EventMessage in the changes collection.//// Changes are new, unpersisted events that have been applied to the aggregate.func (a *AggregateBase) TrackChange(event EventMessage) { a.changes = append(a.changes, event)}// GetChanges returns the collection of new unpersisted events that have// been applied to the aggregate.func (a *AggregateBase) GetChanges() []EventMessage { return a.changes}//ClearChanges removes all unpersisted events from the aggregate.func (a *AggregateBase) ClearChanges() { a.changes = []EventMessage{}}AggregateBase定义了id、version、changes属性;AggregateID办法返回id;OriginalVersion办法返回version;CurrentVersion返回version+len(a.changes);IncrementVersion递增version;TrackChange办法往changes减少event;GetChanges办法返回changes;ClearChanges会清空changesEventMessage// EventMessage is the interface that a command must implement.type EventMessage interface { // AggregateID returns the ID of the Aggregate that the event relates to AggregateID() string // GetHeaders returns the key value collection of headers for the event. // // Headers are metadata about the event that do not form part of the // actual event but are still required to be persisted alongside the event. GetHeaders() map[string]interface{} // SetHeader sets the value of the header specified by the key SetHeader(string, interface{}) // Returns the actual event which is the payload of the event message. Event() interface{} // EventType returns a string descriptor of the command name EventType() string // Version returns the version of the event Version() *int}EventMessage接口定义了AggregateID、GetHeaders、SetHeader、Event、EventType、Version办法EventDescriptor// EventDescriptor is an implementation of the event message interface.type EventDescriptor struct { id string event interface{} headers map[string]interface{} version *int}// NewEventMessage returns a new event descriptorfunc NewEventMessage(aggregateID string, event interface{}, version *int) *EventDescriptor { return &EventDescriptor{ id: aggregateID, event: event, headers: make(map[string]interface{}), version: version, }}// EventType returns the name of the event type as a string.func (c *EventDescriptor) EventType() string { return typeOf(c.event)}// AggregateID returns the ID of the Aggregate that the event relates to.func (c *EventDescriptor) AggregateID() string { return c.id}// GetHeaders returns the headers for the event.func (c *EventDescriptor) GetHeaders() map[string]interface{} { return c.headers}// SetHeader sets the value of the header specified by the keyfunc (c *EventDescriptor) SetHeader(key string, value interface{}) { c.headers[key] = value}// Event the event payload of the event messagefunc (c *EventDescriptor) Event() interface{} { return c.event}// Version returns the version of the eventfunc (c *EventDescriptor) Version() *int { return c.version}EventDescriptor定义了id、event、headers、version属性;其EventType返回typeOf(c.event);AggregateID返回id;GetHeaders返回headers;SetHeader会设置header;Event办法返回event;Version返回version小结go.cqrs的AggregateRoot接口定义了AggregateID、OriginalVersion、CurrentVersion、IncrementVersion、Apply、TrackChange、GetChanges、ClearChanges办法。 ...

April 7, 2021 · 3 min · jiezi

关于golang:手撸golang-学etcd-手写raft协议之12-单元测试

手撸golang 学etcd 手写raft协定之12 单元测试 缘起最近浏览 [云原生分布式存储基石:etcd深刻解析] (杜军 , 2019.1)本系列笔记拟采纳golang练习之 raft分布式一致性算法分布式存储系统通常会通过保护多个副原本进行容错,以进步零碎的可用性。这就引出了分布式存储系统的外围问题——如何保障多个正本的一致性?Raft算法把问题分解成了四个子问题:1. 首领选举(leader election)、2. 日志复制(log replication)、3. 安全性(safety)4. 成员关系变动(membership changes)这几个子问题。源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接:https://my.oschina.net/ioly/blog/5011356指标依据raft协定,实现高可用分布式强统一的kv存储子目标(Day 12)终于能够“点火”了,来到这里不容易 _ 增加大量诊断日志修复若干细节问题编写单元测试代码: 启动多个raft节点检测Leader选举是否胜利向节点1写入若干数据向节点2写入若干数据在节点3读取数据kill掉以后Leader节点,察看从新选举是否胜利单元测试tRaftKVServer_test.go,在本地启动四个raft节点进行功能性测试 package serverimport ( "learning/gooop/etcd/raft/debug" "learning/gooop/etcd/raft/logger" "learning/gooop/etcd/raft/rpc" "testing" "time" nrpc "net/rpc")func Test_RaftKVServer(t *testing.T) { fnAssertTrue := func(b bool, msg string) { if !b { t.Fatal(msg) } } logger.Exclude("RaftRPCServer.Ping") logger.Exclude("RaftRPCServer.Heartbeat") logger.Exclude("feLeaderHeartbeat") logger.Exclude(").Heartbeat") // start node 1 to 3 _ = new(tRaftKVServer).BeginServeTCP("./node-01") _ = new(tRaftKVServer).BeginServeTCP("./node-02") _ = new(tRaftKVServer).BeginServeTCP("./node-03") _ = new(tRaftKVServer).BeginServeTCP("./node-04") // wait for up time.Sleep(1 * time.Second) // tRaftLSMImplement(node-01,1).HandleStateChanged, state=2 fnAssertTrue(logger.Count("HandleStateChanged, state=3") == 1, "expecting leader node") t.Logf("passing electing, leader=%v", debug.LeaderNodeID) // put into node-1 c1,_ := nrpc.Dial("tcp", "localhost:3331") defer c1.Close() kcmd := new(rpc.KVCmd) kcmd.OPCode = rpc.KVPut kcmd.Key = []byte("key-01") kcmd.Content = []byte("content 01") kret := new(rpc.KVRet) err := c1.Call("KVStoreRPCServer.ExecuteKVCmd", kcmd, kret) fnAssertTrue(err == nil && kret.Code == rpc.KVOk, "expecting KVOk") t.Log("passing put into node-01") // put into node-2 c2,_ := nrpc.Dial("tcp", "localhost:3332") defer c2.Close() kcmd.Key = []byte("key-02") kcmd.Content = []byte("content 02") err = c2.Call("KVStoreRPCServer.ExecuteKVCmd", kcmd, kret) fnAssertTrue(err == nil && kret.Code == rpc.KVOk, "expecting KVOk") t.Log("passing put into node-02") // get from node-3 c3,_ := nrpc.Dial("tcp", "localhost:3333") defer c3.Close() kcmd.OPCode = rpc.KVGet kcmd.Key = []byte("key-02") kcmd.Content = nil kret.Content = nil kret.Key = nil err = c3.Call("KVStoreRPCServer.ExecuteKVCmd", kcmd, kret) fnAssertTrue(err == nil && kret.Code == rpc.KVOk, "expecting KVOk") fnAssertTrue(kret.Content != nil && string(kret.Content) == "content 02", "expecting content 02") t.Log("passing get from node-04") // kill leader node debug.KilledNodeID = debug.LeaderNodeID time.Sleep(2 * time.Second) fnAssertTrue(logger.Count("HandleStateChanged, state=3") == 2, "expecting reelecting leader node") t.Logf("passing reelecting, leader=%v", debug.LeaderNodeID) time.Sleep(2 * time.Second)}测试输入能够察看到5个passing,测试ok,从新选举的时延也在预期范畴内,约700ms ...

April 7, 2021 · 6 min · jiezi

关于golang:聊聊dkron的Scheduler

序本文次要钻研一下dkron的Scheduler Scheduler// Scheduler represents a dkron scheduler instance, it stores the cron engine// and the related parameters.type Scheduler struct { Cron *cron.Cron Started bool EntryJobMap sync.Map}// NewScheduler creates a new Scheduler instancefunc NewScheduler() *Scheduler { schedulerStarted.Set(0) return &Scheduler{ Cron: nil, Started: false, EntryJobMap: sync.Map{}, }}// Start the cron scheduler, adding its corresponding jobs and// executing them on time.func (s *Scheduler) Start(jobs []*Job, agent *Agent) error { s.Cron = cron.New(cron.WithParser(extcron.NewParser())) metrics.IncrCounter([]string{"scheduler", "start"}, 1) for _, job := range jobs { job.Agent = agent s.AddJob(job) } s.Cron.Start() s.Started = true schedulerStarted.Set(1) return nil}// Stop stops the scheduler effectively not running any job.func (s *Scheduler) Stop() { if s.Started { log.Debug("scheduler: Stopping scheduler") s.Cron.Stop() s.Started = false // Keep Cron exists and let the jobs which have been scheduled can continue to finish, // even the node's leadership will be revoked. // Ignore the running jobs and make s.Cron to nil may cause whole process crashed. //s.Cron = nil // expvars cronInspect.Do(func(kv expvar.KeyValue) { kv.Value = nil }) } schedulerStarted.Set(0)}// Restart the schedulerfunc (s *Scheduler) Restart(jobs []*Job, agent *Agent) { s.Stop() s.ClearCron() s.Start(jobs, agent)}// Clear cron separately, this can only be called when agent will be stop.func (s *Scheduler) ClearCron() { s.Cron = nil}Scheduler定义了Cron、Started、EntryJobMap属性;NewScheduler办法创立默认的Scheduler;Start办法遍历jobs,挨个设置job.Agent,而后增加到Scheduler中,之后执行Scheduler.Cron.Start();Stop办法执行Scheduler.Cron.Stop();Restart办法执行Stop、ClearCron、Start办法;ClearCron设置Cron为nilAddJob// AddJob Adds a job to the cron schedulerfunc (s *Scheduler) AddJob(job *Job) error { // Check if the job is already set and remove it if exists if _, ok := s.EntryJobMap.Load(job.Name); ok { s.RemoveJob(job) } if job.Disabled || job.ParentJob != "" { return nil } log.WithFields(logrus.Fields{ "job": job.Name, }).Debug("scheduler: Adding job to cron") // If Timezone is set on the job, and not explicitly in its schedule, // AND its not a descriptor (that don't support timezones), add the // timezone to the schedule so robfig/cron knows about it. schedule := job.Schedule if job.Timezone != "" && !strings.HasPrefix(schedule, "@") && !strings.HasPrefix(schedule, "TZ=") && !strings.HasPrefix(schedule, "CRON_TZ=") { schedule = "CRON_TZ=" + job.Timezone + " " + schedule } id, err := s.Cron.AddJob(schedule, job) if err != nil { return err } s.EntryJobMap.Store(job.Name, id) cronInspect.Set(job.Name, job) metrics.IncrCounterWithLabels([]string{"scheduler", "job_add"}, 1, []metrics.Label{{Name: "job", Value: job.Name}}) return nil}AddJob办法先移除EntryJobMap中的同名job,而后执行Cron.AddJob(schedule, job),最初存储到EntryJobMapRemoveJob// RemoveJob removes a job from the cron schedulerfunc (s *Scheduler) RemoveJob(job *Job) { log.WithFields(logrus.Fields{ "job": job.Name, }).Debug("scheduler: Removing job from cron") if v, ok := s.EntryJobMap.Load(job.Name); ok { s.Cron.Remove(v.(cron.EntryID)) s.EntryJobMap.Delete(job.Name) cronInspect.Delete(job.Name) metrics.IncrCounterWithLabels([]string{"scheduler", "job_delete"}, 1, []metrics.Label{{Name: "job", Value: job.Name}}) }}RemoveJob办法先从EntryJobMap移除同名job,而后执行cronInspect.Delete(job.Name)小结dkron的Scheduler定义了Cron、Started、EntryJobMap属性;NewScheduler办法创立默认的Scheduler;它提供了Start、Stop、Restart、ClearCron、AddJob、RemoveJob办法。 ...

April 6, 2021 · 2 min · jiezi

关于算法-数据结构:算法树

type Tree struct { data int left *Tree right *Tree}1. 镜像二叉树func mirrorTree(root *Tree) *Tree { if root == nil { return nil } root.left, root.right = root.right, root.left mirrorTree(root.left) mirrorTree(root.right)}func mirrorTree(root *Tree) *Tree { if root == nil { return nil } mirrorTree(root.left) mirrorTree(root.right) root.left, root.right = right, root.left return root}2. 二叉树最大深度func maxLength(root *Tree) int { if root == nil { return 0 } reutnr max(maxLenth(root.left,maxLentgh(root.right)), right) +1 }3. 合并二叉树func merge(r1, r2 *Tree) *Tree { if r1 == nil { return r2 } if r2 == nil { return r1 } r1.val += r2.val r1.Left = mergeTrees(r1.Left, r2.Left) r1.Right = mergeTrees(r1.Right, r2.Right) return r1}

April 6, 2021 · 1 min · jiezi

关于golang:手撸golang-etcd-raft协议之11

手撸golang etcd raft协定之11 缘起最近浏览 [云原生分布式存储基石:etcd深刻解析] (杜军 , 2019.1)本系列笔记拟采纳golang练习之 raft分布式一致性算法分布式存储系统通常会通过保护多个副原本进行容错,以进步零碎的可用性。这就引出了分布式存储系统的外围问题——如何保障多个正本的一致性?Raft算法把问题分解成了四个子问题:1. 首领选举(leader election)、2. 日志复制(log replication)、3. 安全性(safety)4. 成员关系变动(membership changes)这几个子问题。源码gitee地址:https://gitee.com/ioly/learning.gooop指标依据raft协定,实现高可用分布式强统一的kv存储子目标(Day 11)尽管Leader State还有细节没解决完,但应该能启动并提供根本服务了增加外围性能,为首次“点火”做筹备: config/tRaftConfig:从本地json文件读取集群节点配置,提供IRaftConfig/IRaftNodeConfig的实现lsm/tRaftLSMImplement: 提供对顶层接口IRaftLSM的实现,将“配置/kv存储/节点通信”三大块粘合起来server/IRaftKVServer:server启动器接口server/tRaftKVServer: server启动器的实现,监听raft rpc和kv rpcconfig/tRaftConfig.go从本地json文件读取集群节点配置,提供IRaftConfig/IRaftNodeConfig的实现 package configimport ( "encoding/json" "os")type tRaftConfig struct { ID string Nodes []*tRaftNodeConfig}type tRaftNodeConfig struct { ID string Endpoint string}func (me *tRaftConfig) GetID() string { return me.ID}func (me *tRaftConfig) GetNodes() []IRaftNodeConfig { a := make([]IRaftNodeConfig, len(me.Nodes)) for i,it := range me.Nodes { a[i] = it } return a}func (me *tRaftNodeConfig) GetID() string { return me.ID}func (me *tRaftNodeConfig) GetEndpoint() string { return me.Endpoint}func LoadJSONFile(file string) IRaftConfig { data, err := os.ReadFile(file) if err != nil { panic(err) } c := new(tRaftConfig) err = json.Unmarshal(data, c) if err != nil { panic(err) } return c}lsm/tRaftLSMImplement.go提供对顶层接口IRaftLSM的实现,将“配置/kv存储/节点通信”三大块粘合起来,并增加诊断日志。 ...

April 6, 2021 · 4 min · jiezi

关于golang:基于信号的抢占式调度实现原理

// sigPreempt is the signal used for non-cooperative preemption.//// There's no good way to choose this signal, but there are some// heuristics://// 1. It should be a signal that's passed-through by debuggers by// default. On Linux, this is SIGALRM, SIGURG, SIGCHLD, SIGIO,// SIGVTALRM, SIGPROF, and SIGWINCH, plus some glibc-internal signals.//// 2. It shouldn't be used internally by libc in mixed Go/C binaries// because libc may assume it's the only thing that can handle these// signals. For example SIGCANCEL or SIGSETXID.//// 3. It should be a signal that can happen spuriously without// consequences. For example, SIGALRM is a bad choice because the// signal handler can't tell if it was caused by the real process// alarm or not (arguably this means the signal is broken, but I// digress). SIGUSR1 and SIGUSR2 are also bad because those are often// used in meaningful ways by applications.//// 4. We need to deal with platforms without real-time signals (like// macOS), so those are out.//// We use SIGURG because it meets all of these criteria, is extremely// unlikely to be used by an application for its "real" meaning (both// because out-of-band data is basically unused and because SIGURG// doesn't report which socket has the condition, making it pretty// useless), and even if it is, the application has to be ready for// spurious SIGURG. SIGIO wouldn't be a bad choice either, but is more// likely to be used for real.const sigPreempt = _SIGURG

April 6, 2021 · 2 min · jiezi

关于golang:GolangReverseProxy源码分析

Golang-ReverseProxy源码剖析最近在学golang做点笔记golang 能够通过 ReverseProxy 实现了服务器代理相干的性能 ReverseProxy 位于 net.http.httputil 包下 实现性能:反对自定义批改响应内容反对连接池反对错误信息自定义解决反对 websocket 服务反对自定义负载平衡策略反对 https 代理反对 url 重写源码剖析首先咱们通过 ReverseProxy 实现一个简略的反向代理 var ( openAddr = "127.0.0.1:2002" proxyAddr = "http://127.0.0.1:2003/base")/** * @Author: yang * @Description:通过 Reverse_Proxy 实现简略的代理 * @Date: 2021/3/31 14:19 */func main() { u, err := url.Parse(proxyAddr) if err != nil { log.Println(err) } proxy := httputil.NewSingleHostReverseProxy(u) log.Println("Server is starting :" + openAddr) log.Fatalln(http.ListenAndServe(openAddr, proxy))}咱们监听本机 2002 端口,将申请代理到 http://127.0.0.1:2003/base 中 其实咱们能够发现代理的实现是通过 httputil.NewSingleHostReverseProxy 返回对应的 handler; 咱们能够进去看看这个办法 ...

April 6, 2021 · 3 min · jiezi

关于golang:noescape详解

待续...待续...待续...待续...

April 6, 2021 · 1 min · jiezi

关于golang:极速精简-Go-版-Logstash

前言明天来介绍 go-zero 生态的另一个组件 go-stash。这是一个 logstash 的 Go 语言代替版,咱们用 go-stash 相比原先的 logstash 节俭了2/3的服务器资源。如果你在用 logstash,无妨试试,也能够看看基于 go-zero 实现这样的工具是如许的容易,这个工具作者仅用了两天工夫。 整体架构先从它的配置中,咱们来看看设计架构。 Clusters: - Input: Kafka: # Kafka 配置 --> 联动 go-queue Filters: # filter action - Action: drop - Action: remove_field - Action: transfer Output: ElasticSearch: # es 配置 {host, index}看配置名:kafka 是数据输入端,es 是数据输出端,filter 形象了数据处理过程。 对,整个 go-stash 就是如 config 配置中显示的,所见即所得。 启动从 stash.go 的启动流程大抵分为几个局部。因为能够配置多个 cluster,那从一个 cluster 剖析: 建设与 es 的连贯【传入 es 配置】构建 filter processors【es 前置处理器,做数据过滤以及解决,能够设置多个】欠缺对 es 中 索引配置,启动 handle ,同时将 filter 退出handle【解决输入输出】连贯上游的 kafka,将下面创立的 handle 传入,实现 kafka 和 es 之间的数据生产和数据写入MessageHandler在下面架构图中,两头的 filter 只是从 config 中看到,其实更具体是 MessageHandler 的一部分,做数据过滤和转换,上面来说说这块。 ...

April 6, 2021 · 2 min · jiezi

关于golang:聊聊dkron的fsm

序本文次要钻研一下dkron的fsm MessageType// MessageType is the type to encode FSM commands.type MessageType uint8const ( // SetJobType is the command used to store a job in the store. SetJobType MessageType = iota // DeleteJobType is the command used to delete a Job from the store. DeleteJobType // SetExecutionType is the command used to store an Execution to the store. SetExecutionType // DeleteExecutionsType is the command used to delete executions from the store. DeleteExecutionsType // ExecutionDoneType is the command to perform the logic needed once an exeuction // is done. ExecutionDoneType)MessageType能够分为SetJobType、DeleteJobType、SetExecutionType、DeleteExecutionsType、ExecutionDoneTypedkronFSMtype dkronFSM struct { store Storage // proAppliers holds the set of pro only LogAppliers proAppliers LogAppliers}// NewFSM is used to construct a new FSM with a blank statefunc newFSM(store Storage, logAppliers LogAppliers) *dkronFSM { return &dkronFSM{ store: store, proAppliers: logAppliers, }}// Apply applies a Raft log entry to the key-value store.func (d *dkronFSM) Apply(l *raft.Log) interface{} { buf := l.Data msgType := MessageType(buf[0]) log.WithField("command", msgType).Debug("fsm: received command") switch msgType { case SetJobType: return d.applySetJob(buf[1:]) case DeleteJobType: return d.applyDeleteJob(buf[1:]) case ExecutionDoneType: return d.applyExecutionDone(buf[1:]) case SetExecutionType: return d.applySetExecution(buf[1:]) } // Check enterprise only message types. if applier, ok := d.proAppliers[msgType]; ok { return applier(buf[1:], l.Index) } return nil}func (d *dkronFSM) applySetJob(buf []byte) interface{} { var pj dkronpb.Job if err := proto.Unmarshal(buf, &pj); err != nil { return err } job := NewJobFromProto(&pj) if err := d.store.SetJob(job, false); err != nil { return err } return nil}func (d *dkronFSM) applyDeleteJob(buf []byte) interface{} { var djr dkronpb.DeleteJobRequest if err := proto.Unmarshal(buf, &djr); err != nil { return err } job, err := d.store.DeleteJob(djr.GetJobName()) if err != nil { return err } return job}func (d *dkronFSM) applyExecutionDone(buf []byte) interface{} { var execDoneReq dkronpb.ExecutionDoneRequest if err := proto.Unmarshal(buf, &execDoneReq); err != nil { return err } execution := NewExecutionFromProto(execDoneReq.Execution) log.WithField("execution", execution.Key()). WithField("output", string(execution.Output)). Debug("fsm: Setting execution") _, err := d.store.SetExecutionDone(execution) return err}func (d *dkronFSM) applySetExecution(buf []byte) interface{} { var pbex dkronpb.Execution if err := proto.Unmarshal(buf, &pbex); err != nil { return err } execution := NewExecutionFromProto(&pbex) key, err := d.store.SetExecution(execution) if err != nil { return err } return key}// Snapshot returns a snapshot of the key-value store. We wrap// the things we need in dkronSnapshot and then send that over to Persist.// Persist encodes the needed data from dkronSnapshot and transport it to// Restore where the necessary data is replicated into the finite state machine.// This allows the consensus algorithm to truncate the replicated log.func (d *dkronFSM) Snapshot() (raft.FSMSnapshot, error) { return &dkronSnapshot{store: d.store}, nil}// Restore stores the key-value store to a previous state.func (d *dkronFSM) Restore(r io.ReadCloser) error { defer r.Close() return d.store.Restore(r)}// LogApplier is the definition of a function that can apply a Raft logtype LogApplier func(buf []byte, index uint64) interface{}dkronFSM定义了store、proAppliers属性;Apply办法将raft的log保留到KV存储中,具体分不同msgType做不同解决;最初依据msgType查找LogAppliers小结dkron的FSM依据不同msgType做不同解决,具体有applySetJob、applyDeleteJob、applyExecutionDone、applySetExecution办法。 ...

April 5, 2021 · 3 min · jiezi

关于golang:手撸golang-etcd-raft协议之910

手撸golang etcd raft协定之9,10 缘起最近浏览 [云原生分布式存储基石:etcd深刻解析] (杜军 , 2019.1)本系列笔记拟采纳golang练习之 raft分布式一致性算法分布式存储系统通常会通过保护多个副原本进行容错,以进步零碎的可用性。这就引出了分布式存储系统的外围问题——如何保障多个正本的一致性?Raft算法把问题分解成了四个子问题:1. 首领选举(leader election)、2. 日志复制(log replication)、3. 安全性(safety)4. 成员关系变动(membership changes)这几个子问题。源码gitee地址:https://gitee.com/ioly/learning.gooop指标依据raft协定,实现高可用分布式强统一的kv存储子目标(Day 10)增加put/get/del kv键值对的rpc接口持续欠缺Leader状态的raft协定响应设计rpc/IKVStoreRPC: kv操作的rpc接口store/IKVStore: kv操作的长久化接口stoer/ILogStore: 从IKVStore继承,以反对kv长久化lsm/IRaftState: 继承rpc.IKVStoreRPC接口,以反对kv操作lsm/tLeaderState: 初步实现Leader状态的raft协定解决,事件驱动的逻辑编排,读写拆散的字段治理。rpc/IKVStoreRPC.gokv操作的rpc接口 package rpctype IKVStoreRPC interface { ExecuteKVCmd(cmd *KVCmd, ret *KVRet) error}type KVCmd struct { OPCode KVOPCode Key []byte Content []byte}type KVOPCode intconst ( KVGet KVOPCode = iota KVPut KVOPCode = iota KVDel KVOPCode = iota)type KVRet struct { Code KVRetCode Key []byte Content []byte}type KVRetCode intconst ( KVOk KVRetCode = iota KVKeyNotFound KVRetCode = iota KVInternalError KVRetCode = iota)store/IKVStore.gokv操作的长久化接口 ...

April 5, 2021 · 6 min · jiezi

关于golang:聊聊machinery的TaskProcessor

序本文次要钻研一下machinery的TaskProcessor TaskProcessor// TaskProcessor - can process a delivered task// This will probably always be a worker instancetype TaskProcessor interface { Process(signature *tasks.Signature) error CustomQueue() string PreConsumeHandler() bool}TaskProcessor接口定义了Process、CustomQueue、PreConsumeHandler办法Worker// Worker represents a single worker processtype Worker struct { server *Server ConsumerTag string Concurrency int Queue string errorHandler func(err error) preTaskHandler func(*tasks.Signature) postTaskHandler func(*tasks.Signature) preConsumeHandler func(*Worker) bool}// CustomQueue returns Custom Queue of the running worker processfunc (worker *Worker) CustomQueue() string { return worker.Queue}// Process handles received tasks and triggers success/error callbacksfunc (worker *Worker) Process(signature *tasks.Signature) error { // If the task is not registered with this worker, do not continue // but only return nil as we do not want to restart the worker process if !worker.server.IsTaskRegistered(signature.Name) { return nil } taskFunc, err := worker.server.GetRegisteredTask(signature.Name) if err != nil { return nil } // Update task state to RECEIVED if err = worker.server.GetBackend().SetStateReceived(signature); err != nil { return fmt.Errorf("Set state to 'received' for task %s returned error: %s", signature.UUID, err) } // Prepare task for processing task, err := tasks.NewWithSignature(taskFunc, signature) // if this failed, it means the task is malformed, probably has invalid // signature, go directly to task failed without checking whether to retry if err != nil { worker.taskFailed(signature, err) return err } // try to extract trace span from headers and add it to the function context // so it can be used inside the function if it has context.Context as the first // argument. Start a new span if it isn't found. taskSpan := tracing.StartSpanFromHeaders(signature.Headers, signature.Name) tracing.AnnotateSpanWithSignatureInfo(taskSpan, signature) task.Context = opentracing.ContextWithSpan(task.Context, taskSpan) // Update task state to STARTED if err = worker.server.GetBackend().SetStateStarted(signature); err != nil { return fmt.Errorf("Set state to 'started' for task %s returned error: %s", signature.UUID, err) } //Run handler before the task is called if worker.preTaskHandler != nil { worker.preTaskHandler(signature) } //Defer run handler for the end of the task if worker.postTaskHandler != nil { defer worker.postTaskHandler(signature) } // Call the task results, err := task.Call() if err != nil { // If a tasks.ErrRetryTaskLater was returned from the task, // retry the task after specified duration retriableErr, ok := interface{}(err).(tasks.ErrRetryTaskLater) if ok { return worker.retryTaskIn(signature, retriableErr.RetryIn()) } // Otherwise, execute default retry logic based on signature.RetryCount // and signature.RetryTimeout values if signature.RetryCount > 0 { return worker.taskRetry(signature) } return worker.taskFailed(signature, err) } return worker.taskSucceeded(signature, results)}//SetPreConsumeHandler sets a custom handler for the end of a jobfunc (worker *Worker) SetPreConsumeHandler(handler func(*Worker) bool) { worker.preConsumeHandler = handler}Worker实现了TaskProcessor接口,其Process办法先通过worker.server.GetRegisteredTask获取taskFunc,而后通过signature更新state为RECEIVED,之后设置为STARTED,之后执行task.Call(),最初依据后果更新task为failed或者success小结machinery的TaskProcessor接口定义了Process、CustomQueue、PreConsumeHandler办法。Worker实现了TaskProcessor接口,其Process办法先通过worker.server.GetRegisteredTask获取taskFunc,而后通过signature更新state为RECEIVED,之后设置为STARTED,之后执行task.Call(),最初依据后果更新task为failed或者success。 ...

April 4, 2021 · 2 min · jiezi

关于elasticsearch入门教程:elasticsearch入门篇

原文先发于:https://mp.weixin.qq.com/s/0cTaqiYSJ1Pr9jBEVYfKHw一、elasticsearch背地乏味的故事许多年前,一个刚结婚的名叫 Shay Banon 的就业开发者,跟着他的妻子去了伦敦,他的妻子在那里学习厨师。 在寻找一个赚钱的工作的时候,为了给他的妻子做一个食谱搜索引擎,他开始应用 Lucene 的一个晚期版本。间接应用 Lucene 是很难的,因而 Shay 开始做一个形象层,Java 开发者应用它能够很简略的给他们的程序增加搜寻性能。 他公布了他的第一个开源我的项目 Compass。起初 Shay 取得了一份工作,次要是高性能,分布式环境下的内存数据网格。这个对于高性能,实时,分布式搜索引擎的需要尤为突出, 他决定重写 Compass,把它变为一个独立的服务并取名 Elasticsearch。 第一个公开版本在2010年2月公布,从此以后,Elasticsearch 曾经成为了 Github 上最沉闷的我的项目之一,他领有超过300名 contributors(目前736名 contributors )。 一家公司曾经开始围绕 Elasticsearch 提供商业服务,并开发新的个性,然而,Elasticsearch 将永远开源并对所有人可用。 据说,Shay 的妻子还在等着她的食谱搜索引擎… 二、elasticsearch简介Elasticsearch 是一个开源的搜索引擎,建设在一个全文搜索引擎库 Apache Lucene™ 根底之上。 Lucene 能够说是当下最先进、高性能、全功能的搜索引擎库--无论是开源还是公有。然而 Lucene 仅仅只是一个库。为了充分发挥其性能,你须要应用 Java 并将 Lucene 间接集成到应用程序中。 更蹩脚的是,您可能须要取得信息检索学位能力理解其工作原理。Lucene 十分 简单。Elasticsearch 也是应用 Java 编写的,它的外部应用 Lucene 做索引与搜寻,然而它的目标是使全文检索变得简略, 通过暗藏 Lucene 的复杂性,取而代之的提供一套简略统一的 RESTful API。然而,Elasticsearch 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎。 它能够被上面这样精确的形容: 一个分布式的实时文档存储,每个字段 能够被索引与搜寻一个分布式实时剖析搜索引擎能胜任上百个服务节点的扩大,并反对 PB 级别的结构化或者非结构化数据2.1、elasticsearch性能分布式的搜索引擎es可作为一个分布式的搜索引擎,例如百度,淘宝的商品搜寻,个别web零碎的站内搜索,es都是不错的技术选型。 数据分析引擎es在搜寻的根底上提供了丰盛的API反对个性化的搜寻和数据分析性能,比方电商网站,咱们能够查问最近几天的热销商品等。 对海量数据进行近实时的解决es是一个分布式的搜索引擎,es通过集群和外部分片能够将海量数据扩散到多台服务器上进行存储和检索,大大提高了其可伸缩性和容灾能力。所谓近实时是一个绝对的概念,个别的如果相应速度能达到秒级别,咱们就称为其实近实时的。es的近实时包含两个方面:其一写入的数据在1s后就能够进行检索。其二其检索和剖析响应速度能够达到秒级别。 2.2、elasticsearch的特点分布式es是一个分布式的搜索引擎,能够很好的进行数据的容灾迁徙,动静扩容,负载平衡等分布式个性。 海量数据es能解决PB级别的数据,因为es是一个分布式的架构,反对动静扩容,所以对于海量数据的解决和存储都不再是问题。 ...

April 4, 2021 · 1 min · jiezi

关于golang:聊聊machinery的Lock

序本文次要钻研一下machinery的Lock Locktype Lock interface { //Acquire the lock with retry //key: the name of the lock, //value: at the nanosecond timestamp that lock needs to be released automatically LockWithRetries(key string, value int64) error //Acquire the lock with once //key: the name of the lock, //value: at the nanosecond timestamp that lock needs to be released automatically Lock(key string, value int64) error}Lock接口定义了LockWithRetries、Lock办法redis/Lockvar ( ErrRedisLockFailed = errors.New("redis lock: failed to acquire lock"))type Lock struct { rclient redis.UniversalClient retries int interval time.Duration}func New(cnf *config.Config, addrs []string, db, retries int) Lock { if retries <= 0 { return Lock{} } lock := Lock{retries: retries} var password string parts := strings.Split(addrs[0], "@") if len(parts) == 2 { password = parts[0] addrs[0] = parts[1] } ropt := &redis.UniversalOptions{ Addrs: addrs, DB: db, Password: password, } if cnf.Redis != nil { ropt.MasterName = cnf.Redis.MasterName } lock.rclient = redis.NewUniversalClient(ropt) return lock}func (r Lock) LockWithRetries(key string, unixTsToExpireNs int64) error { for i := 0; i <= r.retries; i++ { err := r.Lock(key, unixTsToExpireNs) if err == nil { //胜利拿到锁,返回 return nil } time.Sleep(r.interval) } return ErrRedisLockFailed}func (r Lock) Lock(key string, unixTsToExpireNs int64) error { now := time.Now().UnixNano() expiration := time.Duration(unixTsToExpireNs + 1 - now) ctx := r.rclient.Context() success, err := r.rclient.SetNX(ctx, key, unixTsToExpireNs, expiration).Result() if err != nil { return err } if !success { v, err := r.rclient.Get(ctx, key).Result() if err != nil { return err } timeout, err := strconv.Atoi(v) if err != nil { return err } if timeout != 0 && now > int64(timeout) { newTimeout, err := r.rclient.GetSet(ctx, key, unixTsToExpireNs).Result() if err != nil { return err } curTimeout, err := strconv.Atoi(newTimeout) if err != nil { return err } if now > int64(curTimeout) { // success to acquire lock with get set // set the expiration of redis key r.rclient.Expire(ctx, key, expiration) return nil } return ErrRedisLockFailed } return ErrRedisLockFailed } return nil}Lock定义了rclient、retries、interval属性;New办法依据cnf、addrs、db、retries创立lock;LockWithRetries办法依据retries次数来尝试r.Lock(key, unixTsToExpireNs),都没有胜利则返回ErrRedisLockFailed;Lock办法执行r.rclient.SetNX,如果不胜利则判断是否过期,过期的话执行执行r.rclient.GetSet,若的确过期了则执行r.rclient.Expire(ctx, key, expiration)更新新的过期工夫小结machinery的Lock接口定义了LockWithRetries、Lock办法;基于redis的实现则通过r.rclient.SetNX、r.rclient.GetSet、r.rclient.Expire实现。 ...

April 3, 2021 · 2 min · jiezi

关于golang:手撸golang-etcd-raft协议之8

手撸golang etcd raft协定之8 缘起最近浏览 [云原生分布式存储基石:etcd深刻解析] (杜军 , 2019.1)本系列笔记拟采纳golang练习之gitee: https://gitee.com/ioly/learning.gooop raft分布式一致性算法分布式存储系统通常会通过保护多个副原本进行容错,以进步零碎的可用性。这就引出了分布式存储系统的外围问题——如何保障多个正本的一致性?Raft算法把问题分解成了四个子问题:1. 首领选举(leader election)、2. 日志复制(log replication)、3. 安全性(safety)4. 成员关系变动(membership changes)这几个子问题。指标依据raft协定,实现高可用分布式强统一的kv存储子目标(Day 8)简化rpc连贯管理器tRaftClientService的实现剥离IRaftLSM的外部反对接口到iRaftStateContext接口实现Candidate状态的解决逻辑设计IRaftLSM:raft无限状态机接口iRaftStateContext:提供状态模式下的上下文反对tCandidateState:Candidate(候选人)状态的实现。基于事件驱动的逻辑编排,基于读写拆散的字段治理。tRaftClient:治理到指定raft节点的rpc连贯tRaftClientService:治理以后节点到其余raft节点的rpc连贯IRaftLSM.goraft无限状态机接口 package lsmimport ( "learning/gooop/etcd/raft/rpc")// IRaftLSM raft无限状态自动机type IRaftLSM interface { rpc.IRaftRPC iRaftStateContext State() IRaftState}iRaftStateContext.go提供状态模式下的上下文反对 package lsmimport ( "learning/gooop/etcd/raft/config" "learning/gooop/etcd/raft/rpc/client" "learning/gooop/etcd/raft/store")type iRaftStateContext interface { Config() config.IRaftConfig Store() store.ILogStore HandleStateChanged(state IRaftState) RaftClientService() client.IRaftClientService}tCandidateState.goCandidate(候选人)状态的实现。基于事件驱动的逻辑编排,基于读写拆散的字段治理。 package lsmimport ( "learning/gooop/etcd/raft/roles" "learning/gooop/etcd/raft/rpc" "learning/gooop/etcd/raft/timeout" "sync" "time")// tCandidateState presents a candidate nodetype tCandidateState struct { tEventDrivenModel context iRaftStateContext mInitOnce sync.Once mStartOnce sync.Once // update: init / ceAskingForVote mTerm int64 // update: ceInit / ceAskingForVote / ceVoteToCandidate mVotedTerm int64 // update: ceInit / ceAskingForVote / ceVoteToCandidate mVotedCandidateID string // update: ceInit / ceAskingForVote / ceVoteToCandidate mVotedTimestamp int64 // update: ceInit / ceAskingForVote / ceReceiveTicket / ceDisposing mTicketCount map[string]bool mTicketMutex *sync.Mutex // update: ceInit / ceDisposing mDisposedFlag bool}// trigger: init()// args: emptyconst ceInit = "candidate.init"// trigger: Start()// args: emptyconst ceStart = "candidate.Start"// trigger: whenAskingForVoteThenWatchElectionTimeout()// args: emptyconst ceElectionTimeout = "candidate.ElectionTimeout"// trigger: Heartbeat() / AppendLog() / CommitLog()// args: emptyconst ceLeaderAnnounced = "candidate.LeaderAnnounced"// trigger: RequestVote()// args: *rpc.RequestVoteCmdconst ceVoteToCandidate = "candidate.VoteToCandidate"// trigger: whenLeaderAnnouncedThenSwitchToFollower()// args: emptyconst ceDisposing = "candidate.Disposing"// trigger: beginAskForVote()// args: emptyconst ceAskingForVote = "candidate.AskingForVote"// trigger: handleRequestVoteOK()// args: emptyconst ceReceiveTicket = "candidate.ReceiveTicket"// trigger: whenReceiveTicketThenCheckTicketCount// args: emptyconst ceWinningTheVote = "candidate.ceWinningTheVote"func newCandidateState(ctx iRaftStateContext, term int64) IRaftState { it := new(tCandidateState) it.init(ctx, term) return it}func (me *tCandidateState) init(ctx iRaftStateContext, term int64) { me.mInitOnce.Do(func() { me.context = ctx me.mTerm = term me.initEventHandlers() me.raise(ceInit) })}func (me *tCandidateState) initEventHandlers() { // write only logic me.hookEventsForTerm() me.hookEventsForVotedTerm() me.hookEventsForVotedCandidateID() me.hookEventsForVotedTimestamp() me.hookEventsForTicketCount() me.hookEventsForDisposedFlag() // read only logic me.hook(ceStart, me.whenStartThenAskForVote) me.hook(ceAskingForVote, me.whenAskingForVoteThenWatchElectionTimeout) me.hook(ceReceiveTicket, me.whenReceiveTicketThenCheckTicketCount) me.hook(ceElectionTimeout, me.whenElectionTimeoutThenAskForVoteAgain) me.hook(ceWinningTheVote, me.whenWinningTheVoteThenSwitchToLeader) me.hook(ceLeaderAnnounced, me.whenLeaderAnnouncedThenSwitchToFollower)}// hookEventsForTerm maintains field: mTerm// update: ceElectionTimeoutfunc (me *tCandidateState) hookEventsForTerm() { me.hook(ceAskingForVote, func(e string, args ...interface{}) { me.mTerm++ })}// hookEventsForVotedTerm maintains field: mVotedTerm// update: ceInit / ceElectionTimeout / ceVoteToCandidatefunc (me *tCandidateState) hookEventsForVotedTerm() { me.hook(ceInit, func(e string, args ...interface{}) { // initially, vote to itself me.mVotedTerm = me.mTerm }) me.hook(ceAskingForVote, func(e string, args ...interface{}) { // when timeout, reset to itself me.mVotedTerm = me.mTerm }) me.hook(ceVoteToCandidate, func(e string, args ...interface{}) { // after vote to candidate cmd := args[0].(*rpc.RequestVoteCmd) me.mVotedTerm = cmd.Term })}// hookEventsForVotedCandidateID maintains field: mVotedCandidateID// update: ceInit / ceElectionTimeout / ceVoteToCandidatefunc (me *tCandidateState) hookEventsForVotedCandidateID() { me.hook(ceInit, func(e string, args ...interface{}) { // initially, vote to itself me.mVotedCandidateID = me.context.Config().ID() }) me.hook(ceAskingForVote, func(e string, args ...interface{}) { me.mVotedCandidateID = me.context.Config().ID() }) me.hook(ceVoteToCandidate, func(e string, args ...interface{}) { // after vote to candidate cmd := args[0].(*rpc.RequestVoteCmd) me.mVotedCandidateID = cmd.CandidateID })}func (me *tCandidateState) hookEventsForVotedTimestamp() { me.hook(ceInit, func(e string, args ...interface{}) { // initially, vote to itself me.mVotedTimestamp = time.Now().UnixNano() }) me.hook(ceAskingForVote, func(e string, args ...interface{}) { me.mVotedTimestamp = time.Now().UnixNano() }) me.hook(ceVoteToCandidate, func(e string, args ...interface{}) { // after vote to candidate me.mVotedTimestamp = time.Now().UnixNano() })}func (me *tCandidateState) hookEventsForTicketCount() { me.hook(ceInit, func(e string, args ...interface{}) { me.mTicketMutex = new(sync.Mutex) me.mTicketCount = make(map[string]bool, 0) me.mTicketCount[me.context.Config().ID()] = true }) me.hook(ceAskingForVote, func(e string, args ...interface{}) { me.mTicketMutex.Lock() defer me.mTicketMutex.Unlock() me.mTicketCount = make(map[string]bool, 0) me.mTicketCount[me.context.Config().ID()] = true }) me.hook(ceReceiveTicket, func(e string, args ...interface{}) { peerID := args[0].(string) me.mTicketMutex.Lock() defer me.mTicketMutex.Unlock() me.mTicketCount[peerID] = true }) me.hook(ceDisposing, func(e string, args ...interface{}) { me.mTicketMutex.Lock() defer me.mTicketMutex.Unlock() me.mTicketCount = make(map[string]bool, 0) })}func (me *tCandidateState) hookEventsForDisposedFlag() { me.hook(ceInit, func(e string, args ...interface{}) { me.mDisposedFlag = false }) me.hook(ceDisposing, func(e string, args ...interface{}) { me.mDisposedFlag = true })}func (me *tCandidateState) Heartbeat(cmd *rpc.HeartbeatCmd, ret *rpc.HeartbeatRet) error { // check term if cmd.Term <= me.mTerm { // bad leader ret.Code = rpc.HBTermMismatch return nil } // new leader me.raise(ceLeaderAnnounced) // return ok ret.Code = rpc.HBOk return nil}func (me *tCandidateState) AppendLog(cmd *rpc.AppendLogCmd, ret *rpc.AppendLogRet) error { // check term if cmd.Term <= me.mTerm { // bad leader ret.Code = rpc.ALTermMismatch return nil } // new leader me.raise(ceLeaderAnnounced) // ignore and return ret.Code = rpc.ALInternalError return nil}func (me *tCandidateState) CommitLog(cmd *rpc.CommitLogCmd, ret *rpc.CommitLogRet) error { // ignore and return ret.Code = rpc.CLInternalError return nil}func (me *tCandidateState) RequestVote(cmd *rpc.RequestVoteCmd, ret *rpc.RequestVoteRet) error { // check voted term if cmd.Term < me.mVotedTerm { ret.Code = rpc.RVTermMismatch return nil } if cmd.Term == me.mVotedTerm { if me.mVotedCandidateID != "" && me.mVotedCandidateID != cmd.CandidateID { // already vote another ret.Code = rpc.RVVotedAnother return nil } else { // already voted ret.Code = rpc.RVOk return nil } } if cmd.Term > me.mVotedTerm { // new term, check log if cmd.LastLogIndex >= me.context.Store().LastCommittedIndex() { // good log me.raise(ceVoteToCandidate, cmd) ret.Code = rpc.RVOk } else { // bad log ret.Code = rpc.RVLogMismatch } return nil } // should not reaches here ret.Code = rpc.RVTermMismatch return nil}func (me *tCandidateState) Role() roles.RaftRole { return roles.Candidate}func (me *tCandidateState) Start() { me.mStartOnce.Do(func() { me.raise(feStart) })}func (me *tCandidateState) whenLeaderAnnouncedThenSwitchToFollower(_ string, _ ...interface{}) { me.raise(ceDisposing) me.context.HandleStateChanged(newFollowerState(me.context))}func (me *tCandidateState) whenElectionTimeoutThenAskForVoteAgain(_ string, _ ...interface{}) { me.beginAskForVote()}func (me *tCandidateState) whenStartThenAskForVote(_ string, _ ...interface{}) { me.beginAskForVote()}func (me *tCandidateState) beginAskForVote() { // raise ceAskingForVote me.raise(ceAskingForVote) // for each node, call node.RequestVote cmd := new(rpc.RequestVoteCmd) cmd.CandidateID = me.context.Config().ID() cmd.Term = me.mTerm store := me.context.Store() cmd.LastLogIndex = store.LastCommittedIndex() cmd.LastLogTerm = store.LastCommittedTerm() term := me.mTerm for _,node := range me.context.Config().Nodes() { if node.ID() == me.context.Config().ID() { continue } peerID := node.ID() go func() { ret := new(rpc.RequestVoteRet) err := me.context.RaftClientService().Using(peerID, func(client rpc.IRaftRPC) error { return client.RequestVote(cmd, ret) }) if err == nil && ret.Code == rpc.RVOk { me.handleRequestVoteOK(peerID, term) } }() }}func (me *tCandidateState) whenAskingForVoteThenWatchElectionTimeout(_ string, _ ...interface{}) { term := me.mTerm go func() { time.Sleep(timeout.RandElectionTimeout()) if me.mDisposedFlag || me.mTerm != term { return } tc := me.getTicketCount() if tc < len(me.context.Config().Nodes())/2 + 1 { me.raise(ceElectionTimeout) } }()}func (me *tCandidateState) handleRequestVoteOK(peerID string, term int64) { if me.mDisposedFlag || me.mTerm != term { return } me.raise(ceReceiveTicket, peerID)}func (me *tCandidateState) whenReceiveTicketThenCheckTicketCount(_ string, _ ...interface{}) { tc := me.getTicketCount() if tc >= len(me.context.Config().Nodes())/2 + 1 { // win the vote me.raise(ceWinningTheVote) }}func (me *tCandidateState) getTicketCount() int { me.mTicketMutex.Lock() defer me.mTicketMutex.Unlock() return len(me.mTicketCount)}func (me *tCandidateState) whenWinningTheVoteThenSwitchToLeader(_ string, _ ...interface{}) { me.raise(ceDisposing) me.context.HandleStateChanged(newLeaderState(me.context, me.mTerm))}tRaftClient.go治理到指定raft节点的rpc连贯 ...

April 3, 2021 · 6 min · jiezi

关于golang:二进制与-Go-的原子操作

二进制与 Go 的原子操作前置浏览: C语言中文网-汇编语言基本概念简介-补码及进制转换《GO 并发编程实战》—— 原子操作二进制相干根底概念有符号二进制整数有负数和正数。在 x86 处理器中,MSB 示意的是符号位:0 示意负数,1 示意正数。下图展现了 8 位的负数和正数: 概念总结: 反码、补码是二进制的一种表现形式;在计算机内所有数值底层都用补码示意,无论正负数(十进制);如果一串二进制值须要视为数值则须要将其视为补码;反码是十进制转二进制计算的一个过程即对一个十进制取补码的过程,个别用在正数转换规则上;反码能够通过二进制值按位取反失去(所有二进制位都取反);负数(十进制)的补码是其二进制自身,正数(十进制)的补码是十进制正数的绝对值求补码后取反码加一;示意负数的补码能够间接转成十进制,示意正数的补码想要转回十进制步骤如下: 对示意正数的补码取反码加一失去正数的十进制绝对值补码;再将正数的十进制绝对值补码转成十进制失去正数的十进制绝对值;最初加上符号位;无论是负数加负数(十进制加法)还是负数/正数加正数(十进制减法)都能够用补码加补码示意;一个值的负数的补码与其正数的补码相加等于 0;反码反码能够通过二进制值按位取反失去(所有二进制位都取反)负数的反码示例: 十进制数值补码反码00000 00001111 111110000 00011111 111020000 00101111 110130000 00111111 110040000 01001111 1011正数的反码示例: 十进制数值补码反码-00000 00001111 1111-11111 11110000 0000-21111 11100000 0001-31111 11010000 0010补码(十进制转二进制)在计算机内所有数值底层都用补码示意,无论正负数(十进制)十进制数值补码00000 000010000 000120000 001030000 0011-00000 0000-11111 1111-21111 1110-31111 1101正数补码计算过程示例: 十进制数值绝对值绝对值补码绝对值补码取反绝对值补码取反加一正确补码十进制数值-000000 00001111 11111111 1111+ 1—————1,0000 00000000 0000-0-110000 00011111 11101111 1110+ 1————— 1111 11111111 1111-1-220000 00101111 11011111 1101+ 1————— 1111 11101111 1110-2-330000 00111111 11001111 1100+ 1————— 1111 11011111 1101-3-440000 01001111 10111111 1011+ 1————— 1111 11001111 1100-4-550000 01011111 10101111 1010+ 1————— 1111 10111111 1011-5补码(二进制转十进制)示意负数的补码能够间接转成十进制,示意正数的补码想要转回十进制步骤如下: ...

April 3, 2021 · 4 min · jiezi

关于golang:手撸golang-etcd-raft协议之7

手撸golang etcd raft协定之7 缘起最近浏览 [云原生分布式存储基石:etcd深刻解析] (杜军 , 2019.1)本系列笔记拟采纳golang练习之gitee: https://gitee.com/ioly/learning.gooop raft分布式一致性算法分布式存储系统通常会通过保护多个副原本进行容错,以进步零碎的可用性。这就引出了分布式存储系统的外围问题——如何保障多个正本的一致性?Raft算法把问题分解成了四个子问题:1. 首领选举(leader election)、2. 日志复制(log replication)、3. 安全性(safety)4. 成员关系变动(membership changes)这几个子问题。指标依据raft协定,实现高可用分布式强统一的kv存储子目标(Day 7)实现各raft节点之间的rpc通信 定义IRaftClientService服务,治理所有节点的tcp长连贯定义IRaftClient接口,封装节点间的rpc调用 基于状态模式,辨别已连贯状态和已断开状态基于事件驱动的逻辑编排基于读写拆散的字段治理设计model/IEventDirvenModel: 事件驱动的逻辑编排基类IRaftClientService:治理所有的节点间rpc连贯IRaftClient:治理以后节点与某个节点间的rpc连贯iClientState:基于状态模式的rpc连贯状态接口iStateContext:状态模式下的连贯状态上下文接口tRaftClient:IRaftClient接口的具体实现,并实现iStateContext接口。tConnectedState: 治理已连贯状态的rpc连贯 定时Ping以检测连贯状态基于事件驱动的逻辑编排基于读写拆散的字段治理tBrokenState:治理已断开状态的rpc连贯 定时Dial以尝试重连贯基于事件驱动的逻辑编排基于读写拆散的字段治理model/IEventDirvenModel.go事件驱动的逻辑编排基类 package modeltype TEventHandleFunc func(e string, args ...interface{})type IEventDrivenModel interface { hook(e string, handleFunc TEventHandleFunc) raise(e string, args ...interface{})}type TEventDrivenModel struct { items map[string][]TEventHandleFunc}func (me *TEventDrivenModel) Hook(e string, handler TEventHandleFunc) { arr, ok := me.items[e] if ok { me.items[e] = append(arr, handler) } else { me.items[e] = []TEventHandleFunc{handler} }}func (me *TEventDrivenModel) Raise(e string, args ...interface{}) { if handlers, ok := me.items[e]; ok { for _, it := range handlers { it(e, args...) } }}IRaftClientService.go治理所有的节点间rpc连贯 ...

April 2, 2021 · 5 min · jiezi

关于golang:golang调度学习调度流程-四-mstart

不论是rt0_go还是go语法(如果在有idle的状况下)最终都会调用mstart mstart// mstart is the entry-point for new Ms.//// This must not split the stack because we may not even have stack// bounds set up yet.//// May run during STW (because it doesn't have a P yet), so write// barriers are not allowed.////go:nosplit//go:nowritebarrierrecfunc mstart() { _g_ := getg() osStack := _g_.stack.lo == 0 if osStack { // Initialize stack bounds from system stack. // Cgo may have left stack size in stack.hi. // minit may update the stack bounds. // // Note: these bounds may not be very accurate. // We set hi to &size, but there are things above // it. The 1024 is supposed to compensate this, // but is somewhat arbitrary. size := _g_.stack.hi if size == 0 { size = 8192 * sys.StackGuardMultiplier } _g_.stack.hi = uintptr(noescape(unsafe.Pointer(&size))) _g_.stack.lo = _g_.stack.hi - size + 1024 } // Initialize stack guard so that we can start calling regular // Go code. _g_.stackguard0 = _g_.stack.lo + _StackGuard // This is the g0, so we can also call go:systemstack // functions, which check stackguard1. _g_.stackguard1 = _g_.stackguard0 mstart1() // Exit this thread. if mStackIsSystemAllocated() { // Windows, Solaris, illumos, Darwin, AIX and Plan 9 always system-allocate // the stack, but put it in _g_.stack before mstart, // so the logic above hasn't set osStack yet. osStack = true } mexit(osStack)}

April 2, 2021 · 2 min · jiezi

关于golang:golang调度学习调度流程-三-wakep-startm

本文次要讲wakep startmwakep在newproc可能会调用(main起来之后)会调用wakepstartm在wakep 中调用mstart 在rt0_go中调用,执行main零碎线程m 在golang中有三种零碎线程:主线程:golang程序启动加载的时候就运行在主线程上,代码中由一个全局的m0示意运行sysmon的线程普通用户线程,用来与p绑定,运行g中的工作的线程,主线程和运行sysmon都是单实例,独自一个线程。而用户线程会有很多事例,他会依据调度器的需要新建,休眠和唤醒。 wakep startm// Tries to add one more P to execute G's.// Called when a G is made runnable (newproc, ready).// 增加一个闲置的p来执行gfunc wakep() { if atomic.Load(&sched.npidle) == 0 { // 没有限度的g, 返回 return } // be conservative about spinning threads if atomic.Load(&sched.nmspinning) != 0 || !atomic.Cas(&sched.nmspinning, 0, 1) { // return } startm(nil, true)}// Schedules some M to run the p (creates an M if necessary).// If p==nil, tries to get an idle P, if no idle P's does nothing.// May run with m.p==nil, so write barriers are not allowed.// If spinning is set, the caller has incremented nmspinning and startm will// either decrement nmspinning or set m.spinning in the newly started M.//// Callers passing a non-nil P must call from a non-preemptible context. See// comment on acquirem below.//// Must not have write barriers because this may be called without a P.// 调度一些M去运行P// 如果p不存在则从缓存获取P,没有P就返回// 如果spinning是true,那个相应的startm须要减去nmspinning// 如果调用者调用含有非空p,那么Callers就不能被抢占//go:nowritebarrierrecfunc startm(_p_ *p, spinning bool) { // Disable preemption. // // Every owned P must have an owner that will eventually stop it in the // event of a GC stop request. startm takes transient ownership of a P // (either from argument or pidleget below) and transfers ownership to // a started M, which will be responsible for performing the stop. // 每个领有的P必须具备一个所有者,该所有者将在GC申请终止的状况下最终将其进行。 // startm获得P的长期所有权(从上面的参数或pidleget中获取),并将所有权转移给已启动的M,该M将负责执行进行。 // // Preemption must be disabled during this transient ownership, // otherwise the P this is running on may enter GC stop while still // holding the transient P, leaving that P in limbo and deadlocking the // STW. // 在此短暂所有权期间,必须禁用抢占,否则正在运行的P可能会在依然放弃该暂态P的同时进入GC进行, // 从而使该P处于混乱状态并使STW死锁。 // Callers passing a non-nil P must already be in non-preemptible // context, otherwise such preemption could occur on function entry to // startm. Callers passing a nil P may be preemptible, so we must // disable preemption before acquiring a P from pidleget below. // 传递非nil P的调用者必须曾经在不可抢占的上下文中,否则这种抢占可能产生在向进入startm的函数时。 // 传递nil P的调用者可能是可抢占的,因而在从上面的pidleget获取P之前,咱们必须先禁用抢占。 mp := acquirem() // 禁止抢占 lock(&sched.lock) if _p_ == nil { _p_ = pidleget() if _p_ == nil { // 没有闲暇的p了,只能回去了 unlock(&sched.lock) if spinning { // The caller incremented nmspinning, but there are no idle Ps, // so it's okay to just undo the increment and give up. if int32(atomic.Xadd(&sched.nmspinning, -1)) < 0 { throw("startm: negative nmspinning") } } releasem(mp) // 能够抢占了 return } } nmp := mget() // 获取一个全局闲暇的m if nmp == nil { // 没有闲暇的m了 // No M is available, we must drop sched.lock and call newm. // However, we already own a P to assign to the M. // // Once sched.lock is released, another G (e.g., in a syscall), // could find no idle P while checkdead finds a runnable G but // no running M's because this new M hasn't started yet, thus // throwing in an apparent deadlock. // // Avoid this situation by pre-allocating the ID for the new M, // thus marking it as 'running' before we drop sched.lock. This // new M will eventually run the scheduler to execute any // queued G's. id := mReserveID() unlock(&sched.lock) var fn func() if spinning { // The caller incremented nmspinning, so set m.spinning in the new M. fn = mspinning } newm(fn, _p_, id) // 简略新建一个m,就能够回去了 // Ownership transfer of _p_ committed by start in newm. // Preemption is now safe. releasem(mp) // 开释以后g的m,能够被抢占了 return } unlock(&sched.lock) if nmp.spinning { throw("startm: m is spinning") } if nmp.nextp != 0 { throw("startm: m has p") } if spinning && !runqempty(_p_) { throw("startm: p has runnable gs") } // The caller incremented nmspinning, so set m.spinning in the new M. nmp.spinning = spinning //标记该M是否在自旋 nmp.nextp.set(_p_) // 暂存P notewakeup(&nmp.park) // 唤醒M // Ownership transfer of _p_ committed by wakeup. Preemption is now // safe. releasem(mp)}startm次要实现工作: ...

April 2, 2021 · 7 min · jiezi

关于golang:go-micro-v2-相关使用

go-micro v2 看下 api 模式服务怎么注册到网关(即通过 http 申请拜访)须要先下载 micro 的 example 相干代码:https://github.com/microhq/ex... 验证 api 性能门路地位:micro\examples\api\api\api.go 先执行 micro 命令micro api --handler=api 启动 api.go 服务go run api.go 而后浏览器拜访:http://localhost:8080/example...能够失去响应: {"message":"got your request john"}mdns 改成 etcd默认 micro v2 是应用 mdns,而且 micro api --handler=api 不反对 consul!api.go 代码 main 办法 etcd 相干配置: // etcd 配置 service := micro.NewService( micro.Name("go.micro.api.example"), micro.Registry(etcd.NewRegistry( registry.Addrs("127.0.0.1:2379"))), ) 启动 api 服务: micro --registry=etcd --registry_address=127.0.0.1:2379 api --handler=api 启动 web 服务: micro --registry=etcd --registry_address=127.0.0.1:2379 web 换成 etcd 之后的服务,无奈间接通过 micro list services 进行查看!须要指定 etcd: ...

April 2, 2021 · 1 min · jiezi

关于golang:聊聊eventhorizon的EventBus

序本文次要钻研一下eventhorizon的EventBus EventBuseventhorizon/eventbus.go type EventBus interface { EventHandler // AddHandler adds a handler for an event. Returns an error if either the // matcher or handler is nil, the handler is already added or there was some // other problem adding the handler (for networked handlers for example). AddHandler(context.Context, EventMatcher, EventHandler) error // Errors returns an error channel where async handling errors are sent. Errors() <-chan EventBusError // Wait wait for all handlers to be cancelled by their context. Wait()}type EventHandler interface { // HandlerType is the type of the handler. HandlerType() EventHandlerType // HandleEvent handles an event. HandleEvent(context.Context, Event) error}type EventMatcher interface { // Match returns true if the matcher matches an event. Match(Event) bool}EventBus接口内嵌了EventHandler接口,定义了AddHandler、Errors、Wait办法EventBuseventhorizon/eventbus/local/eventbus.go ...

April 1, 2021 · 2 min · jiezi

关于golang:golang调度学习调度流程-二

前序上文讲到rt0_go的runtime·schedinit(SB) TEXT runtime·rt0_go<ABIInternal>(SB),NOSPLIT,$0 // 略, 查看 golang调度学习-调度流程 (一) // create a new goroutine to start program MOVQ $runtime·mainPC(SB), AX // entry, 就是 $runtime·main PUSHQ AX // newproc 的第二个参数 PUSHQ $0 // arg size的第一个参数 CALL runtime·newproc(SB) // 调用 runtime·newproc($0, $runtime·mainPC(SB)) POPQ AX POPQ AX // start this M CALL runtime·mstart(SB) CALL runtime·abort(SB) // mstart should never return RET // Prevent dead-code elimination of debugCallV1, which is // intended to be called by debuggers. MOVQ $runtime·debugCallV1<ABIInternal>(SB), AX RETnewproc创立一个新的g运行带siz字节参数的fn, 并且把它放到g.m.p的待运行队列在编写程序中,应用 go func() {}来创立一个goroutine(g),这条语句会被编译器翻译成函数 newproc()。 ...

April 1, 2021 · 6 min · jiezi

关于golang:golang项目结构模块拆分

一、参考如何写出优雅的 Golang 代码 二、目录构造2.1 pkg寄存本我的项目中,能够被内部利用间接调用的代码库 即个别/pkg中寄存的是私有库 2.2 internal公有代码寄存在internal中, 2.3 src不倡议应用 src寄存我的项目, (1) go语言的我的项目默认都会在 $GOPATH/src目录下,如果以后的我的项目中还有src目录,则有 $GOPATH/src/github.com/test/project/src/code.go 这样的目录,看起来十分奇怪 (2) src目录是大多数 Java使用者的开发教训 2.4 平铺(1) 对于框架或者库,将代码平铺在我的项目根目录下很失常, 例如: 如果没有平铺,想要引入一个应用 pkg目录构造的框架时候,须要应用 github.com/test/project/pkg/somepkg 如果将所有代码平铺到根目录,则援用门路为 github.com/test/project/pkg/somepkg (2) 对于一个go服务中,应用平铺形式不是十分适合 2.5 cmd寄存所有的可执行文件 该目录下的每一个子目录,都蕴含了可执行文件 不应该将过多的代码放入,cmd中 2.6 api寄存以后我的项目,对外提供的所有api接口文件 api目录下可能蕴含有二级目录,例如: api/protobuf-spec api/thrift-spec api/http-spec 2.7 Makefile目录 scripts中寄存一些我的项目运行的脚本 通过 Makefile 调用这些脚本 三、模块拆分3.1 按层拆分(java等)web框架的常见架构形式为 MVC 分层,行将服务中的不同组件分为 Model, View, Control 三层 上面是一个常见的python django我的项目构造 按层拆分的益处是: (1) 强调了按层划分职责 (2) 扁平的命名空间 (3) 单体服务的场景 3.2 按职责拆分(go我的项目)divide-by-responsibility

April 1, 2021 · 1 min · jiezi

关于golang:golang调度学习调度流程

golang 1.16.2 am64以下就将会具体介绍golang的调度流程,不便浏览,将会省略局部无关代码。 rt0_go咱们从rt0_go开始讲 // Defined as ABIInternal since it does not use the stack-based Go ABI (and// in addition there are no calls to this entry point from Go code).TEXT runtime·rt0_go<ABIInternal>(SB),NOSPLIT,$0 // ... 略初始化 args // create istack out of the given (operating system) stack. // _cgo_init may update stackguard. MOVQ $runtime·g0(SB), DI // DI = &runtime.g0 LEAQ (-64*1024+104)(SP), BX // BX = SP - 64*1024 + 104 MOVQ BX, g_stackguard0(DI) // runtime.g0.stackguard0 = BX MOVQ BX, g_stackguard1(DI) // runtime.g0.stackguard1 = BX MOVQ BX, (g_stack+stack_lo)(DI) // runtime.g0.stack.stack.lo = BX MOVQ SP, (g_stack+stack_hi)(DI) // runtime.g0.stack.stack.hi = SP // ... 略 CPU信息 ok: // set the per-goroutine and per-mach "registers" get_tls(BX) // BX = &g LEAQ runtime·g0(SB), CX // CX = &runtime.g0 MOVQ CX, g(BX) // &g = &runtime.g0, 切换以后g LEAQ runtime·m0(SB), AX // AX = &runtime.m0 // save m->g0 = g0 MOVQ CX, m_g0(AX) // m0.g0 = g // save m0 to g0->m MOVQ AX, g_m(CX) // g.m = m0 CLD // convention is flat标记 D is always left cleared CALL runtime·check(SB) // 做一些类型检查和调度无关 MOVL 16(SP), AX // copy argc MOVL AX, 0(SP) MOVQ 24(SP), AX // copy argv MOVQ AX, 8(SP) CALL runtime·args(SB) // 初始化 args CALL runtime·osinit(SB) // 初始化ncpu和physPageSize CALL runtime·schedinit(SB) // 初始化调度信息上面马上介绍 // create a new goroutine to start program MOVQ $runtime·mainPC(SB), AX // entry, 就是 $runtime·main PUSHQ AX // newproc 的第二个参数 PUSHQ $0 // arg size的第一个参数 CALL runtime·newproc(SB) // 调用 runtime·newproc($0, $runtime·mainPC(SB)) POPQ AX POPQ AX // start this M CALL runtime·mstart(SB) CALL runtime·abort(SB) // mstart should never return RET // Prevent dead-code elimination of debugCallV1, which is // intended to be called by debuggers. MOVQ $runtime·debugCallV1<ABIInternal>(SB), AX RET // mainPC is a function value for runtime.main, to be passed to newproc.// The reference to runtime.main is made via ABIInternal, since the// actual function (not the ABI0 wrapper) is needed by newproc.DATA runtime·mainPC+0(SB)/8,$runtime·main<ABIInternal>(SB)GLOBL runtime·mainPC(SB),RODATA,$8schedinit调度器的初始化从 schedinit()函数开始,将会设置m最大个数(maxmcount)及p最大个数(GOMAXPROCS)等 ...

April 1, 2021 · 7 min · jiezi

关于golang:手撸golang-etcd-raft协议之6

手撸golang etcd raft协定之6 缘起最近浏览 [云原生分布式存储基石:etcd深刻解析] (杜军 , 2019.1)本系列笔记拟采纳golang练习之gitee: https://gitee.com/ioly/learning.gooop raft分布式一致性算法分布式存储系统通常会通过保护多个副原本进行容错,以进步零碎的可用性。这就引出了分布式存储系统的外围问题——如何保障多个正本的一致性?Raft算法把问题分解成了四个子问题:1. 首领选举(leader election)、2. 日志复制(log replication)、3. 安全性(safety)4. 成员关系变动(membership changes)这几个子问题。指标依据raft协定,实现高可用分布式强统一的kv存储子目标(Day 6)大幅重构,晋升代码的可了解/可管理性: 基于事件驱动的逻辑编排,重构Follower和Candidate状态下的实现将字段状态的治理,履行读写拆散。没看错,代码也是能够"读写拆散"的 ^_^ 设计random:为各种超时工夫增加随机性tFollowerState:基于事件驱动重构Follower状态的逻辑编排,各字段施行读写拆散治理tCandidateState:基于事件驱动重构Candidate状态的逻辑编排,各字段施行读写拆散治理random.go为各种超时工夫增加随机性 package lsmimport ( "math/rand" "time")// fnRandomizeInt64 returns int64 value from v to v*1.3func fnRandomizeInt64(v int64) int64 { return v + v * gRand.Int63n(30) / 100}// fnRandomizeDuration returns duration value from v to v*1.3func fnRandomizeDuration(v time.Duration) time.Duration { i := int64(v) return time.Duration(fnRandomizeInt64(i))}var gRand = rand.New(rand.NewSource(time.Now().UnixNano()))tFollowerState.go基于事件驱动重构Follower状态的逻辑编排,各字段施行读写拆散治理 package lsmimport ( "learning/gooop/etcd/raft/roles" "learning/gooop/etcd/raft/rpc" "learning/gooop/etcd/raft/timeout" "sync" "time")// tFollowerState presents a follower nodetype tFollowerState struct { tEventDrivenModel context IRaftLSM mInitOnce sync.Once mStartOnce sync.Once // update: feInit / feLeaderHeartbeat mTerm int64 // update: feInit / feLeaderHeartbeat mLeaderHeartbeatTimestamp int64 // update: feLeaderHeartbeat mLeaderID string // update: feCandidateRequestVote / feVoteToCandidate mLastVotedTerm int64 // update: feCandidateRequestVote / feVoteToCandidate mLastVotedCandidateID string // update: feCandidateRequestVote / feVoteToCandidate mLastVotedTimestamp int64 // update: feInit / feDisposing mDiseposedFlag bool}// trigger: init()// args: emptyconst feInit = "follower.init"// trigger: Start()// args: emptyconst feStart = "follower.Start"// trigger: Heartbeat()// args: rpc.HeartbeatCmdconst feLeaderHeartbeat = "follower.LeaderHeartbeat"// trigger: whenStartThenBeginWatchLeaderTimeout()// args: emptyconst feLeaderHeartbeatTimeout = "follower.LeaderHeartbeatTimeout"// trigger: RequestVote()// args: rpc.RequestVoteCmdconst feCandidateRequestVote = "candidate.RequestVote"// trigger: RequestVote()// args: rpc.RequestVoteCmdconst feVoteToCandidate = "follower.CandidateRequestVote"// trigger: whenLeaderHeartbeatTimeoutThenSwitchToCandidateStateconst feDisposing = "follower.Disposing"func newFollowerState(ctx IRaftLSM) IRaftState { it := new(tFollowerState) it.init(ctx) return it}func (me *tFollowerState) init(ctx IRaftLSM) { me.mInitOnce.Do(func() { me.context = ctx me.initEventHandlers() })}func (me *tFollowerState) initEventHandlers() { // write only logic me.hookEventsForTerm() me.hookEventsForLeaderHeartbeatTimestamp() me.hookEventsForLeaderID() me.hookEventsForLastVotedTerm() me.hookEventsForLastVotedCandicateID() me.hookEventsForLastVotedTimestamp() me.hookEventsForDisposedFlag() // read only logic me.hook(feStart, me.whenStartThenBeginWatchLeaderTimeout) me.hook(feLeaderHeartbeatTimeout, me.whenLeaderHeartbeatTimeoutThenSwitchToCandidateState)}// hookEventsForTerm maintains field: mTerm// update : feInit / feLeaderHeartbeatfunc (me *tFollowerState) hookEventsForTerm() { me.hook(feInit, func(e string, args ...interface{}) { me.mTerm = me.context.store().LastCommittedTerm() }) me.hook(feLeaderHeartbeat, func(e string, args ...interface{}) { cmd := args[0].(*rpc.HeartbeatCmd) me.mTerm = cmd.Term })}// hookEventsForLeaderHeartbeatClock maintains field: mLeaderHeartbeatClock// update : feLeaderHeartbeat / feLeaderHeartbeatTimeoutfunc (me *tFollowerState) hookEventsForLeaderHeartbeatTimestamp() { me.hook(feInit, func(e string, args ...interface{}) { me.mLeaderHeartbeatTimestamp = time.Now().UnixNano() }) me.hook(feLeaderHeartbeat, func(e string, args ...interface{}) { me.mLeaderHeartbeatTimestamp = time.Now().UnixNano() }) me.hook(feLeaderHeartbeatTimeout, func(e string, args ...interface{}) { me.mLeaderHeartbeatTimestamp = 0 })}// hookEventsForLeaderID maintains field: mLeaderID// update : feLeaderHeartbeat / feLeaderHeartbeatTimeoutfunc (me *tFollowerState) hookEventsForLeaderID() { me.hook(feLeaderHeartbeat, func(e string, args ...interface{}) { cmd := args[0].(*rpc.HeartbeatCmd) me.mLeaderID = cmd.LeaderID }) me.hook(feLeaderHeartbeatTimeout, func(e string, args ...interface{}) { me.mLeaderID = "" })}// hookEventsForLastVotedTerm maintains field: mLastVotedTerm// update : feCandidateRequestVote / feVoteToCandidatefunc (me *tFollowerState) hookEventsForLastVotedTerm() { me.hook(feCandidateRequestVote, func(e string, args ...interface{}) { // before voting, check whether last vote timeout now := time.Now().UnixNano() if time.Duration(now - me.mLastVotedTimestamp) * time.Nanosecond >= fnRandomizeDuration(timeout.ElectionTimeout) { // timeout, reset to empty me.mLastVotedTerm = 0 me.mLastVotedCandidateID = "" me.mLastVotedTimestamp = 0 } }) me.hook(feVoteToCandidate, func(e string, args ...interface{}) { cmd := args[0].(*rpc.RequestVoteCmd) me.mLastVotedTerm = cmd.Term })}// hookEventsForLastVotedCandicateID maintains field: mLastVotedCandidateID// update : feCandidateRequestVote / feVoteToCandidatefunc (me *tFollowerState) hookEventsForLastVotedCandicateID() { me.hook(feCandidateRequestVote, func(e string, args ...interface{}) { // before voting, check whether last vote timeout now := time.Now().UnixNano() if time.Duration(now - me.mLastVotedTimestamp) * time.Nanosecond >= fnRandomizeDuration(timeout.ElectionTimeout) { // timeout, reset to empty me.mLastVotedTerm = 0 me.mLastVotedCandidateID = "" me.mLastVotedTimestamp = 0 } }) me.hook(feVoteToCandidate, func(e string, args ...interface{}) { cmd := args[0].(*rpc.RequestVoteCmd) me.mLastVotedCandidateID = cmd.CandidateID })}// hookEventsForLastVotedTimestamp maintains field: mLastVotedTimestamp// update : feCandidateRequestVote / feVoteToCandidatefunc (me *tFollowerState) hookEventsForLastVotedTimestamp() { me.hook(feCandidateRequestVote, func(e string, args ...interface{}) { // before voting, check whether last vote timeout now := time.Now().UnixNano() if time.Duration(now - me.mLastVotedTimestamp) * time.Nanosecond >= fnRandomizeDuration(timeout.ElectionTimeout) { // timeout, reset to empty me.mLastVotedTerm = 0 me.mLastVotedCandidateID = "" me.mLastVotedTimestamp = 0 } }) me.hook(feVoteToCandidate, func(e string, args ...interface{}) { me.mLastVotedTimestamp = time.Now().UnixNano() })}// hookEventsForDisposedFlag maintains field: mDisposedFlag// update: feInit / feDisposingfunc (me *tFollowerState) hookEventsForDisposedFlag() { me.hook(feInit, func(e string, args ...interface{}) { me.mDiseposedFlag = false }) me.hook(feDisposing, func(e string, args ...interface{}) { me.mDiseposedFlag = true })}func (me *tFollowerState) Start() { me.mStartOnce.Do(func() { me.raise(feStart) })}func (me *tFollowerState) whenStartThenBeginWatchLeaderTimeout(e string, args ...interface{}) { go func() { iCheckingTimeoutInterval := fnRandomizeDuration(timeout.HeartbeatTimeout / 3) for range time.Tick(iCheckingTimeoutInterval) { if me.mDiseposedFlag { return } now := time.Now().UnixNano() iHeartbeatTimeoutNanos := fnRandomizeInt64(int64(timeout.HeartbeatTimeout / time.Nanosecond)) if now - me.mLeaderHeartbeatTimestamp >= iHeartbeatTimeoutNanos { me.raise(feLeaderHeartbeatTimeout) return } } }()}func (me *tFollowerState) whenLeaderHeartbeatTimeoutThenSwitchToCandidateState(_ string, args ...interface{}) { me.raise(feDisposing) me.context.handleStateChanged(newCandidateState(me.context, me.mTerm + 1))}func (me *tFollowerState) Role() roles.RaftRole { return roles.Follower}// Heartbeat leader to followerfunc (me *tFollowerState) Heartbeat(cmd *rpc.HeartbeatCmd, ret *rpc.HeartbeatRet) error { // check term if cmd.Term < me.mTerm { // invalid leader ret.Code = rpc.HBTermMismatch ret.Term = me.mTerm return nil } // raise LeaderHeartbeat me.raise(feLeaderHeartbeat, cmd) // return ret.Code = rpc.HBOk return nil}// AppendLog leader to followerfunc (me *tFollowerState) AppendLog(cmd *rpc.AppendLogCmd, ret *rpc.AppendLogRet) error { ret.Term = me.mTerm if cmd.Term < me.mTerm { // invalid leader ret.Code = rpc.ALTermMismatch return nil } store := me.context.store() entry := cmd.Entry // check log: expecting appending action follows previous committing action if entry.PrevIndex != store.LastCommittedIndex() || entry.PrevTerm != store.LastCommittedTerm() { // check log e, log := store.GetLog(entry.Index) if e != nil { ret.Code = rpc.ALInternalError return nil } if log == nil || log.PrevIndex != entry.PrevIndex || log.PrevTerm != entry.PrevTerm { // bad log ret.Code = rpc.ALIndexMismatch ret.PrevLogIndex = store.LastCommittedIndex() ret.PrevLogTerm = store.LastCommittedTerm() return nil } // good log, but old, just ignore it ret.Code = rpc.ALOk return nil } // good log e := store.Append(entry) if e != nil { ret.Code = rpc.ALInternalError return nil } else { ret.Code = rpc.ALOk return nil }}// CommitLog leader to followerfunc (me *tFollowerState) CommitLog(cmd *rpc.CommitLogCmd, ret *rpc.CommitLogRet) error { store := me.context.store() if cmd.Index != store.LastAppendedIndex() || cmd.Term != store.LastAppendedTerm() { // bad index ret.Code = rpc.CLLogNotFound return nil } e := store.Commit(cmd.Index) if e != nil { ret.Code = rpc.CLInternalError return nil } ret.Code = rpc.CLOk return nil}// RequestVote candidate to followerfunc (me *tFollowerState) RequestVote(cmd *rpc.RequestVoteCmd, ret *rpc.RequestVoteRet) error { // before voting me.raise(feCandidateRequestVote, cmd) // check term if cmd.Term <= me.mTerm { ret.Term = me.mTerm ret.Code = rpc.RVTermMismatch return nil } // check if already voted another if me.mLastVotedTerm >= cmd.Term && me.mLastVotedCandidateID != "" && me.mLastVotedCandidateID != cmd.CandidateID { ret.Code = rpc.RVVotedAnother return nil } // check log index if cmd.LastLogIndex < me.context.store().LastCommittedIndex() { ret.Code = rpc.RVLogMismatch return nil } // vote ok me.raise(feVoteToCandidate, cmd) ret.Term = cmd.Term ret.Code = rpc.RVOk return nil}tCandidateState.go基于事件驱动重构Candidate状态的逻辑编排,各字段施行读写拆散治理 ...

April 1, 2021 · 7 min · jiezi

关于golang:gomicro-安装Windows-10

在学习应用 go-micro 的时候,遇到了很多坑,最令人丧气的便是起步时的装置问题。在网上查找相干材料,尝试了很多计划,基本上都是失败的。通过急躁的尝试还是解决了这个问题,这也是我开始写博客的起因。我的博客内容全副是验证过的,不验证的不写。 装置 micro 失败的问题尝试装置了很屡次 micro 总是失败,前面装置这个操作胜利: # 设置 mod 模式和代理go env -w GO111MODULE=ongo env -w GOPROXY=https://goproxy.cn,https://proxy.golang.org,direct# 装置go get -u -v github.com/golang/protobuf/protoc-gen-go go get -u -v google.golang.org/grpc# 这里若加 -u 选项貌似会报错,就不加了go get -v github.com/micro/micro/v2go get -u -v github.com/micro/go-micro/v2go get -v github.com/micro/micro/v2/cmd/protoc-gen-micro@master 装置胜利后会在 GoPathbin 目录下生成 protoc-gen-micro.exe、micro.exe 文件。 报错问题查看版本报错: PS D:CodeCodeToolsGoPathbin> micro helppanic: qtls.ConnectionState not compatible with tls.ConnectionStategoroutine 1 [running]:github.com/lucas-clemente/quic-go/internal/handshake.init.1() D:/Code/CodeTools/GoPath/pkg/mod/github.com/lucas-clemente/quic-go@v0.14.1/internal/handshake/unsafe.go:17 +0x139 执行 micro 相干命令报错: PS C:UsersYC> cd D:CodeCodeToolsGoPathbinPS D:CodeCodeToolsGoPathbin> micro api --handler=apipanic: qtls.ConnectionState not compatible with tls.ConnectionStategoroutine 1 [running]:github.com/lucas-clemente/quic-go/internal/handshake.init.1() D:/Code/CodeTools/GoPath/pkg/mod/github.com/lucas-clemente/quic-go@v0.14.1/internal/handshake/unsafe.go:17 +0x139 这是因为我是从 1.6.2 开始装置 micro 导致,go 版本换成 1.14.15,删除 GoPath 相干文件之后,从新进行下载,问题解决! ...

April 1, 2021 · 1 min · jiezi

关于golang:转-汇编是深入理解-Go-的基础

作者:ivansli,腾讯 IEG 经营开发工程师 在深刻学习 Golang 的 runtime 和规范库实现的时候发现,如果对 Golang 汇编没有肯定理解的话,很难深刻理解其底层实现机制。在这里整顿总结了一份根底的 Golang 汇编入门常识,通过学习之后可能对其底层实现有肯定的意识。0. 为什么写本文平时业务中始终应用 PHP 编写代码,然而始终对 Golang 比拟感兴趣,空闲、周末之余会看一些 Go 底层源码。 近日在剖析 go 的某些个性底层性能实现时发现:有些又跟 runtime 运行时无关,而要把握这一部分的话,有一道坎是绕不过来的,那就是 Go 汇编。索性就查阅了很多大佬们写的材料,在浏览之余整顿总结了一下,并在这里分享给大家。 本文应用 Go 版本为 go1.14.11. 为什么须要汇编家喻户晓,在计算机的世界里,只有 2 种类型。那就是:0 和 1。 计算机工作是由一系列的机器指令进行驱动的,这些指令又是一组二进制数字,其对应计算机的高低电平。而这些机器指令的汇合就是机器语言,这些机器语言在最底层是与硬件一一对应的。 不言而喻,这样的机器指令有一个致命的毛病:可浏览性太差(恐怕也只有蠢才和疯子才有能力把控得了)。 为了解决可读性的问题以及代码编辑的需要,于是就诞生了最靠近机器的语言:汇编语言(在我看来,汇编语言更像一种助记符,这些人们容易记住的每一条助记符都映射着一条不容易记住的由 0、1 组成的机器指令。你感觉像不像域名与 IP 地址的关系呢?)。 1.1 程序的编译过程 以 C 语言为例来说,从 hello.c 的源码文件到 hello 可执行文件,通过编译器解决,大抵分为几个阶段: 编译器在不同的阶段会做不同的事件,然而有一步是能够确定的,那就是:源码会被编译成汇编,最初才是二进制。 2. 程序与过程源码通过编译之后,失去一个二进制的可执行文件。文件这两个字也就表明,目前失去的这个文件跟其余文件比照,除了是具备肯定的格局(Linux 中是 ELF 格局,即:可运行可链接。executable linkable formate)的二进制组成,并没什么区别。 在 Linux 中文件类型大抵分为 7 种: 1. b: 块设施文件 2. c:字符设施文件3. d:目录4. -:一般文件5. l:链接6. s:socket7. p:管道通过下面能够看到,可执行文件 main 与源码文件 main.go,都是同一种类型,属于一般文件。(当然了,在 Unix 中有一句很经典的话:所有皆文件)。那么,问题来了: ...

March 31, 2021 · 10 min · jiezi

关于golang:手撸golang-etcd-raft协议之5

手撸golang etcd raft协定之5 缘起最近浏览 [云原生分布式存储基石:etcd深刻解析] (杜军 , 2019.1)本系列笔记拟采纳golang练习之gitee: https://gitee.com/ioly/learning.gooop raft分布式一致性算法分布式存储系统通常会通过保护多个副原本进行容错,以进步零碎的可用性。这就引出了分布式存储系统的外围问题——如何保障多个正本的一致性?Raft算法把问题分解成了首领选举(leader election)、日志复制(log replication)、安全性(safety)和成员关系变动(membership changes)这几个子问题。Raft算法的基本操作只需2种RPC即可实现。RequestVote RPC是在选举过程中通过旧的Leader触发的,AppendEntries RPC是领导人触发的,目标是向其余节点复制日志条目和发送心跳(heartbeat)。指标依据raft协定,实现高可用分布式强统一的kv存储子目标(Day 5)从新设计RPC接口,将原有稀释的两个接口合成为更易于了解和实现的四个接口( 尽信书则不如无书 -_-|| )依据新RPC接口重写Follower状态的实现设计IRaftRPC: 将原有稀释的两个接口合成为更易于了解和实现的四个接口IRaftLSM: 增加局部包内反对接口iEventDrivenModel:抽取并实现事件驱动型的逻辑编排ILogStore:革新适配新合成的RPC接口tBoltDBStore:基于boltdb实现日志暂存,提交和利用tFollowerState:依据新合成的RPC接口,重写Follower状态的实现(未实现)IRaftRPC.go将原有稀释的两个接口合成为更易于了解和实现的四个接口。尽信书则不如无书-_-|| package rpcimport "learning/gooop/etcd/raft/model"type IRaftRPC interface { // leader to follower Heartbeat(cmd *HeartbeatCmd, ret *HeartbeatRet) error // leader to follower AppendLog(cmd *AppendLogCmd, ret *AppendLogRet) error // leader to follower CommitLog(cmd *CommitLogCmd, ret *CommitLogRet) error // candidate to follower RequestVote(cmd *RequestVoteCmd, ret *RequestVoteRet) error}type HeartbeatCmd struct { LeaderID string Term int64}type HeartbeatRet struct { Code HBCode Term int64}type HBCode intconst ( HBOk HBCode = iota HBTermMismatch HBCode = iota)type RequestVoteCmd struct { CandidateID string Term int64 LastLogIndex int64 LastLogTerm int64}type RequestVoteRet struct { Code RVCode Term int64}type RVCode intconst ( RVOk RVCode = iota RVLogMismatch RVCode = iota RVTermMismatch RVCode = iota RVVotedAnother RVCode = iota)type AppendLogCmd struct { LeaderID string Term int64 Entry *model.LogEntry}type AppendLogRet struct { Code ALCode Term int64 PrevLogIndex int64 PrevLogTerm int64}type ALCode intconst ( ALOk ALCode = iota ALTermMismatch ALCode = iota ALIndexMismatch ALCode = iota ALInternalError ALCode = iota)type CommitLogCmd struct { LeaderID string Term int64 Index int64}type CommitLogRet struct { Code CLCode}type CLCode intconst ( CLOk CLCode = iota CLLogNotFound CLCode = iota CLInternalError CLCode = iota)IRaftLSM.go增加局部包内反对接口 ...

March 31, 2021 · 6 min · jiezi

关于golang:GO基础知识分享2

[TOC] 兵长:胖sir,咋还在看基础知识嘞?你以前可不是这样的哦 胖sir:切,我明天看的和之前的可不一样 兵长:有啥不一样的,你能给我说出花来嘛 胖sir:小样,你本人好好看看 兵长:看看就看看 GO基础知识分享21、 多重赋值替换2个数字的值 i := 10j := 20i , j = j, i2、复数t := 3 + 5ifmt.Println(t) //(3+5i)fmt.Printf("type == %T", t)//type == complex1283、输出 var tmp int fmt.Scanf("%d", &tmp) fmt.Println("tmp == ", tmp) //tmp == 1 var tmp2 int fmt.Scan(&tmp2) fmt.Printf("type == %T", tmp2)//type == int4、类型转换bool类型不能与int类型互相转化 5、type 自定义类型 type long int64 var a long a = 2 fmt.Printf("type == %T", a) //type == main.long6、switchcase前面不须要写break; 默认就有该性能 ...

March 31, 2021 · 3 min · jiezi

关于golang:GO基础知识分享

[TOC] GO基础知识分享兵长:哟,最近在干啥呢 胖sir:在看我之前的go根底学习材料呢,回顾一下 兵长:那给我分享一下呗,我也想回顾回顾 胖sir:用你的小手指点开你的手机,我来传给你 兵长:你信不信我的小手指能够带你飞整个峡谷 . . . go语言的根本事项go run hello.go 间接运行,输入后果(原理也是编译后执行)go build hello.go 生成可执行程序,运行可执行程序,输入后果留神 go语言中花括号不能独自占一行,否则会报错package mainimport "fmt"func main(){ //go语言中此处的花括号不能独自占一行,否则会报错 fmt.Println("hello world")}go语言一条语句占一行,如果一行须要执行多个语句 应用 分号 隔开go语言的输入语句有3种形式import "fmt" 后实用fmt.Println(x) -- 输入println(x) -- 输入fmt.Printf("%d",x) -- 格式化输入关键字上面列举了 Go 代码中会应用到的 25 个关键字或保留字: breakdefaultfuncinterfaceselectcasedefergomapstructchanelsegotopackageswitchconstfallthroughifrangetypecontinueforimportreturnvar除了以上介绍的这些关键字,Go 语言还有 36 个预约义标识符: appendboolbytecapclosecomplexcomplex64complex128uint16copyfalsefloat32float64imagintint8int16uint32int32int64iotalenmakenewnilpanicuint64printprintlnrealrecoverstringtrueuintuint8uintptr字符串的拼接和变量的定义形式定义变量的三种形式 失常应用var定义变量应用var定义变量,然而不定义类型,通过赋初值的形式,go编译器自动识别应用:=的形式来进行 新变量的定义,仅限于新变量 -- 实用于定义在函数外部//字符串 能够应用+ 进行拼接 fmt.Println("this is my func") fmt.Println("hello ,wolrd" + "xiaozhuzhu")//定义变量 var name string="xiaomotong" var age,tail int=24,170 fmt.Println(name, age , tail) fmt.Println(name) fmt.Println(age) fmt.Println(tail)//定义变量的三种形式//1 var a int = 1 fmt.Println(a)//2 应用var定义变量,然而不定义类型,通过赋初值的形式,go编译器自动识别 var b = "hello" fmt.Println(b)//3 应用:=的形式来进行 新变量的定义,仅限于新变量 //:= 左侧如果没有申明新的变量,就产生编译谬误 c := 20 fmt.Println(c) //c:=30 //报错,因为c曾经不是新变量的 c=30 //正确,是一个失常的赋值操作 fmt.Println(c) c,d:=40,90 //这样是非法的 fmt.Println(c,d)因式分解的形式,仅仅实用于定义全局变量 ...

March 31, 2021 · 5 min · jiezi

关于云原生:OpenYurt-深度解析边缘网关缓存能力的优雅实现

作者 | 何淋波(新胜)起源 | 阿里巴巴云原生公众号 OpenYurt:延长原生 K8s 的能力到边缘阿里云边缘容器服务上线 1 年后,正式开源了云原生边缘计算解决方案 OpenYurt,跟其余开源的容器化边缘计算计划不同的中央在于:OpenYurt 秉持 Extending your native Kubernetes to edge 的理念,对 Kubernetes 零碎零批改,并提供一键式转换原生 Kubernetes 为 OpenYurt,让原生 K8s 集群具备边缘集群能力。 同时随着 OpenYurt 的继续演进,也肯定会持续放弃如下倒退理念: 非侵入式加强 K8s放弃和云原生社区支流技术同步演进OpenYurt 如何解决边缘自治问题想要实现将 Kubernetes 零碎延展到边缘计算场景,那么边缘节点将通过公网和云端连贯,网络连接有很大不可控因素,可能带来边缘业务运行的不稳固因素,这是云原生和边缘计算交融的次要难点之一。 解决这个问题,须要使边缘侧具备自治能力,即当云边网络断开或者连贯不稳固时,确保边缘业务能够继续运行。在 OpenYurt 中,该能力由 yurt-controller-manager 和 YurtHub 组件提供。 1. YurtHub 架构在之前的文章中,咱们具体介绍了 YurtHub 组件的能力。其架构图如下: 图片链接 YurtHub 是一个带有数据缓存性能的“通明网关”,和云端网络断连状态下,如果节点或者组件重启,各个组件(kubelet/kube-proxy 等)将从 YurtHub 中获取到业务容器相干数据,无效解决边缘自治的问题。这也意味着咱们须要实现一个轻量的带数据缓存能力的反向代理。 2. 第一想法实现一个缓存数据的反向代理,第一想法就是从 response.Body 中读取数据,而后别离返回给申请 client 和本地的 Cache 模块。伪代码如下: func HandleResponse(rw http.ResponseWriter, resp *http.Response) { bodyBytes, _ := ioutil.ReadAll(resp.Body) go func() { // cache response on local disk cacher.Write(bodyBytes) } // client reads data from response rw.Write(bodyBytes)}当深刻思考后,在 Kubernetes 零碎中,上述实现会引发上面的问题: ...

March 31, 2021 · 3 min · jiezi

关于golang:golang-morestacknoctxt解析

go version go16.2, maxOS Mojave原理能够从https://juejin.cn/post/684490...,本片单纯解析源码 哪里调用原函数 package mainfunc testMoreStack(a, b int) int { return testMoreStack(1, b)}func main() {}编译 go tool compile -N -l -S ./main2.go > ./main2~~~~.s关上生成文件main2.s "".testMoreStack STEXT size=93 args=0x18 locals=0x28 funcid=0x0 0x0000 00000 (./main2.go:7) TEXT "".testMoreStack(SB), ABIInternal, $40-24 0x0000 00000 (./main2.go:7) MOVQ (TLS), CX // CX = *g 0x0009 00009 (./main2.go:7) CMPQ SP, 16(CX) // if SP > g.stackguard1 0x000d 00013 (./main2.go:7) PCDATA $0, $-2 0x000d 00013 (./main2.go:7) JLS 86 // true 跳到86 0x000f 00015 (./main2.go:7) PCDATA $0, $-1 0x000f 00015 (./main2.go:7) SUBQ $40, SP 0x0013 00019 (./main2.go:7) MOVQ BP, 32(SP) 0x0018 00024 (./main2.go:7) LEAQ 32(SP), BP 0x001d 00029 (./main2.go:7) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (./main2.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (./main2.go:7) MOVQ $0, "".~r2+64(SP) 0x0026 00038 (./main2.go:8) MOVQ "".b+56(SP), AX 0x002b 00043 (./main2.go:8) MOVQ $1, (SP) 0x0033 00051 (./main2.go:8) MOVQ AX, 8(SP) 0x0038 00056 (./main2.go:8) PCDATA $1, $0 0x0038 00056 (./main2.go:8) CALL "".testMoreStack(SB) 0x003d 00061 (./main2.go:8) MOVQ 16(SP), AX 0x0042 00066 (./main2.go:8) MOVQ AX, ""..autotmp_3+24(SP) 0x0047 00071 (./main2.go:8) MOVQ AX, "".~r2+64(SP) 0x004c 00076 (./main2.go:8) MOVQ 32(SP), BP 0x0051 00081 (./main2.go:8) ADDQ $40, SP 0x0055 00085 (./main2.go:8) RET 0x0056 00086 (./main2.go:8) NOP 0x0056 00086 (./main2.go:7) PCDATA $1, $-1 0x0056 00086 (./main2.go:7) PCDATA $0, $-2 0x0056 00086 (./main2.go:7) CALL runtime.morestack_noctxt(SB) 0x005b 00091 (./main2.go:7) PCDATA $0, $-1 0x005b 00091 (./main2.go:7) JMP 0 // 跳到0 0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 47 48 eH..%....H;a.vGH 0x0010 83 ec 28 48 89 6c 24 20 48 8d 6c 24 20 48 c7 44 ..(H.l$ H.l$ H.D 0x0020 24 40 00 00 00 00 48 8b 44 24 38 48 c7 04 24 01 $@....H.D$8H..$. 0x0030 00 00 00 48 89 44 24 08 e8 00 00 00 00 48 8b 44 ...H.D$......H.D 0x0040 24 10 48 89 44 24 18 48 89 44 24 40 48 8b 6c 24 $.H.D$.H.D$@H.l$ 0x0050 20 48 83 c4 28 c3 e8 00 00 00 00 eb a3 H..(........ rel 5+4 t=17 TLS+0 rel 57+4 t=8 "".testMoreStack+0 rel 87+4 t=8 runtime.morestack_noctxt+0本函数栈40字节,调用栈24字节,函数返回PC8字节字节共72字节, 栈地位: ...

March 31, 2021 · 3 min · jiezi

关于golang:用-Go-WebSocket-快速实现一个-chat-服务

前言在 go-zero 开源之后,十分多的用户询问是否能够反对以及什么时候反对 websocket,终于在 v1.1.6 外面咱们从框架层面让 websocket 的反对落地了,上面咱们就以 chat 作为一个示例来解说如何用 go-zero 来实现一个 websocket 服务。 整体设计咱们以 zero-example 中的 chat 聊天室为例来一步步一解说 websocket 的实现,分为如下几个局部: 多客户端接入音讯播送客户端的及时上线下线全双工通信【客户端自身是发送端,也是接收端】先放一张图,大抵的数据传输: 两头有个 select loop 就是整个 chat 的 engine。首先要撑持单方通信: 得有一个交换数据的管道。客户端只管从 管道 读取/输送数据;客户端在线状况。不能说你下线了,还往你那传输数据;数据流数据流是 engine 的次要性能,先不急着看代码,咱们先想 client 怎么接入并被 engine 感知: 首先是从前端发 websocket 申请;建设连贯;筹备接管/发送通道;注册到 engine; // HTML 操作 {js}if (window["WebSocket"]) { conn = new WebSocket("ws://" + document.location.host + "/ws"); conn.onclose = function (evt) { var item = document.createElement("div"); item.innerHTML = "<b>Connection closed.</b>"; appendLog(item); }; ...}// 路由engine.AddRoute(rest.Route{ Method: http.MethodGet, Path: "/ws", Handler: func(w http.ResponseWriter, r *http.Request) { internal.ServeWs(hub, w, r) },})// 接入逻辑func ServeWs(hub *Hub, w http.ResponseWriter, r *http.Request) { // 将http申请降级为websocket conn, err := upgrader.Upgrade(w, r, nil) ... // 构建client:hub{engine}, con{websocker conn}, send{channel buff} client := &Client{ hub: hub, conn: conn, send: make(chan []byte, bufSize), } client.hub.register <- client // 开始客户端双工的通信,接管和写入数据 go client.writePump() go client.readPump()}这样,新接入的 client 就被退出到 注册 通道中。 ...

March 31, 2021 · 2 min · jiezi

关于后端:Go-面试题-new-和-make-是什么差异在哪

若有任何问题或倡议,欢送及时交换和碰撞。我的公众号是 【脑子进煎鱼了】,GitHub 地址:https://github.com/eddycjy。大家好,我是煎鱼。 在 Go 语言中,有两个比拟雷同的内置函数,别离是 new 和 make 办法,其主要用途都是用于调配相应类型的内存空间。 看上去 new 和 make 都是分配内存的,那他们有什么区别呢?这个细节点也成为了不少 Go 语言工程师的面试题之一,值得大家一看。 在明天这篇文章中咱们未来解答这个问题。 根本个性make在 Go 语言中,内置函数 make 仅反对 slice、map、channel 三种数据类型的内存创立,其返回值是所创立类型的自身,而不是新的指针援用。 函数签名如下: func make(t Type, size ...IntegerType) Type具体应用示例: func main() { v1 := make([]int, 1, 5) v2 := make(map[int]bool, 5) v3 := make(chan int, 1) fmt.Println(v1, v2, v3)}在代码中,咱们别离对三种类型调用了 make 函数进行了初始化。你会发现有的入参是有多个长度指定,有的没有。 这块的区别次要是长度(len)和容量(cap)的指定,有的类型是没有容量这一说法,因而天然也就无奈指定。 输入后果: [0] map[] 0xc000044070有一个细节点要留神,调用 make 函数去初始化切片(slice)的类型时,会带有零值,须要明确是否须要。 见过不少的小伙伴在这下面踩坑。 new在 Go 语言中,内置函数 new 能够对类型进行内存创立和初始化。其返回值是所创立类型的指针援用,与 make 函数在本质细节上存在区别。 ...

March 31, 2021 · 1 min · jiezi

关于golang:手撸golang-etcd-raft协议之4

手撸golang etcd raft协定之4 缘起最近浏览 [云原生分布式存储基石:etcd深刻解析] (杜军 , 2019.1)本系列笔记拟采纳golang练习之gitee: https://gitee.com/ioly/learning.gooop raft分布式一致性算法分布式存储系统通常会通过保护多个副原本进行容错,以进步零碎的可用性。这就引出了分布式存储系统的外围问题——如何保障多个正本的一致性?Raft算法把问题分解成了首领选举(leader election)、日志复制(log replication)、安全性(safety)和成员关系变动(membership changes)这几个子问题。Raft算法的基本操作只需2种RPC即可实现。RequestVote RPC是在选举过程中通过旧的Leader触发的,AppendEntries RPC是领导人触发的,目标是向其余节点复制日志条目和发送心跳(heartbeat)。指标依据raft协定,实现高可用分布式强统一的kv存储子目标(Day 4)应用boltdb存储操作日志和kv键值数据 unstable存储桶:已收到未提交的日志,重启后清空committed存储桶:已提交的日志data存储桶:kv键值数据meta存储桶:记录末次提交的index和term设计model/LogEntry: 日志条目ICmd:操作指令接口ICmdFactory:操作指令工厂ILogStore:日志存储接口tCmdBase:指令基类PutCmd:put指令DelCmd:del指令tBoltDBStore:基于boltdb实现日志暂存,提交和利用LogEntry.go日志条目 package modelimport "encoding/json"type LogEntry struct { Tag int Term int64 Index int64 PrevTerm int64 PrevIndex int64 Command []byte}func (me *LogEntry) Marshal() (error, []byte) { j, e := json.Marshal(me) if e != nil { return e, nil } return nil, j}func (me *LogEntry) Unmarshal(data []byte) error { return json.Unmarshal(data, me)}ICmd.go操作指令接口 package storeimport "github.com/boltdb/bolt"type ICmd interface { Marshal() []byte Unmarshal(data []byte) Apply(tx *bolt.Tx) error}ICmdFactory.go操作指令工厂 ...

March 30, 2021 · 4 min · jiezi

关于golang:Go的汇编器快速指南

本文档简要介绍了gcGo编译器应用的非常规模式的汇编语言。该文件不全面。 汇编程序基于Plan 9汇编程序的输出款式,在其余中央具体介绍了该款式 。如果您打算编写汇编语言,则只管其中大部分是特定于Plan 9的,但您仍应浏览该文档。以后文档提供了语法摘要以及与该文档中所解释内容的区别,并形容了编写汇编代码以与Go交互时实用的个性。 对于Go的汇编器,最重要的事件是它不是底层机器的间接示意。一些细节正好映射到机器,但有些则不然。这是因为编译器套件(请参见此 形容)不须要在惯例管道中传递任何汇编程序。取而代之的是,编译器对一种半形象的指令集进行操作,并且指令抉择局部产生在代码生成之后。汇编程序以半形象模式工作,因而当您看到相似MOV 工具链实际上为该操作生成的内容可能基本不是挪动指令,可能是革除指令或加载指令。或者它可能与该名称的机器指令齐全对应。通常,特定于机器的操作偏向于本人呈现,而更通用的概念(如内存挪动和子例程调用与返回)则更为形象。具体细节因架构而异,咱们对此不谨严深表歉意。状况尚不明确。 汇编程序是解析该半形象指令集的形容并将其转换为要输出到链接器的指令的一种形式。如果要查看给定体系结构(例如amd64)的汇编指令的外观,则规范库的源代码中有许多示例,例如 runtime和 math/big。您还能够查看编译器作为汇编代码收回的内容(理论输入可能与您在此处看到的有所不同): $ cat x.gopackage mainfunc main() { println(3)}$ GOOS=linux GOARCH=amd64 go tool compile -S x.go # or: go build -gcflags -S x.go"".main STEXT size=74 args=0x0 locals=0x10 0x0000 00000 (x.go:3) TEXT "".main(SB), $16-0 0x0000 00000 (x.go:3) MOVQ (TLS), CX 0x0009 00009 (x.go:3) CMPQ SP, 16(CX) 0x000d 00013 (x.go:3) JLS 67 0x000f 00015 (x.go:3) SUBQ $16, SP 0x0013 00019 (x.go:3) MOVQ BP, 8(SP) 0x0018 00024 (x.go:3) LEAQ 8(SP), BP 0x001d 00029 (x.go:3) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (x.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (x.go:3) FUNCDATA $2, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (x.go:4) PCDATA $0, $0 0x001d 00029 (x.go:4) PCDATA $1, $0 0x001d 00029 (x.go:4) CALL runtime.printlock(SB) 0x0022 00034 (x.go:4) MOVQ $3, (SP) 0x002a 00042 (x.go:4) CALL runtime.printint(SB) 0x002f 00047 (x.go:4) CALL runtime.printnl(SB) 0x0034 00052 (x.go:4) CALL runtime.printunlock(SB) 0x0039 00057 (x.go:5) MOVQ 8(SP), BP 0x003e 00062 (x.go:5) ADDQ $16, SP 0x0042 00066 (x.go:5) RET 0x0043 00067 (x.go:5) NOP 0x0043 00067 (x.go:3) PCDATA $1, $-1 0x0043 00067 (x.go:3) PCDATA $0, $-1 0x0043 00067 (x.go:3) CALL runtime.morestack_noctxt(SB) 0x0048 00072 (x.go:3) JMP 0...FUNCDATA和PCDATA指令蕴含用于通过垃圾收集器的应用的信息; 它们由编译器引入。要查看链接后放入二进制文件的内容,请应用go tool objdump: ...

March 30, 2021 · 2 min · jiezi

关于golang:Golang内部构件第5部分运行时引导程序

疏导过程是理解Go运行时如何工作的要害。如果您想持续应用Go,学习它是必不可少的。因而,咱们的Golang Internals系列的第五局部专门探讨Go运行时,尤其是Go疏导过程。这次您将理解: 自举可调整大小的堆栈实现外部TLS施行请留神,这篇文章蕴含许多汇编代码,您至多须要一些基础知识能力持续(这里是Go汇编程序的疾速指南)。 寻找一个切入点首先,咱们须要找到启动Go程序后立刻执行的性能。为此,咱们将编写一个简略的Go利用。 package mainfunc main() { print(123)}而后,咱们须要对其进行编译和链接。 go tool compile -N -l -S main.go这将6.out在您的当前目录中创立一个名为的可执行文件。下一步波及objdump工具,该工具特定于Linux。Windows和Mac用户能够找到类似物或齐全跳过此步骤。当初,运行以下命令。 objdump -f 6.out您应该取得输入,其中将蕴含起始地址。 6.out: file format elf64-x86-64architecture: i386:x86-64, flags 0x00000112:EXEC_P, HAS_SYMS, D_PAGEDstart address 0x000000000042f160接下来,咱们须要反汇编可执行文件,并找到哪个函数位于该地址。 objdump -d 6.out > disassemble.txt而后,咱们须要关上disassemble.txt文件并搜寻42f160。咱们失去的输入如下所示 000000000042f160 <_rt0_amd64_linux>: 42f160: 48 8d 74 24 08 lea 0x8(%rsp),%rsi 42f165: 48 8b 3c 24 mov (%rsp),%rdi 42f169: 48 8d 05 10 00 00 00 lea 0x10(%rip),%rax # 42f180 <main> 42f170: ff e0 jmpq *%rax很好,咱们找到了!我的操作系统和体系结构的入口点是一个名为的函数_rt0_amd64_linux。 ...

March 30, 2021 · 3 min · jiezi