Go语言RPC开发深度指南:net/rpc包的实战技巧和优化策略

Go语言RPC开发深度指南:net/rpc包的实战技巧和优化策略

    • 概览
    • 理解`net/rpc`的核心概念
      • RPC的基本原理
      • `net/rpc`的工作模式
      • 关键特性
    • 快速开始
      • 准备RPC服务和客户端的基础环境
      • 构建一个基础的RPC服务端
      • 构建一个基础的RPC客户端
    • 开发一个实际的RPC服务
      • 设计服务接口
      • 实现服务
      • 客户端的构建与功能实现
    • 客户端的构建与优化
      • 如何构建一个高效的客户端
        • 1. **利用连接池管理网络连接**
        • 2. **实现异步调用**
      • 错误处理和异常
        • 1. **处理网络异常和服务错误**
        • 2. **优雅地处理超时和取消**
    • 服务端的性能优化
      • 性能调优技巧
        • 1. **利用并发**
        • 2. **优化资源使用**
        • 3. **减少锁的使用**
      • 并发处理
      • 资源管理
    • `net/rpc`的高级用法
      • 使用编解码器自定义数据格式
        • 自定义JSON编解码器示例
      • 上下文管理和超时控制
      • 安全性考虑和TLS支持
    • 调试与测试RPC应用
      • 日志记录
      • 性能分析
      • 单元测试和集成测试
        • 单元测试
        • 集成测试
    • 实战案例分析
      • 案例一:分布式计算服务
        • 服务端设计
        • 客户端设计
      • 案例二:异步任务队列
        • 服务端设计
        • 客户端设计
      • 小结
    • 总结
      • 主要学到的知识点

在这里插入图片描述

概览

在现代软件开发中,网络编程扮演着至关重要的角色,而远程过程调用(Remote Procedure Call,RPC)技术是实现分布式系统间通信的一种常见方法。net/rpc包是Go语言标凈库中提供的一个RPC实现,它支持高效的网络通信,允许客户端像调用本地函数一样调用远程服务器上的函数。

本文旨在为中高级Go开发者提供一个详尽的net/rpc包教程,内容将全面覆盖从基础用法到高级应用技巧,特别强调实际开发中的应用场景和性能优化。通过本文的学习,读者可以掌握构建RPC服务的完整流程,包括服务端和客户端的开发、调试、测试以及性能调优。文章将结合实际代码示例,逐步引导读者深入理解并有效利用net/rpc的强大功能。

我们不会涉及Go语言的安装过程或其发展历史,而是专注于技术和实战内容,确保读者能够直接将学到的知识应用到实际开发工作中。

理解net/rpc的核心概念

net/rpc包提供了一个简单但强大的RPC实现,使得开发分布式应用变得更加直接和高效。要有效利用这一技术,首先需要理解几个核心概念。

RPC的基本原理

远程过程调用(RPC)是一种技术,它允许一个程序调用另一个地址空间(通常是远程服务器)上的程序或服务,而调用者无需关心底层网络技术的细节。RPC的目标是使远程服务调用尽可能地像本地调用一样简单。

net/rpc的工作模式

net/rpc包使得实现RPC变得非常简单。它默认使用Go的编码方式(gob编码),但也支持其他数据编码方式,如JSON。该包主要包括两部分:服务器端和客户端。服务器端负责注册对象(服务),这些对象拥有可被远程访问的方法;客户端则通过网络调用这些方法。

关键特性

  • 服务注册: 在net/rpc中,你需要注册一个对象,这个对象的方法才能被远程访问。只有符合特定签名的方法才能被注册:方法必须是可导出的、有两个参数,且第二个参数是指针类型,方法还必须有一个返回值error
  • 网络透明性: 调用者无需知道底层的网络实现细节,net/rpc封装了所有的网络交互操作。
  • 同步与异步调用: 默认情况下,RPC调用是同步的,即调用者发起调用后将阻塞,直到收到响应。但net/rpc也支持异步操作,允许调用者继续执行其他任务,直到RPC操作完成。

