首页 文章 精选 留言 我的

精选列表

搜索[分布式调度],共10000篇文章
优秀的个人博客,低调大师

Simple Admin - Go 语言分布式后台管理系统 v1.0.4 发布

Simple Admin Tools V1.5.5 更新 修复: 优化 docker 命令, 目前适配 simple admin 修复: group name 在single api 不生效问题 优化: 合并最新代码 优化: 升级依赖 Simple Admin Backend UI V1.0.4 更新 本次为大更新,建议升级 修复: 重新优化所有组件,修复所有组件在暗黑模式下的适配 优化: 恢复夜间模式和侧边栏颜色调整 优化: 升级依赖并删除部分无用代码 修复: 升级 code editor 为最新的 codemirror 6 修复: tinymce组件部分bug 优化: 重新适配部分页面 Simple Admin Core/Job V1.0.4 更新 优化: 使用最新tool重新生成 docker及rpc文件 注意: rpc client 官方最新代码新增下划线,重新生成rpc后手动删掉旧的client即可,注意api需要修改下包路径 官方文档 (含视频教程):https://doc.ryansu.pro/zh/ 全新后台界面文档网站:https://vben.ryansu.pro/zh/预览网站已更新(登录信息在文档网站):点击访问

优秀的个人博客,低调大师

Simple Admin - Go 语言分布式后台管理系统 v1.0.3 发布

Simple Admin Tools v1.5.4 更新 优化: Vben 生成模板 优化: 删除多余的goctls提示 修复: 多处 test bugs 优化: 更新依赖及最新代码 Simple Admin Backend UI V1.0.3 更新 重大更新: 同步 vben 至最新版本 v2.10.0 修复: 所有已知错误 优化: 更新 vite 至 v4.3.3, 项目编译时间减少 60%+ 优化: 适配所有 views 文件 修复: 用户强制登出bug 注意: 新版本 VITE_PROXY 已删除, 请直接在 vite.config.ts 配置 proxy Simple Admin Core/Common/Job v1.0.3 更新 适配依赖,修复少量问题

优秀的个人博客,低调大师

Simple Admin - Go 语言分布式后台管理系统 v1.0.2 发布

Simple Admin Tools v1.5.3 更新 优化: 升级ent模板至最新 新增: proto merge 支持 enum 修复: authority 中间件 trans err 为 false 生成错误 优化: service 名称在 api 和 rpc 统一为驼峰 新增: validator 新增自定义错误码接口和语言及自定义校验方法注册接口 优化: 合并官方最新代码 Simple Admin Common v1.0.2 更新 优化: New Jwt Token 方法支持自定义多参数 新增: captcha 新增三种驱动支持: digit, math, string 新增: mysql, postgres, sqlite 额外参数配置 优化: 新增多处测试代码 修复: casbin 新版本导致的 redis 错误 Simple Admin Core v1.0.2 更新 优化: captcha 改为全局变量 修复: department校验错误 修复: 字典错误缓存问题 Simple Admin Backend UI v1.0.2 更新 修复:注册页面数据返回bug 优化:axios请求现已支持 successful 信息提示,优化后的接口将更加简洁,将在下版本优化现有代码和生成工具,兼容原有的代码 项目文档:https://doc.ryansu.pro/zh/guide/ Gitee:https://gitee.com/hopefire/simple-admin-core Github:https://github.com/suyuan32/simple-admin-core

优秀的个人博客,低调大师

Simple Admin - Go 语言分布式后台管理系统 v0.3.2 发布

Simple Admin Tools v0.3.2 更新 新增: 额外参数, i18n 和 初始化代码生成 新增: swagger 支持 prefix 前缀 新增: rpc proto merge 支持 map 类型 新增: 命令参数缩写,目前所有命令参数都适配了简写 新增: goctls rpc ent 新增 proto_field_style 用于调整 proto 字段格式 优化: goctls 参数介绍 优化: 合并最新代码 优化: makefile 新增 version 优化: 升级依赖 优化: 删除 migrate 中的无用代码 Simple Admin Core、Job 和 Simple Admin Backend UI 优化: 更新 all in one docker-compose 新增: makefile 新增 version , 通过 tag 获取版本 优化: 少量优化 欢迎使用 docker-compose 本地体验: 点击查看文档 所有生成命令均提供了参数简写,效果如下: 请在命令行下自行查看

