【Go】十六、protobuf构建基础服务信息、grpc服务启动的基础信息

商品服务

服务结构

创建 goods 服务,将之前 user 服务的基本结构迁移到 goods 服务上,完整目录是:

mxshop_srvs

user_srv

tmp

goods_srv

config

config.go 配置的读取表

global

global.go 数据库、日志初始化、全局变量定义

handler

xxx.go 拦截器

initialize

xxx.go 初始化信息

model

xxx.go 数据库表、数据库对象

proto proto 相关信息

xxx.proto

xxx.pb.go

xxx_grpc.pb.go

tests 测试信息

utils

xxx.go 工具

config-debug.yaml 配置信息

main.go 启动类

数据表结构

在 model 目录中创建需要的表结构对应的数据对象:

  • 创建基础数据类:base.go:
package modelimport ("gorm.io/gorm""time"
)type BaseModel struct {ID        int32     `gorm:"primarykey";type:int` // 注意这里对应数据库的int,我们进行统一定义,避免出现问题,若数据量过大也可以采用bigintCreatedAt time.Time `gorm:"column:add_time"`UpdatedAt time.Time `gorm:"column:update_time"`DeletedAt gorm.DeletedAtIsDeleted bool
}
  • 创建商品表
package model// 商品分类数据对象:一级分类、二级分类...
type Category struct {BaseModelName             string    `gorm:"type:varchar(20);not null;"` // 分类名ParentCategoryID int32     // 父分类IDParentCategory   *Category // 父分类对象	此处因为是自己指向自己,必须使用指针Level            int32     `gorm:"type:int;not null;default:1"` // 分类级别IsTab            bool      `gorm:"default:false;not null"`      // 是否显示在 Tab 栏
}// 品牌数据对象
type Brands struct {BaseModelName string `gorm:"type:varchar(20);not null"`Logo string `gorm:"type:varchar(200);default:'';not null"`
}// 品牌 / 类型 对应表
type GoodsCategoryBrand struct {BaseModelCategoryID int32 `gorm:"type:int;index:idx_category_brand,unique"`Category   CategoryBrandsID   int32 `gorm:"type:int;index:idx_category_brand,unique"`Brands     Brands
}// 轮播图数据对象
type Banner struct {BaseModelImage string `gorm:"type:varchar(200);not null"`Url   string `gorm:"type:varchar(200);not null"`Index int32  `gorm:"type:int;default:1;not null"`
}
  • 商品对象的创建,注意:商品对象的创建较为复杂,单独拎出来处理:
// 商品表
type Goods struct {BaseModelCategoryID int32 `gorm:"type:int;not null"`Category   CategoryBrandsID   int32 `gorm:"type:int;not null"`Brands     BrandsOnSale   bool `gorm:"default:false;not null"` // 是否上架ShipFree bool `gorm:"default:false;not null"` // 是否xxxISNew    bool `gorm:"default:false;not null""`IsHot    bool `gorm:"default:false;not null"`Name            string   `gorm:"type:varchar(50);not null"`GoodsSn         string   `gorm:"type:varchar(50);not null"`ClickNum        int32    `gorm:"type:int;default:0;not null"`SoldNum         int32    `gorm:"type:int;default:0;not null"`FavNum          int32    `gorm:"type:int;default:0;not null"`MarketPrice     float32  `gorm:"not null"`ShopPrice       float32  `gorm:"not null"`GoodsBrief      string   `gorm:"type:varchar(100);not null"`Images          GormList `gorm:"type:varchar(1000);not null"`DescImages      GormList `gorm:"type:varchar(1000);not null"`GoodsFrontImage string   `gorm:"type:varchar(200);not null"`
}

这里需要注意的是 对于 图片列表的处理,我们单独存储一个图片是没问题的,但是如果需要存储多个图片的话,我们就有两种方式选择了:

  1. 建立一个图片表,表里是所有的图片,每个图片存储一个归属商品,但这样的缺陷是无法避免连表操作,到后期数据量极大的时候,这种程度的连表能够造成极大的性能隐患。
  2. 直接将图片路径形成 json 格式的字符串,存储在表中,在 代码中通过 marshal 和 unmarshal 进行编码和解码,再进行图片的存取,这种方式有效规避了连表带来的性能损耗。

故而这里选用第二种方式。

这里就需要我们在 model/Base.go 中添加编解码的工具代码

