40分钟学 Go 语言高并发:GRPC框架使用

news2025/4/9 6:20:27

gRPC框架使用

一、gRPC基础知识

知识点说明重要程度应用场景
RPC原理远程过程调用的基本原理⭐⭐⭐⭐⭐分布式系统通信
协议设计Protocol Buffers的使用⭐⭐⭐⭐⭐接口定义、数据序列化
服务定义gRPC服务和方法定义⭐⭐⭐⭐服务接口设计
性能优化连接池、压缩、流式处理⭐⭐⭐⭐高性能RPC服务

让我们通过一个完整的示例来学习gRPC:

syntax = "proto3";

package user;
option go_package = "./pb";

// 用户服务定义
service UserService {
    // 创建用户
    rpc CreateUser (CreateUserRequest) returns (CreateUserResponse);
    // 获取用户
    rpc GetUser (GetUserRequest) returns (GetUserResponse);
    // 更新用户
    rpc UpdateUser (UpdateUserRequest) returns (UpdateUserResponse);
    // 删除用户
    rpc DeleteUser (DeleteUserRequest) returns (DeleteUserResponse);
    // 流式获取用户列表
    rpc ListUsers (ListUsersRequest) returns (stream UserResponse);
}

// 用户信息
message User {
    int64 id = 1;
    string name = 2;
    string email = 3;
    UserStatus status = 4;
    repeated string roles = 5;
    map<string, string> metadata = 6;
}

// 用户状态
enum UserStatus {
    UNKNOWN = 0;
    ACTIVE = 1;
    INACTIVE = 2;
    SUSPENDED = 3;
}

// 创建用户请求
message CreateUserRequest {
    string name = 1;
    string email = 2;
    repeated string roles = 3;
}

// 创建用户响应
message CreateUserResponse {
    User user = 1;
}

// 获取用户请求
message GetUserRequest {
    int64 id = 1;
}

// 获取用户响应
message GetUserResponse {
    User user = 1;
}

// 更新用户请求
message UpdateUserRequest {
    User user = 1;
    repeated string update_fields = 2;
}

// 更新用户响应
message UpdateUserResponse {
    User user = 1;
}

// 删除用户请求
message DeleteUserRequest {
    int64 id = 1;
}

// 删除用户响应
message DeleteUserResponse {
    bool success = 1;
}

// 列出用户请求
message ListUsersRequest {
    int32 page_size = 1;
    string page_token = 2;
}

// 用户响应
message UserResponse {
    User user = 1;
}

首先,创建Protocol Buffers定义文件:

package main

import (
    "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 userServer struct {
    pb.UnimplementedUserServiceServer
    mu    sync.RWMutex
    users map[int64]*pb.User
    pb.UserServiceServer
}

func newUserServer() *userServer {
    return &userServer{
        users: make(map[int64]*pb.User),
    }
}

func (s *userServer) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
    // 实现超时控制
    if deadline, ok := ctx.Deadline(); ok {
        if time.Now().After(deadline) {
            return nil, status.Error(codes.DeadlineExceeded, "deadline exceeded")
        }
    }

    s.mu.Lock()
    defer s.mu.Unlock()

    // 生成用户ID
    userID := int64(len(s.users) + 1)

    // 创建新用户
    user := &pb.User{
        Id:       userID,
        Name:     req.Name,
        Email:    req.Email,
        Roles:    req.Roles,
        Status:   pb.UserStatus_ACTIVE,
        Metadata: make(map[string, string),
    }

    // 存储用户
    s.users[userID] = user

    return &pb.CreateUserResponse{User: user}, nil
}

func (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()

    user, exists := s.users[req.Id]
    if !exists {
        return nil, status.Errorf(codes.NotFound, "user not found: %d", req.Id)
    }

    return &pb.GetUserResponse{User: user}, nil
}

