Hertz+Kitex快速上手开发

news2025/5/26 5:32:54

本篇文章以用户注册接口为例,快速上手Hertz+Kitex

以用户注册接口来演示hertz结合kitex实现网关+微服务架构的最简易版本

项目结构

api- gateway:网关实现,这里采用hertz框架

idl:接口定义用来生成kitex代码

kitex_gen:thrift协议自动生成的代码

rpc_service:微服务的服务逻辑

hertz框架

hertz框架其实和gin作用差不多,为字节跳动开源的go语言web网络框架

框架细节部分 大家感兴趣可以参考官方文档路由 | CloudWeGo

快速引入

服务逻辑

用户请求注册接口->网关层服务拿到进行处理路由,分发到具体的微服务

核心处理路由函数handler.go
package user

import (
	"context"
	"log"
	"time"
	"video_douyin/kitex_gen/user"             // Kitex生成的用户服务数据结构
	"video_douyin/kitex_gen/user/userservice" // Kitex生成的用户服务客户端

	"github.com/cloudwego/hertz/pkg/app"        // Hertz HTTP上下文处理
	"github.com/cloudwego/kitex/client"         // Kitex客户端核心
	"github.com/cloudwego/kitex/client/callopt" // Kitex调用选项
)

// 全局用户服务客户端实例
var userClient userservice.Client

// 初始化Kitex客户端连接
func init() {
	c, err := userservice.NewClient(
		"douyin.video.user",                  // 服务名
		client.WithHostPorts("0.0.0.0:8888"), // 服务地址
	)
	if err != nil {
		panic(err) // 连接失败终止程序
	}
	userClient = c
}

// Handler 处理用户注册HTTP请求
func Handler(ctx context.Context, c *app.RequestContext) {
	req := user.NewRegisterRequest()   // 创建注册请求结构体
	log.Printf("Phone: %s", req.Phone) // 记录请求参数

	// 调用用户服务RPC接口,超时3秒
	resp, err := userClient.Register(
		context.Background(),
		req,
		callopt.WithRPCTimeout(3*time.Second),
	)
	if err != nil {
		log.Fatal(err) // RPC调用失败记录日志
	}
	c.String(200, resp.String()) // 返回RPC响应内容
}

这里服务依赖的kitex目前先不看,只用知道这里做了如下动作

通过hertz服务拿到了请求参数

往微服务进行传递参数

main函数
package main

import (
	"log"

	"api_gateway/user"
	"github.com/cloudwego/hertz/pkg/app/server"
)


func main() {
	// 创建路由分组(符合 RESTful 风格)
	// 第一级分组 /douyin 作为 API 
	douyin := hz.Group("/douyin")
	// 第二级分组 /user 用于用户相关操作
	userGroup := douyin.Group("/user")
	// 注册用户注册接口,POST 方法对应创建操作
	// 处理函数指向 user 包的 Handler 方法
	userGroup.POST("/register/", user.Handler)

	// 启动 HTTP 服务,若失败则记录错误日志
	// Run() 会阻塞直到服务终止:ml-citation{ref="6" data="citationList"}
	if err := hz.Run(); err != nil {
		log.Fatal(err)
	}
}

可以看到这里和gin的操作差不多 gin的笔记:gin框架学习笔记_gin学习-CSDN博客

这里的主要逻辑:路由组注册

导入依赖

go mod tidy

kitex框架

kitex框架可以类比于grpc框架,grpc框架笔记:gRPC学习笔记记录以及整合gin开发-CSDN博客

也可以类比成dubbo和Java的SpringCloud

快速引入

本篇文章采用的协议为thrift

kitex工具下载

这个工具主要用来根据idl定义来自动生成代码,提高开发效率

go install github.com/cloudwego/kitex/tool/cmd/kitex@latest

idl定义

namespace go user

// 用户注册请求
struct RegisterRequest {
    1: required string phone;       // 手机号
    2: required string verifyCode;  // 验证码
}

// 用户注册响应
struct RegisterResponse {
    1: required bool success;       // 是否成功
    2: optional string message;     // 错误信息
    3: optional i64 userId;         // 用户ID
    4: optional string username;    // 自动生成的用户名
}