type GormList []string// 设定这种变量在插入数据库的时候怎么插入:
// 这里是 将json格式的内容转换为 字符串再进行插入
func (g GormList) Value() (driver.Value, error) {return json.Marshal(g)
}// 设定在从数据库中取数据时,自动将数据转换为 []string 的列表
// 从数据库中取出来的时候是 GormList 数据类型,并将它的地址传入这个方法,直接修改其地址中的内容,将其修改为 []string
func (g *GormList) Scan(value interface{}) error {return json.Unmarshal(value.([]byte), &g)
}type BaseModel struct {ID        int32     `gorm:"primarykey";type:int` // 注意这里对应数据库的int,我们进行统一定义,避免出现问题,若数据量过大也可以采用bigintCreatedAt time.Time `gorm:"column:add_time"`UpdatedAt time.Time `gorm:"column:update_time"`DeletedAt gorm.DeletedAtIsDeleted bool
}

这样我们存入的数据或者取出的数据只要是 定义为了 GormList 格式,就会在存入时自动转为字符串,取出是自动转为 json

之后进行数据库创建操作

这里我们默认数据库自行创建完成了,利用 gorm 来建表:

goods_srv/model/main/main.go

package mainimport ("log""os""time""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger""gorm.io/gorm/schema""mxshop_srvs/goods_srv/model"
)// 这里的代码是用来在数据库中建表的
func main() {dsn := "root:123456@tcp(192.168.202.140:3306)/mxshop_goods_srv?charset=utf8mb4&parseTime=True&loc=Local"// 添加日志信息newLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writerlogger.Config{SlowThreshold:             time.Second, // Slow SQL thresholdLogLevel:                  logger.Info, // Log levelIgnoreRecordNotFoundError: true,        // Ignore ErrRecordNotFound error for loggerParameterizedQueries:      true,        // Don't include params in the SQL logColorful:                  true,        // Disable color,true is colorful, false to black and white},)db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{// 阻止向创建的数据库表后添加复数NamingStrategy: schema.NamingStrategy{SingularTable: true,},// 将日添加到配置中Logger: newLogger,})if err != nil {panic(err)}// 建表_ = db.AutoMigrate(&model.Category{}, &model.Brands{}, &model.GoodsCategoryBrand{}, &model.Banner{}, &model.Goods{})
}

这里相当于是一个商品的单元测试,用来做一些一次性的事情

protobuf 数据定义、定义所有的接口和请求和返回信息

注意这里,一定是先确定好需要的所有的需要的接口信息,再进行后续的接口定义操作

下面是全量的详细proto信息:

