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

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

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

  1. 生成证书和密钥:
$ openssl req -newkey rsa:2048 -nodes -keyout server.key \-x509 -days 365 -out server.crt
  1. 定义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
}
  1. 客户端连接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)// ...
}
  1. 在服务实现中添加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).Errorif 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
  1. 在服务启动时注册到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)}// ...
}
  1. 在客户端中从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 []stringfor _, 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
  1. 在服务启动时从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 Configfor _, kv := range resp.Kvs {if err = json.Unmarshal(kv.Value, &config); err != nil {log.Fatalf("failed to unmarshal config from etcd: %v", err)}}// 使用配置项初始化其他组件、对象等// ...
}
  1. 在运行时监视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)// 创建Watcherwatcher := clientv3.NewWatcher(cli)defer watcher.Close()// 监视configKey的变化,返回更新后的配置项ch := make(chan *Config, 1)go func() {var config Configfor {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
  1. 集成gRPC-Go和Logrus:
import ("context""log""google.golang.org/grpc""github.com/sirupsen/logrus"
)func main() {// ...// 使用Logrus记录日志,并配置发送到Fluentdlogrus.SetFormatter(&logrus.JSONFormatter{})logrus.SetLevel(logrus.InfoLevel)logrus.SetOutput(fluentHook{host: "localhost", port: 24224})// ...
}// 定义一个Logrus Hook,用于将日志发送到Fluentd
type fluentHook struct {host stringport int
}func (hook fluentHook) Fire(entry *logrus.Entry) error {tag := entry.Logger.Out.(*fluentLogger).tagevent := 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
}
  1. 在Docker Compose中启动EFK:
version: '3'services:elasticsearch:image: docker.elastic.co/elasticsearch/elasticsearch:7.14.1environment:- discovery.type=single-nodekibana:image: docker.elastic.co/kibana/kibana:7.14.1ports:- "5601:5601"depends_on:- elasticsearchfluentd:image: fluent/fluentd:v1.12-1volumes:- ./conf:/fluentd/etc # 配置文件目录,包含fluent.conf和pos_file目录等- /var/log:/var/log   # 日志文件目录,用于收集系统日志和容器日志等
  1. 在Fluentd中配置Elasticsearch输出插件:
<match **>@type elasticsearch_dynamic_bufferedhosts elasticsearch:9200include_tag_key truetag_key @log_namebuffer_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>
  1. 在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
  1. 在项目根目录创建config目录,并添加默认配置文件config.yaml:
server:port: 8080database:host: localhostport: 3306user: rootpassword: root1234database: testdblogger:level: debug
  1. 在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)}// ...
}
  1. 在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
  1. 在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)// ...
}
  1. 封装Logrus,以便在Golang基于gRPC的微服务开发中进行使用:
import ("github.com/sirupsen/logrus"
)var log *logrus.Entryfunc 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
  1. 在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)// ...
}
  1. 封装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.Entryfunc 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
  1. 在main函数中初始化RedisClient并设置一些参数:
import ("github.com/go-redis/redis/v8"
)var redisClient *redis.Clientfunc main() {// 初始化RedisClient。redisClient = redis.NewClient(&redis.Options{Addr:     "localhost:6379",Password: "",DB:       0,})
}
  1. 封装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
  1. 在main函数中初始化MySQLClient并设置一些参数:
import ("database/sql"_ "github.com/go-sql-driver/mysql"
)var mysqlDB *sql.DBfunc main() {// 初始化MySQLClient。var err errormysqlDB, err = sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname?charset=utf8mb4")if err != nil {...}
}
  1. 封装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相关操作时都需要进行错误处理和事务控制等。

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

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

相关文章

Redis 持久化RDB和AOF

Redis 持久化之RDB和AOF Redis 有两种持久化方案&#xff0c;RDB &#xff08;Redis DataBase&#xff09;和 AOF &#xff08;Append Only File&#xff09;。如果你想快速了解和使用RDB和AOF&#xff0c;可以直接跳到文章底部看总结。本章节通过配置文件&#xff0c;触发快照…

RabbitMQ(一) - 基本结构、SpringBoot整合RabbitMQ、工作队列、发布订阅、直接、主题交换机模式

RabbitMQ结构 Publisher &#xff1a; 生产者 Queue: 存储消息的容器队列&#xff1b; Consumer:消费者 Connection&#xff1a;消费者与消息服务的TCP连接 Channel:信道&#xff0c;是TCP里面的虚拟连接。例如&#xff1a;电缆相当于TCP&#xff0c;信道是一条独立光纤束&…

web开发中的安全和防御入门——csp (content-security-policy内容安全策略)

偶然碰到iframe跨域加载被拒绝的问题&#xff0c;原因是父页面默认不允许加载跨域的子页面&#xff0c;也就是的content-security-policy中没有设置允许跨域加载。 简单地说&#xff0c;content-security-policy能限制页面允许和不允许加载的所有资源&#xff0c;常见的包括&a…

Java超级玛丽小游戏制作过程讲解 第三天 创建并完成常量类02