func (s *userServer) UpdateUser(ctx context.Context, req *pb.UpdateUserRequest) (*pb.UpdateUserResponse, error) {
    s.mu.Lock()
    defer s.mu.Unlock()

    user, exists := s.users[req.User.Id]
    if !exists {
        return nil, status.Errorf(codes.NotFound, "user not found: %d", req.User.Id)
    }

    // 更新字段
    if len(req.UpdateFields) == 0 {
        // 更新所有字段
        user.Name = req.User.Name
        user.Email = req.User.Email
        user.Roles = req.User.Roles
        user.Status = req.User.Status
        user.Metadata = req.User.Metadata
    } else {
        // 只更新指定字段
        for _, field := range req.UpdateFields {
            switch field {
            case "name":
                user.Name = req.User.Name
            case "email":
                user.Email = req.User.Email
            case "roles":
                user.Roles = req.User.Roles
            case "status":
                user.Status = req.User.Status
            case "metadata":
                user.Metadata = req.User.Metadata
            }
        }
    }

    return &pb.UpdateUserResponse{User: user}, nil
}

func (s *userServer) DeleteUser(ctx context.Context, req *pb.DeleteUserRequest) (*pb.DeleteUserResponse, error) {
    s.mu.Lock()
    defer s.mu.Unlock()

    if _, exists := s.users[req.Id]; !exists {
        return nil, status.Errorf(codes.NotFound, "user not found: %d", req.Id)
    }

    delete(s.users, req.Id)
    return &pb.DeleteUserResponse{Success: true}, nil
}

func (s *userServer) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
    s.mu.RLock()
    defer s.mu.RUnlock()

    pageSize := int(req.PageSize)
    if pageSize <= 0 {
        pageSize = 10 // 默认页大小
    }

    // 模拟分页
    start := 0
    for _, user := range s.users {
        if start >= len(s.users) {
            break
        }

        // 发送用户数据
        err := stream.Send(&pb.UserResponse{User: user})
        if err != nil {
            return err
        }

        start++
        if start%pageSize == 0 {
            // 模拟流处理延迟
            time.Sleep(100 * time.Millisecond)
        }
    }

    return nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    // 创建gRPC服务器
    s := grpc.NewServer(
        grpc.UnaryInterceptor(loggingInterceptor),
    )

    // 注册服务
    pb.RegisterUserServiceServer(s, newUserServer())

    fmt.Println("Server starting on :50051...")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

// 日志拦截器
func loggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    start := time.Now()
    
    // 调用处理器
    resp, err := handler(ctx, req)
    
    // 记录请求日志
    log.Printf(
        "Method: %s, Duration: %v, Error: %v",
        info.FullMethod,
        time.Since(start),
        err,
    )
    
    return resp, err
}

接下来,创建服务器实现:

package main

import (
    "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 userServer struct {
    pb.UnimplementedUserServiceServer
    mu    sync.RWMutex
    users map[int64]*pb.User
    pb.UserServiceServer
}

func newUserServer() *userServer {
    return &userServer{
        users: make(map[int64]*pb.User),
    }
}

func (s *userServer) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
    // 实现超时控制
    if deadline, ok := ctx.Deadline(); ok {
        if time.Now().After(deadline) {
            return nil, status.Error(codes.DeadlineExceeded, "deadline exceeded")
        }
    }

    s.mu.Lock()
    defer s.mu.Unlock()

    // 生成用户ID
    userID := int64(len(s.users) + 1)

    // 创建新用户
    user := &pb.User{
        Id:       userID,
        Name:     req.Name,
        Email:    req.Email,
        Roles:    req.Roles,
        Status:   pb.UserStatus_ACTIVE,
        Metadata: make(map[string, string),
    }

    // 存储用户
    s.users[userID] = user

    return &pb.CreateUserResponse{User: user}, nil
}

func (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
    s.mu.RLock()
    defer s.mu.RUnlock()

    user, exists := s.users[req.Id]
    if !exists {
        return nil, status.Errorf(codes.NotFound, "user not found: %d", req.Id)
    }

    return &pb.GetUserResponse{User: user}, nil
}