syntax = "proto3";
import "google/protobuf/empty.proto";
option go_package = ".;proto";// 在这里定义一个个的接口
service Goods {// 商品部分// 获取商品的接口,包括条件获取rpc GoodsList(GoodsFilterRequest) returns(GoodsListResponse);// 批量查询商品信息的接口,避免查商品时发生一个一个调用服务、一条一条查的低效情况rpc BatchGetGoods(BatchGoodsIdInfo) returns(GoodsListResponse);// 添加商品rpc CreateGoods(CreateGoodsInfo) returns(GoodsInfoResponse);// 删除商品,没有明确需要返回的信息,返回一个占位符rpc DeleteGoods(DeleteGoodsInfo) returns(google.protobuf.Empty);// 更新商品信息rpc UpdateGoods(CreateGoodsInfo) returns(google.protobuf.Empty);// 获取商品信息(单独获取)rpc GetGoodsDetail(GoodInfoRequest) returns(GoodsInfoResponse);// 分类部分// 获取所有商品分类rpc GetAllCategorysList(google.protobuf.Empty) returns(CategoryListResponse);// 获取子分类 todo 把这个补齐,文件在桌面上,视频已经看完了rpc GetSubCategory(CategoryListRequest) returns(SubCategoryListResponse);rpc CreateCategory(CategoryInfoRequest) returns(CategoryInfoResponse);rpc DeleteCategory(DeleteCategoryRequest) returns(google.protobuf.Empty);rpc UpdateCategory(CategoryInfoRequest) returns(google.protobuf.Empty);// 品牌部分rpc BrandList(BrandFilterRequest) returns(BrandListResponse);rpc CreateBrand(BrandRequest) returns(BrandInfoResponse);rpc DeleteBrand(BrandRequest) returns(google.protobuf.Empty);rpc UpdateBrand(BrandRequest) returns(google.protobuf.Empty);// 轮播图部分rpc BannerList(google.protobuf.Empty) returns(BannerListResponse);rpc CreateBanner(BannerRequest) returns(BannerResponse);rpc DeleteBranner(BannerRequest) returns(google.protobuf.Empty);rpc UpdateBanner(BannerRequest) returns(google.protobuf.Empty);// 品牌分类信息// 过滤需要的品牌、分类信息rpc CategoryBrandList(CategoryBrandFilterRequest) returns(CategoryBrandListResponse);// 获取某个分类下所有品牌的接口rpc GetCategoryBrandList(CategoryInfoRequest) returns(BrandListResponse);rpc CreateCategoryBrand(CategoryBrandRequest) returns(CategoryBrandResponse);rpc DeleteCategoryBrand(CategoryBrandRequest) returns(google.protobuf.Empty);rpc UpdateCategoryBrand(CategoryBrandRequest) returns(google.protobuf.Empty);
}// 在过滤商品时传入的条件信息
message GoodsFilterRequest {int32 priceMin = 1;int32 priceMax = 2;bool isHot = 3;bool isNew = 4;bool isTab = 5;int32 topCategory = 6;int32 pages = 7;int32 pagePerNums = 8;string keyWords = 9;int32 brand = 10;
}// 单独的一条关联信息
message CategoryBrandRequest{int32 id = 1;int32 categoryId = 2;int32 brandId = 3;
}// 返回的品牌、分类信息集合、也就是联系信息
message CategoryBrandListResponse {int32 total = 1;repeated CategoryBrandResponse data = 2;
}// 返回一个品牌信息、一个分类信息
message CategoryBrandResponse{int32 id = 1;BrandInfoResponse brand = 2;CategoryInfoResponse category = 3;
}// 轮播图的返回结果
message BannerListResponse {int32 total = 1;repeated BannerResponse data = 2;
}// 过滤品牌、分类信息请求
message CategoryBrandFilterRequest  {int32 pages = 1;int32 pagePerNums = 2;
}// 单个轮播图
message BannerResponse {int32 id = 1;int32 index = 2;string image = 3;string url = 4;
}// 单个轮播图的请求
message BannerRequest {int32 id = 1;int32 index = 2;string image = 3;string url = 4;
}// 过滤品牌请求的信息
message BrandFilterRequest {int32 pages = 1;int32 pagePerNums = 2;
}// 品牌查询请求
message BrandRequest {int32 id = 1;string name = 2;string logo = 3;
}// 创建分类的请求信息
message CategoryInfoRequest {int32 id = 1;string name = 2;int32 parentCategory = 3;int32 level = 4;bool isTab = 5;
}// 传入删除信息的ID
message DeleteCategoryRequest {int32 id = 1;
}// 商品ID 列表,便于批量查询
message BatchGoodsIdInfo {repeated int32 id = 1;
}// 获取分类信息集合
message CategoryListResponse {int32 total = 1;repeated CategoryInfoResponse data = 2;string jsonData = 3;
}// 获取子分类集合(需要传入选中分类的id,level选传)
message CategoryListRequest {int32 id = 1;int32 level = 2;
}// 子分类的返回
message SubCategoryListResponse {int32 total = 1;CategoryInfoResponse info = 2;    // 将本分类的所有信息返回repeated CategoryInfoResponse subCategorys = 3;  // 将子分类的所有信息返回
}// 分类信息
message CategoryInfoResponse {int32 id = 1;string name = 2;int32 parentCategory = 3;int32 level = 4;bool isTab = 5;
}// 获取单独商品详情
message GoodInfoRequest {int32 id = 1;
}// 商品列表的返回信息
message GoodsListResponse {int32 total = 1;repeated GoodsInfoResponse data = 2;
}// 单个商品的信息
message GoodsInfoResponse {int32 id = 1;int32 categoryId = 2;string name = 3;string goodsSn = 4;int32 clickNum = 5;int32 soldNum = 6;int32 favNum = 7;float marketPrice = 9;float shopPrice = 10;string goodsBrief = 11;string goodsDesc = 12;bool shipFree = 13;repeated string images = 14;repeated string descImages = 15;string goodsFrontImage = 16;bool isNew = 17;bool isHot = 18;bool onSale = 19;int64 addTime = 20;CategoryBriefInfoResponse category = 21;BrandInfoResponse brand = 22;
}// 删除时传入一个ID
message DeleteGoodsInfo {int32 id = 1;
}// 创建商品去要传递的信息
message CreateGoodsInfo {int32 id = 1;string name = 2;string goodsSn = 3;int32 stocks = 7;float marketPrice = 8;float shopPrice = 9;string goodsBrief = 10;string goodsDesc = 11;bool shipFree = 12;repeated string images = 13;repeated string descImages = 14;string goodsFrontImage = 15;bool isNew = 16;bool isHot = 17;bool onSale = 18;int32 categoryId = 19;int32 brandId = 20;
}// 商品分类的简要信息
message CategoryBriefInfoResponse {int32 id = 1;string name = 2;
}message CategoryFilterRequest {int32 id = 1;string name = 2;
}// 品牌单个信息
message BrandInfoResponse {int32 id = 1;string name = 2;string logo = 3;
}// 品牌列表信息
message BrandListResponse {int32 total = 1;repeated BrandInfoResponse data = 2;
}

