0%

比如有一个请求,需要先解密,返回的内容需要先加密

可以用gin.HandlerFunc统一处理

// Data是加密的,需要统一解密传给后端业务处理
type Request struct {
    AggEntityID string `json:"AggEntityId"`
    Data        string `json:"Data"`
    TimeStamp   string `json:"TimeStamp"` //yyyyMMddHHmmss
    Seq         string `json:"Seq"`       //0001
    Sig         string `json:"Sig"`       // 参数签名
}
// Data是明文的,需要统一加密返回给请求端
type Response struct {
    Ret  int32  `json:"Ret"`
    Msg  string `json:"Msg"`
    Data string `json:"Data"`
    Sig  string `json:"Sig"`
}

统一处理

import (
    "bytes"
    "encoding/json"
    "gitee.com/njderi/public-plat/internal/vpp/front-gateway/model"
    "github.com/gin-gonic/gin"
    "net/http"
)

// EncryptResponse 返回Response时先加密Data字段
func EncryptResponse() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 创建一个缓冲区来捕获响应
        w := &responseWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer}
        c.Writer = w

        c.Next()

        if c.Writer.Status() != http.StatusOK {
            return
        }
        // 获取response
        var res model.Response
        if err := json.Unmarshal(w.body.Bytes(), &res); err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read response body"})
            return
        }
        // 修改response
        res.Ret = 1
        res.Msg = "Encrypted Response"
        encryptedBody, err := json.Marshal(res)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to marshal encrypted response"})
            return
        }
        // 修改response body
        c.Writer = w.ResponseWriter
        c.Writer.Header().Set("Content-Type", "application/json")
        c.Writer.WriteHeader(http.StatusOK)
        c.Writer.Write(encryptedBody)
    }
}

type responseWriter struct {
    gin.ResponseWriter
    body *bytes.Buffer
}

func (w *responseWriter) Write(b []byte) (int, error) {
    return w.body.Write(b)
}
import (
    "bytes"
    "encoding/json"
    "gitee.com/njderi/public-plat/internal/vpp/front-gateway/model"
    "github.com/gin-gonic/gin"
    "io"
    "io/ioutil"
    "net/http"
)

// DecryptRequest 接收到Request后解密Data字段
func DecryptRequest() gin.HandlerFunc {
    return func(c *gin.Context) {
        if c.Request.Method == http.MethodPost || c.Request.Method == http.MethodPut {
            body, err := io.ReadAll(c.Request.Body)
            if err != nil {
                c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
                c.Abort()
                return
            }

            var req model.Request
            if err := json.Unmarshal(body, &req); err != nil {
                c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
                c.Abort()
                return
            }

            req.Data = "decrypt(req.Data, key)"

            decryptedBody, err := json.Marshal(req)
            if err != nil {
                c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to marshal decrypted request"})
                c.Abort()
                return
            }

            c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(decryptedBody))
        }
        c.Next()
    }
}
import (
    "github.com/gin-gonic/gin"
)

func SetupAuthGroup(c *gin.Engine) {
    authGroup := c.Group("/vpp/fg/v1")
    // 这个group下所有的接口都会先解密,返回先加密
    authGroup.Use(controller.DecryptRequest())
    authGroup.Use(controller.EncryptResponse())
    {
        authGroup.POST("/query_token", controller.GetToken)
    }
}

事务隔离级别

MySQL 8.4支持的事务隔离级别包括 READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。

临时修改

-- 查看当前隔离级别的命令
select @@global.transaction_isolation,@@transaction_isolation;
-- 修改全局隔离级别为读提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 修改会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

永久生效

# my.cnf
transaction-isolation = READ-COMMITTED

三个工具

  • protoc
  • protoc-gen-go
  • protoc-gen-go-grpc

protoc

Gitehub下载页下载最新的 protoc ,window 环境下载 protoc-27.0-win64.zip,解压后将 bin/protoc.exe 移动到 $GOPATH/bin 目录下。

注意:$GOPATH/bin 需要配置在系统环境变量 PATH 中.

protoc-gen-go

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

protoc-gen-go-grpc

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

三个工具安装好后,在 $GOPATH/bin 下有:protoc.exeprotoc-gen-go.exeprotoc-gen-go-grpc.exe 就算安装好了。

测试

准备一个 hello.proto :

syntax = "proto3";

option go_package = "./;proto";

package proto;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
  rpc SayHi (HelloRequest) returns (HelloResponse) {}
}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string message = 1;
}

生成 hello.pb.gohello_grpc.pb.go

protoc --go_out=. --go-grpc_out=. hello.proto

MySQL 8.4 版本

不同版本配置不同!

  • server 1
