使用Golang实现一套流程可配置,适用于广告、推荐系统的业务性框架——构建

在这个框架中,构建可以分为两部分。一是对象的构建,二是关系的构建。

对象的构建

自动构建

自动构建是指框架依据配置文件,自行创建出其描述的对象。
在自动构建前,我们需要向对象工厂注册各个自定义的类型。比如example_mix例子中

func main() {factory := factory.NewFactory()factory.Register(reflect.TypeOf(examplelayera.ExampleA1Handler{}))factory.Register(reflect.TypeOf(examplelayera.ExampleA2Handler{}))factory.Register(reflect.TypeOf(examplelayera.ExampleADivider{}))……

Factory在底层维护了一个类型名和其反射Type的映射。

func (f *Factory) Register(concreteType reflect.Type) (err error) {concreteName := concreteType.Name()if _, ok := f.concretesType[concreteName]; !ok {f.concretesType[concreteName] = concreteType} else {err = fmt.Errorf(concreteName + " is already registered.Please modify name.")panic(err.Error())}return nil
}

后续,框架在读取配置文件的过程中,会根据type字段的值构建对象。

func (f *Factory) Create(concreteTypeName string, configure []byte, constructorInterface any) (concrete any, err error) {concreteType, ok := f.concretesType[concreteTypeName]if !ok {err = fmt.Errorf("concrete name not found for %s", concreteTypeName)panic(err.Error())}concrete = reflect.New(concreteType).Interface()concreteInterface, ok := concrete.(frame.ConcreteInterface)if !ok || concreteInterface == nil {err = fmt.Errorf("concrete %s conver to ConcreteInterface error", concreteTypeName)panic(err.Error())}constructorSetterInterface, ok := concrete.(frame.ConstructorSetterInterface)if ok && constructorSetterInterface != nil && constructorInterface != nil {constructorSetterInterface.SetConstructorInterface(constructorInterface)}loadConfigFromMemoryInterface, ok := concrete.(frame.LoadConfigFromMemoryInterface)if ok && loadConfigFromMemoryInterface != nil {err := loadConfigFromMemoryInterface.LoadConfigFromMemory(configure)if err != nil {err = fmt.Errorf("concrete LoadConfigFromMemory error for %s .error: %v", concreteTypeName, err)panic(err.Error())}}loadEnvironmentConfInterface, ok := concrete.(frame.LoadEnvironmentConfInterface)if ok && loadEnvironmentConfInterface != nil {err := loadEnvironmentConfInterface.LoadEnvironmentConf()if err != nil {err = fmt.Errorf("concrete LoadEnvironmentConf error for %s .error: %v", concreteTypeName, err)panic(err.Error())}}concreteName := concreteInterface.Name()if concreteName == "" {err = fmt.Errorf("concrete's Name is empty for %s", concreteTypeName)panic(err.Error())}if _, ok := f.concretes[concreteName]; !ok {f.concretes[concreteName] = concreteInterface} else {err = fmt.Errorf("concrete's Name  %s has already been used, type is %s", concreteName, concreteTypeName)panic(err.Error())}return
}

被自动构建的对象会自动保存起来,并通过下面的方法获取

func (f *Factory) Get(concreteName string) (frame.ConcreteInterface, error) {if concrete, ok := f.concretes[concreteName]; ok {return concrete, nil} else {return nil, nil}
}

举个例子,配置文件目录下存在layer_center.yaml文件

# layer_center.yaml
type: LayerCenter
name: layer_center
layers: - layer_a- layer_b

构建器会通过Get方法检查名字为layer_center的组件是否存在。如果不存在,就调用Create方法创建type为LayerCenter、名字为layer_center的组件。LayerCenter在创建后会自动读取上述配置,发现其layers下有两个组件layer_a和layer_b。然后会检查这两个组件是否存在。如果不存在,则会在构建器中通过组件名,寻找对应的配置文件——这就要求组件名和其配置名强一致。比如layer_a的配置名为layer_a.yaml,layer_b的配置名为layer_b.yaml。

# layer_a.yaml
name: layer_a
type: Layer
divider: ExampleADivider
handlers: - ExampleA1Handler- ExampleA2Handler
# layer_b.yaml
name: layer_b
type: Layer
divider: ExampleBDivider
handlers: - ExampleB1Handler- ExampleB2Handler

这个创建过程通过下面函数实现

func (c *Constructor) createConcreteByObjectName(name string) error {confPath, err := c.concreteConfManager.GetConfPath(name)if err != nil {return err}data, err := os.ReadFile(confPath)if err != nil {return err}var constructorType ConstructorTypeif err := yaml.Unmarshal(data, &constructorType); err != nil {return err}switch constructorType.Type {case TypeNameHandler:_, err := c.handlerConstructorInterface.GetHandler(name)if err != nil {return c.handlerConstructorInterface.CreateHandlerWithConfPath(confPath)}case TypeNameDivider:_, err := c.dividerConstructorInterface.GetDivider(name)if err != nil {return c.dividerConstructorInterface.CreateDividerWithConfPath(confPath)}case TypeNameLayer:_, err := c.layerConstructorInterface.GetLayer(name)if err != nil {return c.layerConstructorInterface.CreateLayerWithConfPath(confPath)}case TypeNameLayerCenter:_, err := c.layerCenterConstructorInterface.GetLayerCenter(name)if err != nil {return c.layerCenterConstructorInterface.CreateLayerCenterWithConfPath(confPath)}case TypeNameHandlerGroup:_, err := c.handlerGroupConstructorInterface.GetHandlerGroup(name)if err != nil {return c.handlerGroupConstructorInterface.CreateHandlerGroupWithConfPath(confPath)}case TypeNameAsyncHandlerGroup:_, err := c.asyncHandlerGroupConstructorInterface.GetAsyncHandlerGroup(name)if err != nil {return c.asyncHandlerGroupConstructorInterface.CreateAsyncHandlerGroupWithConfPath(confPath)}default:err := c.createConcreteByTypeName(constructorType.Type, data)if err != nil {return err}return nil}return fmt.Errorf("class name  %s not found", name)
}

对于框架自有组件,如LayerCenter、HandlerGroup等,它们会由其构建器构建。对于其他自定义的组件,比如自定义的各种Handler,则通过default逻辑中createConcreteByTypeName实现。

func (c *Constructor) createConcreteByTypeName(name string, data []byte) error {if strings.HasSuffix(name, TypeNameAsyncHandlerGroup) {_, err := c.asyncHandlerGroupConstructorInterface.GetAsyncHandlerGroup(name)if err != nil {asyncHandlerGroupInterface, err := c.Create(name, data, c)if err != nil {return err}if namedInterface, ok := asyncHandlerGroupInterface.(frame.ConcreteInterface); ok {return c.asyncHandlerGroupConstructorInterface.RegisterAsyncHandlerGroup(namedInterface.Name(), asyncHandlerGroupInterface.(frame.AsyncHandlerGroupBaseInterface))} else {return c.asyncHandlerGroupConstructorInterface.RegisterAsyncHandlerGroup(name, asyncHandlerGroupInterface.(frame.AsyncHandlerGroupBaseInterface))}}return nil}if strings.HasPrefix(name, TypeNameHandlerGroup) {_, err := c.handlerGroupConstructorInterface.GetHandlerGroup(name)if err != nil {handlerGroupInterface, err := c.Create(name, data, c)if err != nil {return err}if namedInterface, ok := handlerGroupInterface.(frame.ConcreteInterface); ok {return c.handlerGroupConstructorInterface.RegisterHandlerGroup(namedInterface.Name(), handlerGroupInterface.(frame.HandlerGroupBaseInterface))} else {return c.handlerGroupConstructorInterface.RegisterHandlerGroup(name, handlerGroupInterface.(frame.HandlerGroupBaseInterface))}}return nil}if strings.HasSuffix(name, TypeNameDivider) {_, err := c.dividerConstructorInterface.GetDivider(name)if err != nil {dividerInterface, err := c.Create(name, data, c)if err != nil {return err}if namedInterface, ok := dividerInterface.(frame.ConcreteInterface); ok {return c.dividerConstructorInterface.RegisterDivider(namedInterface.Name(), dividerInterface.(frame.DividerBaseInterface))} else {return c.dividerConstructorInterface.RegisterDivider(name, dividerInterface.(frame.DividerBaseInterface))}}return nil}if strings.HasSuffix(name, TypeNameHandler) {_, err := c.handlerConstructorInterface.GetHandler(name)if err != nil {handlerInterface, err := c.Create(name, data, c)if err != nil {return err}if namedInterface, ok := handlerInterface.(frame.ConcreteInterface); ok {return c.handlerConstructorInterface.RegisterHandler(namedInterface.Name(), handlerInterface.(frame.HandlerBaseInterface))} else {return c.handlerConstructorInterface.RegisterHandler(name, handlerInterface.(frame.HandlerBaseInterface))}}return nil}if strings.HasSuffix(name, TypeNameLayer) {_, err := c.layerConstructorInterface.GetLayer(name)if err != nil {layerInterface, err := c.Create(name, data, c)if err != nil {return err}if namedInterface, ok := layerInterface.(frame.ConcreteInterface); ok {return c.layerConstructorInterface.RegisterLayer(namedInterface.Name(), layerInterface.(frame.LayerBaseInterface))} else {return c.layerConstructorInterface.RegisterLayer(name, layerInterface.(frame.LayerBaseInterface))}}return nil}if strings.HasSuffix(name, TypeNameLayerCenter) {_, err := c.layerCenterConstructorInterface.GetLayerCenter(name)if err != nil {layerCenterInterface, err := c.Create(name, data, c)if err != nil {return err}if namedInterface, ok := layerCenterInterface.(frame.ConcreteInterface); ok {return c.layerCenterConstructorInterface.RegisterLayerCenter(namedInterface.Name(), layerCenterInterface.(frame.LayerCenterBaseInterface))} else {return c.layerCenterConstructorInterface.RegisterLayerCenter(name, layerCenterInterface.(frame.LayerCenterBaseInterface))}}return nil}return fmt.Errorf("object name  %s not found", name)
}

在底层,我们需要设计一种规则用于标志这个自定义组件是哪个框架基础组件的子类。这儿就引出这个框架的第二个强制性约定——自定义类型的名称需要以框架基础组件名结尾。比如自定义的ExampleA1Handler是以Handler结尾,这样在底层我们就知道将其构造成一个Handler对象。
所有的自动构建,都依赖于配置文件。于是我们设计了ConcreteConfManager来遍历配置文件目录,这个目录在我们创建构建器时传入的。

	……runPath, errGetWd := os.Getwd()if errGetWd != nil {fmt.Printf("%v", errGetWd)return}concretePath := path.Join(runPath, "conf")constructor := constructorbuilder.BuildConstructor(factory, concretePath)……

然后我们告诉构建器初始组件名,它就会自动像爬虫一样,通过配置文件和之前注册的反射类型,将对象和关系都构建出来。

	……mainProcess := "layer_center"run(constructor, mainProcess)
}func run(constructor *constructor.Constructor, mainProcess string) {if err := constructor.CreateConcrete(mainProcess); err != nil {fmt.Printf("%v", err)}

单一对象

单一对象是指一个类型只有一个对象。
我们在写业务时,往往需要一个简单的逻辑单元处理一个单一的事情,即在任何场景下,它只需要存在一份——属性一样,且不会改变。
这个时候,对象名变得不太重要。我们只要让其Name方法返回其类型名,而不需要再搞一个配置文件,就能实现自动构建。这种场景占绝大多数。

func (e *ExampleA1Handler) Name() string {return reflect.TypeOf(*e).Name()
}

多个对象

有时候,我们希望一个类可以处理一种逻辑,但是其在不同场景下,其属性不一样。这样我们就需要通过配置文件来描述它——描述它不同的名字和对应的属性。比如下面的例子就是从配置文件中读取了名字和其相应属性。

package samplehandlerimport ("fmt""ghgroups/frame"ghgroupscontext "ghgroups/frame/ghgroups_context""gopkg.in/yaml.v2"
)// 自动构建handler,它会自动从配置文件中读取配置,然后根据配置构建handler
// 因为系统使用名称作为唯一检索键,所以自动构建handler在构建过程中,就要被命名,而名称应该来源于配置文件
// 这就要求配置文件中必须有一个名为name的字段,用于指定handler的名称
// 下面例子中confs配置不是必须的,handler的实现者,需要自行解析配置文件,以确保Name方法返回的名称与配置文件中的name字段一致type SampleAutoConstructHandlerConf struct {Name  string                              `yaml:"name"`Confs []SampleAutoConstructHandlerEnvConf `yaml:"confs"`
}type SampleAutoConstructHandlerEnvConf struct {Env         string                                 `yaml:"env"`RegionsConf []SampleAutoConstructHandlerRegionConf `yaml:"regions_conf"`
}type SampleAutoConstructHandlerRegionConf struct {Region          string `yaml:"region"`AwsRegion       string `yaml:"aws_region"`AccessKeyId     string `yaml:"access_key_id"`SecretAccessKey string `yaml:"secret_access_key"`IntKey          int32  `yaml:"int_key"`
}type SampleAutoConstructHandler struct {frame.HandlerBaseInterfaceframe.LoadConfigFromMemoryInterfaceconf SampleAutoConstructHandlerConf
}func NewSampleAutoConstructHandler() *SampleAutoConstructHandler {return &SampleAutoConstructHandler{}
}// ///
// LoadConfigFromMemoryInterface
func (s *SampleAutoConstructHandler) LoadConfigFromMemory(configure []byte) error {sampleHandlerConf := new(SampleAutoConstructHandlerConf)err := yaml.Unmarshal([]byte(configure), sampleHandlerConf)if err != nil {return err}s.conf = *sampleHandlerConfreturn nil
}// ///
// ConcreteInterface
func (s *SampleAutoConstructHandler) Name() string {return s.conf.Name
}// ///
// HandlerBaseInterface
func (s *SampleAutoConstructHandler) Handle(*ghgroupscontext.GhGroupsContext) bool {fmt.Sprintln(s.conf.Name)return true
}// ///