protobuf 文件的生成

在对应的文件夹目录下输入:

// 标准版
protoc --go_out=. xxxx.proto
// gprc 定制版
protoc --go_out=. --go-grpc_out=. *.proto

就可以在当前目录下创建好我们所需要的 xxxx.pb.go 文件,这个文件就是我们的以proto 作为传输协议的正式接口文件。

注意:此命令需要 protoc 环境完善,并配置好完整的环境变量(protoc 的环境变量)

protobuf 的构建

此时,我们发现,我们的接口过于多了,这就需要我们分开进行,我们在 .pb.go 文件中找到: GoodsServer,这里面定义的就是所有的接口,我们在 handler 文件中将所有的接口进行定义:

handler

banner.go

brands.go

goods.go

category.go

category_brand.go

示例定义(goods.go):

测试定义:若我们希望进行快速测试,就可以给 自己的 GoodsServer 添加一个属性,有这个属性存在,就可以进行服务器快速测试。

package handlerimport ("mxshop_srvs/goods_srv/proto"
)type GoodsServer struct {proto.UnimplementedGoodsServer
}

记得修改 main文件中的 注册:

	proto.RegisterGoodsServer(server, &handler.GoodsServer{})

之后进行Nacos 相关配置:
创建一个新的命名空间、添加文件:goods-srv.json :

{"name": "goods-srv","tags": ["imooc", "bobby", "goods", "srv"],"web-host": "192.168.10.108","mysql": {"host": "192.168.202.140","port": 3306,"db": "mxshop_goods_srv","user": "root","password": "123456"},"consul": {"host": "192.168.202.140","port": 8500}
}

之后修改启动的配置文件的命名空间的ID

记得同步修改 config.go 中,新添加了一个 Tags 标签

type ServerConfig struct {// 这里是为了配置服务端口使用的,后期会移植到//Host       string       `mapstruce:"host" json:"host"`//Port       int          `mapstruct:"port" json:"port"`Name       string       `mapstructure:"name" json:"name"`Tags       []string     `mapstructure:"tags" json:"tags"`MysqlInfo  MysqlConfig  `mapstructure:"mysql" json:"mysql"`ConsulInfo ConsulConfig `mapstructure:"consul" json:"consul"`WebHost    string       `json:"web-host"`
}

然后在 main 中读取:

registration.Tags = global.ServerConfig.Tags

GRPC服务启动的相关信息

main.go:

package mainimport ("flag""fmt""go.uber.org/zap""mxshop_srvs/goods_srv/global""mxshop_srvs/goods_srv/initialize""mxshop_srvs/goods_srv/utils""net""os""os/signal""syscall""github.com/hashicorp/consul/api""github.com/satori/go.uuid""google.golang.org/grpc""google.golang.org/grpc/health""google.golang.org/grpc/health/grpc_health_v1""mxshop_srvs/goods_srv/handler""mxshop_srvs/goods_srv/proto"
)func main() {// 由于ip和端口号有可能需要用户输入,所以这里摘出来// flag 包是一个命令行工具包,允许从命令行中设置参数IP := flag.String("ip", "0.0.0.0", "ip地址")Port := flag.Int("port", 0, "端口号")initialize.InitLogger()initialize.InitConfig()flag.Parse()fmt.Println("ip: ", *IP)// 设置端口号自动获取if *Port == 0 {*Port, _ = utils.GetFreePort()}fmt.Println("port: ", *Port)// 创建新服务器server := grpc.NewServer()// 注册自己的已实现的方法进来proto.RegisterGoodsServer(server, &handler.GoodsServer{})//lis, err := net.Listen("tcp", fmt.Sprintf("192.168.202.140:8021"))lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", *IP, *Port))if err != nil {panic("failed to listen" + err.Error())}// 绑定服务健康检查grpc_health_v1.RegisterHealthServer(server, health.NewServer())// 服务注册cfg := api.DefaultConfig()cfg.Address = fmt.Sprintf("%s:%d", global.ServerConfig.ConsulInfo.Host, global.ServerConfig.ConsulInfo.Port)client, err := api.NewClient(cfg)if err != nil {panic(err)}check := &api.AgentServiceCheck{GRPC:     fmt.Sprintf("%s:%d", global.ServerConfig.Host, *Port),Interval: "5s",//Timeout:                        "10s",DeregisterCriticalServiceAfter: "30s",}registration := new(api.AgentServiceRegistration)registration.Address = global.ServerConfig.Host//registration.Address = "127.0.0.1"//registration.ID = global.ServerConfig.Name		// 此处修改为使用 UUID 生成serviceID := fmt.Sprintf("%s", uuid.NewV4()) // 此处修改为使用 UUID 生成registration.ID = serviceIDregistration.Port = *Portregistration.Tags = global.ServerConfig.Tagsregistration.Name = global.ServerConfig.Nameregistration.Check = checkerr = client.Agent().ServiceRegister(registration)if err != nil {panic(err)}//err = server.Serve(lis)// 注意此处是阻塞式的所以需要一个 goroutine 来进行异步操作// 将自己的服务绑定端口go func() {err = server.Serve(lis)if err != nil {panic("fail to start grpc" + err.Error())}}()// 创建一个通道quit := make(chan os.Signal)signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)// 阻塞住,若接到请求则放通,直接将服务注销<-quitif err = client.Agent().ServiceDeregister(serviceID); err != nil {zap.S().Info("注销失败...")}zap.S().Info("注销成功")
}

InitConfig:(配置文件的相关信息)

intialize/config.go:

package initializeimport ("encoding/json""fmt""github.com/nacos-group/nacos-sdk-go/clients""github.com/nacos-group/nacos-sdk-go/vo""github.com/nacos-group/nacos-sdk-go/common/constant""github.com/spf13/viper""go.uber.org/zap""mxshop_srvs/goods_srv/global"
)func GetEnvInfo(env string) bool {viper.AutomaticEnv()var rs boolrs = viper.GetBool(env)return rsreturn true
}func InitConfig() {debug := GetEnvInfo("MXSHOP-DEBUG")zap.S().Info(fmt.Sprintf("------------", debug))configFileNamePrefix := "config"configFileName := fmt.Sprintf("goods_srv/%s-pro.yaml", configFileNamePrefix)if debug {configFileName = fmt.Sprintf("goods_srv/%s-debug.yaml", configFileNamePrefix)}v := viper.New()v.SetConfigFile(configFileName)if err := v.ReadInConfig(); err != nil {panic(err)}// 将配置文件进行解析if err := v.Unmarshal(&global.NacosConfig); err != nil {panic(err)}sc := []constant.ServerConfig{{IpAddr: global.NacosConfig.Host,Port:   global.NacosConfig.Port,},}cc := constant.ClientConfig{TimeoutMs:           5000,NamespaceId:         global.NacosConfig.Namespace,CacheDir:            "tmp/nacos/cache",NotLoadCacheAtStart: true,LogDir:              "tmp/nacos/log",LogLevel:            "debug",}configClient, err := clients.CreateConfigClient(map[string]interface{}{"serverConfigs": sc,"clientConfig":  cc,})if err != nil {zap.S().Fatalf("%s", err.Error())}content, err := configClient.GetConfig(vo.ConfigParam{DataId: global.NacosConfig.Dataid,Group:  global.NacosConfig.Group,})if err != nil {zap.S().Fatalf("%s", err.Error())}err = configClient.ListenConfig(vo.ConfigParam{DataId: global.NacosConfig.Dataid,Group:  global.NacosConfig.Group,OnChange: func(namespace, group, dataId, data string) {fmt.Println("配置文件发生变化")fmt.Println("namespace: " + namespace)fmt.Println("group: " + group)fmt.Println("dataId: " + dataId)fmt.Println("data: " + data)},})if err != nil {zap.S().Fatalf("%s", err.Error())}err = json.Unmarshal([]byte(content), &global.ServerConfig)if err != nil {zap.S().Fatalf("%s", err.Error())}zap.S().Info(global.ServerConfig)
}

