Kubernetes operator(五)api 和 apimachinery 篇
云原生学习路线导航页(持续更新中)
- 本文是 Kubernetes operator学习 系列第五篇,主要对 k8s.io/api 和 k8s.io/apimachinery 两个项目 进行学习
- 基于 kubernetes v1.24.0 代码分析
- Kubernetes operator学习系列 快捷链接
- Kubernetes operator(一)client-go篇
- Kubernetes operator(二)CRD篇
- Kubernetes operator(三)code-generator 篇
- Kubernetes operator(四)controller-tools 篇
- Kubernetes operator(五)api 和 apimachinery 篇
1.k8s.io/api 项目
1.1.k8s.io/api 项目是什么
- 一开始,kubernetes的 内建资源 还不太多,内建资源的 结构定义,都是放在项目里维护的。
- 后来为了方便资源的管理和扩展,将 所有内建资源 的结构定义文件、scheme注册文件、deepcopy等文件,放入了 staging/src 目录下,作为一个单独的项目维护。该项目的名称就是
k8s.io/api
- 因此,简单来说,
k8s.io/api
项目,维护着 Kubernetes 所有内建资源 的 struct定义。
1.2.k8s.io/api 的源码分析
- 下图中的每一个目录,都代表一个group
- 一个 Group 下,可能会存在多个 Version
- 每个version下,都会包含三个文件:
doc.go、register.go、types.go
。- doc.go:声明了按照 package 维度,为所有 structs 提供生成的声明
- types.go:编写资源的详细结构,一般包括:资源、资源List、资源Spec、资源Status 的详细定义
- register.go:提供注册到 runtime.Scheme 的函数
- 以
apps/v1/types.go
为例,查看其内容,发现包含GroupVersion=apps/v1
下的所有Resource结构定义。- 下图只截出了一部分,没有截出全部。
- 下图只截出了一部分,没有截出全部。
- 因此,我们操作内建资源的时候,所有 GVK 内建资源的结构,都是由
k8s.io/api
这个项目提供的。
2.k8s.io/apimachinery 项目
2.1.k8s.io/apimachinery 项目是什么
k8s.io/apimachinery
项目是一个关于Kubernetes API资源的工具集,为k8s.io/api
项目所有的资源,提供下列能力。- ObjectMeta与TypeMeta
- Scheme
- RESTMapper
- 编码与解码
- 版本转换
- …
- 有了
k8s.io/apimachinery
,就可以很方便的操作 kubernetes API。
2.2.k8s.io/apimachinery 提供 TypeMeta 与 ObjectMeta
- TypeMeta 与 ObjectMeta 是特别常用的两个数据结构。kubernetes 的每一个资源,都会包含一个 TypeMeta、一个ObjectMeta。
- TypeMeta是内嵌的,转json的时候不会有嵌套结构
- ObjectMeta,json标签就是 metadata
type Pod struct {metav1.TypeMeta `json:",inline"`metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`Status PodStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` }
- TypeMeta:位于
apimachinery/pkg/runtime/types.go
type TypeMeta struct {// +optionalAPIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty" protobuf:"bytes,1,opt,name=apiVersion"`// +optionalKind string `json:"kind,omitempty" yaml:"kind,omitempty" protobuf:"bytes,2,opt,name=kind"` }
- ObjectMeta:位于
apimachinery/pkg/apis/meta/v1/types.go
type ObjectMeta struct {Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,4,opt,name=selfLink"`UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,6,opt,name=resourceVersion"`Generation int64 `json:"generation,omitempty" protobuf:"varint,7,opt,name=generation"`CreationTimestamp Time `json:"creationTimestamp,omitempty" protobuf:"bytes,8,opt,name=creationTimestamp"`DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"`DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"`Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid" protobuf:"bytes,13,rep,name=ownerReferences"`Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`ZZZ_DeprecatedClusterName string `json:"clusterName,omitempty" protobuf:"bytes,15,opt,name=clusterName"`ManagedFields []ManagedFieldsEntry `json:"managedFields,omitempty" protobuf:"bytes,17,rep,name=managedFields"` }
2.3.k8s.io/apimachinery 的rumetime/schema包提供 GVRK 各种数据结构
- 在kubernetes中,为了方便描述资源,或描述REST 的URL,提出了5个概念:
- GV:GroupVersion
- GR:GroupResource
- GVR:GroupVersionResource
- GK:GroupKind
- GVK:GroupVersionKind
- 其中,GR、GVR都是用来描述 RESTFUL API 的,GK、GVK都是用来描述资源类型的
- 这 5种数据结构的 struct 定义,都是写在
k8s.io/apimachinery/pkg/runtime/schema/group_version.go
文件中 - 该文件中还提供了这5种数据结构相互转换的方法
-
其中,APIVersion Kind,就是我们平时写yaml看到的apiVersion:
Group/Version
Kind
-
各结构的转换方法如下:
-
源码如下:
-
2.4.k8s.io/apimachinery 提供 scheme 数据结构
2.4.1.资源的internal版本、external版本
2.4.1.1.资源的internal版本、external版本是什么
- kubernetes的资源,并非一下就确定好的,是有一个发展过程的,因此一个资源Kind,可能在多个 GroupVersion 下同时存在。比如 Deployment,在apps/v1下存在,在apps/v1beta1下也存在。
- 那么,在kubernetes的开发者想要处理Deployment的时候,到底应该按照哪个版本写程序呢?
- 按理说,每一种GVK都要有相应的处理方法。
- 但是这样实在是太繁琐了,维护起来不方便,还会有大量重复代码
- 因此,为每一种GK,维护了一个internal版本,作为中转节点。
apps/v1/Deployment
和apps/v1beta1/Deployment
的相互转换,均是先转成internal的Deployment,再转成 对外的版本- kubernetes的作者们,只需要对
internal版本
的资源编写逻辑,就可以处理所有version的资源
- 这种设计方式,将GVK之间的 拓扑结构,变成了星型结构,非常巧妙。
2.4.1.2.internal版本 和 external版本 相互转换的源码位置
kubernetes/pkg/apis
中,每个目录都是一个group,每个group都有一个 internal 的 资源 types.go 文件
- external 资源 --> internal 资源 的方法
2.4.2.scheme的作用
- kubernetes的资源版本太多了,没有谁专门有时间去维护,还是让资源自己来注册比较方便。
- scheme就是为资源注册信息设计的一个数据结构,每个GVK,将自己的信息封装成一个scheme对象,并将这个scheme对象交给APIServer统一管理,API Server就能够认识这种 GVK 了
- 在k8s.io/api 项目中,每一个GV下都有一个register.go文件,里面就是将当前GV下的所有Kind,注册到 APIServer 的统一scheme中去。比如
staging/src/k8s.io/api/apps/v1/register.go
文件,package v1import (metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/apimachinery/pkg/runtime""k8s.io/apimachinery/pkg/runtime/schema" )// GroupName is the group name use in this package const GroupName = "apps"// SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}// Resource takes an unqualified resource and returns a Group qualified GroupResource func Resource(resource string) schema.GroupResource {return SchemeGroupVersion.WithResource(resource).GroupResource() }var (// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)localSchemeBuilder = &SchemeBuilderAddToScheme = localSchemeBuilder.AddToScheme )// Adds the list of known types to the given scheme. func addKnownTypes(scheme *runtime.Scheme) error {scheme.AddKnownTypes(SchemeGroupVersion,&Deployment{},&DeploymentList{},&StatefulSet{},&StatefulSetList{},&DaemonSet{},&DaemonSetList{},&ReplicaSet{},&ReplicaSetList{},&ControllerRevision{},&ControllerRevisionList{},)metav1.AddToGroupVersion(scheme, SchemeGroupVersion)return nil }
2.4.3.k8s.io/apimachinery 提供 scheme 数据结构
- 根据前面的描述,我们知道scheme是一个数据结构,它的struct其实就是 k8s.io/apimachinery 提供的
- 在
staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go
文件中,有 Scheme 结构type Scheme struct {// map,记录 gvk-->type。其中type是通过反射的方式记录的gvkToType map[schema.GroupVersionKind]reflect.Type// map,记录 type-->gvktypeToGVK map[reflect.Type][]schema.GroupVersionKind// map,记录 type-->gvk。像pod这种,只有一个version的,就记录在这里。unversionedTypes map[reflect.Type]schema.GroupVersionKind// map,记录 gvk-->type。像pod这种,只有一个version的,就记录在这里。unversionedKinds map[string]reflect.Type// Map from version and resource to the corresponding func to convert// resource field labels in that version to internal version.fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc// map,记录默认方法。为某一个具体的type,设置默认值defaulterFuncs map[reflect.Type]func(interface{})// 转换器converter *conversion.Converter// 记录version的优先级。当没有选择version的时候,优先使用谁versionPriority map[string][]string// observedVersions keeps track of the order we've seen versions during type registrationobservedVersions []schema.GroupVersion// schemeName is the name of this scheme. If you don't specify a name, the stack of the NewScheme caller will be used.// This is useful for error reporting to indicate the origin of the scheme.schemeName string }
- Scheme结构中,所有的字段首字母都是小写的,即非导出的,外界无法访问。为此,
staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go
文件中还提供了一个方法 NewScheme(),用于初始化一个空的Scheme对象func NewScheme() *Scheme {s := &Scheme{gvkToType: map[schema.GroupVersionKind]reflect.Type{},typeToGVK: map[reflect.Type][]schema.GroupVersionKind{},unversionedTypes: map[reflect.Type]schema.GroupVersionKind{},unversionedKinds: map[string]reflect.Type{},fieldLabelConversionFuncs: map[schema.GroupVersionKind]FieldLabelConversionFunc{},defaulterFuncs: map[reflect.Type]func(interface{}){},versionPriority: map[string][]string{},schemeName: naming.GetNameFromCallsite(internalPackages...),}s.converter = conversion.NewConverter(nil)// Enable couple default conversions by default.utilruntime.Must(RegisterEmbeddedConversions(s))utilruntime.Must(RegisterStringConversions(s))return s }
- 此外,
staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go
文件还提供了很多方法,用于将GVK注册到Scheme对象中。用的比较多的是AddKnownTypes()方法func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {s.addObservedVersion(gv)for _, obj := range types {t := reflect.TypeOf(obj)if t.Kind() != reflect.Ptr {panic("All types must be pointers to structs.")}t = t.Elem()s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)} }
2.5.3.Scheme结构提供的常用方法
2.5.3.1.AddKnownTypes
- 方法签名:
func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object)
- 方法功能:向 scheme 中注册GVK,参数1 gv 表示 GroupVersion,参数2 types 是具体的 Kind 类型
- 举例:
staging/src/k8s.io/api/apps/v1/register.go
文件中,使用 AddKnownTypes 方法,将apps/v1下的所有Kind,都注册到scheme中去func addKnownTypes(scheme *runtime.Scheme) error {scheme.AddKnownTypes(SchemeGroupVersion,&Deployment{},&DeploymentList{},&StatefulSet{},&StatefulSetList{},&DaemonSet{},&DaemonSetList{},&ReplicaSet{},&ReplicaSetList{},&ControllerRevision{},&ControllerRevisionList{},)metav1.AddToGroupVersion(scheme, SchemeGroupVersion)return nil }
2.5.3.2.KnownTypes
- 方法签名:
func (s *Scheme) KnownTypes(gv schema.GroupVersion) map[string]reflect.Type
- 方法功能:获取指定GV下所有Kind的Type类型
- 举例
types := Scheme.KnownTypes(schema.GroupVersion{Group: "apps",Version: "v1", })
2.5.3.3.VersionsForGroupKind
- 方法签名:
func (s *Scheme) VersionsForGroupKind(gk schema.GroupKind) []schema.GroupVersion
- 方法功能:获取指定GK的所有Versions,并以GV列表的形式返回
- 举例
groupVersions := Scheme.VersionsForGroupKind(schema.GroupKind{Group: "apps",Kind: "Deployment", }) // 输出 ["apps/v1" "apps/v1beta1" "apps/v1beta2"]
2.5.3.4.ObjectKinds
- 方法签名:
func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error)
- 方法功能:获取指定object 的 所有可能的 group、version、kind 值,并以 GVK 列表的形式返回
- 举例
gvks, notVersioned, err := Scheme.ObjectKinds(&appsv1.Deployment{}) // 输出 ["apps/v1 Deployment"]
2.5.3.5.New
- 方法签名:
func (s *Scheme) New(kind schema.GroupVersionKind) (Object, error)
- 方法功能:根据指定的GVK,创建该资源的一个对象
- 举例:
deployment, err := Scheme.New(schema.GroupVersionKind{Group: "apps",Version: "v1",Kind: "Deployment", })
2.5.3.6.AddConversionFunc
- 方法源码
func (s *Scheme) AddConversionFunc(a, b interface{}, fn conversion.ConversionFunc) error {return s.converter.RegisterUntypedConversionFunc(a, b, fn) }
- 该方法,用于向scheme中注册 不同资源 的自定义转换器。
2.5.k8s.io/apimachinery 提供 RESTMapper 结构
2.5.1.理解GVR和GVK的用途
- 在 上面2.3 中提到,k8s.io/apimachinery 提供了 GR/GVR、GK/GVK 等数据结构。GR和GVR 负责对接 RESTful 风格的url路径,GK和GVK 负责确定一个具体的kubernetes资源
- GVR举例:
- 用户想要获取 apps组下、v1版本的 deployments,如何编写url地址?–>
GET /apis/apps/v1/deployments
- 这个url中,就可以使用 GVR 描述,group为apps,version为v1,Resource为deployments
- 用户想要获取 apps组下、v1版本的 deployments,如何编写url地址?–>
- GVK举例:
- 当kubernetes的代码中,想要操作一个资源的时候,如何找到资源的struct 结构?通过GVK去找
- 比如 apps/v1/Deployment,就可以确定 group为apps,version为v1,kind为Deployment,就可以找到这个资源的struct
2.5.2.RESTMapper是什么
- 当用户使用 REST风格 的 url 访问资源时,kubernetes如何确定需要操作哪一个GVK呢?
- REST风格 的 url,可以从中得到 GVR,只需要完成 GVR 到 GVK 的转换就可以了
- 因此,apimachinery维护了一个数据结构 RESTMapper,记录 GVR 和 GVK 的映射关系
type RESTMapping struct {// Resource is the GroupVersionResource (location) for this endpointResource schema.GroupVersionResource// GroupVersionKind is the GroupVersionKind (data format) to submit to this endpointGroupVersionKind schema.GroupVersionKind// Scope contains the information needed to deal with REST Resources that are in a resource hierarchyScope RESTScope }
- 另外,apimachinery还提供了一个接口 RESTMapper,接口中提供了 将 GVR 转成 GVK 的方法
- 其中,
KindFor
和KindsFor
就是将 GVR 转成 GVK 的方法
type RESTMapper interface {// KindFor takes a partial resource and returns the single match. Returns an error if there are multiple matchesKindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error)// KindsFor takes a partial resource and returns the list of potential kinds in priority orderKindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error)// ResourceFor takes a partial resource and returns the single match. Returns an error if there are multiple matchesResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error)// ResourcesFor takes a partial resource and returns the list of potential resource in priority orderResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error)// RESTMapping identifies a preferred resource mapping for the provided group kind.RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error)// RESTMappings returns all resource mappings for the provided group kind if no// version search is provided. Otherwise identifies a preferred resource mapping for// the provided version(s).RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error)ResourceSingularizer(resource string) (singular string, err error) }
- 其中,
2.5.3.DefaultRESTMapper
- RESTMapper接口,有一个默认的实现 DefaultRESTMapper
- 位于
staging/src/k8s.io/apimachinery/pkg/api/meta/restmapper.go
type DefaultRESTMapper struct {defaultGroupVersions []schema.GroupVersionresourceToKind map[schema.GroupVersionResource]schema.GroupVersionKindkindToPluralResource map[schema.GroupVersionKind]schema.GroupVersionResourcekindToScope map[schema.GroupVersionKind]RESTScopesingularToPlural map[schema.GroupVersionResource]schema.GroupVersionResourcepluralToSingular map[schema.GroupVersionResource]schema.GroupVersionResource }
- 位于
staging/src/k8s.io/apimachinery/pkg/api/meta/restmapper.go
中还提供了一个NewDefaultRESTMapper
方法,用于新建一个DefaultRESTMapper
2.6.k8s.io/apimachinery 提供序列化、编解码能力
2.6.1.k8s.io/apimachinery 的 runtime.serializer 包
- k8s.io/apimachinery 中,关于 序列化 和 编解码 的代码,大都在
staging/src/k8s.io/apimachinery/pkg/runtime/serializer
包下
- json、protobuf、yaml包,分别提供了对应格式的序列化器,共3种序列化器
2.6.2.k8s.io/apimachinery 提供了序列化的通用接口
staging/src/k8s.io/apimachinery/pkg/runtime/interfaces.go
文件中,提供了序列化的通用接口Serializer
。Serializer接口提供了编解码能力。type Serializer interface {EncoderDecoder }
- Encoder是编码器接口,还是在
staging/src/k8s.io/apimachinery/pkg/runtime/interfaces.go
文件中type Encoder interface {Encode(obj Object, w io.Writer) errorIdentifier() Identifier }
- Decoder是解码器接口,还是在
staging/src/k8s.io/apimachinery/pkg/runtime/interfaces.go
文件中type Decoder interface {Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error) }
2.6.3.json 序列化器
staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go
文件中,提供了json序列化器type Serializer struct {meta MetaFactoryoptions SerializerOptionscreater runtime.ObjectCreatertyper runtime.ObjectTyperidentifier runtime.Identifier }
- json序列化器 实现了
runtime.interface.go
中的Serializer接口,实现了 Encode、Decode方法 - 创建一个json序列化器,有多个方法
NewSerializer
、NewSerializerWithOptions
2.6.4.yaml 序列化器
staging/src/k8s.io/apimachinery/pkg/runtime/serializer/yaml/yaml.go
文件中,提供了yaml序列化器type yamlSerializer struct {// the nested serializerruntime.Serializer }
- yaml序列化器 实现了
runtime.interface.go
中的Serializer接口
2.6.5.protobuf 序列化器
staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go
文件中,提供了protobuf序列化器type Serializer struct {prefix []bytecreater runtime.ObjectCreatertyper runtime.ObjectTyper }
- protobuf序列化器 实现了
runtime.interface.go
中的Serializer接口
2.7.k8s.io/apimachinery 提供 不同资源 相互转换能力
- scheme提供了AddConversionFunc方法,用于向scheme中注册 不同资源 的自定义转换器。
- 举例:创建了一个Scheme对象,名为scheme。我们就可以通过下面的方法,注册 appsv1.Deployment 与 appsv1beta1.Deployment 的相互转换方法
scheme.AddConversionFunc((*appsv1.Deployment)(nil),(*appsv1beta1.Deployment)(nil),func(a, b interface{}, scope conversion.Scope) error{v1deploy := a.(*appsv1.Deployment)v1beta1deploy := b.(*appsv1beta1.Deployment)// make conversion herereturn nil })
3.参考博客
- Kubernetes学习笔记[第5章]API Machinery
- runtime/Serializer源码分析
相关文章:

Kubernetes operator(五)api 和 apimachinery 篇
云原生学习路线导航页(持续更新中) 本文是 Kubernetes operator学习 系列第五篇,主要对 k8s.io/api 和 k8s.io/apimachinery 两个项目 进行学习基于 kubernetes v1.24.0 代码分析Kubernetes operator学习系列 快捷链接 Kubernetes operator&a…...

接口自动化测试中解决接口间数据依赖
在实际的测试工作中,在做接口自动化测试时往往会遇到接口间数据依赖问题,即API_03的请求参数来源于API_02的响应数据,API_02的请求参数又来源于API_01的响应数据。 因此通过自动化方式测试API_03接口时,需要预先请求API_02接口&a…...

七、测试计划(软件工程)
1.引言 1.1编写目的 1.2项目背景 1.3定义 1.4参考资料 2.任务概述 2.1目标 2.2运行环境 2.3需求概述 2.4条件与限制 3.计划 3.1测试方案 3.2测试项目 3.3测试准备 3.4测试机构及人员 4.测试项目说明…...

ElementUI Form:Checkbox 多选框
ElementUI安装与使用指南 Checkbox 多选框 点击下载learnelementuispringboot项目源码 效果图 el-checkbox.vue (Checkbox 多选框)页面效果图 项目里el-checkbox.vue代码 <script> const cityOptions [上海, 北京, 广州, 深圳] export def…...

如何统一监听Vue组件报错
window.onerror 全局监听所有JS错误,包括异步错误但是它是JS级别的,识别不了Vue组件信息,Vue内部的错误还是用Vue来监听捕捉一些Vue监听不到的错误 errorCaptured生命周期 监听所有下级组件的错误返回false会阻止向上传播到window.onerror …...

python爬虫4
#1.练习 # (1) 获取网页的源码 # (2) 解析 解析的服务器响应的文件 etree.HTML # (3) 打印 import urllib.request urlhttps://www.baidu.com/ headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit…...

【算法】约数之和(数论)
题目 给定 n 个正整数 ai,请你输出这些数的乘积的约数之和,答案对 1097 取模。 输入格式 第一行包含整数 n。 接下来 n 行,每行包含一个整数 ai。 输出格式 输出一个整数,表示所给正整数的乘积的约数之和,答案需…...

走进CSS过渡效果的奇妙世界:详解CSS Transition
你是否曾在网页上看到一些酷炫的元素在状态变化时平滑而流畅地过渡?这就是CSS过渡效果的魔力所在!在这篇博客中,我们将深入探讨CSS Transition,揭示其神奇的原理和如何在你的网页中运用这项技术。 什么是CSS Transitionÿ…...

C++入坑基础知识点
当学习了C语言之后,很多的小伙伴都想进一步学习C,但两者有相当一部分的内容都是重叠的,不知道该从哪些方面开始入门C,这篇文章罗列了从C到C必学的入门知识,学完就算是踏入C的大门了。 1. 命名空间 写C的时候ÿ…...

RabbitMQ面试
1. 什么是消息中间件 消息中间件是在分布式系统中传递消息的软件服务。它允许不同的系统组件之间通过消息进行通信,而无需直接连接到彼此。消息中间件通常用于解耦系统的各个部分,提高系统的可扩展性、灵活性和可维护性。 2. 消息中间件解决了什么问题…...

计算机网络(第六版)复习提纲21
SS4.6 互联网的路由选择协议 1 关于路由选择协议的基本概念 A 理想的路由算法(路由选择协议的核心)157 1 算法是正确和完整的 2 计算上简单 3 能适应通信量和网络拓扑的变化(自适应性) 4 稳定性 5 公平性 6 应当最佳(特…...

2路DIN2路DO2路AIN远程4GRTU模块钡铼技术S270
钡铼技术的S270远程4G RTU模块是一款高性能的工业级远程终端单元,它支持2路数字输入(DIN)、2路数字输出(DO)以及2路模拟输入(AIN),并通过4G网络实现数据的远程传输。这种模块的设计旨在满足各种工业自动化和监控需求,特别适用于那些位于偏远地…...

从经典到创新,盘点情人节最受欢迎的五款新潮礼物
随着情人节的到来,许多情侣们开始考虑为心爱的人挑选一份特别的礼物。而在这个充满爱意的日子里,我们不仅可以看到经典的礼物款式,也能发现许多新颖、时尚的新潮礼物。以下是今年情人节最受欢迎的五款新潮礼物,每一件都充满了浪漫…...

数据库管理-第141期 DG PDB - Oracle DB 23c(20240129)
数据库管理141期 2024-01-29 第141期 DG PDB - Oracle DB 23c(20240129)1 概念2 环境说明3 操作3.1 数据库配置3.2 配置tnsname3.3 配置强制日志3.4 DG配置3.5 DG配置建立联系3.6 启用所有DG配置3.7 启用DG PDB3.8 创建源PDB的DG配置3.9 拷贝pdbprod1文件…...

MySQL原理(二)存储引擎(3)InnoDB
目录 一、概况: 1、介绍: 2、特点: 二、体系架构 1、后台线程 2、内存池(缓冲池) 三、物理结构 1、数据文件(表数据和索引数据) 1.1、作用: 1.2、共享表空间与独立表空间 …...

基于Springboot的高校心理教育辅导设计与实现(有报告)。Javaee项目,springboot项目。
演示视频: 基于Springboot的高校心理教育辅导设计与实现(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构,…...

jenkins pipeline配置maven可选参数
1、在Manage Jenkins下的Global Tool Configuration下对应的maven项添加我们要用得到的不同版本的maven安装项 2、pipeline文件内容具体如下 我们maven是单一的,所以我们都是配置单选参数 pipeline {agent anyparameters {gitParameter(name: BRANCH_TAG, type: …...

【博士每天一篇论文-算法】Continual Learning Through Synaptic Intelligence,SI算法
阅读时间:2023-11-23 1 介绍 年份:2017 作者:Friedemann Zenke,巴塞尔大学弗里德里希米歇尔研究所(FMI) Ben Poole,谷歌 DeepMind 研究科学家 期刊: International conference on machine learning. PMLR…...

【软件工程】建模工具之开发各阶段绘图——UML2.0常用图实践技巧(功能用例图、静态类图、动态序列图状态图活动图)
更多示例图片可以参考:(除了常见的流程图,其他都有) 概念:类图 静态:用例图 动态:顺序图&状态图&活动图 1、【面向对象】UML类图、用例图、顺序图、活动图、状态图、通信图、构件图、部…...

Typora导出word
Typora导出word Typora是一款简洁易用的Markdown编辑器, Pandoc是一个文档转换工具,可以将Markdown格式的文档转换为其他格式,如HTML、PDF等. linux下安装 Pandoc : sudo apt install -y pandoc安装成功后,typora 会自动监测到. 然后 点击文件->…...

CSS 星空按钮
<template><button class="btn" type="button"><strong>星空按钮</strong><div id="container-stars"><div id="stars"></div></div><div id="glow"><div class=…...

Kotlin快速入门系列10
Kotlin的委托 委托模式是常见的设计模式之一。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。与Java一样,Kotlin也支持委托模式,通过关键字by。 类委托 类的委托即一个类中定义的方…...

Docker中配置MySql环境
目录 一、简单安装 1. 首先从Docker Hub中拉取镜像 2. 启动尝试创建MySQL容器,并设置挂载卷。 3. 查看mysql8这个容器是否启动成功 4. 如果已经成功启动,进入容器中简单测试 4.1 进入容器 4.2 登录mysql中 4.3 进行简单添加查找测试 二、主从复…...

智慧文旅:驱动文化与旅游融合发展的新动力
随着科技的快速发展和人们生活水平的提高,文化和旅游的融合成为了时代发展的必然趋势。智慧文旅作为这一趋势的引领者,通过先进的信息技术手段,推动文化与旅游的深度融合,为产业的发展注入新的活力。本文将深入探讨智慧文旅如何成…...

wordpress怎么做产品展示站?推荐使用MOK主题和ent主题
大多数WordPress站点都是个人博客网站,主要以文章性质的图文为主。不过部分站长想要用WordPress搭建一个产品展示站,应该怎么做呢? 其实,WordPress可以用来建立各种各样的博客网站,包括个人博客、企业网站、商城、影视…...

8、应急响应-战前溯源反制主机蜜罐系统HFishHIDSElkeidWazuh
用途:个人学习笔记,欢迎指正 目录 背景: 一、潮源反制-平台部署-蜜罐-Hfish 二、溯源反制-平台部署-HIDS-Wazuh 三、溯源反制-平台部署-HlDS-Elkeid-hub 背景: 攻击者对服务器存在着各种威胁行为,作为安全人员&am…...

LeetCode:283. 移动零
283. 移动零 1)题目2)代码方法一:两层for循环方法二:使用双指针 3)结果方法一结果方法二结果 1)题目 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的…...

游戏开发丨基于Panda3D的迷宫小球游戏
文章目录 写在前面Panda3D程序设计程序分析运行结果系列文章写在后面 写在前面 本期内容 基于panda3d的迷宫中的小球游戏 所需环境 pythonpycharm或anacondapanda3d 下载地址 https://download.csdn.net/download/m0_68111267/88792121 Panda3D Panda3D是一种开放源代码…...

微信小程序 安卓/IOS兼容问题
一、背景 在开发微信小程序时,不同的手机型号会出现兼容问题,特此记录一下 二、安卓/IOS兼容问题总结 2.1、new Date()时间转换格式时,IOS不兼容 问题:在安卓中时间格式2024-1-31 10:10:10,但是在iOS中是不支持 &q…...

结构体--共用体--枚举 之难点——链表 奋力学习嵌入式的第十六天
结构体 注意: 1.结构体类型 可以定义在 函数里里面 但是此时作用域就被限定在该函数中 2.结构体定义形式 //形式一 限定一类型 后定义变量 struct stu { ... }; struct stu s; //形式二 定义类型的同时 定义变量 struct stu { ... }s1,s2,*s3,s4[10]; struc…...