// 用户登录请求
struct LoginRequest {
    1: required string phone;       // 手机号
    2: required string verifyCode;  // 验证码
}

// 用户登录响应
struct LoginResponse {
    1: required bool success;       // 是否成功
    2: optional string message;     // 错误信息
    3: optional i64 userId;         // 用户ID
    4: optional string token;       // 登录令牌
    5: optional UserInfo userInfo;  // 用户信息
}

// 用户基本信息
struct UserInfo {
    1: required i64 userId;         // 用户ID
    2: required string username;    // 用户名
    3: optional string avatar;      // 头像URL
    4: optional string signature;   // 个性签名
    5: optional i32 followCount;    // 关注数
    6: optional i32 followerCount;  // 粉丝数
    7: optional bool isFollow;      // 当前用户是否关注了该用户
}

// 获取用户信息请求
struct GetUserInfoRequest {
    1: required i64 userId;         // 要查询的用户ID
    2: optional i64 currentUserId;  // 当前登录的用户ID
}

// 获取用户信息响应
struct GetUserInfoResponse {
    1: required bool success;       // 是否成功
    2: optional string message;     // 错误信息
    3: optional UserInfo userInfo;  // 用户信息
}

// 发送验证码请求
struct SendVerifyCodeRequest {
    1: required string phone;       // 手机号
    2: required i32 codeType;       // 验证码类型: 1-注册,2-登录,3-重置密码
}

// 发送验证码响应
struct SendVerifyCodeResponse {
    1: required bool success;       // 是否成功
    2: optional string message;     // 错误信息
}

// 用户服务接口定义
service UserService {
    // 用户注册
    RegisterResponse Register(1: RegisterRequest req);
    
    // 用户登录
    LoginResponse Login(1: LoginRequest req);
    
    // 获取用户信息
    GetUserInfoResponse GetUserInfo(1: GetUserInfoRequest req);
    
    // 发送验证码
    SendVerifyCodeResponse SendVerifyCode(1: SendVerifyCodeRequest req);
} 

这里定义了很多接口,包括用户请求参数结构定义,请求返回参数定义

自动生成代码

执行kitex指令

kitex -module video_douyin idl/user.thrift

返回

代码成功生成

user.go: 根据 IDL 生成的编解码文件,由 IDL 编译器生成

k-consts.go、k-user.go:kitex 专用的一些拓展内容

userservice:kitex 封装代码主要在这里

main.go入口函数定义

package main

import (
	"log"
	user "video_douyin/kitex_gen/user/userservice"
	"video_douyin/pkg/db"
)

func main() {
	// 初始化MySQL
	if err := db.InitMySQL("root:901project@tcp(127.0.0.1:3306)/kanyuServer?charset=utf8mb4&parseTime=True&loc=Local"); err != nil {
		log.Fatalf("MySQL初始化失败: %v", err)
	}

	// 初始化Redis
	if err := db.InitRedis("127.0.0.1:6379", 0); err != nil {
		log.Fatalf("Redis初始化失败: %v", err)
	}

	// 创建UserServiceImpl实例并设置db和redis
	impl := &UserServiceImpl{
		db:    db.DB,
		redis: db.Redis,
	}

	// 创建server
	svr := user.NewServer(impl)

	// 运行服务
	if err := svr.Run(); err != nil {
		log.Fatalf("服务运行失败: %v", err)
	}
}

这里处理的作用:

1,初始化mysql

2,初始化redis

3,新建rpc服务实例注入db和redis

4,新建server

5,运行服务

依赖配置初始化db和redis

package db

import (
	"context"
	"fmt"
	"sync"
	"video_douyin/dal/model"

	"github.com/go-redis/redis/v8"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

var (
	DB        *gorm.DB
	Redis     *redis.Client
	redisOnce sync.Once
)

func InitMySQL(dsn string) error {
	var err error
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true,
		// 禁用表名复数化
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true,
		},
	})
	if err != nil {
		return err
	}

	// 自动迁移
	if err := DB.AutoMigrate(&model.User{}); err != nil {
		return err
	}
	return nil
}

