【GeeRPC】Day3:服务注册(Service Register)
Day3:服务注册(Service Register)
今天的任务是:
- 通过反射实现服务注册功能;
- 在服务端实现服务调用,代码约 150 行;
结构体映射为服务
RPC 框架的一个基本能力是:像调用本地程序一样调用远程服务。如何将程序映射为服务呢?对于 Golang 而言,这个问题就变成了如何将结构体的方法映射为服务。
对 net/rpc
而言,一个函数需要能够被远程调用,需要满足如下五个条件:
- the method’s type is exported. 方法所属的类型是导出的;
- the method’s is exported. 方法是导出的;
- the method has two arguments, both exported (or builtin) types. 两个入参,均为导出或内置类型;
- this method’s second argument is a pointer. 第二个入参必须是指针;
- this method has return type error. 返回值为 error 类型。
更直观一些:
func (t *T) MethodName(argType T1, replyType *T2) error
假设客户端发送过来一个请求,包含 ServiceMethod 和 Argv:
{"ServiceMethod": "T.MethodName""Argv": "0101110101..." // 序列化之后的字节流
}
通过"T.MethodName"
可以确定调用的是类型 T 的 MethodName,如果硬编码实现这个功能,可能是这样的:
switch req.ServiceMethod {case "T.MethodName":t := new(T)reply := new(T2)var argv T1gob.NewDecoder(conn).Decode(&argv)err := t.MethodName(argv, reply)server.sendMessage(reply, err)case "Foo.Sum":f := new(Foo)...
}
即,如果采用硬编码的方式来实现结构体与服务的映射,每暴露一个方法,就需要编写等量的代码。借助反射,我们才能映射过程的自动化。
通过反射,我们可以非常容易地获取某个结构体的所有方法,并且能够通过方法,获得到该方法所有的参数类型和返回值:
func main() {var wg sync.WaitGrouptyp := reflect.TypeOf(&wg)for i := 0; i < typ.NumMethod(); i++ {method := typ.Method(i)argv := make([]string, 0, method.Type.NumIn())returns := make([]string, 0, method.Type.NumOut())// j 从 1 开始,第 0 个入参是 wg 自己。for j := 1; j < method.Type.NumIn(); j++ {argv = append(argv, method.Type.In(j).Name())}for j := 0; j < method.Type.NumOut(); j++ {returns = append(returns, method.Type.Out(j).Name())}log.Printf("func (w *%s) %s(%s) %s",typ.Elem().Name(),method.Name,strings.Join(argv, ","),strings.Join(returns, ","))}
}// OUTPUT:
func (w *WaitGroup) Add(int)
func (w *WaitGroup) Done()
func (w *WaitGroup) Wait()
通过反射实现 service
目前我们服务端的功能不是很完整,仅仅将请求的 header 打印了出来,而没有真正地处理。今天的目的是补全这部分功能。首先我们通过反射实现结构体与服务的映射关系,代码独立放置在service.go
当中。
package geerpcimport ("reflect""sync/atomic"
)// in service/service.gotype methodType struct {method reflect.Method // 反射方法对象,包含方法的信息ArgType reflect.Type // 方法的参数类型ReplyType reflect.Type // 方法的返回值类型numCalls uint64 // 方法被调用的次数
}func (m *methodType) NumCalls() uint64 {return atomic.LoadUint64(&m.numCalls)
}func (m *methodType) newArgv() reflect.Value {var argv reflect.Value// arg may be a pointer type or a value typeif m.ArgType.Kind() == reflect.Ptr {argv = reflect.New(m.ArgType.Elem())} else {argv = reflect.New(m.ArgType).Elem()}return argv // 返回一个 reflect.Value 对象, 表示新创建的参数值.
}func (m *methodType) newReplyv() reflect.Value {// reply must be a pointer typereplyv := reflect.New(m.ReplyType.Elem())switch m.ReplyType.Elem().Kind() {case reflect.Map:replyv.Elem().Set(reflect.MakeMap(m.ReplyType.Elem()))case reflect.Slice:replyv.Elem().Set(reflect.MakeSlice(m.ReplyType.Elem(), 0, 0))}return replyv // 返回一个 reflect.Value 对象, 表示新创建的返回值.
}
每一个 methodType 实例包含了一个方法的完整信息。包括:
- method:方法本身;
- ArgType:第一个参数的类型;
- ReplyType:第二个参数的类型;
- numCalls:后续统计方法调用次数时会用到。
另外,我们还实现了 2 个方法 newArgv
和 newReplyv
,用于创建对应类型的实例。
第二步,定义结构体 service:
type service struct {name stringtyp reflect.Typercvr reflect.Valuemethod map[string]*methodType
}
service 的定义非常简洁。name 是映射的结构体的名称,比如 T
、比如 WaitGroup
。typ 是结构体的类型。rcvr 是结构体的实例本身,保留 rcvr 是因为调用时需要 rcvr 作为第 0 个参数。method 是 map 类型,存储映射的结构体的所有符合条件的 method。
接下来,完成构造函数newService
,入参是任意需要映射为服务的结构体实例:
// newService 创建了一个新的服务实例
func newService(rcvr interface{}) *service {s := new(service)s.rcvr = reflect.ValueOf(rcvr) // 获取服务实例的值s.name = reflect.Indirect(s.rcvr).Type().Name() // 获取服务实例的类型名称s.typ = reflect.TypeOf(rcvr)if !ast.IsExported(s.name) {log.Fatalf("rpc server: %s is not a valid service name", s.name)}s.registerMethods() // 调用 registerMethods 方法注册对应服务的方法, registerMethods 在下面实现return s // 返回一个 service 实例
}// registerMethods 注册了服务的方法
func (s *service) registerMethods() {s.method = make(map[string]*methodType)for i := 0; i < s.typ.NumMethod(); i++ { // 遍历服务的所有方法method := s.typ.Method(i)mType := method.Type// 👇 检查方法的签名是否符合 RPC 方法的规范// 1. 方法必须有三个入参和一个输出参数if mType.NumIn() != 3 || mType.NumOut() != 1 {continue}// 2. 输出参数的类型必须是 error 类型if mType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {continue}argType, replyType := mType.In(1), mType.In(2)// 3. 参数和返回值的类型必须是导出的或内置类型if !isExportedOrBuiltinType(argType) || !isExportedOrBuiltinType(replyType) {continue}// 经过检查, 符合规定的方法注册到 s.method 当中s.method[method.Name] = &methodType{method: method,ArgType: argType,ReplyType: replyType,}// 日志: 输出注册的方法名log.Printf("rpc server: register %s.%s\n", s.name, method.Name)}
}// isExportedOrBuiltinType 的作用是检查类型是否是导出的或内置的类型
func isExportedOrBuiltinType(t reflect.Type) bool {return ast.IsExported(t.Name()) || t.PkgPath() == ""
}
registerMethods
过滤出了符合条件的方法:
- 两个导出或内置类型的入参(反射时为 3 个,第 0 个是自身,类似于 python 的 self,java 中的 this)
- 返回值有且只有一个,类型为 error。
最后,我们还需要实现 call 方法,即,能够通过反射值调用方法:
func (s *service) call(m *methodType, argv, replyv reflect.Value) error {/*s *service: 表示当前服务的实例m *methodType: 表示要调用的方法的信息argv reflect.Value: 表示方法的参数值replyv reflect.Value: 表示方法的返回值(通过指针的方式保存返回值)返回值: error*/atomic.AddUint64(&m.numCalls, 1) // 原子性地增加调用次数, 保证并发安全f := m.method.Func // 获取方法的反射函数returnValues := f.Call([]reflect.Value{s.rcvr, argv, replyv}) // 通过反射调用方法if errInter := returnValues[0].Interface(); errInter != nil { // 处理返回值return errInter.(error)}return nil
}
service 的测试用例
为了保证 service 实现的正确性,我们为 service.go 写了几个测试用例。
定义结构体 Foo,实现 2 个方法,导出方法 Sum 和非导出方法 sum:
package geerpcimport ("fmt""reflect""testing"
)type Foo inttype Args struct{ Num1, Num2 int }func (f Foo) Sum(args Args, reply *int) error {*reply = args.Num1 + args.Num2return nil
}func (f Foo) sum(args Args, reply *int) error {*reply = args.Num1 + args.Num2return nil
}func _assert(condition bool, msg string, v ...interface{}) {if !condition {panic(fmt.Sprintf("assertion failed: "+msg, v...))}
}func TestNewService(t *testing.T) {var foo Foos := newService(&foo)_assert(len(s.method) == 1, "wrong service Method, expect 1, but got %d", len(s.method))mType := s.method["Sum"]_assert(mType != nil, "wrong Method, Sum shouldn't nil")
}func TestMethodType_Call(t *testing.T) {var foo Foos := newService(&foo)mType := s.method["Sum"]argv := mType.newArgv()replyv := mType.newReplyv()argv.Set(reflect.ValueOf(Args{Num1: 1, Num2: 3}))err := s.call(mType, argv, replyv)_assert(err == nil && *replyv.Interface().(*int) == 4 && mType.NumCalls() == 1, "failed to call Foo.Sum")
}
集成到服务端
通过反射结构体已经映射为服务,但请求的处理还没有完成。从接收到请求到回复还差以下几个步骤:
- 根据入参类型,将请求的 body 反序列化;
- 调用
service.call
,完成方法调用; - 将 reply 序列化为字节流,构造响应报文,返回。
回到代码本身,补全之前在 server.go
中遗留的两个 TODO 任务 readRequest
和 handleRequest
即可。
在此之前,我们还需要在 Server 实现一个 Register
方法,配套实现 findService
方法,即通过 ServiceMethod
从 serviceMap 中找到对应的 service:
// Server represents an RPC Server.
type Server struct {serviceMap sync.Map
}// Register publishes in the server the set of methods
func (server *Server) Register(rcvr interface{}) error {s := newService(rcvr)if _, dup := server.serviceMap.LoadOrStore(s.name, s); dup {return errors.New("rpc: service already defined: " + s.name)}return nil
}// Register publishes the receiver's methods in the DefaultServer
func Register(rcvr interface{}) error { return DefaultServer.Register(rcvr) }func (server *Server) findService(serviceMethod string) (svc *service, mtype *methodType, err error) {dot := strings.LastIndex(serviceMethod, ".")if dot < 0 {err = errors.New("rpc server: service/method request ill-formed" + serviceMethod)return}serviceName, methodName := serviceMethod[:dot], serviceMethod[dot+1:]svci, ok := server.serviceMap.Load(serviceName)if !ok {err = errors.New("rpc server: can't find service " + serviceName)return}svc = svci.(*service)mtype = svc.method[methodName]if mtype == nil {err = errors.New("rpc server: can't find method " + methodName)}return
}
findService
看似繁琐,但逻辑非常清晰。ServiceMethod 的构成是“Service.Method”,因此首先将其分割为两部分,第一部分是 Service 的名称,第二部分是方法名。先在 ServiceMap 中找到对应的 service 实例,再从 service 实例的 method 中找到对应的 methodType。
准备工具已经就绪,首先补全 readRequest 方法:
// request stores all information of a call
type request struct {h *codec.Header // header of requestargv, replyv reflect.Value // argv and replyv of requestmtype *methodTypesvc *service
}func (server *Server) readRequest(cc codec.Codec) (*request, error) {h, err := server.readRequestHeader(cc)if err != nil {return nil, err}req := &request{h: h}req.svc, req.mtype, err = server.findService(h.ServiceMethod)if err != nil {return req, err}req.argv = req.mtype.newArgv()req.replyv = req.mtype.newReplyv()// make sure that argvi is a pointer, ReadBody need a pointer as parameterargvi := req.argv.Interface()if req.argv.Type().Kind() != reflect.Ptr {argvi = req.argv.Addr().Interface()}if err = cc.ReadBody(argvi); err != nil {log.Println("rpc server: read argv err:", err)}return req, nil
}
readRequest 方法中最重要的部分,就是通过newArgv()
和 newReplyv()
两个方法创建出两个入参实例,然后通过 cc.ReadBody()
将请求报文反序列化第一个入参 argv,在这里同样需要 argv 可能是值类型,也可能是指针类型,所以处理方式有点差异。
接下来补全 handleRequest 方法:
func (server *Server) handleRequest(cc codec.Codec, req *request, sending *sync.Mutex, wg *sync.WaitGroup) {defer wg.Done()err := req.svc.call(req.mtype, req.argv, req.replyv)if err != nil {req.h.Error = err.Error()server.sendResponse(cc, req.h, invalidRequest, sending)return}server.sendResponse(cc, req.h, req.replyv.Interface(), sending)
}
相对于 readRequest,handleRequest 的实现非常简单,通过req,.svc.call
完成方法调用,将 replyv 传递给 sendResponse 完成序列化即可。
到此,我们已经完成了今天的内容,成功在服务端实现了服务注册与调用。
Demo
最后,写一个main 来验证今天的成果:
package mainimport ("Geektutu/GeeRPC/geerpc""log""net""sync""time"
)// Step 1: 定义结构体 Foo 和方法 Sum
type Foo inttype Args struct{ Num1, Num2 int }func (f Foo) Sum(args Args, reply *int) error {*reply = args.Num1 + args.Num2return nil
}// Step 2: 注册 Foo 到 Server 当中, 并启动 RPC 服务
func startServer(addr chan string) {var foo Fooif err := geerpc.Register(&foo); err != nil {log.Fatal("register error:", err)}l, err := net.Listen("tcp", ":8080")if err != nil {log.Fatal("network error:", err)}log.Println("start rpc server on", l.Addr())addr <- l.Addr().String()geerpc.Accept(l)
}func main() {log.SetFlags(0)addr := make(chan string)go startServer(addr)client, _ := geerpc.Dial("tcp", <-addr)defer func() { _ = client.Close() }()time.Sleep(time.Second)// send request & receive responsevar wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go func(i int) {defer wg.Done()args := &Args{Num1: i,Num2: i * i,}var reply intif err := client.Call("Foo.Sum", args, &reply); err != nil {log.Fatal("call Foo.Sum error:", err)}log.Printf("%d + %d = %d", args.Num1, args.Num2, reply)}(i)}wg.Wait()
}// OUTPUT:
rpc server: register Foo.Sum
start rpc server on [::]:8080
3 + 9 = 12
0 + 0 = 0
1 + 1 = 2
4 + 16 = 20
2 + 4 = 6
相关文章:
【GeeRPC】Day3:服务注册(Service Register)
Day3:服务注册(Service Register) 今天的任务是: 通过反射实现服务注册功能;在服务端实现服务调用,代码约 150 行; 结构体映射为服务 RPC 框架的一个基本能力是:像调用本地程序一…...
c/c++蓝桥杯经典编程题100道(17)二叉树遍历
二叉树遍历 ->返回c/c蓝桥杯经典编程题100道-目录 目录 二叉树遍历 一、题型解释 二、例题问题描述 三、C语言实现 解法1:递归前序遍历(难度★) 解法2:迭代中序遍历(难度★★) 解法3:…...
mysql系统库介绍,数据字典(介绍,存储方式,常见表,访问权限),系统表(介绍,不同功能的表)
目录 mysql系统库 介绍 数据字典 介绍 不同版本下的存储方式 常见的数据字典表 访问权限 系统表 介绍 权限授予系统表 对象信息系统表 服务器端帮助系统表 时区系统表 mysql系统库 介绍 MySQL 默认创建 的特殊数据库,主要用于存储服务器运行时所需的信…...
如何在macOS上安装Ollama
安装Ollama 安装Ollama的步骤相对简单,以下是基本的安装指南: 访问官方网站:打开浏览器,访问Ollama的官方网站。 下载安装包:根据你的操作系统,选择相应的安装包进行下载。 运行安装程序:下载完…...
【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter6-集合引用类型
六、集合引用类型 Object 是 ECMAScript 中最常用的类型之一。虽然 Object 的实例没有多少功能,但很适合存储和在应用程序间交换数据。 显式地创建 Object 的实例有两种方式。第一种是使用 new 操作符和 Object 构造函数。另一种方式是使用对象字面量(ob…...
Spring Boot Actuator使用
说明:本文介绍Spring Boot Actuator的使用,关于Spring Boot Actuator介绍,下面这篇博客写得很好,珠玉在前,我就不多介绍了。 Spring Boot Actuator 简单使用 项目里引入下面这个依赖 <!--Spring Boot Actuator依…...
SwanLab x verl:可视化LLM强化学习后训练教程
文章目录 介绍Verl和SwanLab1. 环境安装2. 使用方法3. 查看训练日志 介绍Verl和SwanLab verl 是一个灵活、高效且可用于生产环境的强化学习(RL)训练框架,专为大型语言模型(LLMs)的后训练设计。它由字节跳动火山引擎团…...
linux安装oracle19c
安装 安装前检查配置: 挂载50g盘: vgcreate oravg /dev/sdb lvcreate -L 49.8G -n oralv oravg lvscan mkfs.xfs /dev/oravg/oralv 查看uuid blkid 复制分区表 cp /etc/fstab /etc/fstab.bakvi /etc/fstab内容为: /dev/oravg/oralv /u01 xfs defau…...
半导体制造工艺讲解
目录 一、半导体制造工艺的概述 二、单晶硅片的制造 1.单晶硅的制造 2.晶棒的切割、研磨 3.晶棒的切片、倒角和打磨 4.晶圆的检测和清洗 三、晶圆制造 1.氧化与涂胶 2.光刻与显影 3.刻蚀与脱胶 4.掺杂与退火 5.薄膜沉积、金属化和晶圆减薄 6.MOSFET在晶圆表面的形…...
VMware下Linux和macOS安装VSCode一些总结
本文介绍VMware下Linux和macOS安装VSCode的一些内容,包括VSCode编译器显示中文以及安装.NET环境和Python环境。 VSCode下载地址:Download Visual Studio Code - Mac, Linux, Windows 一.Linux系统下 1.安装中文包 按 Ctrl Shift P 打开命令面板。输…...
STC51 单片机中,定时器 / 计数器相关的寄存器
在 STC51 单片机中,定时器 / 计数器相关的寄存器主要有定时器控制寄存器(TCON)、定时器工作方式寄存器(TMOD)以及定时器初值寄存器(TH0、TL0、TH1、TL1),下面详细解释这些寄存器各位…...
DeepSeek与人工智能的结合:探索搜索技术的未来
云边有个稻草人-CSDN博客 目录 引言 一、DeepSeek的技术背景 1.1 传统搜索引擎的局限性 1.2 深度学习在搜索中的优势 二、DeepSeek与人工智能的结合 2.1 自然语言处理(NLP) 示例代码:基于BERT的语义搜索 2.2 多模态搜索 示例代码&…...
OpenCV:图像修复
目录 简述 1. 原理说明 1.1 Navier-Stokes方法(INPAINT_NS) 1.2 快速行进方法(INPAINT_TELEA) 2. 实现步骤 2.1 输入图像和掩膜(Mask) 2.2 调用cv2.inpaint()函数 2.3 完整代码示例 2.4 运行结果 …...
解决基于FastAPI Swagger UI的文档打不开的问题
基于FastAPI Swagger UI的文档链接/docs和/redoc在没有外网的状态下无法打开,原因是Swagger依赖的JS和CSS来自CDN。 https://cdn.jsdelivr.net/npm/swagger-ui-dist5/swagger-ui-bundle.js https://cdn.jsdelivr.net/npm/swagger-ui-dist5/swagger-ui.css https://…...
前端开发知识梳理 - HTMLCSS
1. 盒模型 由内容区(content)、内边距(padding)、边框(border)和外边距(margin)组成。 (1)标准盒模型(box-sizing默认值, content-boxÿ…...
Win10环境使用ChatBox集成Deep Seek解锁更多玩法
Win10环境使用ChatBox集成Deep Seek解锁更多玩法 前言 之前部署了14b的Deep Seek小模型,已经验证了命令行及接口方式的可行性。但是纯命令行或者PostMan方式调用接口显然不是那么友好: https://lizhiyong.blog.csdn.net/article/details/145505686 纯…...
LM Studio 部署本地大语言模型
一、下载安装 1.搜索:lm studio LM Studio - Discover, download, and run local LLMs 2.下载 3.安装 4.更改成中文 二、下载模型(软件内下载) 1.选择使用代理,否则无法下载 2.更改模型下载目录 默认下载位置 C:\Users\用户名\.lmstudio\models 3.搜…...
Qt:QWidget核心属性
目录 QWidget核心属性 enab geometry WindowFrame的影响 windowTitle windowIcon qrc文件管理资源 windowOpacity cursor font toolTip focusPolicy styleSheet QWidget核心属性 在Qt中使用QWidget类表示"控件",如按钮、视图、输入框、滚动…...
unity学习29:摄像机camera相关skybox 和 Render Texture测试效果
目录 1 摄像机 1.1 每个Scene里都自带一个摄像机 camera 1.2 可以创建多个camera 1.3 下面先看backgroundtype: 2 backgroundtype: 天空盒 skybox 2.1 清除标志,清除:天空盒 自选天空盒 2.2 window /Asset Store 2.3 导入skybox 3 backgroundtype: 纯色…...
吴恩达深度学习——卷积神经网络的特殊应用
内容来自https://www.bilibili.com/video/BV1FT4y1E74V,仅为本人学习使用。 文章目录 人脸识别相关定义Similarity函数使用Siamese网络实现函数d使用Triplet损失学习参数 神经风格迁移深度卷积网络可视化神经风格迁移的代价函数内容损失函数风格损失函数 人脸识别 …...
go语言文件和目录
打开和关闭文件 os.Open()函数能够打开一个文件,返回一个*File 和一个 err。操作完成文件对象以后一定要记得关闭文件。 package mainimport ("fmt""os" )func main() {// 只读方式打开当前目录下的 main.go 文件file, err : os.Open(".…...
c++ 面试题
C 面试题通常涵盖基础知识、面向对象编程、内存管理、模板、STL(标准模板库)等方面。以下是一些常见的 C 面试题及其简要解答,供你参考: 1. C 基础知识 1.1 C 和 C 的区别是什么? C 是 C 的超集,支持面向…...
JAVA安全—FastJson反序列化利用链跟踪autoType绕过
前言 FastJson这个漏洞我们之前讲过了,今天主要是对它的链条进行分析一下,明白链条的构造原理。 Java安全—log4j日志&FastJson序列化&JNDI注入_log4j漏洞-CSDN博客 漏洞版本 1.2.24及以下没有对序列化的类做校验,导致漏洞产生 1.2.25-1.2.41增加了黑名单限制,…...
Java Stream API:高效数据处理的利器引言
Java Stream API:高效数据处理的利器引言 在 Java 编程中,数据处理是一项极为常见且关键的任务。传统的 for 循环在处理数据集合时,往往会导致代码变得冗长、复杂,这不仅增加了代码的编写难度,还降低了代码的可读性和…...
kubeadm构建k8s源码阅读环境
目标 前面看了minikube的源码了解到其本质是调用了kubeadm来启动k8s集群,并没有达到最初看代码的目的。 所以继续看看kubeadm的代码,看看能否用来方便地构建源码调试环境。 k8s源码编译 kubeadm源码在k8s源码库中,所以要先克隆k8s源码。之…...
Java架构设计亿级流量场景下的本地缓存方案选型
在当今的互联网时代,亿级流量的应用场景已经司空见惯。无论是大型电商平台的促销活动,还是热门社交应用的日常运营,都可能面临每秒数万甚至数十万的请求流量。在这样的高并发、高流量场景下,系统的性能和稳定性面临着巨大的挑战。…...
ChatGPT怎么回事?
纯属发现,调侃一下~ 这段时间deepseek不是特别火吗,尤其是它的推理功能,突发奇想,想用deepseek回答一些问题,回答一个问题之后就回复服务器繁忙(估计还在被攻击吧~_~) 然后就转向了GPT…...
离线安装Appium Server
1、问题概述? 安装Appium通常有两种方式: 第一种:下载exe安装包,这种是Appium Server GUI安装方式,缺点是通过命令启动不方便。 第二种:通过cmd安装appium server,可以通过命令方式启动,比较方便。 问题:在没有外网的情况下,无法通过命令在cmd中安装appium server…...
Jetpack ViewModel
private val deviceViewModel: IDeviceViewModel by viewModels<DeviceViewModel>() 这句代码是 Jetpack ViewModel 在 Fragment 或 Activity 中的标准用法,它的作用是 创建并获取 ViewModel 实例,同时确保 ViewModel 的生命周期与 UI 组件保持一…...
2025年2月9日(数据分析,在最高点和最低点添加注释,添加水印)
要在最高点和最低点添加文本注释,可以使用 plt.annotate() 函数。这个函数允许你在图表中的特定位置添加文本注释,并且可以指定箭头指向特定的数据点。 以下是修改后的代码,添加了在最高点和最低点的文本注释: from matplotlib import pyplot as plt from matplotlib imp…...
如何导入第三方sdk | 引入第三方jar 包
0. 背景1. 上传私有仓库2. 使用本地文件系统 0. 背景 对接一些第三方功能,会拿到第三方的sdk,也就是jar包,如何导入呢 1. 上传私有仓库 最好的方式就是将第三方jar包,上传到私有的仓库,这样直接正常在pom引用即可如果只…...
掌握内容中台与人工智能技术的新闻和应用场景分析
内容概要 在当今数字化快速发展的时代,内容中台与人工智能技术的结合为各行各业带来了新的机遇。这一切都源自于对内容生产和管理能力的需求不断提升,尤其在新闻行业中更是如此。内容中台作为一种集中管理内容资源的平台,能够有效整合与调配…...
c#-枚举
//可空类型:int? num 等价 Nullable<int> num Nullable<int> a null; a 99; Console.WriteLine(a);//合并运算符?? : a有值的话,赋值给b int b a ?? 1; Console.WriteLine(b); 枚举成员不能相同,但枚举的值可…...
青少年编程与数学 02-008 Pyhon语言编程基础 22课题、类的定义和使用
青少年编程与数学 02-008 Pyhon语言编程基础 22课题、类的定义和使用 一、类类的定义和使用示例 二、定义1. 类定义语法2. 属性和方法3. 构造器和初始化4. 实例化5. 类变量和实例变量6. 类方法和静态方法7. 继承8. 多态总结 三、使用1. 创建类的实例2. 访问属性3. 调用方法4. 修…...
【通俗易懂说模型】反向传播(附多元回归与Softmax函数)
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀深度学习_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …...
【人工智能】Python中的深度学习优化器:从SGD到Adam
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在深度学习模型的训练过程中,优化器起着至关重要的作用,它决定了模型的收敛速度以及最终的性能。本文将介绍深度学习中常用的优化器,从传…...
仅128个token达到ImageNet生成SOTA性能!MAETok:有效的扩散模型的关键是什么?(卡内基梅隆港大等)
论文链接:https://arxiv.org/pdf/2502.03444 项目链接:https://github.com/Hhhhhhao/continuous_tokenizer 亮点直击 理论与实验分析:通过实验和理论分析建立了潜空间结构与扩散模型性能之间的联系。揭示了具有更少高斯混合模型(G…...
Listener监听器和Filter过滤器
一.监听器 1.是javaweb的三大组件之一,分别是Servlet程序,Listener监听器,Filter过滤器 2.Listener是JvaEE的规范,就是接口,监听器的作用就是监听某种变化(一般是对象创建/销毁,属性变化),触发对应方法完成相应的任务 3.ServletContextListener:/*当一个类实现了ServletContex…...
我的年度写作计划
目录 计算机经典四件 数据结构 计算机网络体系 经典操作系统与计算机架构 嵌入式领域笔记 其他部分 私货部分 笔者打算在这里理一下今年的写作计划,如下所示: 计算机经典四件 数据结构 笔者因为冲刺面试需要,还是要更加扎实的掌握自…...
kafka专栏解读
kafka专栏文章的编写将根据kafka架构进行编写,即先编辑kafka生产者相关的内容,再编写kafka服务端的内容(这部分是核心,内容较多,包含kafka分区管理、日志存储、延时操作、控制器、可靠性等),最后…...
数据库操作与数据管理——Rust 与 SQLite 的集成
第六章:数据库操作与数据管理 第一节:Rust 与 SQLite 的集成 在本节中,我们将深入探讨如何在 Rust 中使用 SQLite 数据库,涵盖从基本的 CRUD 操作到事务处理、数据模型的构建、性能优化以及安全性考虑等方面。SQLite 是一个轻量…...
Linux文件目录基本操作
目录 目录概述相关操作函数相关数据结构体说明 目录概述 什么是目录? 在linux操作系统中其实目录也是一种文件,相对于普通文件,它的存储内容不同,它的存储内容主要是当前目录下的文件以及子目录文件信息。目录就像是一颗大树&a…...
TaskBuilder项目实战:创建项目
用TaskBuilder开发应用系统的第一步就是创建项目,项目可以是一个简单的功能模块,也可以是很多功能模块的集合,具体怎么划分看各位的实际需要,我们一般会将相互关联比较紧密的一组功能模块放到一个独立的项目内,以便打包…...
使用DeepSeek的技巧笔记
来源:新年逼自己一把,学会使用DeepSeek R1_哔哩哔哩_bilibili 前言 对于DeepSeek而言,我们不再需要那么多的提示词技巧,但还是要有两个注意点:你需要理解大语言模型的工作原理与局限,这能帮助你更好的知道AI可完成任务…...
使用Python实现PDF与SVG相互转换
目录 使用工具 使用Python将SVG转换为PDF 使用Python将SVG添加到现有PDF中 使用Python将PDF转换为SVG 使用Python将PDF的特定页面转换为SVG SVG(可缩放矢量图形)和PDF(便携式文档格式)是两种常见且广泛使用的文件格式。SVG是…...
idea整合deepseek实现AI辅助编程
1.File->Settings 2.安装插件codegpt 3.注册deepseek开发者账号,DeepSeek开放平台 4.按下图指示创建API KEY 5.回到idea配置api信息,File->Settings->Tools->CodeGPT->Providers->Custom OpenAI API key填写deepseek的api key Chat…...
java文件上传粗糙版
粗糙版图片上传 1.导入依赖 <dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.2</version> </dependency> 2.配置minio地址跟对应的桶 业务层实现类 import io.minio.MinioClient; /…...
一种基于Leaflet.Legend的图例动态更新方法
目录 前言 一、场景再现 1、需求描述 2、核心方法介绍 3、存在的问题 二、问题解决 1、重复解决办法 2、图例不展示解决办法 3、成果展示 三、总结 前言 在当今数字化时代,地理信息系统(GIS)技术已经广泛应用于各个领域,…...
Vue Dom截图插件,截图转Base64 html2canvas
安装插件 npm install html2canvas --save插件使用 <template><div style"padding: 10px;"><div ref"imageTofile" class"box">发生什么事了</div><button click"toImage" style"margin: 10px;&quo…...
安宝特方案 | AR眼镜:远程医疗的“时空折叠者”,如何为生命争夺每一分钟?
行业痛点:当“千里求医”遇上“资源鸿沟” 20世纪50年代,远程会诊的诞生曾让医疗界为之一振——患者不必跨越山河,专家无需舟车劳顿,一根电话线、一张传真纸便能架起问诊的桥梁。然而,传统远程医疗的局限也日益凸显&a…...