在CSDN学Golang场景化解决方案(基于grpc的微服务开发脚手架)

一,服务与服务之间采用TLS加密通信

在Golang基于gRPC的微服务开发中,可以采用TLS加密通信来确保服务与服务之间的安全通信。下面是一个简单的设计示例:

  1. 生成证书和密钥:

    $ openssl req -newkey rsa:2048 -nodes -keyout server.key
    -x509 -days 365 -out server.crt

  2. 定义gRPC服务器:

    func newServer() (*grpc.Server, error) {
    // 加载证书和密钥
    creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
    if err != nil {
    return nil, err
    }

     // 创建gRPC服务器并添加证书和拦截器
     srv := grpc.NewServer(
         grpc.Creds(creds),
         grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
             // 添加其他中间件拦截器,如认证、日志等
         )),
     )
    
     // 注册gRPC服务
     pb.RegisterUserServiceServer(srv, &userService{})
    
     return srv, nil
    

    }

  3. 客户端连接gRPC服务器:

    func main() {
    // 加载证书和密钥,并创建凭据对象
    creds, err := credentials.NewClientTLSFromFile("server.crt", "")
    if err != nil {
    log.Fatal(err)
    }

     // 建立连接并创建客户端对象,并添加拦截器(可选)
     conn, err := grpc.Dial(":9000", grpc.WithTransportCredentials(creds))
     if err != nil {
         log.Fatal(err)
     }
     
     defer conn.Close()
    
     client := pb.NewUserServiceClient(conn)
    
     // ...
    

    }

  4. 在服务实现中添加TLS加密通信:

    type userService struct {
    pb.UnimplementedUserServiceServer
    }

    func (s *userService) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
    // 从请求中获取用户ID并查询用户信息
    user := &User{ID: req.Id}
    err := db.First(user).Error
    if err != nil {
    return nil, status.Errorf(codes.NotFound, "User not found")
    }

     // 将用户信息转换为gRPC响应对象并返回
     res := &pb.GetUserResponse{
     	Id:    user.ID,
     	Name:  user.Name,
     	Email: user.Email,
     }
    
     return res, nil
    

    }

    func newServer() (*grpc.Server, error) {
    // 加载证书和密钥,并创建凭据对象
    creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
    if err != nil {
    return nil, err
    }

     // 创建gRPC服务器并添加证书和拦截器
     srv := grpc.NewServer(
         grpc.Creds(creds),
         grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
             // 添加其他中间件拦截器,如认证、日志等
         )),
     )
    
     // 注册gRPC服务
     pb.RegisterUserServiceServer(srv, &userService{})
    
     return srv, nil
    

    }

这样,在Golang基于gRPC的微服务开发中,就可以使用TLS加密通信确保服务与服务之间的安全通信了。注意,在生成证书和密钥时,请按照实际情况替换成自己的证书和密钥。

二,etcd服务注册和服务发现