func InitRedis(addr string, db int) error {
	var initErr error
	redisOnce.Do(func() {
		Redis = redis.NewClient(&redis.Options{
			Addr: addr,
			DB:   db,
		})
		if _, err := Redis.Ping(context.Background()).Result(); err != nil {
			initErr = fmt.Errorf("redis连接失败: %w", err)
		}
	})
	return initErr
}

model定义

这里实现了注册接口,所以定义user_db表

package model

import (
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Phone    string `gorm:"unique"`
	Password string
	Username string
	Avatar   string
}

// TableName 实现接口返回自定义表名
func (User) TableName() string {
	return "user_db" // 指定实际表名
}

接口核心服务逻辑

package main

import (
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"log"
	"math/big"
	"time"
	"video_douyin/dal/model"
	user "video_douyin/kitex_gen/user"

	"github.com/go-redis/redis/v8"
	"gorm.io/gorm"
)

// UserServiceImpl implements the last service interface defined in the IDL.
type UserServiceImpl struct {
	db    *gorm.DB
	redis *redis.Client
}

const (
	verifyCodePrefix = "verify_code:"
	tokenPrefix      = "user_token:"
	codeExpiration   = 5 * time.Minute
	tokenExpiration  = 24 * time.Hour
)

// Register implements the UserServiceImpl interface
func (s *UserServiceImpl) Register(ctx context.Context, req *user.RegisterRequest) (resp *user.RegisterResponse, err error) {

	resp = user.NewRegisterResponse()
	if resp == nil {
		return nil, errors.New("failed to initialize response")
	}
	// 1. Validate the request parameters
	log.Printf("进入rpc: %v\n", req.GetPhone())
	if req.GetPhone() == "" || req.GetVerifyCode() == "" {
		msg := "参数为空错误" // 改为包级变量或堆分配
		resp.SetMessage(&msg)
		resp.SetSuccess(false)
		if resp.Message != nil {
			log.Printf("错误详情: %s\n", *resp.Message)
		}
		return
	}

	// 2. 验证码校验
	storedCode, err := s.redis.Get(ctx, verifyCodePrefix+req.GetPhone()).Result()
	if err != nil || storedCode != req.GetVerifyCode() {
		msg := "验证码错误或已过期"
		resp.SetMessage(&msg)
		resp.SetSuccess(false)
		if resp.Message != nil {
			log.Printf("错误详情: %s\n", *resp.Message)
		}
		return resp, nil
	}

	// 3. 检查用户是否存在 存在则进入注册网页
	var existingUser model.User
	if err := s.db.Where("phone = ?", req.GetPhone()).First(&existingUser).Error; err == nil {
		errorMsg := "用户已存在"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		if resp.Message != nil {
			log.Printf("错误详情: %s\n", *resp.Message)
		}
		return resp, nil
	}

	// 4. 创建用户
	newUser := model.User{
		Phone:    req.GetPhone(),
		Username: fmt.Sprintf("用户%s", req.GetPhone()[:4]),
	}
	if err := s.db.Create(&newUser).Error; err != nil {
		errorMsg := "注册失败"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		if resp.Message != nil {
			log.Printf("错误详情: %s\n", *resp.Message)
		}
		return resp, nil
	}

	// 5. 生成token
	token, err := generateToken()
	if err != nil {
		errorMsg := "系统错误"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		if resp.Message != nil {
			log.Printf("错误详情: %s\n", *resp.Message)
		}
		return resp, nil
	}

	// 6. 存储token
	if err := s.redis.Set(ctx, tokenPrefix+token, newUser.ID, tokenExpiration).Err(); err != nil {
		errorMsg := "系统错误"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		if resp.Message != nil {
			log.Printf("错误详情: %s\n", *resp.Message)
		}
		return resp, nil
	}

	successMsg := "注册成功"
	resp.SetMessage(&successMsg)
	resp.SetSuccess(true)
	// resp.SetToken(token)// 需改下idl
	return resp, nil
}