优秀的个人博客,低调大师

Simple Admin - Go 语言分布式后台管理系统 v0.3.1 发布

Simple Admin Tool v0.3.1 更新 新增: 三端 validate 全自动生成支持 修复: 依赖导致的 goctls 编译错误 优化: 重构所有 command Simple Admin Core v0.3.1 更新 优化: 对所有 API 进行的校验 优化: 删除了部分冗余模块 修复; 部分 bug Simple Admin Backend v0.3.1 更新 优化: 优化了所有模块的校验规则,均采用 新版 goctls 生成 效果展示: 在 api 文件定义 validate swagger 生成 前端生成 实现了一次定义全端生成,大大简化了代码工作 详细文档 : 点此查看

优秀的个人博客,低调大师

Simple Admin - Go 语言分布式后台管理系统 v0.3.0 发布

Simple Admin Tools v0.3.0 更新 优化: 代码生成中 Makefile 和 Dockerfile 格式 修复: 优化 goctls 的 Cmd 代码 优化: 更新所有依赖并同步官方最新代码 修复: go swagger 和 go zero 冲突 bug 修复: proto 文件 optional 合并 bug 修复: 官方 test 错误 现已支持使用 make help 查看所有命令 详情查看快捷命令文档: 点击查看 Simple Admin Tool 已支持 go zero route 与 swagger path的转换,后续将支持 validate 生成 swagger 条件文档 声明: 生成: 详情查看 swagger 文档: 点击查看 Simple Admin Core, Job, File v0.3.0 更新 优化: 适配 v0.3.0 版本的 Common 及 Tool 优化: 更新依赖 Simple Admin Common v0.3.0更新 新增: mongodb 支持 新增示例项目:点击查看

优秀的个人博客,低调大师

Simple Admin - Go 语言分布式后台管理系统 v0.2.8 发布

Simple Admin Tools v0.2.8发布 主要更新: 新增 build_win.sh 用于在 windows 环境下编译 修复 ent 代码生成类型错误bug 修复 windows 下产生的已知bug 同步 go zero 代码至最新 Simple Admin Core v0.2.8 更新 主要更新: 新增任务日志接口及初始化 升级依赖至最新 Backend UI 更新适配页面 新增 postgresql 本地部署文件 Simple Admin Common v0.2.8 更新 主要更新: 升级依赖至最新 修复 asynq 部分bug Simple Admin Job v0.2.8 更新 主要更新: 新增任务日志 修复任务初始化bug 新增任务可选启用配置 更新依赖 详细文档https://doc.ryansu.pro/zh/guide/official-comp/cron.html Simple Admin Doc 文档更新,大幅优化文档内容。 注意: 任务日志 Task Log 是在 Windows下开发,目前已可以在windows下全流程开发。

优秀的个人博客,低调大师

Simple Admin - Go 语言分布式后台管理系统 v0.2.6 更新

Simple Admin Tools V 0.2.2 更新 主要更新: 优化api casbin 初始化语句, 提供redis监视功能 优化ent错误处理,优化ent 生成模板 优化事务处理函数模板 修复windows 下new mod 已存在问题 Simple Admin Core v0.2.6 更新 主要更新: 新增 errorhandler 处理ent 错误,转化为 status error, member RPC 中为 dberrorhandler 包,用于区分,否则同名每次都要选择 优化 withTx 函数, withTx 用于本地数据库事务 修复初始化数据库POST小写错误 Simple Admin Backend UI v0.2.6 更新 主要更新: 修复 menu 授权全选bug 优化 require字段 修复会员初始化页面 bug Simple Admin File v0.2.6 更新 主要更新: 1. 适配 simple admin core v0.2.6

优秀的个人博客,低调大师

浪潮云溪分布式数据库 Tracing(二)—— 源码解析