在Golang基于gRPC的微服务开发中,可以使用etcd来实现服务注册和服务发现。下面是一个简单的设计示例:

  1. 安装etcd客户端:

    $ go get go.etcd.io/etcd/clientv3

  2. 在服务启动时注册到etcd:

    func main() {
    // ...

     // 创建etcd客户端并连接到etcd服务器
     cli, err := clientv3.New(clientv3.Config{
     	Endpoints: []string{"localhost:2379"},
     	DialTimeout: 5 * time.Second,
     })
     if err != nil {
     	log.Fatal(err)
     }
     defer cli.Close()
    
     // 创建gRPC服务器并添加证书和拦截器
     srv := grpc.NewServer(
         // ...
     )
    
     // 注册gRPC服务
     pb.RegisterUserServiceServer(srv, &userService{})
    
     // 启动gRPC服务器
     go func() {
     	lis, err := net.Listen("tcp", ":9000")
     	if err != nil {
     		log.Fatal(err)
     	}
    
         if err = srv.Serve(lis); err != nil {
             log.Fatalf("failed to serve: %v", err)
         }
     }()
    
     // 在etcd中注册服务信息(IP地址、端口等)
     serviceKey := fmt.Sprintf("/services/%s/%s:%d",
                             serviceName, serviceIP, servicePort)
    
     resp, err := cli.Grant(context.Background(), 5*time.Minute)
     if err != nil {
         log.Fatal(err)
     }
    
     if _, err = cli.Put(context.Background(), serviceKey, "", clientv3.WithLease(resp.ID)); 
         if err != nil {
             log.Fatal(err)
         }
     
     // ...
    

    }

  3. 在客户端中从etcd获取服务地址:

    func main() {
    // ...

     // 创建etcd客户端并连接到etcd服务器
     cli, err := clientv3.New(clientv3.Config{
     	Endpoints: []string{"localhost:2379"},
     	DialTimeout: 5 * time.Second,
     })
     if err != nil {
     	log.Fatal(err)
     }
     defer cli.Close()
    
     // 从etcd中获取服务地址
     serviceKey := fmt.Sprintf("/services/%s", serviceName)
    
     resp, err := cli.Get(context.Background(), serviceKey, clientv3.WithPrefix())
     if err != nil {
         log.Fatal(err)
     }
    
     var addresses []string
     for _, kv := range resp.Kvs {
         address := string(kv.Key)[len(serviceKey)+1:] // 去掉前缀
         addresses = append(addresses, address)
     }
    
     // 创建gRPC连接和客户端对象,并添加拦截器(可选)
     conn, err := grpc.Dial(addresses[rand.Int()%len(addresses)],
             grpc.WithTransportCredentials(creds),
             grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(
                 // 添加其他中间件拦截器,如认证、日志等
             )),
         )
     if err != nil {
         log.Fatal(err)
     }
     defer conn.Close()
    
     client := pb.NewUserServiceClient(conn)
    
     // ...
    

    }

这样,在Golang基于gRPC的微服务开发中,就可以使用etcd来实现服务注册和服务发现了。注意,在服务启动时注册到etcd时,需要替换成自己的IP地址和端口号,并按照实际情况修改服务名称和etcd服务器地址。在客户端中从etcd获取服务地址时,需要按照实际情况替换成自己的服务名称。

三,etcd应用配置中心

在Golang基于gRPC的微服务开发中,可以使用etcd作为应用配置中心。下面是一个简单的设计示例:

  1. 安装etcd客户端:

    $ go get go.etcd.io/etcd/clientv3

  2. 在服务启动时从etcd加载配置:

    import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "time"

     clientv3 "go.etcd.io/etcd/client/v3"
    

    )

    type Config struct {
    // 配置项结构体定义
    }

    func main() {
    // ...

     // 创建etcd客户端并连接到etcd服务器
     cli, err := clientv3.New(clientv3.Config{
     	Endpoints: []string{"localhost:2379"},
     	DialTimeout: 5 * time.Second,
     })
     if err != nil {
         log.Fatal(err)
     }
     defer cli.Close()
    
     // 从etcd中获取配置信息
     configKey := fmt.Sprintf("/configs/%s", serviceName)
    
     resp, err := cli.Get(context.Background(), configKey)
     if err != nil {
         log.Fatal(err)
     }
    
     var config Config
     for _, kv := range resp.Kvs {
         if err = json.Unmarshal(kv.Value, &config); err != nil {
             log.Fatalf("failed to unmarshal config from etcd: %v", err)
         }
     }
    
     // 使用配置项初始化其他组件、对象等
    
     // ...
    

    }

  3. 在运行时监视etcd中的配置变化:

    import (
    clientv3 "go.etcd.io/etcd/client/v3"
    )

    func watchConfig(cli *clientv3.Client, serviceName string) (<-chan *Config, error) {
    configKey := fmt.Sprintf("/configs/%s", serviceName)

     // 创建Watcher
     watcher := clientv3.NewWatcher(cli)
     defer watcher.Close()
    
     // 监视configKey的变化,返回更新后的配置项
     ch := make(chan *Config, 1)
     go func() {
     	var config Config
    
     	for {
     		resp, err := cli.Get(context.Background(), configKey)
     		if err != nil {
     			log.Printf("failed to get config from etcd: %v", err)
     			continue
     		}
    
     		if len(resp.Kvs) == 0 {
     			log.Println("no configuration found")
     			continue
     		}
    
     		if err = json.Unmarshal(resp.Kvs[0].Value, &config); err != nil {
     			log.Printf("failed to unmarshal config from etcd: %v", err)
     			continue
     		}
    
             // 将新的配置项发送到ch中
     		select {
     		    case ch <- &config:
     		    default:
     		}
     	}
     }()
    
     return ch, nil
    

    }

