K8S 剖析API对象类型

2023-11-12

K8S API对象类型

一、metav1.TypeMeta, 对象的类型元数据信息。

1.1、类型成员

定义了资源类型和api版本。

type TypeMeta struct {
    
	Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`

	APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"`
}
1.2、相关接口

TypeMeta的相关接口主要实现了查看和设置资源类型和api版本的功能。

func (obj *TypeMeta) GetObjectKind() schema.ObjectKind { return obj }
//实现了schema.ObjectKind接口
type ObjectKind interface {
	// SetGroupVersionKind sets or clears the intended serialized kind of an object. Passing kind nil
	// should clear the current setting.
	SetGroupVersionKind(kind GroupVersionKind)
	// GroupVersionKind returns the stored group, version, and kind of an object, or nil if the object does
	// not expose or provide these fields.
	GroupVersionKind() GroupVersionKind
}

// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func (obj *TypeMeta) SetGroupVersionKind(gvk schema.GroupVersionKind) {
	obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}

// GroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func (obj *TypeMeta) GroupVersionKind() schema.GroupVersionKind {
	return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}

二、metav1.ObjectMeta

定义对象的公共说明属性,包括对象名、命名空间、注释、标签等。

2.1 类型成员
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
// users must create.
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" 

	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" `

	DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" 
	
	DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" 

	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" 
	
	Finalizers []string `json:"finalizers,omitempty" 
	
	ClusterName string `json:"clusterName,omitempty" protobuf:"bytes,15,opt,name=clusterName"`
	
	ManagedFields []ManagedFieldsEntry `json:"managedFields,omitempty" 
}
2.2、相关接口

metav1.ObjectMeta的相关接口主要是对资源公共属性的查看和设置。ObjectMeta实现了metav1.object和ObjectMetaAccessor两个interface,而kubernetes所有单体对象都继承了ObjectMeta,那么所有的API对象就都实现了metav1.object和ObjectMetaAccessor。metav1.Object是对API对象公共属性的抽象(interface);

// TODO: move this, Object, List, and Type to a different package
type ObjectMetaAccessor interface {
	GetObjectMeta() Object
}

// Object lets you work with object metadata from any of the versioned or
// internal API objects. Attempting to set or retrieve a field on an object that does
// not support that field (Name, UID, Namespace on lists) will be a no-op and return
// a default value.
//注意此object为metav1.object
type Object interface {  
	GetNamespace() string
	SetNamespace(namespace string)
	GetName() string
	SetName(name string)
	GetGenerateName() string
	SetGenerateName(name string)
	GetUID() types.UID
	SetUID(uid types.UID)
	GetResourceVersion() string
	SetResourceVersion(version string)
	GetGeneration() int64
	SetGeneration(generation int64)
	GetSelfLink() string
	SetSelfLink(selfLink string)
	GetCreationTimestamp() Time
	SetCreationTimestamp(timestamp Time)
	GetDeletionTimestamp() *Time
	SetDeletionTimestamp(timestamp *Time)
	GetDeletionGracePeriodSeconds() *int64
	SetDeletionGracePeriodSeconds(*int64)
	GetLabels() map[string]string
	SetLabels(labels map[string]string)
	GetAnnotations() map[string]string
	SetAnnotations(annotations map[string]string)
	GetFinalizers() []string
	SetFinalizers(finalizers []string)
	GetOwnerReferences() []OwnerReference
	SetOwnerReferences([]OwnerReference)
	GetClusterName() string
	SetClusterName(clusterName string)
	GetManagedFields() []ManagedFieldsEntry
	SetManagedFields(managedFields []ManagedFieldsEntry)
}


// 代码源自k8s.io/apimachinery/pkg/apis/meta/v1/meta.go,GetObjectMeta是MetaAccessor
// 的接口函数,这个函数说明了ObjectMeta实现了MetaAccessor。
func (obj *ObjectMeta) GetObjectMeta() Object { return obj }
 