// Login implements the UserServiceImpl interface.
func (s *UserServiceImpl) Login(ctx context.Context, req *user.LoginRequest) (resp *user.LoginResponse, err error) {
	resp = user.NewLoginResponse()
	// 1. 校验参数异常
	if req.GetPhone() == "" || req.GetVerifyCode() == "" {

		errorMsg := "Invalid request parameters" // 声明字符串变量
		resp.SetMessage(&errorMsg)               // 传递指针
		resp.SetSuccess(true)
	}
	//2,验证码校验
	// 2. 验证码校验
	storedCode, err := s.redis.Get(ctx, verifyCodePrefix+req.GetPhone()).Result()
	if err != nil || storedCode != req.GetVerifyCode() {
		errorMsg := "验证码错误或已过期"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		return resp, nil
	}
	// 3. 查询用户是否存在
	var existingUser model.User
	if err := s.db.Where("phone = ?", req.GetPhone()).First(&existingUser).Error; err != nil {
		errorMsg := "用户不存在"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		return resp, nil
	}

	// 4. 生成token
	token, err := generateToken()
	if err != nil {
		errorMsg := "系统错误"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		return resp, nil
	}

	// 5. 存储token
	if err := s.redis.Set(ctx, tokenPrefix+token, existingUser.ID, tokenExpiration).Err(); err != nil {
		errorMsg := "系统错误"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		return resp, nil
	}
	successMsg := "登录成功"
	resp.SetMessage(&successMsg)
	resp.SetSuccess(true)
	// resp.SetToken(token) 需加上返回给前端
	return resp, nil
}

// GetUserInfo implements the UserServiceImpl interface.
func (s *UserServiceImpl) GetUserInfo(ctx context.Context, req *user.GetUserInfoRequest) (resp *user.GetUserInfoResponse, err error) {
	resp = user.NewGetUserInfoResponse()

	// 1. 参数校验
	if req.GetUserId() == 0 {
		errorMsg := "Invalid user id"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		return resp, nil
	}

	// 2. 查询用户信息
	var userModel model.User
	if err := s.db.Where("id = ?", req.GetUserId()).First(&userModel).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			errorMsg := "User not found"
			resp.SetMessage(&errorMsg)
			resp.SetSuccess(false)
			return resp, nil
		}
		errorMsg := "Database error"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		return resp, nil
	}

	// 3. 构造UserInfo结构体
	userInfo := &user.UserInfo{
		UserId:   userModel.ID,
		Username: userModel.Username,
		Avatar:   &userModel.Avatar,
	}

	// 4. 设置响应
	resp.SetSuccess(true)
	resp.SetUserInfo(userInfo)
	return resp, nil
}

// SendVerifyCode implements the UserServiceImpl interface.
func (s *UserServiceImpl) SendVerifyCode(ctx context.Context, req *user.SendVerifyCodeRequest) (resp *user.SendVerifyCodeResponse, err error) {
	resp = user.NewSendVerifyCodeResponse()
	phone := req.GetPhone()
	// 1. 校验参数异常
	if phone == "" {

		errorMsg := "Invalid request parameters" // 声明字符串变量
		resp.SetMessage(&errorMsg)               // 传递指针
		resp.SetSuccess(false)
	}
	//2,查询db看用户手机号是否存在
	CheckPhoneExists(s.db, phone)
	//3, 生成验证码
	code, _ := generateCaptcha()

	log.Printf("code: %s", code) // 记录请求参数
	//4. 保存验证码到redis
	if err := s.saveCodeToRedis(phone, code); err != nil {
		errorMsg := "Failed to save verification code"
		resp.SetMessage(&errorMsg)
		resp.SetSuccess(false)
		return resp, nil
	}
	//5. 发送验证码
	successMsg := "success"      // 声明字符串变量
	resp.SetMessage(&successMsg) // 传递指针
	resp.SetSuccess(true)
	return resp, nil
}

func generateCaptcha() (string, error) {
	// 生成一个0到999999之间的随机数(6位数)
	n, err := rand.Int(rand.Reader, big.NewInt(1000000)) // 1000000是10^6,即最大值+1
	if err != nil {
		return "", err
	}
	// 将随机数转换为字符串,并确保长度为6位(不足时前面补0)
	captcha := fmt.Sprintf("%06d", n) // %06d确保至少6位数字,不足时前面补0
	return captcha, nil
}