func (s *userServer) UpdateUser(ctx context.Context, req *pb.UpdateUserRequest) (*pb.UpdateUserResponse, error) {
    s.mu.Lock()
    defer s.mu.Unlock()

    user, exists := s.users[req.User.Id]
    if !exists {
        return nil, status.Errorf(codes.NotFound, "user not found: %d", req.User.Id)
    }

    // 更新字段
    if len(req.UpdateFields) == 0 {
        // 更新所有字段
        user.Name = req.User.Name
        user.Email = req.User.Email
        user.Roles = req.User.Roles
        user.Status = req.User.Status
        user.Metadata = req.User.Metadata
    } else {
        // 只更新指定字段
        for _, field := range req.UpdateFields {
            switch field {
            case "name":
                user.Name = req.User.Name
            case "email":
                user.Email = req.User.Email
            case "roles":
                user.Roles = req.User.Roles
            case "status":
                user.Status = req.User.Status
            case "metadata":
                user.Metadata = req.User.Metadata
            }
        }
    }

    return &pb.UpdateUserResponse{User: user}, nil
}

func (s *userServer) DeleteUser(ctx context.Context, req *pb.DeleteUserRequest) (*pb.DeleteUserResponse, error) {
    s.mu.Lock()
    defer s.mu.Unlock()

    if _, exists := s.users[req.Id]; !exists {
        return nil, status.Errorf(codes.NotFound, "user not found: %d", req.Id)
    }

    delete(s.users, req.Id)
    return &pb.DeleteUserResponse{Success: true}, nil
}

func (s *userServer) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
    s.mu.RLock()
    defer s.mu.RUnlock()

    pageSize := int(req.PageSize)
    if pageSize <= 0 {
        pageSize = 10 // 默认页大小
    }

    // 模拟分页
    start := 0
    for _, user := range s.users {
        if start >= len(s.users) {
            break
        }

        // 发送用户数据
        err := stream.Send(&pb.UserResponse{User: user})
        if err != nil {
            return err
        }

        start++
        if start%pageSize == 0 {
            // 模拟流处理延迟
            time.Sleep(100 * time.Millisecond)
        }
    }

    return nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    // 创建gRPC服务器
    s := grpc.NewServer(
        grpc.UnaryInterceptor(loggingInterceptor),
    )

    // 注册服务
    pb.RegisterUserServiceServer(s, newUserServer())

    fmt.Println("Server starting on :50051...")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

// 日志拦截器
func loggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    start := time.Now()
    
    // 调用处理器
    resp, err := handler(ctx, req)
    
    // 记录请求日志
    log.Printf(
        "Method: %s, Duration: %v, Error: %v",
        info.FullMethod,
        time.Since(start),
        err,
    )
    
    return resp, err
}

创建客户端实现:

package main

import (
    "context"
    "fmt"
    "io"
    "log"
    "time"

    pb "your_project/pb"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

func main() {
    // 建立连接
    conn, err := grpc.Dial(
        ":50051",
        grpc.WithTransportCredentials(insecure.NewCredentials()),
        grpc.WithBlock(),
    )
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()

    // 创建客户端
    client := pb.NewUserServiceClient(conn)

    // 设置超时上下文
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()

    // 创建用户
    user, err := createUser(ctx, client, "John Doe", "john@example.com", []string{"user"})
    if err != nil {
        log.Fatalf("Create user failed: %v", err)
    }
    fmt.Printf("Created user: %v\n", user)

    // 获取用户
    getResp, err := client.GetUser(ctx, &pb.GetUserRequest{Id: user.Id})
    if err != nil {
        log.Fatalf("Get user failed: %v", err)
    }
    fmt.Printf("Got user: %v\n", getResp.User)

    // 更新用户
    updateReq := &pb.UpdateUserRequest{
        User: &pb.User{
            Id:    user.Id,
            Name:  "John Smith",
            Email: user.Email,
            Roles: append(user.Roles, "admin"),
        },
        UpdateFields: []string{"name", "roles"},
    }

    updateResp, err := client.UpdateUser(ctx, updateReq)
    if err != nil {
        log.Fatalf("Update user failed: %v", err)
    }
    fmt.Printf("Updated user: %v\n", updateResp.User)

    // 流式获取用户列表
    stream, err := client.ListUsers(ctx, &pb.ListUsersRequest{PageSize: 5})
    if err != nil {
        log.Fatalf("List users failed: %v", err)
    }

    fmt.Println("\nUser list:")
    for {
        user, err := stream.Recv()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatalf("Failed to receive user: %v", err)
        }
        fmt.Printf("- %v\n", user.User)
    }

    // 删除用户
    deleteResp, err := client.DeleteUser(ctx, &pb.DeleteUserRequest{Id: user.Id})
    if err != nil {
        log.Fatalf("Delete user failed: %v", err)
    }
    fmt.Printf("Delete user success: %v\n", deleteResp.Success)
}