// 下面所有的函数是接口Object的ObjectMeta实现,可以看出来基本就是setter/getter方法。
func (meta *ObjectMeta) GetNamespace() string                { return meta.Namespace }
func (meta *ObjectMeta) SetNamespace(namespace string)       { meta.Namespace = namespace }
func (meta *ObjectMeta) GetName() string                     { return meta.Name }
func (meta *ObjectMeta) SetName(name string)                 { meta.Name = name }
func (meta *ObjectMeta) GetGenerateName() string             { return meta.GenerateName }
func (meta *ObjectMeta) SetGenerateName(generateName string) { meta.GenerateName = generateName }
func (meta *ObjectMeta) GetUID() types.UID                   { return meta.UID }
func (meta *ObjectMeta) SetUID(uid types.UID)                { meta.UID = uid }
func (meta *ObjectMeta) GetResourceVersion() string          { return meta.ResourceVersion }
func (meta *ObjectMeta) SetResourceVersion(version string)   { meta.ResourceVersion = version }
func (meta *ObjectMeta) GetGeneration() int64                { return meta.Generation }
func (meta *ObjectMeta) SetGeneration(generation int64)      { meta.Generation = generation }
func (meta *ObjectMeta) GetSelfLink() string                 { return meta.SelfLink }
func (meta *ObjectMeta) SetSelfLink(selfLink string)         { meta.SelfLink = selfLink }
func (meta *ObjectMeta) GetCreationTimestamp() Time          { return meta.CreationTimestamp }
func (meta *ObjectMeta) SetCreationTimestamp(creationTimestamp Time) {
    meta.CreationTimestamp = creationTimestamp
}
func (meta *ObjectMeta) GetDeletionTimestamp() *Time { return meta.DeletionTimestamp }
func (meta *ObjectMeta) SetDeletionTimestamp(deletionTimestamp *Time) {
    meta.DeletionTimestamp = deletionTimestamp
}
func (meta *ObjectMeta) GetDeletionGracePeriodSeconds() *int64 { return meta.DeletionGracePeriodSeconds }
func (meta *ObjectMeta) SetDeletionGracePeriodSeconds(deletionGracePeriodSeconds *int64) {
    meta.DeletionGracePeriodSeconds = deletionGracePeriodSeconds
}
func (meta *ObjectMeta) GetLabels() map[string]string                 { return meta.Labels }
func (meta *ObjectMeta) SetLabels(labels map[string]string)           { meta.Labels = labels }
func (meta *ObjectMeta) GetAnnotations() map[string]string            { return meta.Annotations }
func (meta *ObjectMeta) SetAnnotations(annotations map[string]string) { meta.Annotations = annotations }
func (meta *ObjectMeta) GetFinalizers() []string                      { return meta.Finalizers }
func (meta *ObjectMeta) SetFinalizers(finalizers []string)            { meta.Finalizers = finalizers }
func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference         { return meta.OwnerReferences }
func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) {
    meta.OwnerReferences = references
}
func (meta *ObjectMeta) GetClusterName() string                 { return meta.ClusterName }
func (meta *ObjectMeta) SetClusterName(clusterName string)      { meta.ClusterName = clusterName }
func (meta *ObjectMeta) GetManagedFields() []ManagedFieldsEntry { return meta.ManagedFields }
func (meta *ObjectMeta) SetManagedFields(managedFields []ManagedFieldsEntry) {
    meta.ManagedFields = managedFields
}

三、资源实例

以pod资源为例

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"`
}

K8S的资源一般都继承了metav1.TypeMeta和metav1.ObjectMeta两个类型成员,主要为资源类型和公共属性相关字段。Spec用于定义资源的私有属性,这也是资源之间的区别所在。所以可以把metav1.TypeMeta和metav1.ObjectMeta两个类型成员作为全部k8s资源对象的基类,这两个基类相关的接口,全部k8s资源对象都可以调用。