server-id=1
log-bin=mysql-bin
binlog-format=ROW
max_binlog_size=1024M
slow_query_log=1
binlog_expire_logs_seconds =864000
# 异步落盘
sync-binlog=0
replica_skip_errors=all
# 自增ID偏移量设置
auto-increment-increment=2
auto-increment-offset=1
# 半同步复制
rpl_semi_sync_source_enabled=1
rpl_semi_sync_replica_enabled=1
# 开启GTID
gtid_mode=ON
enforce-gtid-consistency=ON

mysql_native_password=ON
  • server 2
server-id=2
log-bin=mysql-bin
binlog-format=ROW
max_binlog_size=1024M
slow_query_log=1
binlog_expire_logs_seconds =864000
# 异步落盘
sync-binlog=0
replica_skip_errors=all
# 自增ID偏移量设置
auto-increment-increment=2
auto-increment-offset=2
# 半同步复制
rpl_semi_sync_source_enabled=1
rpl_semi_sync_replica_enabled=1
# 开启GTID
gtid_mode=ON
enforce-gtid-consistency=ON

mysql_native_password=ON

启动半同步复制

-- 要求 MySQL 服务器支持动态加载
show variables like '%have_dynamic_loading%';

INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';

-- 确认安装是否成功:
SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%';

-- 查看半同步监控 
SHOW STATUS LIKE 'Rpl_semi_sync%';

创建复制用户

CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

这个用户直接配置复制会报错。Message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection. 原因是用户默认密码加密方式是 caching_sha2_password, 需要改成 mysql_native_password.

ALTER USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
FLUSH PRIVILEGES;

通过GTID启动复制(2台MySQL分别执行)

-- 注意修改IP、端口等信息
CHANGE REPLICATION SOURCE TO SOURCE_HOST = '172.16.20.153',SOURCE_PORT = 3306,SOURCE_USER = 'repl',SOURCE_PASSWORD = 'password',SOURCE_AUTO_POSITION = 1;

启动

START REPLICA;

查看复制状态

SHOW REPLICA STATUS\G

双主模式下的读写分离

  1. 创建一个普通用户
CREATE USER 'test'@'%' IDENTIFIED BY 'Test@123456';
GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'test'@'%';
FLUSH PRIVILEGES;
  1. 超级用户可以在另一台MySQL动态设置只读
    这里需要开发一个动态设置的读VIP所在MySQL只读的工具.
-- 普通用户只能读,超级用户依然可以写
set global read_only = 1;

-- 检查设置的状态
show global variables like '%read_only%';

额外说明:super_read_onlyread_only不一样,super_read_only是所有用户都不能写。

1. 打开官网MySQL安装文档

2. 根据Linux版本下载yum源

cat /etc/os-release
yum localinstall mysql84-community-release-el7-1.noarch.rpm

3. 查看可安装的MySQL包

yum repolist enabled | grep mysql.*-community

4. 安装MySQL

# EL8 systems 需要先禁止.
yum module disable mysql
yum install mysql-community-server

5. 启动MySQL

systemctl start mysqld

6. 登录MySQL

# 查看生成的随机密码
cat /var/log/mysqld.log |grep password

mysql -uroot -p

7. 初始化操作

-- 重置root密码
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Test@123';
-- 设置root可以远程登录
update mysql.user set  host='%' WHERE user='root';
-- 刷新权限
FLUSH PRIVILEGES;

问题: Error: 13 (Permission denied)

vim /etc/selinux/config 
# 关闭后重启系统
SELINUX=disabled

Raw和Exec

GORM 中,db.Rawdb.Exec 是执行 原生 SQL 查询的两种不同方法,它们有不同的用途和返回值。

  • db.Raw 用于执行原生 SQL 查询并返回记录。通常用于执行 SELECT 查询,需要使用 Scan 方法将结果扫描到指定的结构体或变量中
  • db.Exec 用于执行不需要返回记录的原生 SQL 查询,通常用于 INSERTUPDATEDELETE 语句,或者执行 DDL(数据定义语言)语句,如创建表或修改表结构, 不需要 Scan,可以直接检查 result.RowsAffectedresult.Error.

Raw

var users []User
result := db.Raw("SELECT id, name, email FROM users").Scan(&users)
if result.Error != nil {
    fmt.Println(result.Error)
    return
}

for _, user := range users {
    fmt.Printf("ID: %d, Name: %s, Email: %s\n", user.ID, user.Name, user.Email)
}

Exec

result := db.Exec("INSERT INTO users (name, email) VALUES (?, ?)", "John Doe", "john@example.com")
if result.Error != nil {
    fmt.Println(result.Error)
    return
}

fmt.Printf("Rows affected: %d\n", result.RowsAffected)