func createUser(ctx context.Context, client pb.UserServiceClient, name, email string, roles []string) (*pb.User, error) {
    resp, err := client.CreateUser(ctx, &pb.CreateUserRequest{
        Name:  name,
        Email: email,
        Roles: roles,
    })
    if err != nil {
        return nil, err
    }
    return resp.User, nil
}

让我们创建gRPC服务的流程图:
在这里插入图片描述

二、gRPC性能优化

1. 连接管理

// 连接池配置
type GrpcClientPool struct {
    conns    []*grpc.ClientConn
    mu       sync.Mutex
    current  int
    size     int
}

// 创建连接池
func NewGrpcClientPool(target string, size int) (*GrpcClientPool, error) {
    pool := &GrpcClientPool{
        conns: make([]*grpc.ClientConn, size),
        size:  size,
    }

    for i := 0; i < size; i++ {
        conn, err := grpc.Dial(
            target,
            grpc.WithTransportCredentials(insecure.NewCredentials()),
            grpc.WithBlock(),
        )
        if err != nil {
            return nil, err
        }
        pool.conns[i] = conn
    }

    return pool, nil
}

// 获取连接
func (p *GrpcClientPool) GetConn() *grpc.ClientConn {
    p.mu.Lock()
    defer p.mu.Unlock()

    conn := p.conns[p.current]
    p.current = (p.current + 1) % p.size
    return conn
}

2. 压缩选项

// 服务端压缩配置
s := grpc.NewServer(
    grpc.DefaultCompression(grpc.Gzip),
)

// 客户端压缩配置
conn, err := grpc.Dial(
    target,
    grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip")),
)

3. 流式处理优化

优化方向方法效果
批量处理合并多个小请求减少网络开销
流控制控制发送速率避免过载
缓冲区优化调整缓冲区大小提高吞吐量

三、gRPC高级特性

1. 拦截器

2. 负载均衡

// 自定义负载均衡器
type customBalancer struct {
    conns []*grpc.ClientConn
    mu    sync.Mutex
    index int
}

func (b *customBalancer) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
    b.mu.Lock()
    defer b.mu.Unlock()
    
    conn := b.conns[b.index]
    b.index = (b.index + 1) % len(b.conns)
    
    return balancer.PickResult{SubConn: conn}, nil
}

3. 健康检查

// 实现健康检查服务
type healthServer struct {
    pb.UnimplementedHealthServer
}

func (s *healthServer) Check(ctx context.Context, req *pb.HealthCheckRequest) (*pb.HealthCheckResponse, error) {
    return &pb.HealthCheckResponse{
        Status: pb.HealthCheckResponse_SERVING,
    }, nil
}

func (s *healthServer) Watch(req *pb.HealthCheckRequest, stream pb.Health_WatchServer) error {
    return stream.Send(&pb.HealthCheckResponse{
        Status: pb.HealthCheckResponse_SERVING,
    })
}

四、最佳实践建议

1. 服务设计

  1. 接口定义

    • 清晰的服务边界
    • 合理的方法命名
    • 版本控制
  2. 错误处理

    • 使用标准错误码
    • 详细的错误信息
    • 优雅的降级策略
  3. 安全性

    • TLS加密
    • 认证授权
    • 请求限流