这些核心概念为理解如何有效地使用net/rpc奠定了基础。接下来的章节将通过具体的示例展示如何创建RPC服务和客户端,并探讨各种实战技巧。

快速开始

为了帮助读者快速掌握net/rpc的使用,我们将从搭建一个简单的RPC服务和客户端开始。这将为后续更复杂的应用打下坚实的基础。

准备RPC服务和客户端的基础环境

首先,确保你的开发环境中已安装Go语言。接下来,创建两个文件:server.go用于服务器端代码,client.go用于客户端代码。

构建一个基础的RPC服务端

  1. 定义服务和方法
    创建一个名为Arithmetic的服务,提供一个名为Multiply的方法,用于计算两个整数的乘积。

    package mainimport ("net""net/rpc"
    )type Arithmetic inttype Args struct {A, B int
    }func (t *Arithmetic) Multiply(args *Args, reply *int) error {*reply = args.A * args.Breturn nil
    }func main() {arith := new(Arithmetic)rpc.Register(arith)listener, err := net.Listen("tcp", ":1234")if err != nil {panic(err)}defer listener.Close()rpc.Accept(listener)
    }
    
  2. 注册服务
    使用rpc.Register函数注册Arithmetic服务,使其可以接收远程调用。

  3. 启动服务监听
    在TCP端口1234上监听来自客户端的连接请求。

构建一个基础的RPC客户端

  1. 连接到RPC服务器
    客户端通过rpc.Dial连接到服务器。

    package mainimport ("fmt""log""net/rpc"
    )func main() {client, err := rpc.Dial("tcp", "localhost:1234")if err != nil {log.Fatal("Dialing:", err)}args := &Args{7, 6}var reply interr = client.Call("Arithmetic.Multiply", args, &reply)if err != nil {log.Fatal("Arithmetic error:", err)}fmt.Printf("Result: %d\n", reply)
    }
    
  2. 进行RPC调用
    使用client.Call发起远程过程调用。在这里,我们调用了Arithmetic.Multiply方法,并等待它返回结果。

通过这个基本示例,您已经可以看到net/rpc的基础用法。在这个框架下,您可以继续添加更多的方法和功能,增强服务的能力。

开发一个实际的RPC服务

在快速开始指南中,我们通过一个基本的例子介绍了如何设置RPC服务器和客户端。接下来,我们将深入探讨如何开发一个具体的、功能更丰富的RPC服务。

设计服务接口

设计良好的服务接口是构建高效RPC服务的关键。接口应当简洁明了,同时满足功能需求。假设我们要开发一个订单管理系统,其中包括增加订单、查询订单等功能。

  1. 定义服务及其方法

    • AddOrder:添加新订单。
    • GetOrder:根据订单ID获取订单详情。
    type OrderService struct{}type Order struct {ID       stringItemName stringQuantity intPrice    float64
    }type OrderID struct {ID string
    }func (s *OrderService) AddOrder(order *Order, reply *OrderID) error {// 实现添加订单到数据库的逻辑*reply = OrderID{ID: "生成的订单ID"}return nil
    }func (s *OrderService) GetOrder(orderID *OrderID, reply *Order) error {// 实现根据订单ID查询订单的逻辑*reply = Order{ID: orderID.ID, ItemName: "示例商品", Quantity: 1, Price: 99.99}return nil
    }
    

实现服务

  1. 注册和监听服务

    • 将服务对象注册到RPC系统。
    • 配置服务器以监听特定端口。
    func main() {orderService := new(OrderService)rpc.Register(orderService)listener, err := net.Listen("tcp", ":1234")if err != nil {log.Fatal("监听端口失败:", err)}defer listener.Close()fmt.Println("服务已启动,监听端口1234")rpc.Accept(listener)
    }
    