这样,在Golang基于gRPC的微服务开发中,就可以使用etcd作为应用配置中心了。注意,在服务启动时从etcd加载配置时,需要按照实际情况修改服务名称和etcd服务器地址。在运行时监视etcd中的配置变化时,需要将返回值ch传递给其他组件、对象等,以便它们在配置变更时能够重新初始化自己。

四,EFK统一日志采集

在Golang基于gRPC的微服务开发中,可以使用EFK(Elasticsearch + Fluentd + Kibana)作为统一日志采集方案。下面是一个简单的设计示例:

  1. 安装依赖:

    $ go get google.golang.org/grpc
    $ go get github.com/golang/protobuf/proto
    $ go get github.com/golang/protobuf/protoc-gen-go

  2. 集成gRPC-Go和Logrus:

    import (
    "context"
    "log"

     "google.golang.org/grpc"
     "github.com/sirupsen/logrus"
    

    )

    func main() {
    // ...

     // 使用Logrus记录日志,并配置发送到Fluentd
     logrus.SetFormatter(&logrus.JSONFormatter{})
     logrus.SetLevel(logrus.InfoLevel)
     logrus.SetOutput(fluentHook{host: "localhost", port: 24224})
    
     // ...
    

    }

    // 定义一个Logrus Hook,用于将日志发送到Fluentd
    type fluentHook struct {
    host string
    port int
    }

    func (hook fluentHook) Fire(entry *logrus.Entry) error {
    tag := entry.Logger.Out.(*fluentLogger).tag

     event := map[string]interface{}{
     	"message": entry.Message,
     }
     for k, v := range entry.Data {
     	event[k] = v
     }
    
     if _, err := fluent.New().PostWithTime(tag, time.Now(), event); err != nil {
     	log.Printf("failed to send log to fluentd: %v", err)
         return err
     }
     return nil
    

    }

    func (hook fluentHook) Levels() []logrus.Level {
    return logrus.AllLevels[:]
    }

    // 定义一个实现io.Writer接口的Fluentd Logger
    type fluentLogger struct {
    tag string
    }

    func (logger *fluentLogger) Write(p []byte) (int, error) {
    event := map[string]interface{}{
    "message": string(p),
    }
    if _, err := fluent.New().PostWithTime(logger.tag, time.Now(), event); err != nil {
    log.Printf("failed to send log to fluentd: %v", err)
    return 0, err
    }
    return len(p), nil
    }

  3. 在Docker Compose中启动EFK:

    version: '3'

    services:
    elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.14.1
    environment:
    - discovery.type=single-node

    kibana:
    image: docker.elastic.co/kibana/kibana:7.14.1
    ports:
    - "5601:5601"
    depends_on:
    - elasticsearch

    fluentd:
    image: fluent/fluentd:v1.12-1
    volumes:
    - ./conf:/fluentd/etc # 配置文件目录,包含fluent.conf和pos_file目录等
    - /var/log:/var/log # 日志文件目录,用于收集系统日志和容器日志等

  4. 在Fluentd中配置Elasticsearch输出插件:

    <match **>
    @type elasticsearch_dynamic_buffered
    hosts elasticsearch:9200

    include_tag_key true
    tag_key @log_name

    buffer_type file # 使用file缓存方式,当然也可以使用memory或redis等方式。
    buffer_path /var/log/td-agent/buffer/out_*.log # 缓存文件的路径,可以按照实际情况修改
    buffer_chunk_limit 1m # 缓存文件大小限制
    buffer_queue_limit 100 # 缓存队列长度限制

    logstash_format true # 使用logstash格式输出到Elasticsearch中
    </match>

  5. 在Golang基于gRPC的微服务开发中使用Logrus记录日志:

    import (
    "github.com/sirupsen/logrus"
    )

    func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    logrus.WithFields(logrus.Fields{
    "name": in.Name,
    "age": in.Age,
    }).Info("received a request")

     // ...
    
     return &pb.HelloReply{Message: "Hello " + in.Name}, nil
    

    }