按照【云溪数据库Tracing(一)】介绍的使用opentracing要求,本文着重介绍云溪数据库Tracing模块中是如何实现Span,SpanContexts和Tracer的。 Part 1-Tracing 模块调用关系 1.1Traincg模块包含的文件列表 Tracer.go :定义了opentracing 中的trace相关接口的实现。 Tracer_span.go :定义了opentracing中的span 相关操作的实现。 Tags.go :定义了 opentracing中关于tags的相关接口。 Shadow.go :不是opentracing中的概念,这里主要实现与zipkin的通信,用于tracing 信息推送到外部的zipkin中。 1.2各个文件之间的调用关系 在cluster_settings.go中会创建tracer,供全局使用,其他模块中使用这个Tracer实现span的创建和其他操作,例如设定span名称、设定tag 、增加log等操作。 Part 2-Opentracing 在云溪数据库中的实现 以下是只是列出了部分接口实现,并非全部。 2.1Span 接口实现: GetContext实现:API用于获取Span中的SpanContext,主要功能是先创建一个map[string]string类型的baggageCopy,将span中的mu.Baggage读出写入baggageCopy,创建新的spanContext,并且返回。 func (s *span) Context() opentracing.SpanContext { s.mu.Lock() defer s.mu.Unlock() baggageCopy := make(map[string]string, len(s.mu.Baggage)) for k, v := range s.mu.Baggage { baggageCopy[k] = v } sc := &spanContext{ spanMeta: s.spanMeta, Baggage: baggageCopy, } if s.shadowTr != nil { sc.shadowTr = s.shadowTr sc.shadowCtx = s.shadowSpan.Context() } if s.isRecording() { sc.recordingGroup = s.mu.recordingGroup sc.recordingType = s.mu.recordingType } return sc} Finished实现:API用于结束一个Span的记录和追踪。​​​​​​ func (s *span) Finish() { s.FinishWithOptions(opentracing.FinishOptions{})} SetTag实现:用于向指定的Span添加Tag信息。 func(s*span)SetTag(keystring,valueinterface{})opentracing.Span{ return s.setTagInner(key, value, false /* locked */)} Log实现:用于向指定的Span添加Log信息。 func (s *span) LogKV(alternatingKeyValues ...interface{}) { fields, err := otlog.InterleavedKVToFields(alternatingKeyValues...) if err != nil { s.LogFields(otlog.Error(err), otlog.String("function", "LogKV")) return } s.LogFields(fields...)} SetBaggageItem实现:用于向指定的Span增加Baggage信息,主要是用于跨进程追踪使用。 func (s *span) SetBaggageItem(restrictedKey, value string) opentracing.Span { s.mu.Lock() defer s.mu.Unlock() return s.setBaggageItemLocked(restrictedKey, value)} BaggageItem实现:用于获取指定的Baggage信息。 func (s *span) BaggageItem(restrictedKey string) string { s.mu.Lock() defer s.mu.Unlock() return s.mu.Baggage[restrictedKey]} SetOperationName实现:用于设定Span 的名称。 func (s *span) SetOperationName(operationName string) opentracing.Span { if s.shadowTr != nil { s.shadowSpan.SetOperationName(operationName) } s.operation = operationName return s } Tracer实现:用于获取Span属于哪个Tracer。 // Tracer is part of the opentracing.Span interface.func (s *span) Tracer() opentracing.Tracer { return s.tracer } 2.2SpanContext 接口实现: ForeachBaggageItem实现:用于遍历spanContext中的baggage信息。 func (sc *spanContext) ForeachBaggageItem(handler func(k, v string) bool) { for k, v := range sc.Baggage { if !handler(k, v) { break } }} 2.3 Tracer接口实现: Inject实现:用于向carrier中注入SpanContext信息 // Inject is part of the opentracing.Tracer interface.func (t *Tracer) Inject( osc opentracing.SpanContext, format interface{}, carrier interface{},) error { …… // We onlysupport the HTTPHeaders/TextMap format. if format != opentracing.HTTPHeaders && format != opentracing.TextMap { return opentracing.ErrUnsupportedFormat } mapWriter, ok := carrier.(opentracing.TextMapWriter) if !ok { return opentracing.ErrInvalidCarrier } sc, ok := osc.(*spanContext) if !ok { return opentracing.ErrInvalidSpanContext } mapWriter.Set(fieldNameTraceID, strconv.FormatUint(sc.TraceID, 16)) mapWriter.Set(fieldNameSpanID, strconv.FormatUint(sc.SpanID, 16)) for k, v := range sc.Baggage { mapWriter.Set(prefixBaggage+k, v) } …… return nil} Extract实现:用于从carrier中抽取出SpanContext信息。 func (t *Tracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) { // We onlysupport the HTTPHeaders/TextMap format. if format != opentracing.HTTPHeaders && format != opentracing.TextMap { return noopSpanContext{}, opentracing.ErrUnsupportedFormat } mapReader, ok := carrier.(opentracing.TextMapReader) if !ok { return noopSpanContext{}, opentracing.ErrInvalidCarrier } var sc spanContext …… err :=mapReader.ForeachKey(func(k, v string) error { switch k = strings.ToLower(k); k { case fieldNameTraceID: var err error sc.TraceID, err = strconv.ParseUint(v, 16, 64) if err != nil { return opentracing.ErrSpanContextCorrupted } case fieldNameSpanID: var err error sc.SpanID, err = strconv.ParseUint(v, 16, 64) if err != nil { return opentracing.ErrSpanContextCorrupted } case fieldNameShadowType: shadowType = v default: if strings.HasPrefix(k, prefixBaggage) { if sc.Baggage == nil { sc.Baggage = make(map[string]string) } sc.Baggage[strings.TrimPrefix(k, prefixBaggage)] = v } else if strings.HasPrefix(k, prefixShadow) { if shadowCarrier == nil { shadowCarrier = make(opentracing.TextMapCarrier) } // We build ashadow textmap with the original shadow keys. shadowCarrier.Set(strings.TrimPrefix(k, prefixShadow), v) } } return nil }) if err != nil { return noopSpanContext{}, err } if sc.TraceID == 0 &&sc.SpanID == 0 { return noopSpanContext{}, nil } …… return &sc, nil} StartSpan接口实现:用于创建一个新的Span,可根据传入不同opts来实现不同Span的初始化。 func (t *Tracer) StartSpan( operationName string, opts ...opentracing.StartSpanOption,) opentracing.Span { // Fast paths toavoid the allocation of StartSpanOptions below when tracing // is disabled: if we have no optionsor a single SpanReference (the common // case) with a noop context, return anoop span now. if len(opts) == 1 { if o, ok := opts[0].(opentracing.SpanReference); ok { if IsNoopContext(o.ReferencedContext) { return &t.noopSpan } } } shadowTr := t.getShadowTracer() …… return s} 2.4 noop span 实现: noop span实现:使监控代码不依赖Tracer和Span的返回值,防止程序异常退出。 type noopSpan struct { tracer *Tracer} var _ opentracing.Span = &noopSpan{} func (n *noopSpan) Context() opentracing.SpanContext { return noopSpanContext{} }func (n *noopSpan) BaggageItem(key string) string { return "" }func (n *noopSpan) SetTag(key string, value interface{}) opentracing.Span { return n }func (n *noopSpan) Finish() {}func (n *noopSpan) FinishWithOptions(opts opentracing.FinishOptions) {}func (n *noopSpan) SetOperationName(operationName string) opentracing.Span { return n }func (n *noopSpan) Tracer() opentracing.Tracer { return n.tracer }func (n *noopSpan) LogFields(fields ...otlog.Field) {}func (n *noopSpan) LogKV(keyVals ...interface{}) {}func (n *noopSpan) LogEvent(event string) {}func (n *noopSpan) LogEventWithPayload(event string, payload interface{}) {}func (n *noopSpan) Log(data opentracing.LogData) {} func (n *noopSpan) SetBaggageItem(key, val string) opentracing.Span { if key == Snowball { panic("attempting to set Snowball on a noop span; use the Recordable optionto StartSpan") } return n} Part3-云溪数据库中 Opentracing 简单使用示例 3.1 开启Tracer Recording测试 云溪数据库中 开始创建的span均是no operator span,需要手动调用StartRecording,将span转换为可record状态,才能正常对span进行操作。 func TestTracerRecording(t *testing.T) { tr := NewTracer() noop1 := tr.StartSpan("noop") if _, noop := noop1.(*noopSpan); !noop { t.Error("expected noop span") } noop1.LogKV("hello", "void") noop2 := tr.StartSpan("noop2", opentracing.ChildOf(noop1.Context())) if _, noop := noop2.(*noopSpan); !noop { t.Error("expected noop child span") } noop2.Finish() noop1.Finish() s1 := tr.StartSpan("a", Recordable) if _, noop := s1.(*noopSpan); noop { t.Error("Recordable (but not recording) span should not be noop") } if !IsBlackHoleSpan(s1) { t.Error("Recordable span should be black hole") } // Unless recording is actually started, child spans are still noop. noop3 := tr.StartSpan("noop3", opentracing.ChildOf(s1.Context())) if _, noop := noop3.(*noopSpan); !noop { t.Error("expected noop child span") } noop3.Finish() s1.LogKV("x", 1) StartRecording(s1, SingleNodeRecording) s1.LogKV("x", 2) s2 := tr.StartSpan("b", opentracing.ChildOf(s1.Context())) if IsBlackHoleSpan(s2) { t.Error("recording span should not be black hole") } s2.LogKV("x", 3) if err := TestingCheckRecordedSpans(GetRecording(s1), ` span a: tags: unfinished= x: 2 span b: tags: unfinished= x: 3 `); err != nil { t.Fatal(err) } if err := TestingCheckRecordedSpans(GetRecording(s2), ` span b: tags: unfinished= x: 3 `); err != nil { t.Fatal(err) } s3 := tr.StartSpan("c", opentracing.FollowsFrom(s2.Context())) s3.LogKV("x", 4) s3.SetTag("tag", "val") s2.Finish() if err := TestingCheckRecordedSpans(GetRecording(s1), ` span a: tags: unfinished= x: 2 span b: x: 3 span c: tags: tag=val unfinished= x: 4 `); err != nil { t.Fatal(err) } s3.Finish() if err := TestingCheckRecordedSpans(GetRecording(s1), ` span a: tags: unfinished= x: 2 span b: x: 3 span c: tags: tag=val x: 4 `); err != nil { t.Fatal(err) } StopRecording(s1) s1.LogKV("x", 100) if err := TestingCheckRecordedSpans(GetRecording(s1), ``); err != nil { t.Fatal(err) } // The child span is still recording. s3.LogKV("x", 5) if err := TestingCheckRecordedSpans(GetRecording(s3), ` span c: tags: tag=val x: 4 x: 5 `); err != nil { t.Fatal(err) } s1.Finish()} 3.2 创建childSpan 测试 测试StartChildSpan,根据已有span创建出一个新的span,为已有span的子span。 func TestStartChildSpan(t *testing.T) { tr := NewTracer() sp1 := tr.StartSpan("parent", Recordable) StartRecording(sp1, SingleNodeRecording) sp2 := StartChildSpan("child", sp1, nil /* logTags */, false /*separateRecording*/) sp2.Finish() sp1.Finish() if err := TestingCheckRecordedSpans(GetRecording(sp1), ` span parent: span child: `); err != nil { t.Fatal(err) } sp1 = tr.StartSpan("parent", Recordable) StartRecording(sp1, SingleNodeRecording) sp2 = StartChildSpan("child", sp1, nil /* logTags */, true /*separateRecording*/) sp2.Finish() sp1.Finish() if err := TestingCheckRecordedSpans(GetRecording(sp1), ` span parent: `); err != nil { t.Fatal(err) } if err := TestingCheckRecordedSpans(GetRecording(sp2), ` span child: `); err != nil { t.Fatal(err) } sp1 = tr.StartSpan("parent", Recordable) StartRecording(sp1, SingleNodeRecording) sp2 = StartChildSpan( "child", sp1, logtags.SingleTagBuffer("key", "val"), false, /*separateRecording*/ ) sp2.Finish() sp1.Finish() if err := TestingCheckRecordedSpans(GetRecording(sp1), ` span parent: span child: tags: key=val `); err != nil { t.Fatal(err) }} 3.3 跨进程追踪测试 测试跨进程追踪功能,主要是测试inject接口和 extract 接口,Inject用于向carrier中注入SpanContext信息,Extract用于从carrier中抽取出SpanContext信息。 funcTestTracerInjectExtract(t*testing.T){ tr := NewTracer() tr2 := NewTracer() // Verify that noop spans become noop spans on the remote side. noop1 := tr.StartSpan("noop") if _, noop := noop1.(*noopSpan); !noop { t.Fatalf("expected noop span: %+v", noop1) } carrier := make(opentracing.HTTPHeadersCarrier) if err := tr.Inject(noop1.Context(), opentracing.HTTPHeaders, carrier); err != nil { t.Fatal(err) } if len(carrier) != 0 { t.Errorf("noop span has carrier: %+v", carrier) } wireContext, err := tr2.Extract(opentracing.HTTPHeaders, carrier) if err != nil { t.Fatal(err) } if _, noopCtx := wireContext.(noopSpanContext); !noopCtx { t.Errorf("expected noop context: %v", wireContext) } noop2 := tr2.StartSpan("remote op", opentracing.FollowsFrom(wireContext)) if _, noop := noop2.(*noopSpan); !noop { t.Fatalf("expected noop span: %+v", noop2) } noop1.Finish() noop2.Finish() // Verify that snowball tracing is propagated and triggers recording on the // remote side. s1 := tr.StartSpan("a", Recordable) StartRecording(s1, SnowballRecording) carrier = make(opentracing.HTTPHeadersCarrier) if err := tr.Inject(s1.Context(), opentracing.HTTPHeaders, carrier); err != nil { t.Fatal(err) } wireContext, err = tr2.Extract(opentracing.HTTPHeaders, carrier) if err != nil { t.Fatal(err) } s2 := tr2.StartSpan("remote op", opentracing.FollowsFrom(wireContext)) // Compare TraceIDs trace1 := s1.Context().(*spanContext).TraceID trace2 := s2.Context().(*spanContext).TraceID if trace1 != trace2 { t.Errorf("TraceID doesn't match: parent %d child %d", trace1, trace2) } s2.LogKV("x", 1) s2.Finish() // Verify that recording was started automatically. rec := GetRecording(s2) if err := TestingCheckRecordedSpans(rec, ` span remote op: tags: sb=1 x: 1 `); err != nil { t.Fatal(err) } if err := TestingCheckRecordedSpans(GetRecording(s1), ` span a: tags: sb=1 unfinished= `); err != nil { t.Fatal(err) } if err := ImportRemoteSpans(s1, rec); err != nil { t.Fatal(err) } s1.Finish() if err := TestingCheckRecordedSpans(GetRecording(s1), ` span a: tags: sb=1 span remote op: tags: sb=1 x: 1 `); err != nil { t.Fatal(err) }}