四、runtime包

import "k8s.io/apimachinery/pkg/runtime"

Package runtime defines conversions between generic types and structs to map query strings to struct objects.

Package runtime includes helper functions for working with API objects that follow the kubernetes API object convention.

4.1 Scheme 资源序列化和反序列化的方法以及资源类型和版本的对应关系

metav1.TypeMeta实现了schema.ObjectKind接口,所以K8S资源类型都可以调用schema.ObjectKind。

  • 从Scheme序列化的所有对象都对其类型信息进行编码。 序列化使用此接口,将Scheme中的类型信息设置到对象的序列化版本上。 对于无法序列化或具有独特要求的对象,此接口可能是无操作的。

    // All objects that are serialized from a Scheme encode their type information. This interface is used
    // by serialization to set type information from the Scheme onto the serialized version of an object.
    // For objects that cannot be serialized or have unique requirements, this interface may be a no-op.
    type ObjectKind interface {
    	// SetGroupVersionKind sets or clears the intended serialized kind of an object. Passing kind nil
    	// should clear the current setting.
    	SetGroupVersionKind(kind GroupVersionKind)
    	// GroupVersionKind returns the stored group, version, and kind of an object, or nil if the object does
    	// not expose or provide these fields.
    	GroupVersionKind() GroupVersionKind
    }
    
  • “k8s.io/apimachinery/pkg/runtime/schema” 包中k8s schema.GroupVersionKind结构体存储group、version、kind字段的值。

    // GroupVersionKind unambiguously identifies a kind.  It doesn't anonymously include GroupVersion
    // to avoid automatic coercion.  It doesn't use a GroupVersion to avoid custom marshalling
    type GroupVersionKind struct {
    	Group   string
    	Version string
    	Kind    string
    }
    
4.2 runtime.Object (所有API单体对象的根类(interface))

"k8s.io/apimachinery/pkg/runtime"包中runtime.Object接口,k8s的资源类型都应该实现了该接口。在Scheme上注册的所有API类型都必须支持object对象接口。 对象必须提供接口允许序列化程序设置对象表示的种类,版本和组。 在不希望序列化对象的情况下,对象可以选择返回无操作ObjectKindAccessor。

如果想通过一个基类的指针指向任意API单体对象,schema.ObjecKind和metav1.Object都不合适,因为他们所能访问的域是有限。如果有一个函数需要访问任何API对象的类型和公共属性,那么就要传入同一个对象的两个指针(schema.ObjecKind和metav1.Object),这就太让人难以接受了。有没有一个类型作为API单体对象的统一的基类呢?这就是本节要讨论的:runtime.Object。

// Object interface must be supported by all API types registered with Scheme. Since objects in a scheme are
// expected to be serialized to the wire, the interface an Object must provide to the Scheme allows
// serializers to set the kind, version, and group the object is represented as. An Object may choose
// to return a no-op ObjectKindAccessor in cases where it is not expected to be serialized.
type Object interface {
    //metav1.TypeMeta实现了schema.ObjectKind接口,接口包含了此方法。
	GetObjectKind() schema.ObjectKind
    //deepcopy是golang深度复制对象的方法,各个类型都实现了此方法。
	DeepCopyObject() Object
}

以pod资源为例:

//core>v1>zz_generated.deepcopy.go
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Pod) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}

如前所述,K8S的资源实现了runtime.object接口,其实大多数资源结构体继承了ObjectMeta也实现了metav1.object接口来获取资源的公共属性。若资源要访问类型之外的公共属性,主要是ObjectMeta结构相关,使用以下的处理方式。Accessor()函数可以把obj安全的转换为metav1.Object,这样也就避免了每个API对象类型都需要实现类似GetObjectMeta()的接口了。meta包中使用了MetadataAccessor 接口聚合了对ObjectMeta和TypeMeta的属性访问和设置。