于是我们在不同组件关系中,通过该类型的不同对象名来组织关系,从而实现一套逻辑,不同配置的应用场景。
比如下面的两个配置,描述了同一个类型的不同配置

# sample_handler_a.yaml
type: SampleAutoConstructHandler
name: sample_handler_a
confs: - env: Onlineregions_conf:- region: us-east-1aws_region: us-east-1int_key: 1- region: us-east-2aws_region: us-east-2
# sample_handler_b.yaml
type: SampleAutoConstructHandler
name: sample_handler_b
confs: - env: Onlineregions_conf:- region: us-east-1aws_region: us-east-1int_key: 2- region: us-east-2aws_region: us-east-2

然后在下面关系中予以区分调用

name: Sample
divider: divider_sample_a
handlers: - handler_sample_a- handler_sample_b

手工构建

如果由于某些原因,自动构建不能满足需求,我们可以手工构建对象。这个时候我们不需要向对象工厂注册其反射(Register),只要手工构建出对象后,调用工厂的注册对象方法(比如RegisterHandler),告诉框架某个名字的组件存在。这样在构建关系时,就会自动识别——这就要求手工构建要在自动构建之前完成,否则框架无法识别它们。

package samplehandlerimport ("fmt""ghgroups/frame"ghgroupscontext "ghgroups/frame/ghgroups_context"
)// 这是相对简单的handler,它只用实现HandlerInterface两个接口
// 系统使用名称作为唯一检索键,通过构造不同的对象拥有不同的名字,可以在系统中有多个该名字的handler实例,即一个类型(struct)可以有多个该名字的handler实例type SampleSelfConstructHandlerMulti struct {frame.HandlerBaseInterfacename string
}func NewSampleSelfConstructHandlerMulti(name string) *SampleSelfConstructHandlerMulti {return &SampleSelfConstructHandlerMulti{name: name,}
}// ///
// ConcreteInterface
func (s *SampleSelfConstructHandlerMulti) Name() string {return s.name
}// ///
// HandlerBaseInterface
func (s *SampleSelfConstructHandlerMulti) Handle(*ghgroupscontext.GhGroupsContext) bool {fmt.Sprintln(s.Name())return true
}// ///

