Go 学习之 接口多态(同一接口的不同结构体实现不同行为)

该代码示例展示了在Kubernetes环境中如何通过定义接口实现不同行为。`nodeLabeler`结构体使用了`labelFunc`接口,允许`modelFunc`和`stringFunc`这两种具体类型来实现不同的`GetLabel`方法。`modelFunc`用于从GPU设备获取模型名称,而`stringFunc`则直接返回一个字符串。这些行为被用来自动为节点添加或更新标签,如GPU模型标签。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接口

  • 给调用者【统一的接口和方法】,方便调用
  • 由【具体类型】来实现【不同的行为】
  • 可以理解为【不同类型,都会定义同一种方法,来实现此接口;但不同类型的方法实现不同,因此导致行为是不同的】
package watchdog

import (
	"os"
	"regexp"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/klog"
	"tkestack.io/nvml"
)

const (
	gpuModelLabel = "gaia.tencent.com/gpu-model"
)

// 接口,方法 —— 提供统一方法,方便被调用
type labelFunc interface {
	GetLabel() string
}

type nodeLabeler struct {
	hostName    string
	client      v1core.CoreV1Interface
	labelMapper map[string]labelFunc
}

// 定义一个【具体类型1】,实现一种行为
type modelFunc struct{}
// 定义另一个【具体类型2】,实现另一种行为
type stringFunc string

var modelFn = modelFunc{}

// 【具体类型1】的行为实现
func (m modelFunc) GetLabel() (model string) {
	// dfy: 初始化 nvml 库
	if err := nvml.Init(); err != nil {
		klog.Warningf("Can't initialize nvml library, %v", err)
		return
	}

	defer nvml.Shutdown()

	// Assume all devices on this node are the same model
	// dfy: 通过 nvml 获取第 0 块显卡设备
	dev, err := nvml.DeviceGetHandleByIndex(0)
	if err != nil {
		klog.Warningf("Can't get device 0 information, %v", err)
		return
	}

	rawName, err := dev.DeviceGetName()
	if err != nil {
		klog.Warningf("Can't get device name, %v", err)
		return
	}

	klog.V(4).Infof("GPU name: %s", rawName)

	return getTypeName(rawName)
}

// 【具体类型2】的行为实现
func (s stringFunc) GetLabel() string {
	return string(s)
}

var modelNameSplitPattern = regexp.MustCompile("\\s+")

func getTypeName(name string) string {
	splits := modelNameSplitPattern.Split(name, -1)

	if len(splits) > 2 {
		return splits[1]
	}

	klog.V(4).Infof("GPU name splits: %v", splits)

	return ""
}

//NewNodeLabeler returns a new nodeLabeler
func NewNodeLabeler(client v1core.CoreV1Interface, hostname string, labels map[string]string) *nodeLabeler {
	if len(hostname) == 0 {
		hostname, _ = os.Hostname()
	}

	klog.V(2).Infof("Labeler for hostname %s", hostname)

	labelMapper := make(map[string]labelFunc)
	for k, v := range labels {
		// dfy:注意此处,若是 gpuModelLabel ,则值为 modelFn (一个空结构体);若不是,则设为对应的值
		if k == gpuModelLabel {
      // 定义为 【具体类型1】
			labelMapper[k] = modelFn
		} else {
      // 定义为 【具体类型2】
			labelMapper[k] = stringFunc(v)
		}
	}

	return &nodeLabeler{
		hostName:    hostname,
		client:      client,
		labelMapper: labelMapper,
	}
}

func (nl *nodeLabeler) Run() error {
	err := wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
		node, err := nl.client.Nodes().Get(nl.hostName, metav1.GetOptions{})
		if err != nil {
			return false, err
		}

		for k, fn := range nl.labelMapper {
      // GetLabel 方法的调用,视 fn 的【具体类型】,
      // 若是【具体类型1】,就实现【具体类型1】的行为;若是【具体类型2】,就实现【具体类型2】的行为
			l := fn.GetLabel()
			if len(l) == 0 {
				klog.Warningf("Empty label for %s", k)
				continue
			}

			klog.V(2).Infof("Label %s %s=%s", nl.hostName, k, l)
			node.Labels[k] = l
		}

		_, updateErr := nl.client.Nodes().Update(node)
		if updateErr != nil {
			if errors.IsConflict(updateErr) {
				return false, nil
			}
			return true, updateErr
		}

		return true, nil
	})

	if err != nil {
		return err
	}

	klog.V(2).Infof("Auto label is running")

	return nil
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值