2. 性能优化

  1. 连接管理

    • 使用连接池
    • 复用连接
    • 及时关闭
  2. 数据处理

    • 批量处理
    • 压缩数据
    • 流式传输
  3. 监控告警

    • 性能指标
    • 错误统计
    • 及时报警

怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML Input 文件上传功能全解析:从基础到优化

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【ETCD】【源码阅读】ETCD启动流程源码解读

启动流程的图如下&#xff1a; 1、主函数入口 ETCD 启动的入口在 etcd/server/main.go 文件中。 package mainimport ("os""go.etcd.io/etcd/server/v3/etcdmain" )func main() {etcdmain.Main(os.Args) }这里调用了 etcdmain.Main()&#xff0c;这是 …

【RBF SBN READ】hadoop社区基于RBF的SBN READ请求流转

读写分离功能的背景及架构 当前联邦生产集群的各个子集群只有Active NameNode在工作,当读写任务变得繁忙的时候,只有一个Active负责处理的话,此时集群的响应和处理能力业务侧感知会明显下降,为此,我们将引入Observer架构,实现读写功能的分离,使得Active只负责写请求,而…

计算机运行时提示错误弹窗“由于找不到 quazip.dll,无法继续执行代码。”是什么原因?“quazip.dll文件缺失”要怎么解决?

计算机运行时错误解析&#xff1a;解决“quazip.dll缺失”问题指南 在软件开发和日常计算机使用中&#xff0c;我们经常会遇到各种运行时错误。今天&#xff0c;我们将深入探讨一个常见的错误提示&#xff1a;“由于找不到quazip.dll&#xff0c;无法继续执行代码。”这一弹窗…

NuHertz/HFSS: 使用矩形、径向和阻抗短截线的平面 LPF 切比雪夫-II 实现

我们今天的主题是使用 NuHertz 和 HFSS 设计 Microstrip Lowpass Chebyshev-Type2 滤波器。切比雪夫 2 型在通带中具有平坦的响应&#xff0c;在阻带中具有波纹。我们将比较 NuHertz 中的不同选项。 低通滤波器由集总 L 和 C 组件制成。这种方法很难用于高频应用程序。高频滤波…

SpringBoot整合knife4j,以及会遇到的一些bug

这篇文章主要讲解了“Spring Boot集成接口管理工具Knife4j怎么用”&#xff0c;文中的讲解内容简单清晰&#xff0c;易于学习与理解&#xff0c;下面请大家跟着小编的思路慢慢深入&#xff0c;一起来研究和学习“Spring Boot集成接口管理工具Knife4j怎么用”吧&#xff01; 一…

高效的 Java 对象映射库“Orika”

什么是 Orika Orika 是一个高效的 Java 对象映射库&#xff0c;专门用于简化 Java 应用程序中对象之间的转换。它以自动化和优化的方式将一个对象的属性映射到另一个对象&#xff0c;从而减少了手动编写重复代码的需要。Orika 特别适合处理复杂的对象结构以及数据传输对象 &am…

汽车总线协议分析-CAN-FD总线

随着汽车功能的增多&#xff0c;各ECU之间的信息交互也越来越频繁&#xff0c;导致总线负载持续走高&#xff0c;CAN2.0报文只有约40%-50%带宽实际用于数据传输&#xff0c;响应机制易受车内布线的物理特性限制&#xff0c;如广播延迟、导线延迟等&#xff0c;CAN的局限性也逐渐…

【JavaEE】多线程(6)

一、用户态与内核态 【概念】 用户态是指用户程序运行时的状态&#xff0c;在这种状态下&#xff0c;CPU只能执行用户态下的指令&#xff0c;并且只能访问受限的内存空间 内核态是操作系统内核运行时的状态&#xff0c;内核是计算机系统的核心部分&#xff0c;CPU可以执行所有…

故障处理--kuboard无法访问,etcd磁盘空间不足

问题现象&#xff1a; kuboard页面报错 排查过程&#xff1a; 1、查看kuboard是否正常。 2、查看kuboard容器的日志&#xff1a; docker logs -f --tail10 kuboard 大概内容如下&#xff1a; levelerror msg"failed to rotate keys: etcdserver: mvcc: database sp…