注册代码如下

	……constructor := utils.BuildConstructor("")sampleSelfConstructHandlerMultiNameA := "sample_self_construct_handler_multi_a"sampleSelfConstructHandlerMultiA := NewSampleSelfConstructHandlerMulti(sampleSelfConstructHandlerMultiNameA)constructor.RegisterHandler(sampleSelfConstructHandlerMultiA.Name(), sampleSelfConstructHandlerMultiA)sampleSelfConstructHandlerMultiNameB := "sample_self_construct_handler_multi_b"sampleSelfConstructHandlerMultiB := NewSampleSelfConstructHandlerMulti(sampleSelfConstructHandlerMultiNameB)constructor.RegisterHandler(sampleSelfConstructHandlerMultiB.Name(), sampleSelfConstructHandlerMultiB)……

关系的构建

自动构建

关系的自动构建依赖于配置文件的描述。
每个组件在读取配置文件后,会构建不存在的子组件,并加载其配置。
在这个递归过程中,整个关系网就会被构建起来。
比如LayerCenter的构建过程

func (l *LayerCenter) LoadConfigFromMemory(configure []byte) error {var layerCenterConf LayerCenterConferr := yaml.Unmarshal(configure, &layerCenterConf)if err != nil {return err}l.conf = layerCenterConfreturn l.init()
}func (l *LayerCenter) init() error {for _, layerName := range l.conf.Layers {if err := l.constructorInterface.CreateConcrete(layerName); err != nil {return err}if someInterface, err := l.constructorInterface.GetConcrete(layerName); err != nil {return err} else {if layerBaseInterface, ok := someInterface.(frame.LayerBaseInterface); !ok {return fmt.Errorf("layer %s is not frame.LayerBaseInterface", layerName)} else {l.layers = append(l.layers, layerBaseInterface)}}}return nil
}