这样,在Golang基于gRPC的微服务开发中,就可以使用EFK作为统一日志采集方案了。注意,在集成gRPC-Go和Logrus时,需要在日志输出前配置Logrus Hook,并将日志发送到Fluentd。在Docker Compose中启动EFK时,需要分别启动Elasticsearch、Kibana和Fluentd三个容器,并在Fluentd中配置Elasticsearch输出插件。最后,在Golang基于gRPC的微服务开发中使用Logrus记录日志即可自动发送到EFK进行统一管理和查询。

五,viper配置文件读取

在Golang基于gRPC的微服务开发中,可以使用Viper作为配置文件读取方案。下面是一个简单的设计示例:

  1. 安装依赖:

    $ go get google.golang.org/grpc
    $ go get github.com/golang/protobuf/proto
    $ go get github.com/golang/protobuf/protoc-gen-go

    安装viper

    $ go get github.com/spf13/viper

  2. 在项目根目录创建config目录,并添加默认配置文件config.yaml:

    server:
    port: 8080

    database:
    host: localhost
    port: 3306
    user: root
    password: root1234
    database: testdb

    logger:
    level: debug

  3. 在main函数中初始化Viper并读取配置文件:

    import (
    "log"

     "github.com/spf13/viper"
    

    )

    func main() {
    // 初始化Viper,并设置默认值和搜索路径等参数。
    v := viper.New()
    v.SetConfigName("config")
    v.AddConfigPath("./config")
    v.SetDefault("server.port", "8080")
    v.SetDefault("database.host", "localhost")
    v.SetDefault("database.port", "3306")
    v.SetDefault("database.user", "root")
    v.SetDefault("database.password", "")
    v.SetDefault("database.database", "testdb")

     // 如果存在环境变量CONFIG_PATH,则将其加入搜索路径。
     if path := os.Getenv("CONFIG_PATH"); path != "" {
     	log.Printf("adding config search path %s\n", path)
     	v.AddConfigPath(path)
     }
    
     // 根据以上配置信息进行初始化工作。
     if err := v.ReadInConfig(); err != nil {
     	log.Fatalf("failed to read config file: %v", err)
     }
     // ...
    

    }

  4. 在Golang基于gRPC的微服务开发中使用Viper读取配置文件:

    import (
    "github.com/spf13/viper"
    )

    type server struct {
    port string
    }

    func main() {
    // ...

     // 从配置文件中读取server.port参数,如果未设置,则使用默认值8080。
     s := &server{port: viper.GetString("server.port")}
    
     // ...
    

    }

这样,在Golang基于gRPC的微服务开发中,就可以使用Viper作为配置文件读取方案了。注意,在main函数中需要初始化Viper,并设置默认值和搜索路径等参数,然后根据以上配置信息进行初始化工作。在Golang基于gRPC的微服务开发中使用Viper读取配置文件时,只需要调用viper.GetString等方法即可获取指定的参数值。

六,logurs日志组件封装

