整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          C# 中的 get 和 set 訪問器:深入解析屬性

          C# 中的 get 和 set 訪問器:深入解析屬性訪問機制

          C#編程語言中,屬性(Properties)是一種特殊的類成員,它們提供了對字段(Fields)的靈活訪問。通過屬性,我們可以控制對類內部數據的訪問,并執行一些額外的邏輯,如數據驗證或轉換。C#中的屬性通常是通過get和set訪問器來定義的,這兩個訪問器分別用于讀取和寫入屬性的值。

          一、屬性的基本概念

          屬性在C#中是一種特殊的類成員,它們提供了對私有字段的公共訪問。通過屬性,我們可以隱藏類的內部狀態,只暴露必要的接口給類的使用者。這樣,我們可以更好地控制對類內部數據的訪問,確保數據的完整性和安全性。

          二、get訪問器

          get訪問器用于讀取屬性的值。當我們在代碼中引用一個屬性時,實際上是在調用該屬性的get訪問器。get訪問器必須返回一個值,該值的類型必須與屬性的聲明類型相匹配。

          下面是一個簡單的示例,展示了一個帶有get訪問器的屬性:

          public class Person
          {
          private string _name; // 私有字段

          public string Name // 公共屬性
          {
          get // get訪問器
          {
          return _name; // 返回私有字段的值
          }
          }
          }

          在上面的示例中,Name屬性通過get訪問器暴露了_name字段的值。當我們創建一個Person對象并嘗試訪問其Name屬性時,實際上是在調用Name屬性的get訪問器,并返回_name字段的值。

          三、set訪問器

          set訪問器用于寫入屬性的值。當我們為屬性賦值時,實際上是在調用該屬性的set訪問器。set訪問器通常接受一個與屬性類型相同的參數,并將其賦值給內部的私有字段。

          下面是一個帶有get和set訪問器的屬性的示例:

          public class Person
          {
          private string _name; // 私有字段

          public string Name // 公共屬性
          {
          get // get訪問器
          {
          return _name; // 返回私有字段的值
          }
          set // set訪問器
          {
          _name=value; // 將傳入的值賦給私有字段
          }
          }
          }

          在上面的示例中,Name屬性不僅可以通過get訪問器讀取值,還可以通過set訪問器寫入值。當我們為Name屬性賦值時,實際上是在調用set訪問器,并將傳入的值賦給_name字段。在set訪問器內部,我們使用了一個特殊的value關鍵字來表示傳入的值。

          四、屬性的使用場景

          屬性的使用場景非常廣泛,它們可以用于控制對類內部數據的訪問權限、執行數據驗證、轉換數據類型等。例如,我們可以在set訪問器中添加一些邏輯來確保賦給屬性的值是有效的,或者在get訪問器中返回計算后的值而不是直接返回字段的值。

          此外,屬性還可以用于實現一些設計模式,如觀察者模式(Observer Pattern)或依賴注入(Dependency Injection)。通過屬性的靈活訪問機制,我們可以更好地控制類的行為和狀態。

          五、總結

          C#中的get和set訪問器是屬性訪問機制的重要組成部分。它們允許我們靈活地控制對類內部數據的訪問,并執行額外的邏輯。通過合理使用get和set訪問器,我們可以創建出更加健壯、安全和易于使用的類庫和應用程序。掌握這一機制對于深入理解C#面向對象編程至關重要。

          impleDateFormat 是 Java提供的一個格式化和解析日期的工具類,日常開發中應該經常會用到,但是它是線程不安全的。

          多線程公用一個 SimpleDateFormat實例 對日期進行解析或者格式化會導致程序出錯,本節就討論下它為何是線程不安全的,以及如何避免。

          一、SimpleDateFormat 線程不安全演示和分析

          1.1、問題復現

          public class TestSimpleDateFormat {
           //(1)創建單例實例
           static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
           public static void main(String[] args) {
            //(2)創建多個線程,并啟動
            for (int i=0; i <10 ; ++i) {
             Thread thread=new Thread(new Runnable() {
              public void run() {
               try {//(3)使用單例日期實例解析文本
                System.out.println(sdf.parse("2017-12-13 15:17:27"));
               } catch (ParseException e) {
                e.printStackTrace();
               }
              }
             });
             thread.start();//(4)啟動線程
            }
           }

          代碼(1)創建了SimpleDateFormat的一個實例;

          代碼(2)創建10個線程,每個線程都共用同一個sdf對象對文本日期進行解析,多運行幾次就會拋出java.lang.NumberFormatException異常,加大線程的個數有利于該問題復現。

          1.2、問題分析

          為了便于分析首先奉上SimpleDateFormat的類圖結構:

          SimpleDateFormat 類結構圖

          1. 可知每個SimpleDateFormat實例里面有一個Calendar對象。
          2. 從后面會知道其實 SimpleDateFormat 之所以是線程不安全的就是因為Calendar是線程不安全的。
          3. SimpleDateFormat 之所以是線程不安全的是因為其中存放日期數據的變量都是線程不安全的,比如里面的fields,time等。

          下面從代碼層面看下parse方法做了什么事情:

          #### parse() 方法

          public Date parse(String text, ParsePosition pos)
          {
           //(1)解析日期字符串放入CalendarBuilder的實例calb中
           .....
            Date parsedDate;
           try {//(2)使用calb中解析好的日期數據設置calendar
            parsedDate=calb.establish(calendar).getTime();
            ...
            }
           catch (IllegalArgumentException e) {
            ...
             return null;
           }
           return parsedDate;
          }

          ## establish() 方法

          Calendar establish(Calendar cal) {
             ...
             //(3)重置日期對象cal的屬性值
             cal.clear();
             //(4) 使用calb中中屬性設置cal
             ...
             //(5)返回設置好的cal對象
             return cal;
          }
          • 代碼(1)主要的作用是解析字符串日期并把解析好的數據放入了 CalendarBuilder的實例calb中,CalendarBuilder是一個建造者模式,用來存放后面需要的數據。
          • 代碼(3)重置Calendar對象里面的屬性值,如下代碼:
          public final void clear() {
              for (int i=0; i < fields.length; ) {
                  stamp[i]=fields[i]=0; // UNSET==0
                  isSet[i++]=false;
              }
              areAllFieldsSet=areFieldsSet=false;
              isTimeSet=false;
          }
          • 代碼(4)使用calb中解析好的日期數據設置cal對象
          • 代碼(5) 返回設置好的cal對象

          從上面步驟可知步驟(3)(4)(5)操作不是原子性操作。

          當多個線程調用parse方法時候比如線程A執行了步驟(3)(4)也就是設置好了cal對象,在執行步驟(5)前線程B執行了步驟(3)清空了cal對象,由于多個線程使用的是一個cal對象,所以線程A執行步驟(5)返回的就可能是被線程B清空后的對象,當然也有可能線程B執行了步驟(4)被線程B修改后的cal對象。從而導致程序錯誤。

          二、SimpleDateFormat 線程不安全的解決方式

          方式一:每個線程創建一個 SimpleDateFormat實例

          每次使用時候new一個SimpleDateFormat的實例,這樣可以保證每個實例使用自己的Calendar實例,但是每次使用都需要new一個對象,并且使用后由于沒有其它引用,就會需要被回收,開銷會很大。

          方式二:synchronized 同步鎖

          究其原因是因為多線程下步驟(3)(4)(5)三個步驟不是一個原子性操作,那么容易想到的是對其進行同步,讓(3)(4)(5)成為原子操作,可以使用synchronized進行同步。

          public class TestSimpleDateFormat {
           // (1)創建單例實例
           static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
           public static void main(String[] args) {
            // (2)創建多個線程,并啟動
            for (int i=0; i < 10; ++i) {
             Thread thread=new Thread(new Runnable() {
              public void run() {
               try {// (3)使用單例日期實例解析文本
                synchronized (sdf) {
                 System.out.println(sdf.parse("2017-12-13 15:17:27"));
                }
               } catch (ParseException e) {
                e.printStackTrace();
               }
              }
             });
             thread.start();// (4)啟動線程
            }
           }
          }

          使用同步意味著多個線程要競爭鎖,在高并發場景下會導致系統響應性能下降。

          方式三:使用ThreadLocal (推薦)

          每個線程只需要使用一個 SimpleDateFormat 實例相比第一種方式大大節省了對象的創建銷毀開銷,并且不需要對多個線程直接進行同步。

          public class TestSimpleDateFormat2 {
           // (1)創建threadlocal實例
           static ThreadLocal<DateFormat> safeSdf=new ThreadLocal<DateFormat>(){
            @Override 
            protected SimpleDateFormat initialValue(){
             return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
           };
           
           public static void main(String[] args) {
            // (2)創建多個線程,并啟動
            for (int i=0; i < 10; ++i) {
             Thread thread=new Thread(new Runnable() {
              public void run() {
               try {// (3)使用單例日期實例解析文本
                 System.out.println(safeSdf.get().parse("2017-12-13 15:17:27"));
               } catch (ParseException e) {
                e.printStackTrace();
               }
              }
             });
             thread.start();// (4)啟動線程
            }
           }
          }
          • 代碼(1)創建了一個線程安全的SimpleDateFormat實例。
          • 步驟(3)在使用的時候首先使用get()方法獲取當前線程下SimpleDateFormat的實例,在第一次調用ThreadLocal的get()方法時會觸發其initialValue方法用來創建當前線程所需要的SimpleDateFormat對象。

          三、問題思考

          3.1、采用new 對象作為局部變量的局限性

          如果線程調用多個類的其他方法,并且其他地方需要日期格式化,在線程代碼中new的對象,其他地方不一定會訪問得到。如果想復用的話,

          1. 在方法之間需要將其作為方法的參數進行傳遞。
          2. 方法間不傳遞的話,需要new 多個實例。

          3.2、使用 ThreadLocal 的優點

          【ThreadLocal 中設置的變量是線程本身變量池的值,所以只要是同一線程,在執行任何類的代碼的時候都可以獲取得到;只需要創建一個實例】。

          就像在使用pagehelper設置分頁參數時,它就是放在ThreadLocal中的,所以后續的查詢調用其他類的其他方法,需要這幾個值都是直接從線程本身取這個值。

          不過ThreadLocal使用完其中的值后最好remove下,不然一些情況會造成內存泄露。

          原文 https://cn-blogs.cn/archives/10783.html

          迎訪問我的GitHub

          這里分類和匯總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

          本篇概覽

          • 本文是《client-go實戰》系列的第十篇,kubernetes真是博大精深,盡管前面已有九篇實戰,依然有個十分重要的基礎知識點沒覆蓋到,這也是今天的重要內容:標簽選擇器labels.Selector
          • 本文由以下內容組成
          1. 準備工作:部署nginx的deployment和service
          2. 按照官方文檔,解讀LabelSelector
          3. 什么是標簽選擇器(labels.Selector),和LabelSelector的區別
          4. 編碼實戰:在查找pod時用labels.Selector過濾查詢結果,用labels.Selector匹配
          5. 編碼實戰:List&Watch場景,用labels.Selector過濾其他消息

          源碼下載

          • 上述完整源碼可在GitHub下載到,地址和鏈接信息如下表所示(https://github.com/zq2599/blog_demos):

          名稱

          鏈接

          備注

          項目主頁

          https://github.com/zq2599/blog_demos

          該項目在GitHub上的主頁

          git倉庫地址(https)

          https://github.com/zq2599/blog_demos.git

          該項目源碼的倉庫地址,https協議

          git倉庫地址(ssh)

          git@github.com:zq2599/blog_demos.git

          該項目源碼的倉庫地址,ssh協議

          • 這個git項目中有多個文件夾,本篇的源碼在tutorials/client-go-tutorials文件夾下,如下圖紅框所示:



          重要:labels.Selector和LabelSelector是不同的概念,切記!!!

          • 有兩個重要概念需要在本文一開始就說清楚,以免引起混淆,請務必注意!
          • labels.Selector和LabelSelector是不同的概念,重要的事情說三遍!請一定要將labels.Selector和LabelSelector分開認識和理解
          1. 作為client-go的使用者,我們用的是labels.Selector,它的源碼是個interface
          2. LabelSelector是個資源定義,類似Pod,Deployment那樣的資源定義,它的源碼是個struct
          • 在編碼開發時,主要用到的是labels.Selector,本篇的核心也是labels.Selector
          • labels.Selector和LabelSelector是不同概念,但是它們功能類似,都用于標簽選擇,接下來說說什么是標簽選擇

          什么是標簽?舉個例子

          • 用一個電腦上的常見功能來說明什么是標簽
          • 來看Mac操作系統的標簽功能,如下圖所示,Mac系統中可以給文件夾打上標簽,例如我給blog_demosquarkus這兩個文件夾都打上了博客標簽,然后只要點擊博客標簽(如下圖綠色箭頭位置),就能列出blog_demosquarkus這兩個文件夾



          labels.Selector和LabelSelector的作用

          • 盡管labels.Selector和LabelSelector是不同的概念,然而他們的功能大致相同:與Mac系統的標簽選擇類似,在查找各種K8S資源時,labels.Selector和LabelSelector都能根據指定的標簽對資源進行過濾
          • 本篇的核心是labels.Selector,當然也會提到LabelSelector,因為用對比的方式去學習的時候印象會更深刻
          • 接下來咱們慢慢聊,先從LabelSelector說起,它最簡單也最具體,可以作為突破口

          準備工作:部署nginx的deployment和service

          • 先準備好kubernetes環境,部署好一些資源,再用這些資源來學習LabelSelector
          • 在kubernetes環境創建名為deployment-svc.yaml的文件,內容如下
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: nginx-deployment
          spec:
            selector:
              matchLabels:
                app: nginx
            replicas: 1
            template:
              metadata:
                labels:
                  app: nginx
                  bind-service: nginx
              spec:
                containers:
                - name: nginx
                  image: nginx:latest
                  ports:
                  - containerPort: 80
          ---
          apiVersion: v1
          kind: Service
          metadata:
            name: nginx-service
          spec:
            type: NodePort
            selector:
              app: nginx
            ports:
            - name: http
              port: 80
              targetPort: 80
              nodePort: 30000
          
          
          • 再執行以下命令創建deployment和service資源
          kubectl apply -f deployment-svc.yaml
          
          • 訪問kubernetes服務器的30000端口,可以看到nginx的首頁,證明deployment和service都部署成功了



          了解標簽選擇器LabelSelector

          • 回顧前面寫的deployment-svc.yaml文件,如下圖所示,deployment有自己的標簽,service通過標簽選擇器找到了deployment,與之匹配,使得外部請求可以順利到達nginx的pod



          • 上圖中的標簽選擇器,就是咱們前面提到的LabelSelector,很顯然,yaml中的selector就是一種資源類型,在代碼中對應的就是LabelSelector這個結構體
          • kubernetes官方對標簽選擇器的描述在這里:https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/labels/,這里面細說了關于標簽的各種寫法和約束,值得認真看一遍

          什么時候該用labels.Selector

          • 從前面的操作可見:如果是編寫yaml文件,配置類似于deployment-svc.yaml這樣的內容,那么用到的就是LabelSelector,因為它對應著yaml里的一個selector對象
          • 現在換個場景:如果在用client-go編寫代碼,對kubernetes的資源做過濾呢?例如查詢pod列表的時候,想要根據標簽做過濾,那么用LabelSelector就不合適了,因為這是代碼,沒有yaml文件,自然也就沒有LabelSelector了
          • 此時labels.Selector就派上用場了:編碼時,用labels.Selector可以對資源按照標簽做過濾,接下來就寫代碼試試labels.Selector怎么用
          • 現在k8s環境里只有一個pod,就是前面創建的那個,為了演示labels.Selector的效果,咱們再添加一個deployment,腳本如下,這個pod的app標簽值等于other
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: other-deployment
          spec:
            selector:
              matchLabels:
                app: other
            replicas: 1
            template:
              metadata:
                labels:
                  app: other
                  bind-service: none
              spec:
                containers:
                - name: other
                  image: nginx:latest
                  ports:
                  - containerPort: 80
          
          • 現在有兩個pod了,接下來寫代碼,用選出其中一個
           kubectl get pods
          NAME                                READY   STATUS    RESTARTS   AGE
          nginx-deployment-544dc8b7c4-xlkj8   1/1     Running   0          2m20s
          other-deployment-7659c57b9d-2slm8   1/1     Running   0          5s
          

          如何創建labels.Selector

          • 接下來就要編碼實戰了,先了解一下如何創建labels.Selector
          • 一共有四種方法創建labels.Selector,可以用在不同的使用場景
          1. 創建NewRequirement對象,加入labels.Selector
          2. labels.Parse方法,將字符串轉為labels.Selector對象(最簡單)
          3. labels.SelectorFromSet方法,用map生成labels.Selector對象
          4. metav1.LabelSelectorAsSelector方法,將LabelSelector對象轉為labels.Selector對象
          • 理論已經差不多了,開始編碼實戰吧

          編碼

          • 為了后續文章的實戰代碼能統一管理,這里繼續使用前文《client-go實戰之七:準備一個工程管理后續實戰的代碼
            》創建的
            client-go-tutorials工程,將代碼寫在這個工程中
          • client-go-tutorials工程中新增名label.go的文件,整個工程結構如下圖所示
          tree client-go-tutorials 
          client-go-tutorials
          ├── action
          │   ├── action.go
          │   ├── conflict.go
          │   ├── controller.go
          │   ├── controller_demo.go
          │   ├── label.go
          │   └── list_pod.go
          ├── go.mod
          ├── go.sum
          └── main.go
          
          1 directory, 9 files
          
          • 為了個整個系列保持一致,先新增一個結構體
          type Lable struct{}
          
          • 然后是個輔助方法listPods,接收labels.Selector對象作為入參,然后在查詢pod列表的時候用這個labels.Selector來過濾
          // listPods 根據傳入的selector過濾
          func listPods(clientset *kubernetes.Clientset, selector labels.Selector, prefix string) {
          	namespace :="default"
          
          	// 查詢pod列表
          	pods, err :=clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
          		// 傳入的selector在這里用到
          		LabelSelector: selector.String(),
          	})
          
          	if err !=nil {
          		panic(err.Error())
          	}
          
          	nums :=len(pods.Items)
          
          	log.Printf("[%v] 查到[%d]個pod\n", prefix, nums)
          
          	// 如果沒有pod就返回了
          	if nums < 1 {
          		return
          	}
          
          	// 遍歷列表中的每個pod
          	for index, pod :=range pods.Items {
          		log.Printf("[%v] %v. pod : %v\n", prefix, index+1, pod.Name)
          	}
          }
          
          • 然后是主方法,里面展示了前面提到的四種labels.Selector對象的創建方式,以及如何用做匹配功能
          func (lable Lable) DoAction(clientset *kubernetes.Clientset) error {
          	// 第一種: 創建Requirement對象,指定類型是Equals(等于)
          	equalRequirement, err :=labels.NewRequirement("app", selection.Equals, []string{"other"})
          
          	if err !=nil {
          		log.Println("1. create equalRequirement fail, ", err)
          		return err
          	}
          
          	selector :=labels.NewSelector().Add(*equalRequirement)
          
          	// 驗證,應該只查到app等于other的pod
          	listPods(clientset, selector, "用Requirement創建,Equal操作")
          
          	// 第一種: 創建Requirement對象,指定類型是In,not_exists不會有任何pod匹配到
          	inRequirement, err :=labels.NewRequirement("app", selection.In, []string{"other", "nginx", "not_exists"})
          
          	if err !=nil {
          		log.Println("2. create equalRequirement fail, ", err)
          		return err
          	}
          
          	selector=labels.NewSelector().Add(*inRequirement)
          
          	// 驗證,應該查到app=other的pod
          	listPods(clientset, selector, "用Requirement創建,In操作")
          
          	// 第二種:labels.Parse方法
          	parsedSelector, err :=labels.Parse("bind-service=none,app notin (not_exists)")
          
          	if err !=nil {
          		log.Println("3. create equalRequirement fail, ", err)
          		return err
          	}
          
          	// 驗證,應該查到app=other的pod
          	listPods(clientset, parsedSelector, "用Parse創建")
          
          	// 第三種:labels.SelectorFromSet方法
          	setSelector :=labels.SelectorFromSet(labels.Set(map[string]string{"app": "nginx"}))
          
          	// 驗證,應該查到app=nginx的pod
          	listPods(clientset, setSelector, "用SelectorFromSet創建")
          
          	// 第四種:metav1.LabelSelectorAsSelector方法
          	// 適用于當前環境已有資源對象的場景,可以取出LabelSelector對象來轉換成labels.Selector
          	// 先創建一個LabelSelector
          	labelSelector :=&metav1.LabelSelector{
          		MatchLabels: map[string]string{"app": "other"},
          	}
          
          	// 將LabelSelector轉為labels.Selector
          	convertSelector, err :=metav1.LabelSelectorAsSelector(labelSelector)
          
          	if err !=nil {
          		log.Println("4. create equalRequirement fail, ", err)
          		return err
          	}
          
          	// 驗證,應該查到app=nginx的pod
          	listPods(clientset, convertSelector, "用LabelSelector轉換")
          
          	// labels.Selector的第五種用法:用labels.Selector匹配
          
          	// 準備好一個selector
          	matchSelector :=labels.SelectorFromSet(labels.Set(map[string]string{"app": "nginx"}))
          
          	// 查詢pod列表
          	pods, err :=clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
          	if err !=nil {
          		panic(err.Error())
          	}
          
          	// 遍歷列表中的每個pod
          	for _, pod :=range pods.Items {
          		if matchSelector.Matches(labels.Set(pod.GetLabels())) {
          			log.Printf("app=nginx匹配成功[%s]\n", pod.Name)
          		} else {
          			log.Printf("app=nginx匹配失敗[%s]\n", pod.Name)
          		}
          	}
          
          	return nil
          }
          
          • 再回到main.go文件,增加一個新的action的支持,如下圖黃框所示



          • 有了上面的代碼,在運行程序的時候,增加-action=label參數,就會執行前面的lable.go中的代碼了
          • 如果您使用的是vscode,將launch.json改為以下內容,就能直接運行代碼了
          {
              "version": "0.2.0",
              "configurations": [
                  
                  {
                      "name": "Launch Package",
                      "type": "go",
                      "request": "launch",
                      "mode": "auto",
                      "program": "${workspaceFolder}",
                      "args": ["-action=label"]
                  }
              ]
          }
          
          • 運行結果如下,功能正常,符合預期
          2023/03/11 19:57:53 解析命令完畢,開始加載配置文件
          2023/03/11 19:57:53 加載配置文件完畢,即將執行業務 [label]
          2023/03/11 19:57:53 [用Requirement創建,Equal操作] 查到[1]個pod
          2023/03/11 19:57:53 [用Requirement創建,Equal操作] 1. pod : other-deployment-7b57cc4f89-bdxj8
          2023/03/11 19:57:53 [用Requirement創建,In操作] 查到[2]個pod
          2023/03/11 19:57:53 [用Requirement創建,In操作] 1. pod : nginx-deployment-5659dc6c45-hsx7j
          2023/03/11 19:57:53 [用Requirement創建,In操作] 2. pod : other-deployment-7b57cc4f89-bdxj8
          2023/03/11 19:57:53 [用Parse創建] 查到[1]個pod
          2023/03/11 19:57:53 [用Parse創建] 1. pod : other-deployment-7b57cc4f89-bdxj8
          2023/03/11 19:57:53 [用SelectorFromSet創建] 查到[1]個pod
          2023/03/11 19:57:53 [用SelectorFromSet創建] 1. pod : nginx-deployment-5659dc6c45-hsx7j
          2023/03/11 19:57:53 [用LabelSelector轉換] 查到[1]個pod
          2023/03/11 19:57:53 [用LabelSelector轉換] 1. pod : other-deployment-7b57cc4f89-bdxj8
          2023/03/11 19:57:53 執行完成
          

          在List&Watch場景

          • labels.Selector除了用在獲取資源列表的時候,在Controller的List&Watch時也能用到,咱們使用List&Watch來監聽指定類型的資源變化,可以用labels.Selector來監聽指定標簽的資源,這樣其他資源的變化事件就不會推送過來
          • 關于Controller和List&Watch,可以參考《client-go實戰之九:手寫一個kubernetes的controller》,先來來看原有的List&Watch代碼,在controller_demo.go文件中



          • 修改后的完整DoAction方法如下,構造labels.Selector對象,改用NewFilteredListWatchFromClient方法,如此,只有匹配了這個labels.Selector的pod的事件,才會被apiserver發送到informer
          func (controllerDemo ControllerDemo) DoAction(clientset *kubernetes.Clientset) error {
          	setSelector :=labels.SelectorFromSet(labels.Set(map[string]string{"app": "nginx"}))
          	optionsModifer :=func(options *metav1.ListOptions) {
          		options.LabelSelector=setSelector.String()
          	}
          
          	podListWatcher :=cache.NewFilteredListWatchFromClient(clientset.CoreV1().RESTClient(), "pods", metav1.NamespaceDefault, optionsModifer)
          
          	// 創建ListWatch對象,指定要監控的資源類型是pod,namespace是default
          	// podListWatcher :=cache.NewListWatchFromClient(clientset.CoreV1().RESTClient(), "pods", v1.NamespaceDefault, fields.Everything())
          
          	// 創建工作隊列
          	queue :=workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
          
          	// 創建informer,并將返回的存儲對象保存在變量indexer中
          	indexer, informer :=cache.NewIndexerInformer(podListWatcher, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
          		// 響應新增資源事件的方法,可以按照業務需求來定制,
          		// 這里的做法比較常見:寫入工作隊列
          		AddFunc: func(obj interface{}) {
          			key, err :=cache.MetaNamespaceKeyFunc(obj)
          			if err==nil {
          				queue.Add(key)
          			}
          		},
          		// 響應修改資源事件的方法,可以按照業務需求來定制,
          		// 這里的做法比較常見:寫入工作隊列
          		UpdateFunc: func(old interface{}, new interface{}) {
          			key, err :=cache.MetaNamespaceKeyFunc(new)
          			if err==nil {
          				queue.Add(key)
          			}
          		},
          		// 響應修改資源事件的方法,可以按照業務需求來定制,
          		// 這里的做法比較常見:寫入工作隊列,注意刪除的時候生成key的方法和新增修改不一樣
          		DeleteFunc: func(obj interface{}) {
          			// IndexerInformer uses a delta queue, therefore for deletes we have to use this
          			// key function.
          			key, err :=cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
          			if err==nil {
          				queue.Add(key)
          			}
          		},
          	}, cache.Indexers{})
          
          	// 創建Controller對象,將所需的三個變量對象傳入
          	controller :=NewController(queue, indexer, informer)
          
          	// Now let's start the controller
          	stop :=make(chan struct{})
          	defer close(stop)
          	// 在協程中啟動controller
          	go controller.Run(1, stop)
          
          	// Wait forever
          	select {}
          	return nil
          }
          
          • 至此,labels.Selector的實戰就完成了,這是個重要的功能,在查找和監聽的場景都會用到,希望本文能夠給給您一些參考,幫助您在client-go開發中做到精確過濾和選擇

          歡迎關注頭條號:程序員欣宸

          • 學習路上,你不孤單,欣宸原創一路相伴...

          主站蜘蛛池模板: 国产剧情国产精品一区| 无码人妻精品一区二区三| 天堂一区二区三区精品| 亚洲一区二区三区在线观看蜜桃| 国产不卡视频一区二区三区| 免费无码毛片一区二区APP| 亚洲爆乳精品无码一区二区| 亚洲欧洲精品一区二区三区| 亚洲午夜精品一区二区| 日本一区二区三区在线视频观看免费| 亚洲福利视频一区二区三区| 在线播放国产一区二区三区| 久久免费区一区二区三波多野| 中文字幕在线播放一区| 亚洲乱码日产一区三区| 免费一区二区三区四区五区| 亚洲欧洲一区二区| 在线免费观看一区二区三区| 日本国产一区二区三区在线观看| 性色av闺蜜一区二区三区| 亚洲一区在线视频| 国产福利无码一区在线| 国产精品无码一区二区三级| 亚欧色一区W666天堂| 国模大胆一区二区三区| 国产亚洲自拍一区| 在线一区二区观看| 精品亚洲A∨无码一区二区三区| 久久一区二区三区精华液使用方法| 精品国产亚洲第一区二区三区| 国产精品一区三区| 亚洲AV香蕉一区区二区三区| 中文字幕亚洲一区二区三区| 国产精品高清视亚洲一区二区| 无码精品人妻一区二区三区影院| 91一区二区三区| 国产精品电影一区二区三区| 少妇激情av一区二区| 综合久久一区二区三区| 变态拳头交视频一区二区| 国产无套精品一区二区 |