// MetadataAccessor exposes Interface in a way that can be used with multiple objects.
type MetadataAccessor interface {
	APIVersion(obj runtime.Object) (string, error)
	SetAPIVersion(obj runtime.Object, version string) error

	Kind(obj runtime.Object) (string, error)
	SetKind(obj runtime.Object, kind string) error

	Namespace(obj runtime.Object) (string, error)
	SetNamespace(obj runtime.Object, namespace string) error
	...
}
//k8s.io/apimachinery/pkg/api/meta/meta.go
//实现了对ObjectMeta的属性访问和设置
func Accessor(obj interface{}) (metav1.Object, error) { 
    // 使用了golang的switch type语法
    switch t := obj.(type) {
    // 因为API对象类型都继承了metav1.ObjectMeta,也就自然实现了metav1.Object。
    case metav1.Object:
        return t, nil
    // metav1.ObjectMeta也实现了metav1.ObjectMetaAccessor
    case metav1.ObjectMetaAccessor:
        if m := t.GetObjectMeta(); m != nil {
            return m, nil
        }
        return nil, errNotObject
    default:
        return nil, errNotObject
    }
}

//实现了对TypeMeta的属性访问和设置
type objectAccessor struct {   
	runtime.Object		
}

func (obj objectAccessor) GetKind() string {
	return obj.GetObjectKind().GroupVersionKind().Kind
}

func (obj objectAccessor) SetKind(kind string) {
	gvk := obj.GetObjectKind().GroupVersionKind()
	gvk.Kind = kind
	obj.GetObjectKind().SetGroupVersionKind(gvk)
}
...

func NewAccessor() MetadataAccessor {
	return resourceAccessor{}
}

// resourceAccessor implements ResourceVersioner and SelfLinker.
//聚合objectAccessor结构体方法和Accessor函数实现了MetadataAccessor接口
type resourceAccessor struct{} 

func (resourceAccessor) APIVersion(obj runtime.Object) (string, error) {
	return objectAccessor{obj}.GetAPIVersion(), nil
}

func (resourceAccessor) SetAPIVersion(obj runtime.Object, version string) error {
	objectAccessor{obj}.SetAPIVersion(version)
	return nil
}

func (resourceAccessor) Namespace(obj runtime.Object) (string, error) {
	accessor, err := Accessor(obj)
	if err != nil {
		return "", err
	}
	return accessor.GetNamespace(), nil
}

func (resourceAccessor) SetNamespace(obj runtime.Object, namespace string) error {
	accessor, err := Accessor(obj)
	if err != nil {
		return err
	}
	accessor.SetNamespace(namespace)
	return nil
}
4.3 runtime.RawExtension

runtime.RawExtension结构体,存储资源runtime.object和object的json字节流数据。

type RawExtension struct {
	// Raw is the underlying serialization of this object.
	//
	// TODO: Determine how to detect ContentType and ContentEncoding of 'Raw' data.
	Raw []byte `json:"-" protobuf:"bytes,1,opt,name=raw"`
	// Object can hold a representation of this extension - useful for working with versioned
	// structs.
	Object Object `json:"-"`
}

五、非结构化数据类型

无法预知数据结构的数据类型或属性名称不确定的数据类型是非结构化数据,其无法通过构建预定的struct数据结构来序列化或反序列化。当无法预知确定的数据结构类型或属性名称不确定时,可以通过以下结构解决。每个字符串对应一个json属性,其映射为interface{}类型对应值。

var result map[string]interface{}

Kubernetes 的所有 Resource 都可以转换为该结构类型。处理完成后,再将 Unstructured 转换成想要的确切类型。整个过程类似于 Go 语言的 interface{} 断言转换过程。另外,Unstructured 结构类型是通过 map[string]interface{} 转换的, 即k8s的非结构化数据通过map[string]interface{}表达。