在Golang基于gRPC的微服务开发中,可以使用logrus作为日志组件。下面是一个简单的设计示例:

  1. 安装依赖:

    $ go get google.golang.org/grpc
    $ go get github.com/golang/protobuf/proto
    $ go get github.com/golang/protobuf/protoc-gen-go

    安装logrus和logrus的json格式输出插件。

    $ go get github.com/sirupsen/logrus
    $ go get github.com/mattn/go-isatty

  2. 在main函数中初始化Logrus并设置日志级别、输出格式等参数:

    import (
    "log"
    "os"

     "github.com/sirupsen/logrus"
     "github.com/mattn/go-isatty"
    

    )

    func main() {
    // 初始化Logrus,并设置日志级别、输出格式等参数。
    logrus.SetFormatter(&logrus.JSONFormatter{})
    if isatty.IsTerminal(os.Stdout.Fd()) {
    logrus.SetLevel(logrus.DebugLevel)
    } else {
    logrus.SetLevel(logrus.InfoLevel)
    }
    logrus.SetOutput(os.Stdout)

     // ...
    

    }

  3. 封装Logrus,以便在Golang基于gRPC的微服务开发中进行使用:

    import (
    "github.com/sirupsen/logrus"
    )

    var log *logrus.Entry

    func init() {
    log = logrus.WithFields(logrus.Fields{
    "app": "myapp",
    "env": "production",
    })
    }

    func doSomething() {
    log.Debugf("debug message")
    log.Infof("info message")
    log.Warnf("warning message")
    log.Errorf("error message")
    }

这样,在Golang基于gRPC的微服务开发中,就可以使用Logrus作为日志组件了。注意,在main函数中需要初始化Logrus,并设置日志级别、输出格式等参数。封装Logrus时,可以在init函数中创建全局log实例,并设置一些默认的字段值,然后在需要打印日志的地方调用对应的方法即可。这种方式可以避免重复地创建log实例,并且保证了所有日志都有相同的字段值。

Golang云原生学习路线图、教学视频、文档资料、面试题资料(资料包括C/C++、K8s、golang项目实战、gRPC、Docker、DevOps等)免费分享 有需要的可以加qun:793221798领取

七,分布式日志链路追踪设计

在Golang基于gRPC的微服务开发中,常常需要对分布式系统进行日志和链路追踪,以便更好地进行监控和调试。下面是一个简单的设计示例:

  1. 安装依赖:

    $ go get google.golang.org/grpc
    $ go get github.com/golang/protobuf/proto
    $ go get github.com/golang/protobuf/protoc-gen-go

    安装logrus和logrus的json格式输出插件。

    $ go get github.com/sirupsen/logrus
    $ go get github.com/mattn/go-isatty

    安装opentracing相关库。

    $ go get github.com/opentracing/opentracing-go
    $ go get github.com/uber/jaeger-client-go/config

  2. 在main函数中初始化Logrus、Jaeger等组件并设置日志级别、输出格式等参数:

    import (
    "log"
    "os"

     "github.com/sirupsen/logrus"
     "github.com/mattn/go-isatty"
    
     "github.com/opentracing/opentracing-go"
     "github.com/uber/jaeger-client-go/config"
    

    )

    func main() {
    // 初始化Logrus,并设置日志级别、输出格式等参数。
    logrus.SetFormatter(&logrus.JSONFormatter{})
    if isatty.IsTerminal(os.Stdout.Fd()) {
    logrus.SetLevel(logrus.DebugLevel)
    } else {
    logrus.SetLevel(logrus.InfoLevel)
    }
    logrus.SetOutput(os.Stdout)

     // 初始化Jaeger Tracer。
     cfg, err := config.FromEnv()
     if err != nil {
         log.Fatalf("Failed to read Jaeger config from env: %v", err)
     }
     tracer, closer, err := cfg.NewTracer()
     if err != nil {
     	log.Fatalf("Failed to create Jaeger tracer: %v", err)
     }
     defer closer.Close()
     opentracing.SetGlobalTracer(tracer)
    
     // ...
    

    }

  3. 封装Logrus和Jaeger,以便在Golang基于gRPC的微服务开发中进行使用:

    import (
    "github.com/sirupsen/logrus"

     "github.com/opentracing/opentracing-go"
     "github.com/opentracing/opentracing-go/ext"
     "github.com/uber/jaeger-client-go"
     jaegercfg "github.com/uber/jaeger-client-go/config"
    

    )

    var log *logrus.Entry

    func init() {
    log = logrus.WithFields(logrus.Fields{
    "app": "myapp",
    "env": "production",
    })
    }

    func doSomething(ctx context.Context) {
    // 创建Span。
    span, ctx := opentracing.StartSpanFromContext(ctx, "doSomething")
    defer span.Finish()

     // 记录日志。
     log.WithFields(logrus.Fields{
     	"operation": span.OperationName(),
         "trace_id":  span.Context().(jaeger.SpanContext).TraceID().String(),
         "span_id":   span.Context().(jaeger.SpanContext).SpanID().String(),
         }).Infof("doing something")
    
     // 发起下游调用,将当前Span传递给下游服务。
     req := &pb.Request{...}
     resp, err := client.Call(ctx, req)
     if err != nil {...}
    
     // 记录返回结果,并设置相关标签。
     ext.HTTPStatusCode.Set(span, uint16(resp.StatusCode))
     log.WithFields(logrus.Fields{
     	"operation":     span.OperationName(),
         "trace_id":      span.Context().(jaeger.SpanContext).TraceID().String(),
         "span_id":       span.Context().(jaeger.SpanContext).SpanID().String(),
         "response_code": resp.StatusCode,
         }).Infof("got response")
    
     // ...
    

    }