其在底层会创建Layer对象,进而触发Layer子组件的构建

func (l *Layer) LoadConfigFromMemory(configure []byte) error {var layerConf LayerConferr := yaml.Unmarshal(configure, &layerConf)if err != nil {return err}l.conf = layerConfreturn l.init()
}func (l *Layer) init() error {if l.handlers == nil {l.handlers = make(map[string]frame.HandlerBaseInterface)}err := l.initDivider(l.conf.Divider)if err != nil {return err}err = l.initHandlers(l.conf.Handlers)if err != nil {return err}return nil
}func (l *Layer) initDivider(dividerName string) error {if err := l.constructorInterface.CreateConcrete(dividerName); err != nil {return err}if someInterface, err := l.constructorInterface.GetConcrete(dividerName); err != nil {return err} else {if dividerInterface, ok := someInterface.(frame.DividerBaseInterface); !ok {return fmt.Errorf("handler %s is not frame.DividerBaseInterface", dividerName)} else {err = l.SetDivider(dividerName, dividerInterface)if err != nil {return err}}}return nil
}func (l *Layer) initHandlers(handlersName []string) error {for _, handlerName := range handlersName {if err := l.constructorInterface.CreateConcrete(handlerName); err != nil {return err}if someInterface, err := l.constructorInterface.GetConcrete(handlerName); err != nil {return err} else {if handlerInterface, ok := someInterface.(frame.HandlerBaseInterface); !ok {return fmt.Errorf("handler %s is not frame.HandlerBaseInterface", handlerName)} else {err = l.AddHandler(handlerName, handlerInterface)if err != nil {return err}}}}return nil
}