优秀的个人博客,低调大师

浪潮云溪分布式数据库协议代码解析(2)

- 数据请求阶段 - Part 1 - 简单查询 1.客户端发送Query (‘Q’)消息给服务端,包含了一条字符串类型的SQL语句。 func(cn *conn) query(query string, args []driver.Value)(_ *rows, err error){ ... // Check to see if we can use the "simpleQuery"interface, which is // *much* faster than going through prepare/exec iflen(args)==0{ return cn.simpleQuery(query) } ...} 2.服务端收到Query消息,解析SQL语句,生成抽象语法树(AST),并传给执行器执行,获得结果。 func(c *conn) serveImpl( ctx context.Context, draining func()bool, sqlServer *sql.Server, reserved mon.BoundAccount, stopper *stop.Stopper,)error{ ...Loop: for{ typ, n, err = c.readBuf.ReadTypedMsg(&c.rd) if err !=nil{ break Loop } ... switch typ { case pgwirebase.ClientMsgSimpleQuery: ... case pgwirebase.ClientMsgExecute: ... case pgwirebase.ClientMsgParse: ... case pgwirebase.ClientMsgDescribe: ... case pgwirebase.ClientMsgBind: ... case pgwirebase.ClientMsgSync: ... } } ...} 3.服务端根据SQL结果,首先发送RowDescription(B:‘T’)消息,包含列的数量,列名,列的类型等参数。​​​​​​​ func(c *conn) writeRowDescription( ctx context.Context, columns []sqlbase.ResultColumn, formatCodes []pgwirebase.FormatCode, w io.Writer,)error{ c.msgBuilder.initMsg(pgwirebase.ServerMsgRowDescription) c.msgBuilder.putInt16(int16(len(columns))) for i, column :=range columns { ... c.msgBuilder.writeTerminatedString(column.Name) ... c.msgBuilder.putInt32(0)//Table OID (optional). c.msgBuilder.putInt16(0)//Column attribute ID (optional). c.msgBuilder.putInt32(int32(typ.oid)) c.msgBuilder.putInt16(int16(typ.size)) ... } ...} 4. RowDescription消息后面将跟着多个DataRow(B:‘D’)消息,每个DataRow消息包含一行的数据。 func(c *conn) bufferRow( ctx context.Context, row tree.Datums, formatCodes []pgwirebase.FormatCode, convsessiondata.DataConversionConfig, types []*types.T,){ c.msgBuilder.initMsg(pgwirebase.ServerMsgDataRow) c.msgBuilder.putInt16(int16(len(row))) for i, col :=range row { ... switch fmtCode { case pgwirebase.FormatText: c.msgBuilder.writeTextDatum(ctx, col, conv, types[i]) case pgwirebase.FormatBinary: c.msgBuilder.writeBinaryDatum(ctx, col, conv.Location, types[i]) ... } if err := c.msgBuilder.finishMsg(&c.writerState.buf); err !=nil{ panic(fmt.Sprintf("unexpected err from buffer: %s", err)) }} 5.发送CommandComplete(B:‘C’)消息表示这个SQL请求执行结束了。 6.服务端发送ReadyForQuery(‘Z’),通知客户端可以发送下一条SQL请求了。 func(r *commandResult) Close(ctx context.Context, t sql.TransactionStatusIndicator){ ... switch r.typ { case commandComplete: tag := cookTag( r.cmdCompleteTag, r.conn.writerState.tagBuf[:0], r.stmtType, r.rowsAffected, ) r.conn.bufferCommandComplete(tag) case parseComplete: r.conn.bufferParseComplete() case bindComplete: r.conn.bufferBindComplete() case closeComplete: r.conn.bufferCloseComplete() case readyForQuery: r.conn.bufferReadyForQuery(byte(t)) // The error is saved on conn.err. _ /* err */= r.conn.Flush(r.pos) ... } ...} 7.客户端根据接受SQL请求的结果。 func(cn *conn) simpleQuery(q string)(res *rows, err error){ b := cn.writeBuf('Q') b.string(q) cn.send(b) for{ t, r := cn.recv1() switch t { case'C','I': ... case'Z': ... case'E': ... case'D': ... case'T': ... } }} Part 2 - 扩展查询 1.客户端发送扩展查询请求,依次发送Parse (F:‘P’), Bind (F:‘B’), Describe (F:‘D’), Execute (F:‘E’), Sync(F:‘S’)消息。 func(cn *conn) query(query string, args []driver.Value)(_ *rows, err error){ ... if cn.binaryParameters { cn.sendBinaryModeQuery(query, args) cn.readParseResponse() cn.readBindResponse() rows :=&rows{cn: cn} rows.rowsHeader = cn.readPortalDescribeResponse() cn.postExecuteWorkaround() return rows,nil } ...} func(cn *conn) sendBinaryModeQuery(query string, args []driver.Value){ b := cn.writeBuf('P') b.byte(0)//unnamed statement b.string(query) b.int16(0) b.next('B') b.int16(0)//unnamed portal and statement cn.sendBinaryParameters(b, args) b.bytes(colFmtDataAllText) b.next('D') b.byte('P') b.byte(0)//unnamed portal b.next('E') b.byte(0) b.int32(0) b.next('S') cn.send(b)} 2.服务端处理扩展查询请求。 3.服务端发送回应消息,ParseComplete (B:‘1’), BindComplete (B:‘2’), ParameterDescription(B:‘t’), CommandComplete (B:‘C’), CloseComplete (B:‘3’), ReadyForQuery (B:‘Z’)。​​​​​​​ func(r *commandResult) Close(ctx context.Context, t sql.TransactionStatusIndicator){ ... switch r.typ { case commandComplete: tag := cookTag( r.cmdCompleteTag, r.conn.writerState.tagBuf[:0], r.stmtType, r.rowsAffected, ) r.conn.bufferCommandComplete(tag) case parseComplete: r.conn.bufferParseComplete() case bindComplete: r.conn.bufferBindComplete() case closeComplete: r.conn.bufferCloseComplete() case readyForQuery: r.conn.bufferReadyForQuery(byte(t)) // The error is saved on conn.err. _ /* err */= r.conn.Flush(r.pos) ... } ...}

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册