unity3d—demo(实现给出图集名字和图片名字生成对应的图片)

目录 实现给出图集名字和图片名字生成对应的图片&#xff1a; 代码示例&#xff1a; dic: 键 是图集名称 值是一个字典 该字典键是图片名称 值是图片&#xff0c;结构如图&#xff1a; 测试代码&#xff1a; 结果&#xff1a; SpriteRenderer 讲解&#xff1a; Resour…

工业异常检测-CVPR2024-新的3D异常数据合成办法和自监督网络IMRNet

论文&#xff1a;https://arxiv.org/pdf/2311.14897v3.pdf 项目&#xff1a;https://github.com/chopper-233/anomaly-shapenet 这篇论文主要关注的是3D异常检测和定位&#xff0c;这是一个在工业质量检查中至关重要的任务。作者们提出了一种新的方法来合成3D异常数据&#x…

WPF编写工业相机镜头选型程序

该程序满足面阵和线阵的要求。 前端代码 <Window x:Class"相机镜头选型.MainWindow" Loaded"Window_Loaded"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml…

springai结合ollama

目录 ollama 介绍 使用 下载&#xff1a; 安装&#xff1a; 点击这个玩意next就行了。 运行 spring ai使用ollama调用本地部署的大模型 加依赖 配置yml 写代码 ollama 介绍 官网&#xff1a;Ollama Ollama是一个用于部署和运行各种开源大模型的工具&#xff1b; …

Linux 统信UOS 设置程序“桌面快捷方式”与“开机自启动”

最近在统信uos系统 arm64架构上进行QT程序的开发&#xff0c;基本开发完毕后&#xff0c;开始着手准备程序的开机自启动模块&#xff0c;因为一般来说&#xff0c;程序在客户现场使用都是需要开机自启的。 然后在百度海淘&#xff0c;很少有这类相关的博客介绍&#xff0c;有一…

WiFi受限不再愁,电脑无网络快速修复指南

有时在试图连接WiFi时&#xff0c;会发现网络连接受限&#xff0c;或无法正常访问互联网。这种情况不仅影响了工作效率&#xff0c;还可能错过重要的信息。那么&#xff0c;究竟是什么原因导致了电脑WiFi连接受限呢&#xff1f;又该如何解决这一问题呢&#xff1f;小A今天就来教…

java注解(一):什么是注解?什么是元注解?如何自定义注解?注解的原理是什么?

目录 1、什么是注解&#xff1f; 2、什么是元注解 1、Target() 2、Retention() 3、Documented 4、Inherited 3、如何自定义注解以解使用 4、注解的原理 本篇文章主要是介绍注解的概念、原理&#xff0c;以及通过代码演示4种元注解、如何自定义注解。通过反编译的形式进…

【Docker】Linux与Windows系统安装Docker+Docker上简单安装MySQL

一、Windows安装Docker 由于我在许多平台搜索Windows下安装Docker的方法&#xff0c;都提到了Win10家庭版无法直接安装Docker。个人电脑就是Win10家庭版&#xff0c;本着实践出真知的想法&#xff0c;个人在本机Win10家庭版实验结果为需要采用下述传统手动安装的办法&#xff…

阿里云整理(二)

阿里云整理 1. 访问网站2. 专业名词2.1 域名2.2 域名备案2.3 云解析DNS2.4 CDN2.5 WAF 1. 访问网站 用户使用浏览器访问网站大体分为几个过程&#xff1a; 用户在浏览器输入域名URL&#xff0c;例如www.baidu.com。 不过&#xff0c;浏览器并不知道为该域名提供服务的服务器具…

Robust Depth Enhancement via Polarization Prompt Fusion Tuning

paper&#xff1a;论文地址 code&#xff1a;github项目地址 今天给大家分享一篇2024CVPR上的文章&#xff0c;文章是用偏振做提示学习&#xff0c;做深度估计的。模型架构图如下 这篇博客不是讲这篇论文的内容&#xff0c;感兴趣的自己去看paper&#xff0c;主要是分享环境&…