在这个设计中,我们使用了Logrus作为日志组件,并使用Jaeger作为分布式链路追踪组件。在main函数中初始化Logrus和Jaeger,并设置日志级别、输出格式等参数。封装Logrus和Jaeger时,可以在init函数中创建全局log实例和tracer实例,并设置一些默认的字段值,然后在需要打印日志或者记录Span的地方调用对应的方法即可。注意,在记录日志和记录Span时都要将当前上下文ctx传递给相关方法,以便进行跨服务间的信息传递。同时,在发起下游调用时也需要将当前Span传递给下游服务,以便进行链路追踪。

八,redis数据缓存

在Golang基于gRPC的微服务开发中,为了提高系统的性能和可扩展性,常常需要使用缓存技术来加速数据访问。下面是一个简单的设计示例:

  1. 安装依赖:

    $ go get google.golang.org/grpc
    $ go get github.com/golang/protobuf/proto
    $ go get github.com/golang/protobuf/protoc-gen-go

    安装Redis客户端库。

    $ go get github.com/go-redis/redis/v8

  2. 在main函数中初始化RedisClient并设置一些参数:

    import (
    "github.com/go-redis/redis/v8"
    )

    var redisClient *redis.Client

    func main() {
    // 初始化RedisClient。
    redisClient = redis.NewClient(&redis.Options{
    Addr: "localhost:6379",
    Password: "",
    DB: 0,
    })
    }

  3. 封装Redis缓存相关操作:

    import (
    "time"

     "github.com/go-redis/redis/v8"
    

    )

    func getUserFromCache(userId string) (*User, error) {
    // 先从缓存读取用户信息。
    key := fmt.Sprintf("user:%s", userId)
    val, err := redisClient.Get(ctx, key).Result()
    if err == nil {
    user := &User{}
    err := json.Unmarshal([]byte(val), user)
    if err != nil {...}
    return user, nil
    }

     if err != redis.Nil {...}
    
     // 如果缓存中不存在该用户信息,则从数据库中读取,并将其写入缓存。
     user, err := getUserFromDB(userId)
     if err != nil {...}
     val, err = json.Marshal(user)
     if err != nil {...}
     err = redisClient.Set(ctx, key, val, 1*time.Hour).Err()
     if err != nil {...}
    
     return user, nil
    

    }

    func updateUserCache(user User) error {
    // 更新用户信息,并将其写入缓存。
    key := fmt.Sprintf("user:%s", user.Id)
    val, err := json.Marshal(user)
    if err != nil {...}
    err = redisClient.Set(ctx, key, val, 1
    time.Hour).Err()
    if err != nil {...}

     return nil
    

    }

    func deleteUserFromCache(userId string) error {
    // 删除缓存中的用户信息。
    key := fmt.Sprintf("user:%s", userId)
    err := redisClient.Del(ctx, key).Err()
    if err != nil && err != redis.Nil {...}

     return nil
    

    }