type Unstructured struct {
	// Object is a JSON compatible map with string, float, int, bool, []interface{}, or
	// map[string]interface{}
	// children.
	Object map[string]interface{}
}

应用示例:

func (h *handler) decode(r io.Reader) error {
	decoder := yaml.NewYAMLOrJSONDecoder(r, 4096)
	for {
//提供object序列化后的存储
		ext := runtime.RawExtension{}
//每次序列化一个object到ext中,把接收的yaml文件流转为json流到ext中
		if err := decoder.Decode(&ext); err != nil {
			if err == io.EOF {
				return nil
			}
			return fmt.Errorf("error parsing: %v", err)
		}
		ext.Raw = bytes.TrimSpace(ext.Raw)
		if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) {
			continue
		}
//关键步骤反序列化json数据为相应的runtime.Object,并返回相应的gkv结构
		obj, gkv, err := unstructured.UnstructuredJSONScheme.Decode(ext.Raw, nil, nil)
		if err != nil {
			return err
		}
		ext.Object = obj
		h.groupVersionKinds = append(h.groupVersionKinds, gkv)
		h.exts = append(h.exts, &ext)
	}
}

unstructured结构体实现了runtime.object接口。

//代码位于 k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go
type Unstructured struct {
	// Object is a JSON compatible map with string, float, int, bool, []interface{}, or
	// map[string]interface{}
	// children.
	Object map[string]interface{}
}


// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Unstructured) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}

func (obj *Unstructured) GetObjectKind() schema.ObjectKind { 
	return obj 
}

//Unstructured中schema.ObjectKind接口的实现
func (u *Unstructured) GroupVersionKind() schema.GroupVersionKind {
	gv, err := schema.ParseGroupVersion(u.GetAPIVersion())
	if err != nil {
		return schema.GroupVersionKind{}
	}
	gvk := gv.WithKind(u.GetKind())
	return gvk
}

func (u *Unstructured) GetAPIVersion() string {
	return getNestedString(u.Object, "apiVersion")
}

func (u *Unstructured) SetAPIVersion(version string) {
	u.setNestedField(version, "apiVersion")
}

//从map[string]interface查找特定key的value
func getNestedString(obj map[string]interface{}, fields ...string) string {
	val, found, err := NestedString(obj, fields...)
	if !found || err != nil {
		return ""
	}
	return val
}

// NestedString returns the string value of a nested field.
// Returns false if value is not found and an error if not a string.
func NestedString(obj map[string]interface{}, fields ...string) (string, bool, error) {
	val, found, err := NestedFieldNoCopy(obj, fields...)
	if !found || err != nil {
		return "", found, err
	}
	s, ok := val.(string)
	if !ok {
		return "", false, fmt.Errorf("%v accessor error: %v is of the type %T, expected string", jsonPath(fields), val, val)
	}
	return s, true, nil
}


func NestedFieldNoCopy(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
	var val interface{} = obj

	for i, field := range fields {
		if val == nil {
			return nil, false, nil
		}
		if m, ok := val.(map[string]interface{}); ok {
			val, ok = m[field]
			if !ok {
				return nil, false, nil
			}
		} else {
			return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected map[string]interface{}", jsonPath(fields[:i+1]), val, val)
		}
	}
	return val, true, nil
}

如前所述,K8S的资源实现了runtime.object接口,其实大多数资源结构体继承了ObjectMeta也实现了metav1.object接口来获取资源的公共属性。但是unstructured结构体中没有继承ObjectMeta结构体,所以unstructured单独实现了metav1.object接口,来获取资源对象的元数据的公共属性。

func (u *Unstructured) GetNamespace() string {
	return getNestedString(u.Object, "metadata", "namespace")
}

func (u *Unstructured) SetNamespace(namespace string) {
	if len(namespace) == 0 {
		RemoveNestedField(u.Object, "metadata", "namespace")
		return
	}
	u.setNestedField(namespace, "metadata", "namespace")
}