// 生成token
func generateToken() (string, error) {
	b := make([]byte, 32)
	if _, err := rand.Read(b); err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", b), nil
}

// CheckPhoneExists 检查手机号是否已存在
func CheckPhoneExists(db *gorm.DB, phone string) (bool, error) {
	var user = &model.User{}
	result := db.Where("phone = ?", phone).First(user)

	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return false, nil // 手机号不存在
		}
		return false, result.Error // 查询出错
	}
	return true, nil // 手机号已存在
}

// 存入redis
func (s *UserServiceImpl) saveCodeToRedis(phone, code string) error {
	key := verifyCodePrefix + phone
	return s.redis.Set(context.Background(), key, code, codeExpiration).Err()
}

导入依赖

go mod tidy

服务启动与测试

rpc服务启动

http服务启动

请求

返回

看db存储

源码

GitHub - enjoykanyu/video_feed: 仿抖音后端项目 springcloud+springboot+mysql+redis+rabiitmq

觉得不错的话可以帮点个star呗,感谢

参考

Hertz | CloudWeGo

Kitex | CloudWeGo

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

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

相关文章

机器学习课程设计报告 —— 基于二分类的岩石与金属识别模型

机器学习课程设计报告 题 目: 基于二分类的岩石与金属识别模型 专 业: 机器人工程 学生姓名: XXX 指导教师: XXX 完成日期&#xff1a…

分词算法BPE详解和CLIP的应用

一、TL;DR BPE通过替换相邻最频繁的字符和持续迭代来实现压缩CLIP对text进行标准化和预分词后,对每一个单词进行BPE编码和查表,完成token_id的转换 二、BPE算法 2.1 核心思想和原理 paper:Neural Machine Translation of Rare…

STM32F103_Bootloader程序开发02 - Bootloader程序架构与STM32F103ZET6的Flash内存规划

导言 在工业设备和机器人项目中,固件远程升级能力已成为提升设备维护性与生命周期的关键手段。本文将围绕STM32平台,系统性介绍一个简洁、可靠的Bootloader程序设计思路。 我们将Bootloader核心流程划分为五大功能模块: 启动入口与升级模式判…

通过Auto平台与VScode搭建远程开发环境(以Stable Diffusion Web UI为例)