public class StaticValue {//背景public static BufferedImage bgnull;public static BufferedImage bg2null;//马里奥向左跳跃public static BufferedImage jump_Lnull;//马里奥向右跳跃public static BufferedImage jump_Rnull;//马里奥向左站立public static BufferedImage…

原型链污染

文章目录 1. javascript 原型链2. 原型链变量的搜索3. prototype 原型链污染4. 原型链污染例题4.1 题1&#xff1a;4.2.题2&#xff1a; 1. javascript 原型链 js在ECS6之前没有类的概念&#xff0c;之前的类都是用funtion来声明的。如下 可以看到b在实例化为test对象以后&…

LeetCode笔记:Weekly Contest 357

LeetCode笔记&#xff1a;Weekly Contest 357 1. 题目一 1. 解题思路2. 代码实现 2. 题目二 1. 解题思路2. 代码实现 3. 题目三 1. 解题思路2. 代码实现 4. 题目四 比赛链接&#xff1a;https://leetcode.com/contest/weekly-contest-357 1. 题目一 给出题目一的试题链接如下…

【C语言进阶】指针的高级应用(下)

文章目录 一、指针数组与数组指针1.1 指针数组与数组指针的表达式 二、函数指针2.1 函数指针的书写方式 三、二重指针与一重指针3.1 二重指针的本质3.2 二重指针的用法3.3 二重指针与数组指针 总结 一、指针数组与数组指针 (1)指针数组的实质是一个数组&#xff0c;这个数组中存…

Linux进程(二)

文章目录 进程&#xff08;二&#xff09;Linux的进程状态R &#xff08;running&#xff09;运行态S &#xff08;sleeping&#xff09;阻塞状态D &#xff08;disk sleep&#xff09;深度睡眠T&#xff08;stopped&#xff09;状态X&#xff08;dead&#xff09;状态Z&#x…

【力扣】21. 合并两个有序链表

以下为力扣官方题解&#xff0c;及本人代码 21. 合并两个有序链表 题目题意示例 1示例 2示例 3提示 官方题解迭代思路算法复杂度 本人代码Java提交结果&#xff1a;通过 题目 题意 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成…

【Linux】【预】配置VSCode阅读和编写Linux驱动的代码环境

配置VSCode阅读和编写Linux驱动代码环境 1. 安装vscode&#xff0c;以及配置vscode环境2. 安装VScode相关的插件3. 配置vscode4.总结 1. 安装vscode&#xff0c;以及配置vscode环境 安装vscode请点击如下连接 https://code.visualstudio.com/download2. 安装VScode相关的插件…

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--搭建Vue 前端工程[一]

文章目录 SSM--搭建Vue 前端工程--项目基础界面实现功能01-搭建Vue 前端工程需求分析/图解代码实现搭建Vue 前端工程下载node.js LTS 并安装: node.js 的npm创建Vue 项目使用idea 打开ssm_vue 项目, 并配置项目启动 Vue3 项目目录结构梳理Vue3 项目结构介绍 配置Vue 服务端口El…

记一次kernel patch(附开源贡献相关)

文章目录 开源操作系统流程手记smatch能发现的典型问题常见的修复方案附&#xff1a;偶然发现&#xff0c;unlikely函数搞开源贡献的一些捷径 开源操作系统 看了zhihu上的一些科普&#xff0c;明白二次开发是常见现象&#xff0c;套壳、抄袭、自研都不是很科学的说法。中外大厂…

Dockerfile构建mysql

使用dockerfile构建mysql详细教学加案例 Dockerfile 文件 # 使用官方5.6版本&#xff0c;latest为默认版本 FROM mysql:5.6 #复制my.cof至容器内 ADD my.cnf /etc/mysql/my.cof #设置环境变量 密码 ENV MYSQL_ROOT_PASSWORD123456my.cof 文件 [mysqld] character-set-server…

IDEA SpringBoot Maven profiles 配置

IDEA SpringBoot Maven profiles 配置 IDEA版本&#xff1a; IntelliJ IDEA 2022.2.3 注意&#xff1a;切换环境之后务必点击一下刷新&#xff0c;推荐点击耗时更短。 application.yaml spring:profiles:active: env多环境文件名&#xff1a; application-dev.yaml、 applicat…

【MATLAB第63期】基于MATLAB的改进敏感性分析方法IPCC,拥挤距离与皮尔逊系数法结合实现回归与分类预测

【MATLAB第63期】基于MATLAB的改进敏感性分析方法IPCC&#xff0c;拥挤距离与皮尔逊系数法结合实现回归与分类预测 思路 考虑拥挤距离指标与PCC皮尔逊相关系数法相结合&#xff0c;对回归或分类数据进行降维&#xff0c;通过SVM支持向量机交叉验证得到平均指标&#xff0c;来…

基于CentOS 7构建LVS-DR集群

DIPVIPRIPClient192.169.41.139 LVS 192.168.41.134192.169.41.10RS1192.168.41.135RS2192.168.41.138 要求&#xff1a; node4为客户端&#xff0c;node2为LVS&#xff0c;node3和node4为RS。 1.配置DNS解析&#xff08;我这里使用本地解析&#xff09; 192.168.41.134 www.y…

盛最多水的容器 LeetCode热题100

题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 思路 双指针法&#xff0c;一左le…

一、8.分页

当物理内存不够时就把不常用的内存暂时存入磁盘&#xff0c;并且描述符的P位置0&#xff0c;把要使用的段放入内存&#xff0c;描述符P位置1 但是这种方式会产生大量内存碎片&#xff0c;影响内存分配效率 设想一个虚拟内存&#xff0c;每隔任务都有他独立的虚拟内存&#xf…

Node.js-path模块操作路径的基本使用

path模块提供了操作路径的功能&#xff0c;以下为常用的API。 path.resolve()&#xff1a;拼接规范的绝对路径 const path require("path"); // 目录的绝对路径 // __dirname: D:\node\path const pathStr path.resolve(__dirname, "index.html"); // 拼…

golang pprof 监控系列—— cpu 占用率 统计原理

经过前面的几节对pprof的介绍&#xff0c;对pprof统计的原理算是掌握了七八十了&#xff0c;我们对memory,block,mutex,trace,goroutine,threadcreate这些维度的统计原理都进行了分析&#xff0c;但唯独还没有分析pprof 工具是如何统计cpu使用情况的&#xff0c;今天我们来分析…