此处还需要注意配置文件和 Nacos 的配置文件:

config-debug.yml

host: '192.168.202.140'
port: 8848
namespace: '043d2547-bd1e-44df-b097-75f649848099'
user: 'nacos'
password: 'nacos'
dataid: 'goods-srv.json'
group: 'dev'

Nacos配置:

{"name": "goods-srv","host": "192.168.10.107","mysql": {"host": "192.168.202.140","port": 3306,"db": "mxshop_user_srv","user": "root","password": "123456"},"consul": {"host": "192.168.202.140","port": 8500}
}

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

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

相关文章

Redis 持久化方式:RDB(Redis Database)和 AOF(Append Only File)

本部分内容是关于博主在学习 Redis 时关于持久化部分的记录&#xff0c;介绍了 RDB 和 AOF 两种持久化方式&#xff0c;详细介绍了持久化的原理、配置、使用方式、优缺点和使用场景。并对两种持久化方式做了对比。文章最后介绍了 Redis 持久化的意义并与其他常见的缓存技术做了…

Linux中lshw相关的命令

​ lshw&#xff08;List Hardware&#xff09;是一个在 Linux 系统中用于显示硬件详细信息的强大工具。以下是一些常见的 lshw 相关命令及其用法&#xff1a; 1. 安装 lshw 在使用 lshw 之前&#xff0c;你可能需要先安装它。不同的 Linux 发行版安装方式有所不同&#xff1…

爬虫第九篇-结束爬虫循环

最近在学习Python爬虫的过程中&#xff0c;遇到了一个很有趣的问题&#xff1a;如何优雅地结束爬虫循环&#xff1f;今天&#xff0c;我想和大家分享一下我的发现和心得。 一、爬虫循环结束的常见问题 在写爬虫时&#xff0c;我们经常会遇到这样的情况&#xff1a;当爬取到的…

Vue3状态管理新选择:Pinia使用完全指南

一、为什么需要状态管理&#xff1f; 在Vue应用开发中&#xff0c;当我们的组件树变得复杂时&#xff0c;组件间的数据传递会成为棘手的问题。传统方案&#xff08;如props/$emit&#xff09;在多层嵌套组件中会变得笨拙&#xff0c;这时状态管理工具应运而生。Vue3带来了全新…

一文掌握模拟登录的基本原理和实战

文章目录 1. 模拟登录的基本原理1.1 登录流程1.2 关键技术2. 模拟登录的实战步骤2.1 分析登录页面2.2 使用 Requests 实现模拟登录2.3 处理验证码2.4 使用 Selenium 实现模拟登录3. 实战案例:模拟登录豆瓣3.1 分析豆瓣登录页面3.2 实现代码4. 注意事项5. 总结模拟登录是爬虫开…

推荐算法工程师的技术图谱和学习路径

推荐算法工程师的技术图谱和学习路径可以从多个维度进行概述,可以总结如下: 一、技术图谱 推荐算法工程师需要掌握的技术栈主要分为以下几个方面: 数学基础: 微积分、线性代数、概率论与统计学是推荐算法的基础,用于理解模型的数学原理和优化算法。高等数学、最优化理论…

ONNX转RKNN的环境搭建

将ONNX模型转换为RKNN模型的过程记录 工具准备 rknn-toolkit:https://github.com/rockchip-linux/rknn-toolkit rknn-toolkit2:https://github.com/airockchip/rknn-toolkit2 rknn_model_zoo:https://github.com/airockchip/rknn_model_zoo ultralytics_yolov8:https://github…

华为认证考试证书下载步骤(纸质+电子版)

华为考试证书可以通过官方渠道下载相应的电子证书&#xff0c;部分高级认证如HCIE还支持申请纸质证书。 一、华为电子版证书申请步骤如下&#xff1a; ①访问华为培训与认证网站 打开浏览器&#xff0c;登录华为培训与认证官方网站 ②登录个人账号 在网站首页&#xff0c;点…

面试八股文--数据库基础知识总结(2) MySQL