客户端的构建与功能实现

  1. 实现客户端调用

    • 客户端需要能够创建订单并查询订单详情。
    func main() {client, err := rpc.Dial("tcp", "localhost:1234")if err != nil {log.Fatal("连接服务失败:", err)}// 添加订单newOrder := &Order{ItemName: "新书", Quantity: 1, Price: 45.50}orderID := new OrderID{}err = client.Call("OrderService.AddOrder", newOrder, &orderID)if err != nil {log.Fatal("添加订单失败:", err)}fmt.Printf("新订单ID: %s\n", orderID.ID)// 查询订单queryOrder := new(Order)err = client.Call("OrderService.GetOrder", orderID, queryOrder)if err != nil {log.Fatal("查询订单失败:", err)}fmt.Printf("订单详情: %+v\n", queryOrder)
    }
    

通过这些步骤,我们构建了一个功能更全面的RPC服务。这种方式展示了如何为实际的业务需求设计和实现RPC接口。

客户端的构建与优化

构建RPC客户端不仅仅是实现功能调用那么简单,还需要考虑效率、错误处理和用户体验。本节将探讨如何构建一个高效的客户端,实现异步调用,以及如何处理各种可能的错误。

如何构建一个高效的客户端

1. 利用连接池管理网络连接

在高并发环境下,频繁地打开和关闭网络连接会极大地影响性能。使用连接池可以重用现有的连接,减少开销。

type RPCClientPool struct {pool *sync.Poolnetwork, address string
}func NewRPCClientPool(network, address string) *RPCClientPool {return &RPCClientPool{network: network,address: address,pool: &sync.Pool{New: func() interface{} {client, err := rpc.Dial(network, address)if err != nil {return nil}return client},},}
}func (p *RPCClientPool) GetClient() *rpc.Client {return p.pool.Get().(*rpc.Client)
}func (p *RPCClientPool) PutClient(client *rpc.Client) {p.pool.Put(client)
}
2. 实现异步调用

异步调用允许客户端在等待服务器响应的同时继续进行其他操作,提高了应用程序的响应性。

// 异步RPC调用示例
call := client.Go("OrderService.AddOrder", newOrder, &orderID, nil)
replyCall := <-call.Done
if replyCall.Error != nil {log.Fatal("添加订单失败:", replyCall.Error)
}
fmt.Printf("新订单ID: %s\n", orderID.ID)

错误处理和异常

1. 处理网络异常和服务错误

网络问题或服务端错误都可能导致RPC调用失败。合理处理这些错误对于构建可靠的应用至关重要。