func (u *Unstructured) GetName() string {
	return getNestedString(u.Object, "metadata", "name")
}

func (u *Unstructured) SetName(name string) {
	if len(name) == 0 {
		RemoveNestedField(u.Object, "metadata", "name")
		return
	}
	u.setNestedField(name, "metadata", "name")
}
....

unstructured.UnstructuredJSONScheme.Decode源码Decode比较复杂,它尝试把提供的数据转换成YAML或者JSON,从中提取出其中存储的group、kind等,并合并缺省的gvk,然后在数据加载到对象中。

//代码位于 k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go
func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
	var err error
	if obj != nil {
		err = s.decodeInto(data, obj)
	} else {
		obj, err = s.decode(data)
	}

	if err != nil {
		return nil, nil, err
	}

	gvk := obj.GetObjectKind().GroupVersionKind()
	if len(gvk.Kind) == 0 {
		return nil, &gvk, runtime.NewMissingKindErr(string(data))
	}

	return obj, &gvk, nil
}

func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) {
	type detector struct {
		Items gojson.RawMessage
	}
	var det detector
	if err := json.Unmarshal(data, &det); err != nil {
		return nil, err
	}

	if det.Items != nil {
		list := &UnstructuredList{}
		err := s.decodeToList(data, list)
		return list, err
	}

	// No Items field, so it wasn't a list.
	unstruct := &Unstructured{}
	err := s.decodeToUnstructured(data, unstruct)
	return unstruct, err
}

