【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.pswp.cn/diannao/71989.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/71989.shtml
英文地址,請注明出處:http://en.pswp.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…