if err := client.Call("Service.Method", args, &reply); err != nil {if err == rpc.ErrShutdown {// 处理服务关闭的情况fmt.Println("RPC服务已关闭")} else {// 记录错误并重试或返回错误log.Printf("RPC调用失败: %v", err)}
}
2. 优雅地处理超时和取消

控制调用超时对于维护良好的用户体验非常重要,尤其是在网络延迟或服务响应慢的情况下。

timeout := time.After(5 * time.Second)
done := make(chan bool)
go func() {err := client.Call("Service.Method", args, &reply)done <- (err == nil)
}()select {
case success := <-done:if !success {fmt.Println("操作失败,请重试")}
case <-timeout:fmt.Println("操作超时")
}

这些技术和策略将帮助开发者构建出更稳定、高效和用户友好的RPC客户端。

服务端的性能优化

在RPC服务中,服务端的性能优化是确保快速响应和处理大量并发请求的关键。本节将探讨一些关于性能调优、并发处理和资源管理的实用技巧。

性能调优技巧

1. 利用并发

Go 语言天生支持并发,这使得在服务端实现高效的并发处理变得相对简单。利用Go的goroutine,可以轻松地为每个RPC请求分配一个单独的执行线程。

func main() {listener, err := net.Listen("tcp", ":1234")if err != nil {log.Fatal("监听端口失败:", err)}defer listener.Close()fmt.Println("服务已启动,监听端口1234")for {conn, err := listener.Accept()if err != nil {log.Print("接受连接失败:", err)continue}go rpc.ServeConn(conn)}
}
2. 优化资源使用

避免在处理RPC请求时进行昂贵的操作,如频繁的磁盘I/O或数据库访问。应当尽可能利用缓存和内存存储中间结果。

3. 减少锁的使用

在多线程环境中,锁的使用可能成为性能瓶颈。尽可能使用无锁设计或其他并发控制机制,比如channel,来减少锁的争用。

并发处理

处理并发不仅仅是启动多个goroutine那么简单。它还涉及合理地设计数据访问策略和资源共享机制,以避免竞态条件和数据不一致。

  • 使用channel进行通信:在Go中,推荐使用channel而不是共享内存来进行goroutine之间的通信。
  • 控制goroutine的数量:虽然goroutine非常轻量,但过多的goroutine仍然会消耗大量内存和调度资源。合理控制goroutine的数量是优化并发处理的关键。

资源管理

合理管理内存和连接资源对于提高服务稳定性和性能至关重要。

  • 使用对象池:对于频繁创建和销毁的对象,使用对象池可以减少GC压力并提高性能。
  • 连接复用:对于数据库连接或其他外部服务连接,使用连接池进行管理和复用,避免频繁建立和断开连接。

通过以上技巧,开发者可以显著提高RPC服务的处理能力和效率,满足更高的业务需求。

net/rpc的高级用法

在掌握了net/rpc基础用法之后,了解其高级功能可以帮助我们构建更复杂、更灵活的RPC应用。本节将介绍一些高级技术,如自定义编解码器、使用上下文管理和超时控制,以及增强安全性。

使用编解码器自定义数据格式

默认情况下,net/rpc使用Go的gob编解码器进行数据序列化和反序列化。然而,你可以通过实现rpc.ServerCodecrpc.ClientCodec接口来使用其他编解码器,比如JSON,以支持不同的数据交互需求。

自定义JSON编解码器示例
type JsonCodec struct {conn io.ReadWriteCloserdec  *json.Decoderenc  *json.EncoderencBuf *bufio.Writer
}func NewJsonCodec(conn io.ReadWriteCloser) *JsonCodec {buf := bufio.NewWriter(conn)return &JsonCodec{conn: conn,dec:  json.NewDecoder(conn),enc:  json.NewEncoder(buf),encBuf: buf,}
}func (c *JsonCodec) ReadRequestHeader(r *rpc.Request) error {return c.dec.Decode(r)
}func (c *JsonCodec) ReadRequestBody(body interface{}) error {return c.dec.Decode(body)
}func (c *JsonCodec) WriteResponse(r *rpc.Response, body interface{}) (err error) {if err = c.enc.Encode(r); err != nil {return}if err = c.enc.Encode(body); err != nil {return}return c.encBuf.Flush()
}func (c *JsonCodec) Close() error {return c.conn.Close()
}

通过自定义编解码器,你可以轻松实现与其他语言编写的客户端或服务端的交互。

上下文管理和超时控制

net/rpc默认不支持传递上下文(context.Context),这在需要处理超时和取消操作时显得不够灵活。通过包装调用或使用中间件,我们可以增加这种支持。

func CallWithContext(ctx context.Context, client *rpc.Client, serviceMethod string, args, reply interface{}) error {done := client.Go(serviceMethod, args, reply, nil).Doneselect {case <-ctx.Done():return ctx.Err()case call := <-done:return call.Error}
}

安全性考虑和TLS支持

在进行RPC调用时,尤其是跨网络时,确保数据传输的安全是非常重要的。net/rpc可以结合crypto/tls包来实现基于TLS的连接加密。

config := &tls.Config{Certificates: []tls.Certificate{cert},// 更多的TLS配置...
}
conn, err := tls.Dial("tcp", "server:1234", config)
if err != nil {log.Fatal(err)
}
client := rpc.NewClient(conn)

这些高级技术使得net/rpc更加强大和灵活,能够满足更多复杂场景的需求。

调试与测试RPC应用

为确保RPC应用的稳定性和可靠性,有效的调试和测试策略是必不可少的。本节将介绍如何进行日志记录、性能分析以及如何实现RPC应用的单元测试和集成测试。

日志记录

日志是诊断问题的关键工具。在RPC服务中合理地记录日志可以帮助开发者追踪请求的流程,发现并解决问题。

func (t *Arithmetic) Multiply(args *Args, reply *int) error {log.Printf("Multiply called with: %d and %d", args.A, args.B)*reply = args.A * args.Blog.Printf("Multiply result: %d", *reply)return nil
}

性能分析

Go提供了强大的内置工具来进行性能分析,包括CPU和内存的使用情况。使用这些工具,你可以发现程序的瓶颈并进行优化。

import "net/http"
import _ "net/http/pprof"func main() {go func() {log.Println(http.ListenAndServe("localhost:6060", nil))}()// 其他服务代码
}

访问http://localhost:6060/debug/pprof,可以查看和下载性能分析数据。

单元测试和集成测试

单元测试

为RPC方法编写单元测试通常涉及到模拟网络请求和响应。可以使用Go的标准库net/rpc进行单元测试。

func TestArithmeticMultiply(t *testing.T) {arith := new(Arithmetic)args := Args{A: 7, B: 6}var reply interr := arith.Multiply(&args, &reply)if err != nil {t.Errorf("Multiply failed: %s", err)}if reply != 42 {t.Errorf("Multiply returned wrong result: got %v, want %v", reply, 42)}
}
集成测试

集成测试通常涉及到运行一个完整的RPC服务器和客户端,确保它们在真实环境下能正常工作。

func TestRPCIntegration(t *testing.T) {// 启动RPC服务go startRPCServer()time.Sleep(1 * time.Second) // 确保服务器启动client, err := rpc.Dial("tcp", "localhost:1234")if err != nil {t.Fatal("Dialing:", err)}// 测试RPC调用args := &Args{7, 6}var reply interr = client.Call("Arithmetic.Multiply", args, &reply)if err != nil {t.Fatal("Arithmetic error:", err)}if reply != 42 {t.Errorf("Wrong answer: %d != %d", reply, 42)}
}

通过这些调试和测试方法,你可以确保RPC应用的健売性和效率。

实战案例分析

在本节中,我们将通过一些具体的实战案例来展示net/rpc在现实世界中的应用。这些案例将帮助读者更深入地理解RPC技术的实际使用场景和解决方案。

案例一:分布式计算服务

假设我们需要开发一个分布式计算服务,该服务能够接收来自客户端的大量计算任务,分发到多个服务器节点进行处理,最后收集结果返回给客户端。

服务端设计
type ComputeService struct{}type Task struct {Data  []intOp    stringTaskID int
}type Result struct {TaskID intOutput int
}func (c *ComputeService) Execute(task *Task, result *Result) error {output := 0switch task.Op {case "sum":for _, num := range task.Data {output += num}case "multiply":output = 1for _, num := range task.Data {output *= num}}result.TaskID = task.TaskIDresult.Output = outputreturn nil
}func startServer() {compute := new(ComputeService)rpc.Register(compute)listener, _ := net.Listen("tcp", ":1234")defer listener.Close()rpc.Accept(listener)
}
客户端设计
func sendTasks() {client, _ := rpc.Dial("tcp", "localhost:1234")tasks := []Task{{Data: []int{1, 2, 3, 4, 5}, Op: "sum", TaskID: 1},{Data: []int{1, 2, 3, 4, 5}, Op: "multiply", TaskID: 2},}for _, task := range tasks {result := new(Result)client.Call("ComputeService.Execute", &task, result)fmt.Printf("Task %d: Result = %d\n", result.TaskID, result.Output)}
}

案例二:异步任务队列

假设我们的服务端负责接收任务,将任务存入队列,并异步执行。完成后,通过RPC回调通知客户端。

服务端设计
  1. 任务定义和队列管理
    服务端维护一个任务队列,每接收到一个任务,就将其加入队列,并由工作线程异步处理。
type Task struct {ID      intContent string
}type Result struct {ID     intStatus string
}type TaskQueue struct {tasks chan Task
}func NewTaskQueue(maxQueueSize int) *TaskQueue {return &TaskQueue{tasks: make(chan Task, maxQueueSize),}
}func (tq *TaskQueue) Enqueue(task Task) {tq.tasks <- task
}func (tq *TaskQueue) Worker(resultChan chan Result) {for task := range tq.tasks {// 模拟任务处理fmt.Printf("Processing task %d\n", task.ID)time.Sleep(time.Duration(rand.Intn(5)) * time.Second) // Random sleep to simulate workresultChan <- Result{ID: task.ID, Status: "Completed"}}
}
  1. RPC服务注册和启动工作线程
    注册RPC服务并启动工作线程,监听队列并处理任务。
type AsyncTaskService struct {Queue     *TaskQueueResultChan chan Result
}func (s *AsyncTaskService) SubmitTask(task *Task, reply *Result) error {s.Queue.Enqueue(*task)*reply = Result{ID: task.ID, Status: "Queued"}return nil
}func startServer() {taskQueue := NewTaskQueue(10)resultChan := make(chan Result)rpcServer := new(AsyncTaskService)rpcServer.Queue = taskQueuerpcServer.ResultChan = resultChanrpc.Register(rpcServer)listener, _ := net.Listen("tcp", ":1234")defer listener.Close()go taskQueue.Worker(resultChan)rpc.Accept(listener)
}
客户端设计

客户端发送任务到服务器,并可接收任务完成的通知。

func main() {client, _ := rpc.Dial("tcp", "localhost:1234")task := Task{ID: 1, Content: "Data processing"}result := Result{}err := client.Call("AsyncTaskService.SubmitTask", &task, &result)if err != nil {log.Fatal("Task submission failed:", err)}fmt.Printf("Task %d: %s\n", result.ID, result.Status)// 实现回调逻辑或其他形式的结果监听
}

小结

这个案例展示了如何使用net/rpc构建一个支持异步任务处理和结果通知的系统。服务端通过队列管理和工作线程来处理任务,客户端则通过RPC调用提交任务,并能获取任务状态。

总结

通过本文的学习,我们详细探讨了Go语言标准库中net/rpc包的使用,从基础概念到实际开发,再到性能优化和高级应用,最后通过具体案例展示了其在实战中的应用。这些内容不仅涵盖了RPC技术的基本操作,还包括了许多高级技巧,如自定义编解码器、上下文管理、异步处理以及安全性增强等。

主要学到的知识点

  1. net/rpc的基本使用:我们学习了如何设置RPC服务器和客户端,以及如何通过RPC进行基本的方法调用。
  2. 性能优化:介绍了服务端和客户端的性能优化技巧,包括并发处理、资源管理和连接复用等。
  3. 高级功能:探讨了使用自定义编解码器进行数据序列化,以及如何实现安全的RPC通信。
  4. 调试与测试:了解了如何使用日志、性能分析工具和测试方法来确保RPC应用的健壮性和有效性。
  5. 实战案例:通过实战案例,我们看到了net/rpc在处理分布式计算和异步任务队列中的实用性。

随着微服务和分布式系统的日益普及,对于高效、可靠的RPC框架的需求将持续增长。net/rpc虽然提供了基本的RPC功能,但在实际应用中,可能还需要结合其他技术和工具,如消息队列、服务发现和负载均衡等,来构建更复杂的服务架构。

我们希望本文能帮助读者在Go语言的RPC开发旅程中找到启发和帮助,并鼓励大家不断探索和实验,以解决更多复杂的技术挑战。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/353459.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

男士内裤哪个品牌质量好?国内质量好的男士内裤推荐

今天想和大家分享一个虽不起眼但至关重要的时尚单品——男士内裤。它可能不像外套或鞋子那样引人注目&#xff0c;但却承载着男士们日常的舒适与健康。选择一款合适的男士内裤&#xff0c;不仅能提升穿着体验&#xff0c;更是展现个人品味和生活态度的关键。以下是一些选择内裤…

JS读取目录下的所有图片/require动态加载图片/文字高亮

<template class"aa"><div class"demo-image__lazy container"><div class"head"><div class"left-bar"><div><span>综合</span></div><div><span>定位</span><…

睡眠脑电 | 多导睡眠图技术

摘要 多导睡眠图(PSG)一词由Holland等人于1974年提出&#xff0c;用于描述在睡眠期间同时记录、分析和解释多个生理特征。PSG是诊断睡眠障碍患者和增进我们对正常睡眠认识的重要工具。这是一个复杂的过程&#xff0c;应由训练有素的技术人员执行。本文回顾了多导睡眠图(PSG)的…

享元和代理模式

文章目录 享元模式1.引出享元模式1.展示网站项目需求2.传统方案解决3.问题分析 2.享元模式1.基本介绍2.原理类图3.外部状态和内部状态4.类图5.代码实现1.AbsWebSite.java 抽象的网站2.ConcreteWebSite.java 具体的网站&#xff0c;type属性是内部状态3.WebSiteFactory.java 网站…

JavaScript和promise——0_1 promise

文章目录 是什么&#xff1f;未来值回调和未来值在回调环境下这么和未来值交互&#xff1f;群居的未来值其他的解决方案 这样写可以实现目标效果。可是&#xff0c;这样写优雅吗&#xff1f; 英雄登场关键词&#xff1a;then关键词&#xff1a;回调 为什么promise不需要start函…

vue修改node_modules打补丁步骤和注意事项_node_modules 打补丁

1、vue-pdf问题解决及patch-package简介&#xff1a;https://www.jianshu.com/p/d1887e02f8d6 2、使用“黑魔法”优雅的修改第三方依赖包&#xff1a;https://zhuanlan.zhihu.com/p/412753695 3、使用patch-package定制node_modules中的依赖包&#xff1a;https://blog.csdn.…

自动驾驶仿真:Carsim转向传动比设置

文章目录 一、转向传动比概念二、设置转向传动比1、C factor概念2、Steer Kinematics概念3、传动比计算公式 三、转向传动比验证 一、转向传动比概念 转向传动比&#xff08;Steering Ratio&#xff09;表示方向盘转动角度与车轮转动角度之间的关系。公式如下&#xff1a; 转向…

电脑怎么录音?分享2种音频录制方法

在日常生活和工作中&#xff0c;我们经常需要录制电脑上的音频&#xff0c;无论是为了记录会议内容、保存网络课程&#xff0c;还是为了制作自己的音频素材&#xff0c;录音功能都显得尤为重要。那么电脑怎么录音&#xff1f;本文将详细介绍2种方法教你如何在电脑上进行录音&am…

Ajax的应用

1. Ajax Ajax是Asynchronous Javascript And XML&#xff08;异步JavaScript和XML&#xff09;的缩写。 Ajax技术描述了使用脚本操纵HTTP和Web服务器进行数据交换&#xff0c;在页面不刷新的情况下&#xff0c;实现页面的局部更新。 重点&#xff1a; Ajax 是一种在无需重新加…

C++设计模式——Facade外观模式

一&#xff0c;外观模式简介 外观模式是一种结构型设计模式&#xff0c; 又称为门面模式&#xff0c;也是一种基于创建对象来实现的模式&#xff0c;为子系统中的各组接口的使用提供了统一的访问入口。 外观模式对外提供了一个对象&#xff0c;让外部客户端(Client)对子系统的…

四十七、openlayers官网示例Image Filters——给地图添加锐化、浮雕、边缘等滤镜效果

官网demo示例&#xff1a; Image Filters 这篇讲的是如何给地图添加滤镜。 一看代码&#xff0c;&#xff0c;好家伙&#xff0c;信息量满满&#xff0c;全都看不懂。。。 咱只能一段一段扒。。。 首先添加一个底图到地图上&#xff0c;这个好理解。 const imagery new Til…

macOS vscode常用快捷键

1、shiftoption上下箭头 复制当前行 2、commandd 选定多个相同的单词 先双击选定一个单词&#xff0c;然后按下commandd 依次选中要修改的单词&#xff0c;直接修改即可 3、全局替换某个单词 comandh 4、快速定位到某一行 controlg 5、选中某个区域 shiftoption&#xff0c;然…

Adobe Photoshop cc快速抠图与精致抠图方法

一、背景 Photoshop cc绝对是最好用的抠图and修图软件&#xff0c;但是即使最简单的抠图&#xff0c;每次用时都忘记怎么做&#xff0c;然后再去B站搜&#xff0c;非常费时&#xff0c;下面记录一下抠图过程&#xff0c;方便查阅。 一、Adobe Photoshop快速抠图 选择——主体…

web系统数据库敏感数据处理

一、前言 web系统数据库中保存的公民信息不允许明文存储&#xff0c;比如手机号&#xff0c;身份证号&#xff0c;收货地址等。 二、处理方式 数据库中密文存储&#xff0c;web通过注解的方式对数据加解密处理&#xff0c;下面是处理方法 1、编写接口 public interface E…

提取人脸——OpenCV

提取人脸 导入所需的库创建窗口显示原始图片显示检测到的人脸创建全局变量定义字体对象定义一个函数select_image定义了extract_faces函数设置按钮运行GUI主循环运行显示 导入所需的库 tkinter&#xff1a;用于创建图形用户界面。 filedialog&#xff1a;用于打开文件对话框。 …

【JS】上传文件显示文件的为空,显示的文件参数内容只有uid

上传的文件参数file里面只包含uid&#xff0c;没有其他信息 例子解决办法 例子 例如使用elment ui的el-upload组件上传文件&#xff0c;会导致上传的文件参数file里面只包含uid&#xff0c;没有其他信息&#xff0c;如图&#xff1a; 正确应为如下图&#xff1a; 解决办法 …

MySQL 基本语法讲解及示例(上)

第一节&#xff1a;MySQL的基本操作 1. 创建数据库 在 MySQL 中&#xff0c;创建数据库的步骤如下&#xff1a; 命令行操作 打开 MySQL 命令行客户端或连接到 MySQL 服务器。 输入以下命令创建一个数据库&#xff1a; CREATE DATABASE database_name;例如&#xff0c;创建一…

了解Nest.js

一直做前端开发&#xff0c;都会有成为全栈工程师的想法&#xff0c;而 Nest 就是一个很好的途径&#xff0c;它是 Node 最流行的企业级开发框架&#xff0c;提供了 IOC、AOP、微服务等架构特性。接下来就让我们一起来学习Nest.js Nest.js官网地址 一&#xff0c;了解Nest Cli …

第十二章:会话控制

会话控制 文章目录 会话控制一、介绍二、cookie2.1 cookie 是什么2.2 cookie 的特点2.3 cookie 的运行流程2.4 浏览器操作 cookie2.5 cookie 的代码操作&#xff08;1&#xff09;设置 cookie&#xff08;2&#xff09;读取 cookie&#xff08;3&#xff09;删除 cookie 三、se…

56.SAP MII开发的一个系统响应错误 Error code: ICMETIMEOUT

问题 一个SAP MII开发的项目&#xff0c;最近新增了一个功能&#xff0c;查询数据源量比较大&#xff0c;逻辑有点复杂&#xff0c;大约7-8分钟。发布到生产系统后&#xff0c;发生响应错误&#xff0c;返回 Error code: ICMETIMEOUT <!-- Error code: ICMETIMEOUT -->\r…