【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"`
}
这里需要注意的是 对于 图片列表的处理,我们单独存储一个图片是没问题的,但是如果需要存储多个图片的话,我们就有两种方式选择了:
- 建立一个图片表,表里是所有的图片,每个图片存储一个归属商品,但这样的缺陷是无法避免连表操作,到后期数据量极大的时候,这种程度的连表能够造成极大的性能隐患。
- 直接将图片路径形成 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}
}
相关文章:
【Go】十六、protobuf构建基础服务信息、grpc服务启动的基础信息
商品服务 服务结构 创建 goods 服务,将之前 user 服务的基本结构迁移到 goods 服务上,完整目录是: mxshop_srvs user_srv … tmp … goods_srv config config.go 配置的读取表 global global.go 数据库、日志初始化、全局变量定义 handler …...
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…...
解决npm run dev报错
解决:Node.js 版本更新后与 OpenSSL 不兼容导致的npm报错“Error: error:0308010C:digital envelope routines::unsupported” 方法一:更改系统环境变量方法二:更改项目环境变量方法三:更换 Node.js 版本方法四:升级依…...
【Kubernetes】对资源进行PATCH
文章目录 1 更新资源的方式2 PATCH的三种方式2.1 JSON Patch2.2 Merge Patch2.3 Strategic Merge Patch 3 kubectl中的patch命令4 PATCH的优势和问题5 参考文档 1 更新资源的方式 K8S的核心就是各种资源以及针对资源的控制器,为了能够操作资源对象,apis…...
打破关节动力桎梏!杭州宇树科技如何用“一体化设计”重塑四足机器人性能?
核心价值:通过集成电机与行星减速器、创新双联齿轮结构,实现机器人关节动力单元体积缩小50%,力矩控制精度提升30%。(申请人:杭州宇树科技有限公司,申请号:201821267397.0) 一、技术解…...
一劳永逸解决vsocde模块import引用问题
这里写目录标题 原因解决方案 原因解决方案 原因: VSCode中需要显式地声明PYTHONPATH,不然根本找不到本项目内的模块和包的路径。 解决方法,加入到setting。json里当前Project路径,以后运行就自动添加了: 打开设置 …...
在 Vue 组件中,如何确认父组件在 add 模式下传入 value 的情况及其对子组件 getProducts() 方法的触发影响?
文章目录 父组件中 <ave-form> 的使用add 模式下触发逻辑value 的传入情况是否触发 getProducts()? 验证 add 模式下 getProducts() 是否触发结论: 检查父组件传入 value 的完整情况如何明确知道父组件传入的 value最终回答 父组件 index.vue子组件…...
Unity XR-XR Interaction Toolkit开发使用方法(十)组件介绍(XR Interaction Group)
目录 一、插件介绍 二、主要组件 XR Interaction Manager XR Controller XR Interactor XR Direct Interactor XR Ray Interactor XR Socket Interactor XR Gaze Interactor 三、XR Interaction Group 1、组件介绍 2、核心功能与特点 优先级与冲突管理 动态交互切…...
docker简介-学习与参考
docker Docker 是一个开源的应用容器引擎,基于 Go 语言并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。 容器是完全使用沙箱…...
3dtiles平移旋转工具制作
3dtiles平移旋转缩放原理及可视化工具实现 背景 平时工作中,通过cesium平台来搭建一个演示场景是很常见的事情。一般来说,演示场景不需要多完善的功能,但是需要一批三维模型搭建,如厂房、电力设备、园区等。在实际搭建过程中&…...
【第十节】C++设计模式(结构型模式)-Flyweight( 享元)模式
目录 一、问题背景 二、模式选择 三、代码实现 四、总结讨论 一、问题背景 享元模式(Flyweight Pattern)在对象存储优化中的应用 在面向对象系统的设计与实现中,创建对象是最常见的操作之一。然而,如果一个应用程序使用了过多…...
VScode在windows10上使用clang-format
用途:自动调整代码格式,如缩进等。 clang-format官方文档:ClangFormat — Clang 21.0.0git documentation 前提:有一个.clang-format文件 下载LLVM:https://github.com/llvm/llvm-project/releases,将可…...
青少年编程与数学 02-010 C++程序设计基础 11课题、程序结构
青少年编程与数学 02-010 C程序设计基础 11课题、程序结构 一、C程序结构二、main函数1. main 函数的基本形式1.1 无参数形式1.2 带参数形式 2. 参数解释3. 示例3.1 无参数形式3.2 带参数形式 4. 编译和运行4.1 编译4.2 运行 5. main 函数的返回值6. 总结 三、预处理指令1. #in…...
Node.js与MySQL的深入探讨
Node.js与MySQL的深入探讨 引言 Node.js,一个基于Chrome V8引擎的JavaScript运行时环境,以其非阻塞、事件驱动的方式在服务器端应用中占据了一席之地。MySQL,作为一款广泛使用的开源关系型数据库管理系统,凭借其稳定性和高效性,成为了许多应用的数据库选择。本文将深入探…...
【洛谷贪心算法题】P2240部分背包问题
【解题思路】 贪心策略选择 对于部分背包问题,关键在于如何选择物品放入背包以达到最大价值。由于物品可以分割,遍历排序后的物品数组,根据物品重量和背包剩余容量的关系,决定是将整个物品放入背包还是分割物品放入背包ÿ…...
ArcGIS Pro技巧实战:高效矢量化天地图地表覆盖图
在地理信息系统(GIS)领域,地表覆盖图的矢量化是一项至关重要的任务。天地图作为中国国家级的地理信息服务平台,提供了丰富且详尽的地表覆盖数据。然而,这些数据通常以栅格格式存在,不利于进行空间分析和数据…...
AF3 pair_sequences函数解读
AlphaFold3 msa_pairing模块的pair_sequences函数的核心目标是基于 MSA(多序列比对)中的物种信息,在多条链之间建立 MSA 配对索引,从而帮助 AlphaFold3 捕捉共进化信息,提升蛋白复合物预测的准确性。函数pair_sequences 通过调用 _make_msa_df、 _create_species_dict 以…...
在VSCode 中使用通义灵码最新版详细教程
在 VSCode 中使用通义灵码:最新版详细教程与使用场景 Visual Studio Code(简称 VSCode)是一款由微软开发的轻量级、功能强大的开源代码编辑器,支持多种编程语言,深受开发者喜爱。而通义灵码(TONGYI Lingma…...
ssh和rdp踩坑
ssh和rdp(远程桌面)踩坑 使用微软账号登录windows的话,ssh的用户名是本地用户名(就是c盘用户文件夹下的用户名),rdp的用户名是微软账号用户名,但是密码都是微软账号的密码,跟登录密…...
Linux驱动学习(四)--字符设备注册
上一节讲到的字符设备注册与销毁是通过cdev_init、cdev_add、cdev_del等函数分步执行的,本小节用一种更简单的方式,来注册字符设备 register_chrdev 如果major为0,该函数将动态的分配一个主设备号并且返回对应的值如果major > 0ÿ…...
vue3 下载文件 responseType-blob 或者 a标签
在 Vue 3 中,你可以使用 axios 或 fetch 来下载文件,并将 responseType 设置为 blob 以处理二进制数据。以下是一个使用 axios 的示例: 使用 axios 下载文件 首先,确保你已经安装了 axios: npm install axios然后在你…...
Meta最新研究:从单张照片到3D数字人的革命性突破
随着人工智能技术的发展,3D建模和虚拟人物生成逐渐变得更加普及和高效。Meta(前身为Facebook)的最新研究成果展示了如何仅通过一张普通手机拍摄的照片就能生成高质量、全方位的3D数字人。这项技术不仅适用于虚拟试衣、游戏角色建模,还能广泛应用于AR/VR内容生成等领域。本文…...
大中型虚拟化园区网络设计
《大中型虚拟化园区网络设计》属于博主的“园区网”专栏,若想成为HCIE,对于园区网相关的知识需要非常了解,更多关于园区网的内容博主会更新在“园区网”专栏里,请持续关注! 一.前言 华为云园区网络解决方案(简称Cloud…...
Vue 项目中配置代理的必要性与实现指南
Vue 项目中配置代理的必要性与实现指南 在 Vue 前端项目的开发过程中,前端与后端地址通常不同,可能引发跨域问题。为了在开发环境下顺畅地请求后端接口,常常会通过配置**代理(proxy)**来解决问题。这篇文章将详细解析…...
chromadb向量数据库使用 (1)
目录 完整代码代码解释 完整代码 import chromadb chroma_client chromadb.Client()collection chroma_client.create_collection(name"my_collection")collection.add(documents["This is a document about pineapple","This is a document about…...
玩机日记 12 fnOS使用lucky反代https转发到外网提供服务
目录 1、安装lucky 2、更新lucky 3、上传ssl证书 4、设置安全入口,替换fnOS的应用url 5、添加https反代 这一篇主要是解决一下飞牛反代https的问题。可以先看玩机日记 12.5 在PVE Windows11上部署本地AI模型,使用群晖反代https转发到外网提供服务&a…...
5分钟学会SpringAI
引言 要开发一个Spring AI的入门案例,我们可以从一个简单的Spring Boot项目开始,然后集成Spring AI的功能来实现基本的生成式AI任务。下面是一个步骤指南,帮助你快速启动并运行一个简单的Spring AI应用。 步骤 1: 准备环境 首先࿰…...
专业的UML开发工具StarUML
专业的UML开发工具StarUML 可靠的软件建模软件StarUML StarUML 是一款支持统一建模语言 (UML)框架的开源建模软件。它提供了几种类型的图表,并允许用户生成多种语言的代码。在它的帮助下,软件开发人员可以创建设计、概念和编码解决方案。但是࿰…...
go语言环境下载与配置(Windows)
下载 Go下载 - Go语言中文网 - Golang中文社区 建议在D盘中创建文件夹安装到 D 盘 ,方便进行管理,然后进行傻瓜式安装。 安装 验证安装 go version 安装成功 配置环境变量 winE --> 右击此电脑 --> 选择属性 --> 高级系统设置 --> 点击…...
矩阵系列 题解
1.洛谷 P1962 斐波那契数列 题意 大家都知道,斐波那契数列是满足如下性质的一个数列: F n { 1 ( n ≤ 2 ) F n − 1 F n − 2 ( n ≥ 3 ) F_n \left\{\begin{aligned} 1 \space (n \le 2) \\ F_{n-1}F_{n-2} \space (n\ge 3) \end{aligned}\right. …...
C++ 正则表达式分组捕获入门指南
在 C 中,正则表达式(regex)是一种用于匹配字符串模式的强大工具。正则表达式不仅能帮助你查找符合特定模式的字符,还能捕获匹配的子字符串(即分组捕获)。这篇文章将介绍 C 正则表达式中的分组捕获机制&…...
动态数据表格:基于 PrimeFaces 的运行时列选择实现
在现代的 Web 应用开发中,动态数据表格是一个非常实用的功能,它允许用户根据自己的需求选择显示哪些列。这种灵活性不仅提升了用户体验,还能适应不同的数据展示需求。今天,我们将通过一个具体的实现案例,展示如何使用 …...
Plugin ‘mysql_native_password‘ is not loaded`
Plugin ‘mysql_native_password’ is not loaded mysql_native_password介绍1. 使用默认的认证插件2. 修改 my.cnf 或 my.ini 配置文件3. 加载插件(如果确实没有加载)4. 重新安装或检查 MySQL 版本 遇到错误 ERROR 1524 (HY000): Plugin mysql_nativ…...
Kotlin 知识点二 延迟初始化和密封类
对变量延迟初始化 Kotlin 语言的许多特性,包括变量不可变,变量不可为空,等等。这些特性 都是为了尽可能地保证程序安全而设计的,但是有些时候这些特性也会在编码时给我们带来不 少的麻烦。 比如,如果你的类中存在很多…...
DeepSeek开源周第二弹:DeepEP如何用RDMA+FP8让MoE模型飞起来?
一、引言:MoE模型的通信瓶颈与DeepEP的诞生 在混合专家(MoE)模型训练中,专家间的全对全(All-to-All)通信成为性能瓶颈。传统方案在跨节点传输时带宽利用率不足50%,延迟高达300μs以上。DeepSee…...
C++ Primer 成员访问运算符
欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...
无人机遥控器的亮度 和 两个工作频率
工作频率 2.4000-2.4835 GHz , 5.725-5.850 GHz 1.这是一个无人机的遥控器的两个工作频率,为什么会有两个工作频率? 无人机的遥控器采用双频段设计(2.4GHz 和 5.8GHz),主要是为了解决以下问题并优化性…...
ubuntu20.04安装docker
3台主机,2台都能正确安装,第三台怎么都安装不成功; 3台主机都是一样的配置和系统; 后来看来是其外网的ip不一样,导致第三台主机可能被Qiang,不过错误只是提示签名不正确,在设置签名时好像没有…...
【含文档+PPT+源码】基于过滤协同算法的旅游推荐管理系统设计与实现
项目介绍 本课程演示的是一款基于过滤协同算法的旅游推荐管理系统设计与实现,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系…...
深入解析Crawl4AI:为AI应用量身定制的高效开源爬虫框架
引言 在当今数据驱动的时代,人工智能(AI)和大型语言模型(LLM)的发展对高质量数据的需求日益增长。如何高效地从互联网上获取、处理和提取有价值的数据,成为了研究人员和开发者面临的关键挑战。Crawl4AI作为…...
《Effective Objective-C》阅读笔记(下)
目录 内存管理 理解引用计数 引用计数工作原理 自动释放池 保留环 以ARC简化引用计数 使用ARC时必须遵循的方法命名规则 变量的内存管理语义 ARC如何清理实例变量 在dealloc方法中只释放引用并解除监听 编写“异常安全代码”时留意内存管理问题 以弱引用避免保留环 …...
深度生成模型(二)——基本概念与数学建模
上一篇笔记中提到了端到端模型底层核心采用了深度生成模型,先简单梳理一下 生成式人工智能(Artificial Intelligence Generated Content,AIGC)经历了从早期基于概率模型和规则系统的方法到现代深度生成模型的跨越式发展 深度神经…...
4.WebSocket 配置与Nginx 的完美结合
序言 在现代 web 应用中,WebSocket 作为一种全双工通信协议,为实时数据传输提供了强大的支持。若要确保 WebSocket 在生产环境中的稳定性和性能,使用 Nginx 作为反向代理服务器是一个明智的选择。本篇文章将带你了解如何在 Nginx 中配置 Web…...
【R语言】dplyr包经典函数summarise函数
dplyr包经典函数summarise函数,后面改名乘reframe函数了,但是summarise仍然适用 这个函数的返回结果是一个新的数据框,下面讲一下几种常见用法 示例数据为R自带的数据集mtcars 1.不分组 mtcars %>%summarise(mean mean(disp), n n()…...
Cuppa CMS v1.0 任意文件读取(CVE-2022-25401)
漏洞简介: Cuppa CMS v1.0 administrator/templates/default/html/windows/right.php文件存在任意文件读取漏洞 漏洞环境: 春秋云镜中的漏洞靶标,CVE编号为CVE-2022-25401 漏洞复现 弱口令行不通 直接访问administrator/templates/defau…...
8.Dashboard的导入导出
分享自己的Dashboard 1. 在Dashboard settings中选择 JSON Model 2. 导入 后续请参考第三篇导入光放Dashboard,相近...
RabbitMQ 的介绍与使用
一. 简介 1> 什么是MQ 消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。 其主要用途:不同进程Process/线程T…...
unity学习58:下拉列表框 dropdown的caption和options
目录 1 下拉列表框 dropdown 1.1 创建dropdown 1.2 dropdown的子物体构成 1.3 drop的子物体构成:默认灰色的模板 template 1.3.1 默认灰色的模板 template 实际是生效的,只是不直接显示 1.3.2 其中 item下面是下拉选项的格式 1.3.3 可以修改和新增 1.4 drop…...
STM32中使用PWM对舵机控制
目录 1、硬件JIE 2、PWM口配置 3、角度转换 4、main函数中应用 5、工程下载连接 1、硬件介绍 单片机:STM32F1 舵机:MG995 2、PWM口配置 20毫秒的PWM脉冲占空比,对舵机控制效果较好 计算的公式: PSC、ARR值的选取…...
如何免费使用稳定的deepseek
0、背景: 在AI辅助工作中,除了使用cursor做编程外,使用deepseek R1进行问题分析、数据分析、代码分析效果非常好。现在我经常会去拿行业信息、遇到的问题等去咨询R1,也给了自己不少启示。但是由于官网稳定性很差,很多…...