本文介绍关于MySQL的相关面试知识 一、关系型数据库 1、定义 关系型数据库&#xff08;Relational Database&#xff09;是一种基于关系模型的数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;它将数据存储在表格&#xff08;表&#xff09;中&#xff0c;并通过表格…

介绍下pdf打印工具类 JasperPrint

JasperPrint 工具类深度解析 JasperPrint 是 JasperReports 框架中实现 PDF 打印的核心载体类&#xff0c;其本质是 填充数据后的可打印报表对象&#xff0c;承担着从模板编译、数据填充到格式输出的全流程控制。以下从 7 个维度展开深度解析&#xff1a; 一、核心定位与生命周…

LVS+Keepalived 高可用集群搭建

一、高可用集群&#xff1a; 1.什么是高可用集群&#xff1a; 高可用集群&#xff08;High Availability Cluster&#xff09;是以减少服务中断时间为目地的服务器集群技术它通过保护用户的业务程序对外不间断提供的服务&#xff0c;把因软件、硬件、人为造成的故障对业务的影响…

从【人工智能】到【计算机视觉】,【深度学习】引领的未来科技创新与变革

前几天偶然发现了一个超棒的人工智能学习网站&#xff0c;内容通俗易懂&#xff0c;讲解风趣幽默&#xff0c;简直让人欲罢不能。忍不住分享给大家&#xff0c;点击这里立刻跳转&#xff0c;开启你的AI学习之旅吧&#xff01; 前言 – 人工智能教程https://www.captainbed.cn/l…

银河麒麟高级服务器操作系统在线调整/pro/{PID}/limits文件中nofile的软限制和硬限制参数值操作方法

银河麒麟高级服务器操作系统在线调整/pro/{PID}/limits文件中nofile的软限制和硬限制参数值操作方法 一 系统环境二 使用场景三 操作步骤 一 系统环境 [rootlocalhost ~]# nkvers ############## Kylin Linux Version ################# Release: Kylin Linux Advanced Server…

数据结构-直接插入和希尔排序

这次&#xff0c;我们来讲数据结构的排序的直接插入。 一&#xff1a;排序的思想&#xff1a;把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 相当于&#xff0c;我们打牌如上图…

基于coze+微信小程序的ai对话

界面介绍&#xff1a; 代码&#xff1a;&#xff08;替换你的coze的配置&#xff09; <template><view class"container"><!-- 高斯模糊背景 --><view class"animated-bg"><view class"gradient-blob"></view…

Day11,Hot100(贪心算法)

贪心 &#xff08;1&#xff09;121. 买卖股票的最佳时机 第 i 天卖出的最大利润&#xff0c;即在前面最低价的时候买入 class Solution:def maxProfit(self, prices: List[int]) -> int:min_price prices[0]ans 0for price in prices:ans max(ans, price - min_price…

Linux内核自定义协议族开发指南:理解net_device_ops、proto_ops与net_proto_family

在Linux内核中开发自定义协议族需要深入理解网络协议栈的分层模型。net_device_ops、proto_ops和net_proto_family是三个关键结构体,分别作用于不同的层次。本文将详细解析它们的作用、交互关系及实现方法,并提供一个完整的开发框架。 一、核心结构体的作用与层级关系 struct…

SpringBoot 中的 Redis 序列化

SpringBoot 中的 Redis 序列化 在 Spring Boot 中&#xff0c;Redis 的序列化是指将 Java 对象转换为字节流&#xff08;序列化&#xff09;以便存储到 Redis 中&#xff0c;以及从 Redis 中读取字节流并将其转换回 Java 对象&#xff08;反序列化&#xff09;。 这是因为在 R…

vLLM服务设置开机自启动(Linux)

要在开机时进入指定的 conda 环境并启动此 vllm 服务&#xff0c;您可以通过以下步骤设置一个 systemd 服务来自动执行脚本。 一、第一步&#xff1a;创建一个启动脚本 1.打开终端并创建启动脚本&#xff0c;例如 /home/username/start_vllm.sh&#xff08;请替换 username 为…

AI绘画软件Stable Diffusion详解教程(3):Windows系统本地化部署操作方法(通用版)

上一篇教程介绍了如何在本地部署Stable Diffusion专业版&#xff0c;虽然便于技术人员研究&#xff0c;但是普通人使用起来不便捷&#xff0c;每次只能通过cmd窗口的指令形式或者python代码方式来画图&#xff0c;要记很多的指令很繁琐。 本篇教程教您搭建webui版的&#xff0…