手工构建

手工构建是不推荐的形式,因为它可能会让维护成本上升,但是框架仍然支持这种形式。
这儿只是做个简单介绍,如下例子

	constructor := utils.BuildConstructor("")layerCenter := NewLayerCenter(constructor)testLayer := layer.NewLayer("test_layer", constructor)layerCenter.Add(testLayer)

layerCenter通过Add新增了一个Layer。
更多具体例子可以参考源码中的单测文件。

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

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

相关文章

Day01-作业(HTMLCSS)

作业1:通过HTML的标签及CSS样式,完成如下企业简介html页面的制作 A. 最终效果如下: B. 文字素材如下: 企业简介传智教育(股票代码 003032),隶属江苏传智播客教育科技股份有限公司,注册资本4亿元&#xff0…

常见的相似性度量方法

有如下几种计算相似性方法: 点积相似度 X ⋅ Y ∣ X ∣ ∣ Y ∣ c o s θ ∑ i 1 n x i ∗ y i \begin{aligned} X \cdot Y & |X||Y|cos\theta \\ & \sum_{i1}^n x_i * y_i \end{aligned} X⋅Y​∣X∣∣Y∣cosθi1∑n​xi​∗yi​​ 向量内积的结果是没…

java 框架

目录 Spring 如何解决 bean 的循环依赖?什么是 AOP?Spring 如何实现的?BeanFactory 和 ApplicationContext 有什么区别?介绍一下 Spring bean 的生命周期Spring 的隔离级别Spring 框架用到了哪些设计模式?并举出典型例子Spring 如何解决 bean 的循环依赖? Spring中引入三…

