40分钟学 Go 语言高并发:RPC服务开发实战
RPC服务开发实战
一、RPC服务基础概览
开发阶段 | 关键点 | 重要程度 | 考虑因素 |
---|---|---|---|
接口设计 | API定义、协议选择、版本控制 | ⭐⭐⭐⭐⭐ | 可扩展性、兼容性 |
服务实现 | 业务逻辑、并发处理、资源管理 | ⭐⭐⭐⭐⭐ | 性能、可靠性 |
错误处理 | 异常捕获、错误码、故障恢复 | ⭐⭐⭐⭐ | 稳定性、可维护性 |
性能测试 | 负载测试、压力测试、基准测试 | ⭐⭐⭐⭐ | 性能指标、瓶颈分析 |
让我们通过一个完整的订单服务示例来学习RPC服务开发:
syntax = "proto3";package order;
option go_package = "./pb";// 订单服务定义
service OrderService {// 创建订单rpc CreateOrder (CreateOrderRequest) returns (CreateOrderResponse);// 查询订单rpc GetOrder (GetOrderRequest) returns (GetOrderResponse);// 更新订单状态rpc UpdateOrderStatus (UpdateOrderStatusRequest) returns (UpdateOrderStatusResponse);// 取消订单rpc CancelOrder (CancelOrderRequest) returns (CancelOrderResponse);// 订单状态流式更新rpc WatchOrderStatus (WatchOrderRequest) returns (stream OrderStatusUpdate);
}// 订单信息
message Order {string order_id = 1;string user_id = 2;repeated OrderItem items = 3;OrderStatus status = 4;double total_amount = 5;int64 created_at = 6;int64 updated_at = 7;
}// 订单项
message OrderItem {string product_id = 1;int32 quantity = 2;double price = 3;
}// 订单状态
enum OrderStatus {UNKNOWN = 0;PENDING = 1;PAID = 2;PROCESSING = 3;SHIPPED = 4;DELIVERED = 5;CANCELLED = 6;
}// 创建订单请求
message CreateOrderRequest {string user_id = 1;repeated OrderItem items = 2;
}// 创建订单响应
message CreateOrderResponse {Order order = 1;
}// 查询订单请求
message GetOrderRequest {string order_id = 1;
}// 查询订单响应
message GetOrderResponse {Order order = 1;
}// 更新订单状态请求
message UpdateOrderStatusRequest {string order_id = 1;OrderStatus status = 2;
}// 更新订单状态响应
message UpdateOrderStatusResponse {Order order = 1;
}// 取消订单请求
message CancelOrderRequest {string order_id = 1;string reason = 2;
}// 取消订单响应
message CancelOrderResponse {bool success = 1;string message = 2;
}// 监听订单状态请求
message WatchOrderRequest {string order_id = 1;
}// 订单状态更新
message OrderStatusUpdate {string order_id = 1;OrderStatus status = 2;string message = 3;int64 timestamp = 4;
}
现在实现服务端代码:
package mainimport ("context""fmt""log""net""sync""time"pb "your_project/pb""google.golang.org/grpc""google.golang.org/grpc/codes""google.golang.org/grpc/status"
)type orderServer struct {pb.UnimplementedOrderServiceServermu sync.RWMutexorders map[string]*pb.OrderstatusSubs map[string][]chan *pb.OrderStatusUpdate
}func newOrderServer() *orderServer {return &orderServer{orders: make(map[string]*pb.Order),statusSubs: make(map[string][]chan *pb.OrderStatusUpdate),}
}// 生成订单ID
func generateOrderID() string {return fmt.Sprintf("ORD%d", time.Now().UnixNano())
}// 计算订单总金额
func calculateTotal(items []*pb.OrderItem) float64 {var total float64for _, item := range items {total += float64(item.Quantity) * item.Price}return total
}// 创建订单
func (s *orderServer) CreateOrder(ctx context.Context, req *pb.CreateOrderRequest) (*pb.CreateOrderResponse, error) {if req.UserId == "" {return nil, status.Error(codes.InvalidArgument, "user_id is required")}if len(req.Items) == 0 {return nil, status.Error(codes.InvalidArgument, "order items cannot be empty")}now := time.Now().Unix()order := &pb.Order{OrderId: generateOrderID(),UserId: req.UserId,Items: req.Items,Status: pb.OrderStatus_PENDING,TotalAmount: calculateTotal(req.Items),CreatedAt: now,UpdatedAt: now,}s.mu.Lock()s.orders[order.OrderId] = orders.mu.Unlock()return &pb.CreateOrderResponse{Order: order}, nil
}// 查询订单
func (s *orderServer) GetOrder(ctx context.Context, req *pb.GetOrderRequest) (*pb.GetOrderResponse, error) {s.mu.RLock()order, exists := s.orders[req.OrderId]s.mu.RUnlock()if !exists {return nil, status.Errorf(codes.NotFound, "order not found: %s", req.OrderId)}return &pb.GetOrderResponse{Order: order}, nil
}// 更新订单状态
func (s *orderServer) UpdateOrderStatus(ctx context.Context, req *pb.UpdateOrderStatusRequest) (*pb.UpdateOrderStatusResponse, error) {s.mu.Lock()defer s.mu.Unlock()order, exists := s.orders[req.OrderId]if !exists {return nil, status.Errorf(codes.NotFound, "order not found: %s", req.OrderId)}// 状态校验if !isValidStatusTransition(order.Status, req.Status) {return nil, status.Errorf(codes.FailedPrecondition, "invalid status transition from %v to %v", order.Status, req.Status)}order.Status = req.Statusorder.UpdatedAt = time.Now().Unix()// 通知订阅者s.notifyStatusUpdate(order)return &pb.UpdateOrderStatusResponse{Order: order}, nil
}// 取消订单
func (s *orderServer) CancelOrder(ctx context.Context, req *pb.CancelOrderRequest) (*pb.CancelOrderResponse, error) {s.mu.Lock()defer s.mu.Unlock()order, exists := s.orders[req.OrderId]if !exists {return nil, status.Errorf(codes.NotFound, "order not found: %s", req.OrderId)}if order.Status != pb.OrderStatus_PENDING && order.Status != pb.OrderStatus_PAID {return &pb.CancelOrderResponse{Success: false,Message: "order cannot be cancelled in current status",}, nil}order.Status = pb.OrderStatus_CANCELLEDorder.UpdatedAt = time.Now().Unix()// 通知订阅者s.notifyStatusUpdate(order)return &pb.CancelOrderResponse{Success: true,Message: "order cancelled successfully",}, nil
}// 监听订单状态
func (s *orderServer) WatchOrderStatus(req *pb.WatchOrderRequest, stream pb.OrderService_WatchOrderStatusServer) error {if req.OrderId == "" {return status.Error(codes.InvalidArgument, "order_id is required")}s.mu.RLock()_, exists := s.orders[req.OrderId]s.mu.RUnlock()if !exists {return status.Errorf(codes.NotFound, "order not found: %s", req.OrderId)}// 创建状态更新通道updates := make(chan *pb.OrderStatusUpdate, 10)s.mu.Lock()s.statusSubs[req.OrderId] = append(s.statusSubs[req.OrderId], updates)s.mu.Unlock()defer func() {s.mu.Lock()// 移除订阅subs := s.statusSubs[req.OrderId]for i, ch := range subs {if ch == updates {s.statusSubs[req.OrderId] = append(subs[:i], subs[i+1:]...)break}}s.mu.Unlock()close(updates)}()// 监听状态更新for update := range updates {if err := stream.Send(update); err != nil {return err}}return nil
}// 通知订阅者状态更新
func (s *orderServer) notifyStatusUpdate(order *pb.Order) {update := &pb.OrderStatusUpdate{OrderId: order.OrderId,Status: order.Status,Message: fmt.Sprintf("Order status updated to %v", order.Status),Timestamp: time.Now().Unix(),}// 发送更新给所有订阅者for _, ch := range s.statusSubs[order.OrderId] {select {case ch <- update:default:// channel已满,跳过}}
}// 验证状态转换是否有效
func isValidStatusTransition(from, to pb.OrderStatus) bool {switch from {case pb.OrderStatus_PENDING:return to == pb.OrderStatus_PAID || to == pb.OrderStatus_CANCELLEDcase pb.OrderStatus_PAID:return to == pb.OrderStatus_PROCESSING || to == pb.OrderStatus_CANCELLEDcase pb.OrderStatus_PROCESSING:return to == pb.OrderStatus_SHIPPEDcase pb.OrderStatus_SHIPPED:return to == pb.OrderStatus_DELIVEREDdefault:return false}
}func main() {lis, err := net.Listen("tcp", ":50051")if err != nil {log.Fatalf("failed to listen: %v", err)}s := grpc.NewServer(grpc.UnaryInterceptor(orderInterceptor),)pb.RegisterOrderServiceServer(s, newOrderServer())log.Println("Order server starting on :50051...")if err := s.Serve(lis); err != nil {log.Fatalf("failed to serve: %v", err)}
}// 订单服务拦截器
func orderInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {start := time.Now()resp, err := handler(ctx, req)duration := time.Since(start)// 记录请求日志log.Printf("Method: %s, Duration: %v, Error: %v",info.FullMethod,duration,err,)return resp, err
}
让我们创建服务的工作流程图:
现在添加性能测试代码:
package mainimport ("context""log""sync""testing""time"pb "your_project/pb""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"
)func BenchmarkCreateOrder(b *testing.B) {// 建立连接conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {b.Fatalf("did not connect: %v", err)}defer conn.Close()client := pb.NewOrderServiceClient(conn)ctx := context.Background()// 准备测试数据testOrder := &pb.CreateOrderRequest{UserId: "test-user",Items: []*pb.OrderItem{{ProductId: "prod-1",Quantity: 1,Price: 99.99,},},}b.ResetTimer()b.RunParallel(func(pb *testing.PB) {for pb.Next() {_, err := client.CreateOrder(ctx, testOrder)if err != nil {b.Errorf("CreateOrder failed: %v", err)}}})
}// 并发压力测试
func TestOrderConcurrency(t *testing.T) {conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {t.Fatalf("did not connect: %v", err)}defer conn.Close()client := pb.NewOrderServiceClient(conn)ctx := context.Background()concurrentUsers := 100ordersPerUser := 10var wg sync.WaitGrouperrorChan := make(chan error, concurrentUsers*ordersPerUser)start := time.Now()for i := 0; i < concurrentUsers; i++ {wg.Add(1)go func(userID int) {defer wg.Done()for j := 0; j < ordersPerUser; j++ {order := &pb.CreateOrderRequest{UserId: fmt.Sprintf("user-%d", userID),Items: []*pb.OrderItem{{ProductId: fmt.Sprintf("prod-%d", j),Quantity: 1,Price: 99.99,},},}_, err := client.CreateOrder(ctx, order)if err != nil {errorChan <- err}}}(i)}// 等待所有goroutine完成wg.Wait()close(errorChan)duration := time.Since(start)totalOrders := concurrentUsers * ordersPerUserordersPerSecond := float64(totalOrders) / duration.Seconds()// 统计错误var errorCount intfor err := range errorChan {t.Errorf("Order creation error: %v", err)errorCount++}t.Logf("Performance Results:")t.Logf("Total Orders: %d", totalOrders)t.Logf("Total Time: %v", duration)t.Logf("Orders/Second: %.2f", ordersPerSecond)t.Logf("Error Rate: %.2f%%", float64(errorCount)/float64(totalOrders)*100)
}// 延迟测试
func TestOrderLatency(t *testing.T) {conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {t.Fatalf("did not connect: %v", err)}defer conn.Close()client := pb.NewOrderServiceClient(conn)ctx := context.Background()var latencies []time.DurationtestCount := 100for i := 0; i < testCount; i++ {order := &pb.CreateOrderRequest{UserId: "latency-test",Items: []*pb.OrderItem{{ProductId: "prod-1",Quantity: 1,Price: 99.99,},},}start := time.Now()_, err := client.CreateOrder(ctx, order)latency := time.Since(start)latencies = append(latencies, latency)if err != nil {t.Errorf("CreateOrder failed: %v", err)}time.Sleep(time.Millisecond * 100) // 避免过度压力}// 计算统计信息var totalLatency time.Durationvar maxLatency time.Durationvar minLatency = latencies[0]for _, l := range latencies {totalLatency += lif l > maxLatency {maxLatency = l}if l < minLatency {minLatency = l}}avgLatency := totalLatency / time.Duration(len(latencies))t.Logf("Latency Results:")t.Logf("Average Latency: %v", avgLatency)t.Logf("Max Latency: %v", maxLatency)t.Logf("Min Latency: %v", minLatency)
}// 负载测试监控器
type LoadTestMonitor struct {successCount int64errorCount int64totalLatency time.Durationmu sync.Mutex
}func (m *LoadTestMonitor) recordSuccess(latency time.Duration) {m.mu.Lock()defer m.mu.Unlock()m.successCount++m.totalLatency += latency
}func (m *LoadTestMonitor) recordError() {m.mu.Lock()defer m.mu.Unlock()m.errorCount++
}func (m *LoadTestMonitor) getStats() (int64, int64, time.Duration) {m.mu.Lock()defer m.mu.Unlock()avgLatency := time.Duration(0)if m.successCount > 0 {avgLatency = m.totalLatency / time.Duration(m.successCount)}return m.successCount, m.errorCount, avgLatency
}func TestLoadScenarios(t *testing.T) {conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {t.Fatalf("did not connect: %v", err)}defer conn.Close()client := pb.NewOrderServiceClient(conn)ctx := context.Background()scenarios := []struct {name stringconcurrency intduration time.Duration}{{"Light Load", 10, 30 * time.Second},{"Medium Load", 50, 30 * time.Second},{"Heavy Load", 100, 30 * time.Second},}for _, scenario := range scenarios {t.Run(scenario.name, func(t *testing.T) {monitor := &LoadTestMonitor{}var wg sync.WaitGroup// 启动工作goroutinefor i := 0; i < scenario.concurrency; i++ {wg.Add(1)go func(workerID int) {defer wg.Done()start := time.Now()for time.Since(start) < scenario.duration {order := &pb.CreateOrderRequest{UserId: fmt.Sprintf("load-test-%d", workerID),Items: []*pb.OrderItem{{ProductId: "prod-1",Quantity: 1,Price: 99.99,},},}requestStart := time.Now()_, err := client.CreateOrder(ctx, order)if err != nil {monitor.recordError()} else {monitor.recordSuccess(time.Since(requestStart))}time.Sleep(time.Millisecond * 100) // 控制请求速率}}(i)}wg.Wait()success, errors, avgLatency := monitor.getStats()t.Logf("Scenario: %s", scenario.name)t.Logf("Success Count: %d", success)t.Logf("Error Count: %d", errors)t.Logf("Average Latency: %v", avgLatency)t.Logf("Requests/Second: %.2f", float64(success+errors)/scenario.duration.Seconds())})}
}
这里我们为订单服务实现了一套完整的性能测试方案,包括:
-
基准测试
- 测试单个API的性能
- 并发测试
- 可重复性测试
-
并发压力测试
- 模拟多用户场景
- 测试系统稳定性
- 性能数据收集
-
延迟测试
- 响应时间测试
- 统计分析
- 性能瓶颈定位
-
负载测试
- 不同负载场景
- 系统极限测试
- 性能监控
五、性能优化建议
-
接口层面
- 使用流式处理大数据
- 批量处理请求
- 合理设置超时
-
服务层面
- 使用连接池
- 优化资源使用
- 缓存热点数据
-
系统层面
- 调整系统参数
- 监控资源使用
- 及时处理故障
六、总结
本节课我们实现了一个完整的RPC订单服务,涵盖了:
-
服务设计与实现
- 接口定义
- 业务逻辑
- 错误处理
-
性能测试与优化
- 基准测试
- 压力测试
- 监控分析
-
最佳实践
- 代码组织
- 错误处理
- 性能优化
怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!
相关文章:
40分钟学 Go 语言高并发:RPC服务开发实战
RPC服务开发实战 一、RPC服务基础概览 开发阶段关键点重要程度考虑因素接口设计API定义、协议选择、版本控制⭐⭐⭐⭐⭐可扩展性、兼容性服务实现业务逻辑、并发处理、资源管理⭐⭐⭐⭐⭐性能、可靠性错误处理异常捕获、错误码、故障恢复⭐⭐⭐⭐稳定性、可维护性性能测试负载…...
Linux 无界面模式下使用 selenium
文章目录 前言什么是无界面模式?具体步骤安装谷歌浏览器查看安装的谷歌浏览器的版本下载对应版本驱动并安装Python 测试代码 总结个人简介 前言 在 Linux 服务器上运行自动化测试或网页爬虫时,常常需要使用 Selenium 来驱动浏览器进行操作。然而&#x…...
算法第一弹-----双指针
目录 1.移动零 2.复写零 3.快乐数 4.盛水最多的容器 5.有效三角形的个数 6.查找总价值为目标值的两个商品 7.三数之和 8.四数之和 双指针通常是指在解决问题时,同时使用两个指针(变量,常用来指向数组、链表等数据结构中的元素位置&am…...
学习如何解决“区间划分”问题(一般方法论+实例应用讲解)
文章目录 解决“区间划分”问题的一般方法论方法论:解决区间划分问题的四步法1. 问题分析与建模2. 动态规划状态的定义3. 状态转移方程4. 初始条件与边界 方法论应用:最小和最大石子合并得分问题描述步骤 1:问题分析与建模步骤 2:…...
消息中间件-Kafka2-3.9.0源码构建
消息中间件-Kafka2-3.9.0源码构建 1、软件环境 JDK Version 1.8Scala Version 2.12.0Kafka-3.9.0 源码包 下载地址:https://downloads.apache.org/kafka/3.9.0/kafka-3.9.0-src.tgzGradle Version > 8.8Apache Zookeeper 3.7.0 2、源码编译 打开源码根目录修改…...
达梦归档文件名与实例对应关系
默认的,达梦归档文件名比较难以看懂,且多实例下不好区分 靠它就行 select upper(to_char((select DB_MAGIC), xxxxxxxxxx)) mag_id; 这样就对上号了。...
STL算法之sort
STL所提供的各式各样算法中,sort()是最复杂最庞大的一个。这个算法接受两个RandomAccessIterators(随机存取迭代器),然后将区间内的所有元素以渐增方式由小到大重新排列。还有一个版本则是允许用户指定一个仿函数代替operator<作为排序标准。STL的所有…...
elementui table滚动分页加载
文章目录 概要 简化的实现示例: 小结 概要 在使用 Element UI 的 Table 组件时,如果需要实现滚动分页加载的功能,可以通过监听 Table 的滚动事件来动态加载更多数据。 简化的实现示例: <template><el-table ref"…...
【MySQL 进阶之路】索引的使用
5.索引的使用规则 在数据库管理系统(DBMS)中,索引是提高查询效率的关键机制之一。MySQL索引优化是指通过设计、调整和选择合适的索引策略,以提高数据库的查询性能和降低资源消耗。以下是一些关键的索引使用规则: 1. …...
FPGA中所有tile介绍
FPGA中包含的tile类型,以xinlinx 7k为例,可以通过f4pga项目中的原语文件夹查看,主要包含以下这些: 以下是您提到的 Xilinx 7 系列 FPGA 中各种模块的含义及用途: 1. BRAM (Block RAM) BRAM 是 FPGA 中的块存储资源&…...
理解 Python PIL库中的 convert(‘RGB‘) 方法:为何及如何将图像转换为RGB模式
理解 Python PIL库中的 convert(RGB) 方法:为何及如何将图像转换为RGB模式 在图像处理中,保持图像数据的一致性和可操作性是至关重要的。Python的Pillow库(继承自PIL, Python Imaging Library)提供了强大的工具和方法来处理图像&…...
LVS默认的工作模式支持哪些负载均衡算法?
LVS默认的工作模式支持哪些负载均衡算法? LVS(Linux Virtual Server)默认支持多种负载均衡算法,这些算法在不同的场景下具有各自的优势。以下是 LVS 默认支持的负载均衡算法及其特点: 1. 轮询调度(Round Robin Sched…...
C/C++中的调用约定
在C/C编程中,调用约定(calling conventions)是一组指定如何调用函数的规则。主要在你调用代码之外的函数(例如OS API,操作系统应用程序接口)或OS调用你(如WinMain的情况)时起作用。如果编译器不知道正确的调用约定,那么你很可能会遇到非常奇怪…...
RAG评估指南:从检索到生成,全面解析LLM性能评估方法
前言 这一节我们将从时间线出发对RAG的评估方式进行对比,这些评估方式不仅限于RAG流程之中,其中基于LLM的评估方式更加适用于各行各业。 RAG常用评估方式 上一节我们讲了如何用ROUGE 这个方法评估摘要的相似度,由于篇幅限制,没…...
极兔速递开放平台快递物流查询API对接流程
目录 极兔速递开放平台快递物流查询API对接流程API简介物流查询API 对接流程1. 注册用户2. 申请成为开发者3. 企业认证4. 联调测试5. 发布上线 签名机制详解1. 提交方式2. 签名规则3. 字段类型与解析约定 物流轨迹服务极兔快递单号查询的其他方案总结 极兔速递开放平台快递物流…...
FFmpeg:强大的音视频处理工具指南
FFmpeg:强大的音视频处理工具指南 1. FFmpeg简介2. 核心特性2.1 基础功能2.2 支持的格式和编解码器 3. 主要组件3.1 命令行工具3.2 开发库 4. 最新发展5. 安装指南5.1 Windows系统安装5.1.1 直接下载可执行文件5.1.2 使用包管理器安装 5.2 Linux系统安装5.2.1 Ubunt…...
项目集成篇:springboot集成redistemple实现自定义缓存,并且可以设置过期时间
在Spring Boot中集成Redis并使用RedisTemplate实现自定义缓存功能,同时能够设置缓存项的过期时间,可以通过以下步骤来完成。我们将创建一个服务层方法,该方法将使用RedisTemplate直接与Redis交互,并为每个缓存项设置特定的过期时间…...
ClickHouse守护进程
背景描述 维护CK过程中,有时候会有CK OOM,并且CK自己没有自动拉起的情况出现;那么这个时候就需要守护进程,最初我不说了Supervisor来做守护进程,但是当我手动kill的时候发现并没有自动拉起。 解决方案 于是乎自己写…...
【Vivado】xdc约束文件编写
随手记录一下项目中学到的约束文件编写技巧。 时序约束 创建生成时钟 参考链接: Vivado Design Suite Tcl Command Reference Guide (UG835) Vivado Design Suite User Guide: Using Constraints (UG903) 通过Clocking Wizard IP创建的时钟(MMCM或…...
Nginx静态资源配置
基本配置原则 明确资源目录:为不同类型的静态资源指定不同的路径,这样可以避免路径冲突,并且便于管理。正确设置文件权限:确保 Nginx 具有读取静态资源的权限。缓存优化:为静态资源设置缓存头(如 expires&…...
365天深度学习训练营-第P7周:马铃薯病害识别(VGG-16复现)
文为「365天深度学习训练营」内部文章 参考本文所写记录性文章,请在文章开头带上「👉声明」 🍺 要求: 自己搭建VGG-16网络框架【达成√】调用官方的VGG-16网络框架【达成√】如何查看模型的参数量以及相关指标【达成√】 &#…...
docker学习笔记(三)--容器数据卷
文章目录 一、数据卷的介绍二、简单用法--直接指定挂载路径三、具名挂载与匿名挂载具名挂载匿名挂载 一、数据卷的介绍 docker将应用和环境打包成一个镜像,形成一个容器运行。那么容器产生的数据,如果不通过docker commit命令提交生成新的镜像ÿ…...
联通光猫DT741-csf 完全po解 改桥接
1.管理员密码破解,把光猫的loid pppoe用户名密码,各个连接vlan id记下来 打开链接 http://192.168.1.1/hidden_version_switch.html version选择Default Version,点击submit,光猫默认重启。重启后ip地址变为192.168.1.1 并且dhcp…...
Java Web 2 JS Vue快速入门
一 JS快速入门 1.什么是JavaScript? 页面交互: 页面交互是指用户与网页之间的互动过程。例如,当用户点击一个按钮,网页会做出相应的反应,如弹出一个对话框、加载新的内容或者改变页面的样式等;当用户在表…...
【数据结构】动态规划-基础篇
针对动态规划问题,我总结了以下5步: 确定dp数组以及下标的含义; 递推公式; dp数组如何初始化; 遍历顺序; 打印dp数组(用来debug); 以上5步适用于任何动态规划问题&#x…...
从watch、watchEffect、useEffect原理到vue、react响应原理
正文 1.核心原理 Vue中的watch、watchEffect是基于Vue的响应式系统(Proxy),依赖于ref或reactive数据的变化。React中的useEffect基于状态驱动的重新渲染机制,通过依赖数组 [dependency],手动声明需要追踪的状态或属性…...
Cursor+Devbox AI开发快速入门
1. 前言 今天无意间了解到 Cursor 和 Devbox 两大开发神器,初步尝试以后发现确实能够大幅度提升开发效率,特此想要整理成博客以供大家快速入门. 简单理解 Cursor 就是一款结合AI大模型的代码编辑器,你可以将自己的思路告诉AI,剩下的目录结构的搭建以及项目代码的实现均由AI帮…...
SpringBoot+MyBatis整合ClickHouse实践
整合Spring Boot、MyBatis和ClickHouse可以让你使用Java开发的应用程序高效地与ClickHouse数据库进行交互。以下是一个基本的步骤指南,帮助你完成这个整合过程: 1. 添加依赖 首先,在你的pom.xml文件中添加必要的Maven依赖。你需要引入Sprin…...
在数据库设计中同步冗余字段的思考与实践
目录 前言1. 冗余字段设计的背景与场景1.1 场景描述1.2 冗余字段的必要性 2. 冗余字段设计的优点2.1 提高查询效率2.2 简化应用逻辑 3. 冗余字段设计的缺点与挑战3.1 数据不一致问题3.2 更新开销增加3.3 数据冗余占用存储空间 4. 如何同步更新冗余字段4.1 手动更新方式4.2 使用…...
MacOS安装sshfs挂载远程电脑硬盘到本地
文章目录 sshfs简介sshfs安装下载安装macFUSE安装sshfs sshfs使用注意事项 sshfs简介 SSHFS(SSH Filesystem)是一种基于FUSE(用户空间文件系统)的文件系统,它允许你通过SSH协议挂载远程文件系统。使用SSHFS࿰…...
6.824/6.5840(2024)环境配置wsl2+vscode
本文是经过笔者实践得出的最速の环境配置 首先,安装wsl2和vscode 具体步骤参见Mit6.s081环境配置踩坑之旅WSL2VScode_mit6s081-CSDN博客 接下来开始为Ubuntu(笔者使用的版本依然是20.04)配置go的相关环境 1、更新Ubuntu的软件包 sudo apt-get install build-es…...
查询产品所涉及的表有(product、product_admin_mapping)
文章目录 1、ProductController2、AdminCommonService3、ProductApiService4、ProductCommonService5、ProductSqlService1. 完整SQL分析可选部分(条件筛选): 2. 涉及的表3. 总结4. 功能概述 查询指定管理员下所有产品所涉及的表?…...
C# 冒泡的算法
C# 冒泡的算法 public void BubbleSort(int[] arr) {int temp;for (int j 0; j < arr.Length - 2; j){for (int i 0; i < arr.Length - 2; i){if (arr[i] > arr[i 1]){temp arr[i 1];arr[i 1] arr[i];arr[i] temp;}}} }使用方法 int[] array new int[] { 5,…...
前端上传后端接收参数为null
记录一下工作中的问题 前端明明把文件传到后台了,但是后台接收参数为null 原因: 前端上传文件的name和后端接收参数名称不匹配 前端 后端 把前端上传的name由upfile改为file即可 本来是很基本的小问题,但因为自己钻了牛角尖一直没搞定&…...
思考:如何把知识更轻松的传递给别人
为什么我会来思考这个问题呢,我想要把我学到的东西传递给其他人,也就是能够成为一个老师,我曾多次尝试解决问题,但是事情总是不如我所愿。现在我进行一定的总结,来复盘一下我的教授过程。 在学生面对新鲜事物的同时&am…...
BERT的中文问答系统50
我们将对BERT的中文问答系统48-1代码进行以下改进: 1.增加时间日期和日历功能:在GUI中增加显示当前时间和日期的功能,并提供一个日历组件。 2.增加更多模型类型:增加娱乐、电脑、军事、汽车、植物、科技、历史(朝代、皇帝)、名人、生活(出行、菜品、菜谱、居家),法律、…...
node.js实现分页,jwt鉴权机制,token,cookie和session的区别
文章目录 1. 分⻚功能2. jwt鉴权机制1.jwt是什么2.jwt的应用3.优缺点 3. cookie,token,session的对比 1. 分⻚功能 为什么要分页 如果数据量很⼤,⽐如⼏万条数据,放在⼀个⻚⾯显⽰的话显然不友好,这时候就需要采⽤分⻚…...
OpenHarmony-4.GPIO驱动
GPIO 1.功能简介 GPIO(General-purpose input/output)即通用型输入输出。GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。可以通过软件来控制其输入和输出,即I/O控制。通常&…...
static关键字在嵌入式C编程中的应用
目录 一、控制变量的存储周期和可见性 1.1. 局部静态变量 1.2. 全局静态变量 二、控制函数的可见性 2.1. 静态函数 2.2. 代码示例(假设有两个文件:file1.c和file2.c) 三、应用场景 3.1. 存储常用数据 3.2. 实现内部辅助函数 四、注…...
图形开发基础之在WinForms中使用OpenTK.GLControl进行图形绘制
前言 GLControl 是 OpenTK 库中一个重要的控件,专门用于在 Windows Forms 应用程序中集成 OpenGL 图形渲染。通过 GLControl,可以轻松地将 OpenGL 的高性能图形绘制功能嵌入到传统的桌面应用程序中。 1. GLControl 的核心功能 OpenGL 渲染上下文&…...
macOS sequoia 15.1中应用程序“程序坞”没有权限打开
在macOS sequoia 15.1版本中新安装的应用程序在访达中打开报错显示应用程序“程序坞”没有权限打开“(null)”。 解决办法 在启动台中找到终端,点击打开,切换到应用目录下,输入 cd /Applications/ 找到需要打开的应用程序目录࿰…...
汉诺塔递归问题(C++)
汉诺塔递归问题 汉诺塔是典型的递归问题,这个问题可以这样描述: 完成目标: 将n个盘子从A搬运到C,求需要移动多少次完成? **约束条件:**搬运的过程中每次只能移动一个盘子,且不能出现大的盘子…...
【开源】A060-基于Spring Boot的游戏交易系统的设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…...
抖音SEO短视频矩阵源码私有化部署
为了开发一套高效的抖音短视频SEO矩阵系统,开发者需要掌握以下核心技术: 网络编程:具备使用Python、Java或其他编程语言进行网络编程的能力,能够利用爬虫技术从抖音平台获取数据。 数据处理:熟悉并能够应用数据处理工…...
深入浅出:Python 编程语言的学习之路
文章目录 1. Python 简介2. Python 的安装与环境配置2.1 安装 Python2.2 配置开发环境 3. Python 基础语法3.1 变量与数据类型示例代码:定义变量 3.2 控制结构示例代码:条件语句示例代码:循环语句 3.3 函数与模块示例代码:定义函数…...
R语言机器学习论文(三):特征提取
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据一、数据归一化二、离散型分类变量的编码三、筛选特征四、重要特征五、输出结果六、总结系统信息介绍 在数据分析和机器学习项目中,经常需要对数据进行预…...
【C#设计模式(17)——迭代器模式(Iterator Pattern)】
前言 迭代器模式可以使用统一的接口来遍历不同类型的集合对象,而不需要关心其内部的具体实现。 代码 //迭代器接口 public interface Iterator {bool HashNext();object Next(); } //集合接口 public interface Collection {Iterator CreateIterator(); } //元素迭…...
【云原生系列】云计算中的负载均衡是什么,有什么用
云计算里有一个非常重要的概念叫“负载均衡”,如果你经常听到这个词但还不太明白具体是怎么回事,这篇文章可以给你一些思路。负载均衡简单来说就是“分担压力”,确保访问量被合理地分配到各个服务器上,让系统高效且稳定地运行。 …...
笔记本电脑usb接口没反应怎么办?原因及解决方法
笔记本电脑的USB接口是我们日常使用中非常频繁的一个功能,无论是数据传输、充电还是外接设备,都离不开它。然而,当USB接口突然没有反应时,这无疑会给我们的工作和学习带来不小的困扰。下面,我们就来探讨一下笔记本USB接…...
容器运行应用及Docker命令
文章目录 一、使用容器运行Nginx应用1_使用docker run命令运行Nginx应用1 观察下载容器镜像过程2 观察容器运行情况 2_访问容器中运行的Nginx服务1 确认容器IP地址2 容器网络说明3 使用curl命令访问 二、Docker命令1_Docker命令获取帮助方法2_Docker官网提供的命令说明3_docker…...