func (unstructuredJSONScheme) decodeToUnstructured(data []byte, unstruct *Unstructured) error {
	m := make(map[string]interface{})
	if err := json.Unmarshal(data, &m); err != nil {
		return err
	}

	unstruct.Object = m

	return nil
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

K8S 剖析API对象类型 的相关文章

随机推荐

  • c++处理数据

    处理数据 简单变量 const限定符 类型转换和auto声明 整型 char类型 字符和小整数 成员函数cout put 通用字符名 signed char和unsigned char wchar t 浮点数 bool类型 c 算术运算符
  • Python入门笔记(1)

    纯小写的就是python的BIF 内置函数 dir builtins ArithmeticError AssertionError AttributeError BaseException BlockingIOError BrokenPip
  • 避免Unity错误剥离代码

    开启ProjectSettings里的Strip Engine Code项目后 Unity会尝试剥离未使用的引擎代码 以降低包体大小和运行时内存占用 但是某些运行时需要的组件会被错误剥离
  • Hibernate之关于多对多单向关联映射

    Hibernate 之关于多对多单向关联映射 老师和学生 最典型的多对多关联 Teacher和Student 所谓单向意思就是说 老师知道自己的教的是哪些学生而学生不知道是哪些老师教 也可以这么说 在查询的时候 通过老师可以级联查询出学生
  • 有些话,只说给懂的人听

    生活中最 孤独的时候 往往不是因为孤身一人 而是即使身边有很多人 有些心事依旧无人能懂 以前的时候 我们常常会把所有的喜怒悲欢 都说与别人听 后来渐渐变得沉默了 不是因为学会了独自消化 只是明白了 有些话只能说给懂的人听 人与人之间 只有彼
  • mybatis+mysql insert时返回自增主键

    mybatis mysql insert时返回自增主键 mysqlmybatisinsert返回自增主键 使用mybatis执行insert操作时 需要返回自增主键 网上清一色的答案 useGeneratedKeys设置为true keyP
  • qt开发使用camera类获取摄像头信息并拍照保存

    首先是UI布局 在pro文件中添加两个类 QT multimedia QT multimediawidgets 然后我们需要包含几个摄像头使用的头文件并创建摄像头的对象 include
  • win10远程桌面的坑

    win10的远程桌面的确是清晰度非常好 操作非常流程的 但是还是有坑的 举两个踩坑例子 1 录屏软件在远程桌面退出后无效了 无法录制屏幕了 2 监控客户端在退出远程桌面后 再进去远程桌面 打圈圈卡死 因此一些应用不适合在win10远程桌面办
  • GNS3 配置GRE

    1 简述 GRE Generic Routing Encapsulation GRE是一种最传统的隧道协议 其根本功能就是要实现隧道功能 以实现异地网络之间可以通过内部私网相互访问 以上图为例 假设IP地址为10 1 1 1的XP1想访问I
  • 基于Zigbee的SHT10温湿度数据采集系统(已实现控制12个终端节点)——Zigbee协调器主要代码解析

    之前实现了基于Zigbee的SHT10温湿度数据采集系统 这里来重新复盘一些主要的知识和代码 写在前面 1 功能介绍 使用Zigbee终端节点采集环境的温度和湿度数据 然后将数据无线发送的Zigbee协调器 最后在电脑端显示获得到的数据 2
  • Ubuntu初学思维导图(后继续补充)

    关于虚拟机 Ubuntu的命令内容简要 1 创建用户 sudo adduser user01 创建用户时 同步创建对应组 同步创建家目录 sudo useradd user02 仅创建用户 单独设置完密码后 才能登陆 2 修改用户密码 su
  • http请求头部(header)详解

    当我们在浏览器中访问一个网站时 我们的浏览器实际上会向该网站发送一个 HTTP 请求 而 HTTP 请求头部 Header 则是一组包含请求信息的键值对 用来描述 HTTP 请求的各种属性和特征 以下是 HTTP 请求头部的一些常见属性和含
  • linux重启服务的脚本命令

    最近做网站测试 每次测试完成都要重启服务 为此写了一个简单的shell脚本 linux服务重启shell脚本示例 2014年12月18日 linux服务重启脚本 如何实现linux服务的定时重启 可以借助shell脚本来完成 ps命令捕获进
  • 方差分析在特征筛选中的应用

    方差分析在特征筛选中的应用 方差分析 Analysis of Variance 简称ANOVA 是一种常见的统计分析方法 它可以用于比较两个或多个组之间的均值差异 在机器学习中 我们可以应用方差分析来进行特征筛选 从而得到对模型有显著影响的
  • 高光谱图像端元提取——vertex component analysis(VCA/python)

    在高光谱图像中 VCA是一种常用的端元提取方法 算法来源 Vertex Component Analysis A Fast Algorithm to Unmix Hyperspectral Data submited to IEEE Tra
  • 伺服电机堵转检测

    一 电流数据的分析 电机工作时的电流如下图 电机正常工作时 电机电流具有两个状态 正常旋转和堵转 正常旋转时 电流在控制算法的作用下 一开始会有很快的上升 过程中电流受到控制算法的作用 没有平稳阶段 堵转时 电机结束了控制算法 所以堵转时电
  • 常见信息安全加密算法及Python库源码实例

    1 常见的信息安全加密算法 1 1 对称加密算法 AES Advanced Encryption Standard 对称密钥加密算法 被广泛使用且安全可靠 DES Data Encryption Standard 对称密钥加密算法 已被AE
  • 系统服务器性能巡检报告,pc服务器巡检报告

    pc服务器巡检报告 内容精选 换一换 简要介绍iPerf是一种命令行工具 它通过测量服务器可以处理的最大网络吞吐量来测试网络速度 因此在遇到网络速度问题时特别有用 通过该工具可以确定哪台服务器存在性能问题 语言 C C 一句话概述 网络性能
  • 当HBase遇上MapReduce

    第1关 HBase的MapReduce快速入门 本关任务 编写一个HBase的MapReduce配置程序 package com processdata import java io IOException import java util
  • K8S 剖析API对象类型

    K8S API对象类型 一 metav1 TypeMeta 对象的类型元数据信息 1 1 类型成员 定义了资源类型和api版本 type TypeMeta struct Kind string json kind omitempty pro