在这个设计中,我们使用了Redis作为数据缓存组件,并使用go-redis作为Redis客户端库。在main函数中初始化RedisClient,并设置一些参数。封装Redis缓存相关操作时,我们首先从缓存中读取数据,如果数据不存在则从数据库中读取,并将其写入缓存。在更新和删除操作时同样也需要同步更新或删除缓存中的对应数据。注意,在进行Redis相关操作时都需要传递当前上下文ctx以便进行错误处理和超时控制等。

九,mysql数据存储

在Golang基于gRPC的微服务开发中,为了持久化数据,通常会使用关系型数据库MySQL。下面是一个简单的设计示例:

  1. 安装依赖:

    $ go get google.golang.org/grpc
    $ go get github.com/golang/protobuf/proto
    $ go get github.com/golang/protobuf/protoc-gen-go

    安装MySQL客户端库。

    $ go get github.com/go-sql-driver/mysql

  2. 在main函数中初始化MySQLClient并设置一些参数:

    import (
    "database/sql"

     _ "github.com/go-sql-driver/mysql"
    

    )

    var mysqlDB *sql.DB

    func main() {
    // 初始化MySQLClient。
    var err error
    mysqlDB, err = sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname?charset=utf8mb4")
    if err != nil {...}
    }

  3. 封装MySQL存储相关操作:

    import (
    "database/sql"
    "errors"

     _ "github.com/go-sql-driver/mysql"
    

    )

    type User struct {
    Id string json:"id"
    Name string json:"name"
    }

    func getUserFromDB(userId string) (*User, error) {
    // 从数据库中读取用户信息。
    row := mysqlDB.QueryRow("SELECT id, name FROM users WHERE id=?", userId)
    user := &User{}
    err := row.Scan(&user.Id, &user.Name)
    if err != nil {
    if errors.Is(err, sql.ErrNoRows) {
    return nil, ErrNotFound
    }
    return nil, err
    }

     return user, nil
    

    }

    func updateUserToDB(user *User) error {
    // 更新用户信息。
    _, err := mysqlDB.Exec("UPDATE users SET name=? WHERE id=?", user.Name, user.Id)
    if err != nil {
    if errors.Is(err, sql.ErrNoRows) {
    return ErrNotFound
    }
    return err
    }

     return nil
    

    }

    func deleteUserFromDB(userId string) error {
    // 删除数据库中的用户信息。
    res, err := mysqlDB.Exec("DELETE FROM users WHERE id=?", userId)
    if err != nil {...}
    num, _ := res.RowsAffected()
    if num == 0 {...}

     return nil
    

    }

在这个设计中,我们使用了MySQL作为数据存储组件,并使用go-sql-driver/mysql作为MySQL客户端库。在main函数中初始化MySQLClient,并设置一些参数。封装MySQL存储相关操作时,我们可以通过QueryRow和Exec等方法进行查询、更新和删除等操作。注意,在进行MySQL相关操作时都需要进行错误处理和事务控制等。

相关推荐
农民小飞侠7 分钟前
python AutoGen接入开源模型xLAM-7b-fc-r,测试function calling的功能
开发语言·python
指尖流烟9 分钟前
C#调用图表的使用方法
开发语言·c#
敲代码不忘补水12 分钟前
Python 项目实践:简单的计算器
开发语言·python·json·项目实践
蒟蒻的贤21 分钟前
Web APIs 第二天
开发语言·前端·javascript
ljp_nan28 分钟前
QT --- 初识QT
开发语言·qt
ᅠᅠᅠ@33 分钟前
异常枚举;
开发语言·javascript·ecmascript
编程版小新39 分钟前
C++初阶:STL详解(四)——vector迭代器失效问题
开发语言·c++·迭代器·vector·迭代器失效
c4fx1 小时前
Delphi5利用DLL实现窗体的重用
开发语言·delphi·dll
鸽芷咕1 小时前
【Python报错已解决】ModuleNotFoundError: No module named ‘paddle‘
开发语言·python·机器学习·bug·paddle
Jhxbdks2 小时前
C语言中的一些小知识(二)
c语言·开发语言·笔记