918. 环形子数组的最大和;2531. 使字符串总不同字符的数目相等;1238. 循环码排列

918. 环形子数组的最大和 核心思想:其实这题不加环形很好做,就是一个动态规划或者贪心就能够解决。加了环形我们应该怎么考虑呢,无非就是两种,第1种是子数组只包含首尾的一个,我们直接求子数组的最大连续和即可&#…

服务器运行python程序的使用说明

服务器的使用与说明 文章目录 服务器的使用与说明1.登录2.Python的使用2.1 服务器已安装python32.2 往自己的用户目录安装python31.首先下载安装包2.解压缩3.编译与安装 2.3 新建环境变量2.4 测试 3 创建PBS作业并提交 1.登录 windowsr打开运行命令窗口,在运行框中…

Leaflet.Control.Opacity 控制图层的透明度

最新有一个需求,能动态的控制Leaflet.js 地图图层的透明度,官网文档: https://leafletjs.com/reference.html#gridlayer-setopacity 一直有个setOpacity方法,我以为拿来就能使呢,其实不行。后来找到一个日本人开发的demo: 右侧Co…

2023值得关注的人工智能7大发展趋势

随着人工智能技术的不断创新和应用,我们可以看到人工智能在各个领域的应用越来越广泛。其中,有一些趋势特别值得我们关注。 1)深度学习技术的发展 深度学习技术在图像识别、语音识别、自然语言处理等领域表现出色,随着硬件计算能…

部署安装私服-Gitlab

一、国内的gitlab是极狐 www.gitlab.cn 国服 www.github.com 国际服 二、国服的gitlab蛮适合中国国情的 1.提交申请可以获得30天的订阅版服务,有需要的话可以先提交一下。订阅后功能多一些。 Gitlab中文官网下载_GitLab免费下载安装_极狐GitLab免…

OpenCV中常用算子

一、图像运算 Mat src, src1, src2, dst;// 图像运算 加 减 乘 除cv::add(src1, src2, dst); // 相加:src1src2cv::scaleAdd(src1, 1.0, src2, dst); // 相加:1.0*src1src2cv::addWeighted(src1, 0.3, src2, 0.7,…

计算机视觉:替换万物Inpaint Anything