文章目录 Stable Diffusion Web UI一、🎯主要功能概述二、🧠支持的主要模型体系三、📦安装方式简述✅ 一、前提准备✅ 二、安装步骤混乱版本(仅用于记录测试过程)第一步:克隆仓库(使用清华大学镜…

Unity 打包程序全屏置顶无边框

该模块功能: 1. 打包无边框 2. 置顶 3. 不允许切屏 4.多显示器状态下,程序只在主显示上运行 5.全屏 Unity 打包设置: 如果更改打包设置,最好将Version版本增加一下,否则可能不会覆盖前配置文件 代码: 挂在场景中即可 using UnityEngine; using System; // 确保这行存…

GAMES104 Piccolo引擎搭建配置

操作系统:windows11 家庭版 inter 17 12 th 显卡:amd 运行内存:>12 1、如何构建? 在github下载:网址如下 https://github.com/BoomingTech/Piccolo 下载后安装 git、vs2022 Git Visual Studio 2022 IDE - …

用service 和 SCAN实现sqlplus/jdbc连接Oracle 11g RAC时负载均衡

说明 11.2推出的SCAN ,简化了客户端连接(当增加或者减少RAC实例时,不需要修改客户端配置,并且scan listener有各个实例的负载情况,可以实现连接时负载均衡。 不过客户端需要使用专门建立的service,而不能用RAC数据库…

防火墙流量管理

带宽管理介绍 针对企业用户流量,防火墙提供了带宽管理功能,基于出/入接口、源/目的安全区域、源/目的地址、时间段、报文DSCP优先级等信息,对通过自身的流量进行管理和控制。 带宽管理提供带宽限制、带宽保证和连接数限制功能,可…

Linux系统移植①:uboot概念

Linux系统移植①:uboot概念 uboot概念 1、uboot是一个比较复杂的裸机程序。 2、uboot就是一个bootloader,作用就是用原于启动Linux或其他系统。uboot最主要的工作就是初始化DDR。因为Linux是运行再DDR里面的。一般Linux镜像zImage(uImage)设…

DAY 35

import torch import torch.nn as nn import torch.optim as optim from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler import time import matplotlib.pyplot as plt# 设置GPU设…

AWS EC2实例安全远程访问最佳实践

EC2 远程连接方案对比 远程访问 Amazon EC2 实例主要有以下四种方式: Secure Shell (SSH) 远程访问AWS Systems Manager 会话管理器适用于 Linux 实例的 EC2 Serial ConsoleAmazon EC2 Instance Connect SSH 远程访问 SSH(Secure Shell)广…

【强化学习】#7 基于表格型方法的规划和学习

主要参考学习资料:《强化学习(第2版)》[加]Richard S.Suttion [美]Andrew G.Barto 著 文章源文件:https://github.com/INKEM/Knowledge_Base 本章更是厘清概念厘到头秃,如有表达不恰当之处还请多多指教—— 概述 环境…

EasyRTC嵌入式音视频通信SDK一对一音视频通信,打造远程办公/医疗/教育等场景解决方案

一、方案概述​ 数字技术发展促使在线教育、远程医疗等行业对一对一实时音视频通信需求激增。传统方式存在低延迟、高画质及多场景适配不足等问题,而EasyRTC凭借音视频处理、高效信令交互与智能网络适配技术,打造稳定低延迟通信,满足基础通信…

网络安全-等级保护(等保) 3-2-1 GB/T 28449-2019 第6章 方案编制活动

################################################################################ GB/T 28449-2019《信息安全技术 网络安全等级保护测评过程指南》是规定了等级测评过程,是纵向的流程,包括:四个基本测评活动:测评准备活动、方案编制活…

【免费使用】剪Y专业版 8.1/CapCut 视频编辑处理,素材和滤镜

—————【下 载 地 址】——————— 【​本章下载一】:https://pan.xunlei.com/s/VOQxk38EUe3_8Et86ZCH84JsA1?pwdkp7h# 【​本章下载二】:https://pan.quark.cn/s/388008091ab4 【​本章下载三】:https://drive.uc.cn/s/d5ae5c725637…

实现rpc通信机制(待定)

一、概述 (1)rpc(remote procedure call, 远程接口调用),就像在本地调用函数一样,是应用组成服务内部分布式的基础功能。应用场景是在内网中的计算,比如:(a) 为上传的一张图片加水印、&#xf…

代码随想录打卡|Day45 图论(孤岛的总面积 、沉没孤岛、水流问题、建造最大岛屿)

图论part03 孤岛的总面积 代码随想录链接 题目链接 视频讲解链接 思路:既然某个网格在边界上的岛屿不是孤岛,那么就把非 孤岛的所有岛屿变成海洋,最后再次统计还剩余的岛屿占据的网格总数即可。 dfs: import java.util.Scanner…

SpringCloud实战:使用Sentinel构建可靠的微服务熔断机制

上篇文章简单介绍了SpringCloud系列Gateway的基本用法以及Demo搭建,今天继续讲解下SpringCloud Gateway实战指南!在分享之前继续回顾下本次SpringCloud的专题要讲的内容: 本教程demo源码已放入附件内 技术准备 读者须知: 本教程…

张 Prompt Tuning--中文数据准确率提升:理性与冲动识别新突破

Prompt Tuning–中文数据准确率提升:理性与冲动识别新突破 中文数据,准确率 数据标签三类:冲动21,理性21,(中性设为理性40:说明prompt 修正的有效性) 测试数据:冲动4,理性4,中性设为理性10 为了可视化做了 词嵌入 空间的相似文本计算,但是实际当loss 比较小的时…

深入解析 MySQL 中的 SHOW_ROUTINE 权限

目录 前言 权限作用 授权方法 MySQL8.0.20以上 MySQL8.0.20以下 总结 前言 SHOW_ROUTINE 是 MySQL 中用于控制用户查看存储过程和函数定义的权限。拥有该权限的用户可以通过 SHOW CREATE PROCEDURE 和 SHOW CREATE FUNCTION 等语句查看存储过程和函数的详细定义&#xff…