目录 1 Inpaint Anything介绍 1.1 为什么我们需要Inpaint Anything 1.2 Inpaint Anything工作原理 1.3 Inpaint Anything的功能是什么 1.4 Segment Anything模型(SAM) 1.5 Inpaint Anything 1.5.1 移除任何物体 1.5.2 填充任意内容 1.5.3 替换任…

【设计模式——学习笔记】23种设计模式——访问者模式Visitor(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录 案例引入要求传统方案 介绍基本介绍应用场景登场角色尚硅谷版本《图解设计模式》版本 案例实现案例一实现拓展 案例二(个人感觉这个案例较好)实现分析拓展一拓展二拓展三 总结额外知识双重分发 文章说明 案例引入 要求 测评系统需求:将观众分为男人和女人…

学了一个礼拜 JavaScript 为什么还是学不会?

前言 首先从你的描述里面我先以我的主观臆断来猜测一下你是没有任何编程的基础的,Js按理来说在各语言中并不是非常难学,当然如果你是纯新手入门,那么确实前期需要时间来沉淀一下语法,一个礼拜的话,按理来说应该是在沉…

以Llama-2为例,在生成模型中使用自定义LogitsProcessor

以Llama-2为例,在生成模型中使用自定义LogitsProcessor 1. 前言2. 场景介绍3. 解决方法4. 结语 1. 前言 在上一篇文章 以Llama-2为例,在生成模型中使用自定义StoppingCriteria中,介绍了怎样在生成的过程中,使用stopping criteria…

T31开发笔记:librtmp拉流测试

若该文为原创文章,转载请注明原文出处。 T31使用librtmp拉流并保存成FLV文件或H264和AAC文件。 librtmp编译在前面有教程,自行编译。 实现的目的是想要获取获取rtmp的AAC流并播放,实时双向对讲功能。 一、硬件和开发环境 1、硬件&#xff1…

Vue组件化开发--公共组件的封装

在Vue中,组件是构建用户界面的基本单位。封装公共组件是一种良好的实践,可以提高代码的可复用性和可维护性。下面是一个示例,演示了如何封装一个公共的按钮组件。 首先,创建一个名为Button.vue的Vue组件文件。这个组件将封装一个…

node.js相关的npm包的集合

一、实用功能 1. qs 一个简单易用的字符串解析和格式化库 2.rxjs RxJS是一组模块化的库,用于使用 JavaScript 中的可观察集合和组合来组合异步和基于事件的程序。 3. mitt 微型 200b 功能事件发射器/发布订阅. 4.Underscore.js Underscore.js是一个用于 JavaScript…

Linux C++ 链接数据库并对数据库进行一些简单的操作

一.引言(写在之前) 在我们进行网络业务代码书写的时候,我们总是避免对产生的数据进行增删改查,为此,本小博主在这里简历分享一下自己在Linux中C语言与数据之间交互的代码的入门介绍。 二.代码书写以及一些变量和函数的…

Vue3 nodejs 安装和配置---vue3教程一

文章目录 前言1、nodejs安装2、配置缓存路径:3、 阿里镜像cnpm使用4、 yarn安装5、配置nodejs国内镜像6、查看各个版本7、node npm yarn概念8、nodejs 和vue 关系外传 前言 本人主做后端Java,以前搞全栈开发是,还没有vue,rect等前端框架&…

MGRE综合

实验 一、实验思路 1.先按照上图配置IP地址及环回 2.写缺省使公网可通 3.让R1、R4、R5每台路由器均成为中心站点形成全连网状结构拓扑 4.让R1成为中心站点R2R3为分支站点 5.分区域宣告ospf之后更改ospf在虚拟接口Tunnel工作方式为broadcast及让R1 当选DR 二、上虚拟机操作…

[CKA]考试之一个 Pod 封装多个容器

由于最新的CKA考试改版,不允许存储书签,本博客致力怎么一步步从官网把答案找到,如何修改把题做对,下面开始我们的 CKA之旅 题目为: Task 创建一个Pod,名字为kucc1,这个Pod包含4容器&#xff…