Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 久久精品亚洲综合,精品久久久久中文字幕日本,国产91在线免费

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          C# 中的 get 和 set 訪問(wèn)器:深入解析屬性

          C# 中的 get 和 set 訪問(wèn)器:深入解析屬性訪問(wèn)機(jī)制

          C#編程語(yǔ)言中,屬性(Properties)是一種特殊的類成員,它們提供了對(duì)字段(Fields)的靈活訪問(wèn)。通過(guò)屬性,我們可以控制對(duì)類內(nèi)部數(shù)據(jù)的訪問(wèn),并執(zhí)行一些額外的邏輯,如數(shù)據(jù)驗(yàn)證或轉(zhuǎn)換。C#中的屬性通常是通過(guò)get和set訪問(wèn)器來(lái)定義的,這兩個(gè)訪問(wèn)器分別用于讀取和寫入屬性的值。

          一、屬性的基本概念

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

          二、get訪問(wèn)器

          get訪問(wèn)器用于讀取屬性的值。當(dāng)我們?cè)诖a中引用一個(gè)屬性時(shí),實(shí)際上是在調(diào)用該屬性的get訪問(wèn)器。get訪問(wèn)器必須返回一個(gè)值,該值的類型必須與屬性的聲明類型相匹配。

          下面是一個(gè)簡(jiǎn)單的示例,展示了一個(gè)帶有g(shù)et訪問(wèn)器的屬性:

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

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

          在上面的示例中,Name屬性通過(guò)get訪問(wèn)器暴露了_name字段的值。當(dāng)我們創(chuàng)建一個(gè)Person對(duì)象并嘗試訪問(wèn)其Name屬性時(shí),實(shí)際上是在調(diào)用Name屬性的get訪問(wèn)器,并返回_name字段的值。

          三、set訪問(wèn)器

          set訪問(wèn)器用于寫入屬性的值。當(dāng)我們?yōu)閷傩再x值時(shí),實(shí)際上是在調(diào)用該屬性的set訪問(wèn)器。set訪問(wèn)器通常接受一個(gè)與屬性類型相同的參數(shù),并將其賦值給內(nèi)部的私有字段。

          下面是一個(gè)帶有g(shù)et和set訪問(wèn)器的屬性的示例:

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

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

          在上面的示例中,Name屬性不僅可以通過(guò)get訪問(wèn)器讀取值,還可以通過(guò)set訪問(wèn)器寫入值。當(dāng)我們?yōu)?code style='color: rgb(250, 139, 115);font-size: 14px;line-height: 1.8em;letter-spacing: 0em;background: none 0% 0% / auto no-repeat scroll padding-box border-box rgba(27, 31, 35, 0.05);width: auto;height: auto;margin-left: 2px;margin-right: 2px;padding: 2px 4px;border-style: none;border-width: 3px;border-color: rgb(0, 0, 0) rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.4);border-radius: 4px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;'>Name屬性賦值時(shí),實(shí)際上是在調(diào)用set訪問(wèn)器,并將傳入的值賦給_name字段。在set訪問(wèn)器內(nèi)部,我們使用了一個(gè)特殊的value關(guān)鍵字來(lái)表示傳入的值。

          四、屬性的使用場(chǎng)景

          屬性的使用場(chǎng)景非常廣泛,它們可以用于控制對(duì)類內(nèi)部數(shù)據(jù)的訪問(wèn)權(quán)限、執(zhí)行數(shù)據(jù)驗(yàn)證、轉(zhuǎn)換數(shù)據(jù)類型等。例如,我們可以在set訪問(wèn)器中添加一些邏輯來(lái)確保賦給屬性的值是有效的,或者在get訪問(wèn)器中返回計(jì)算后的值而不是直接返回字段的值。

          此外,屬性還可以用于實(shí)現(xiàn)一些設(shè)計(jì)模式,如觀察者模式(Observer Pattern)或依賴注入(Dependency Injection)。通過(guò)屬性的靈活訪問(wèn)機(jī)制,我們可以更好地控制類的行為和狀態(tài)。

          五、總結(jié)

          C#中的get和set訪問(wèn)器是屬性訪問(wèn)機(jī)制的重要組成部分。它們?cè)试S我們靈活地控制對(duì)類內(nèi)部數(shù)據(jù)的訪問(wèn),并執(zhí)行額外的邏輯。通過(guò)合理使用get和set訪問(wèn)器,我們可以創(chuàng)建出更加健壯、安全和易于使用的類庫(kù)和應(yīng)用程序。掌握這一機(jī)制對(duì)于深入理解C#面向?qū)ο缶幊讨陵P(guān)重要。

          impleDateFormat 是 Java提供的一個(gè)格式化和解析日期的工具類,日常開發(fā)中應(yīng)該經(jīng)常會(huì)用到,但是它是線程不安全的。

          多線程公用一個(gè) SimpleDateFormat實(shí)例 對(duì)日期進(jìn)行解析或者格式化會(huì)導(dǎo)致程序出錯(cuò),本節(jié)就討論下它為何是線程不安全的,以及如何避免。

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

          1.1、問(wèn)題復(fù)現(xiàn)

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

          代碼(1)創(chuàng)建了SimpleDateFormat的一個(gè)實(shí)例;

          代碼(2)創(chuàng)建10個(gè)線程,每個(gè)線程都共用同一個(gè)sdf對(duì)象對(duì)文本日期進(jìn)行解析,多運(yùn)行幾次就會(huì)拋出java.lang.NumberFormatException異常,加大線程的個(gè)數(shù)有利于該問(wèn)題復(fù)現(xiàn)。

          1.2、問(wèn)題分析

          為了便于分析首先奉上SimpleDateFormat的類圖結(jié)構(gòu):

          SimpleDateFormat 類結(jié)構(gòu)圖

          1. 可知每個(gè)SimpleDateFormat實(shí)例里面有一個(gè)Calendar對(duì)象。
          2. 從后面會(huì)知道其實(shí) SimpleDateFormat 之所以是線程不安全的就是因?yàn)镃alendar是線程不安全的。
          3. SimpleDateFormat 之所以是線程不安全的是因?yàn)槠渲写娣湃掌跀?shù)據(jù)的變量都是線程不安全的,比如里面的fields,time等。

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

          #### parse() 方法

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

          ## establish() 方法

          Calendar establish(Calendar cal) {
             ...
             //(3)重置日期對(duì)象cal的屬性值
             cal.clear();
             //(4) 使用calb中中屬性設(shè)置cal
             ...
             //(5)返回設(shè)置好的cal對(duì)象
             return cal;
          }
          • 代碼(1)主要的作用是解析字符串日期并把解析好的數(shù)據(jù)放入了 CalendarBuilder的實(shí)例calb中,CalendarBuilder是一個(gè)建造者模式,用來(lái)存放后面需要的數(shù)據(jù)。
          • 代碼(3)重置Calendar對(duì)象里面的屬性值,如下代碼:
          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中解析好的日期數(shù)據(jù)設(shè)置cal對(duì)象
          • 代碼(5) 返回設(shè)置好的cal對(duì)象

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

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

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

          方式一:每個(gè)線程創(chuàng)建一個(gè) SimpleDateFormat實(shí)例

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

          方式二:synchronized 同步鎖

          究其原因是因?yàn)槎嗑€程下步驟(3)(4)(5)三個(gè)步驟不是一個(gè)原子性操作,那么容易想到的是對(duì)其進(jìn)行同步,讓(3)(4)(5)成為原子操作,可以使用synchronized進(jìn)行同步。

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

          使用同步意味著多個(gè)線程要競(jìng)爭(zhēng)鎖,在高并發(fā)場(chǎng)景下會(huì)導(dǎo)致系統(tǒng)響應(yīng)性能下降。

          方式三:使用ThreadLocal (推薦)

          每個(gè)線程只需要使用一個(gè) SimpleDateFormat 實(shí)例相比第一種方式大大節(jié)省了對(duì)象的創(chuàng)建銷毀開銷,并且不需要對(duì)多個(gè)線程直接進(jìn)行同步。

          public class TestSimpleDateFormat2 {
           // (1)創(chuàng)建threadlocal實(shí)例
           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)創(chuàng)建多個(gè)線程,并啟動(dòng)
            for (int i=0; i < 10; ++i) {
             Thread thread=new Thread(new Runnable() {
              public void run() {
               try {// (3)使用單例日期實(shí)例解析文本
                 System.out.println(safeSdf.get().parse("2017-12-13 15:17:27"));
               } catch (ParseException e) {
                e.printStackTrace();
               }
              }
             });
             thread.start();// (4)啟動(dòng)線程
            }
           }
          }
          • 代碼(1)創(chuàng)建了一個(gè)線程安全的SimpleDateFormat實(shí)例。
          • 步驟(3)在使用的時(shí)候首先使用get()方法獲取當(dāng)前線程下SimpleDateFormat的實(shí)例,在第一次調(diào)用ThreadLocal的get()方法時(shí)會(huì)觸發(fā)其initialValue方法用來(lái)創(chuàng)建當(dāng)前線程所需要的SimpleDateFormat對(duì)象。

          三、問(wèn)題思考

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

          如果線程調(diào)用多個(gè)類的其他方法,并且其他地方需要日期格式化,在線程代碼中new的對(duì)象,其他地方不一定會(huì)訪問(wèn)得到。如果想復(fù)用的話,

          1. 在方法之間需要將其作為方法的參數(shù)進(jìn)行傳遞。
          2. 方法間不傳遞的話,需要new 多個(gè)實(shí)例。

          3.2、使用 ThreadLocal 的優(yōu)點(diǎn)

          【ThreadLocal 中設(shè)置的變量是線程本身變量池的值,所以只要是同一線程,在執(zhí)行任何類的代碼的時(shí)候都可以獲取得到;只需要?jiǎng)?chuàng)建一個(gè)實(shí)例】。

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

          不過(guò)ThreadLocal使用完其中的值后最好remove下,不然一些情況會(huì)造成內(nèi)存泄露。

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

          迎訪問(wèn)我的GitHub

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

          本篇概覽

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

          源碼下載

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

          名稱

          鏈接

          備注

          項(xiàng)目主頁(yè)

          https://github.com/zq2599/blog_demos

          該項(xiàng)目在GitHub上的主頁(yè)

          git倉(cāng)庫(kù)地址(https)

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

          該項(xiàng)目源碼的倉(cāng)庫(kù)地址,https協(xié)議

          git倉(cāng)庫(kù)地址(ssh)

          git@github.com:zq2599/blog_demos.git

          該項(xiàng)目源碼的倉(cāng)庫(kù)地址,ssh協(xié)議

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



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

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

          什么是標(biāo)簽?舉個(gè)例子

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



          labels.Selector和LabelSelector的作用

          • 盡管labels.Selector和LabelSelector是不同的概念,然而他們的功能大致相同:與Mac系統(tǒng)的標(biāo)簽選擇類似,在查找各種K8S資源時(shí),labels.Selector和LabelSelector都能根據(jù)指定的標(biāo)簽對(duì)資源進(jìn)行過(guò)濾
          • 本篇的核心是labels.Selector,當(dāng)然也會(huì)提到LabelSelector,因?yàn)橛脤?duì)比的方式去學(xué)習(xí)的時(shí)候印象會(huì)更深刻
          • 接下來(lái)咱們慢慢聊,先從LabelSelector說(shuō)起,它最簡(jiǎn)單也最具體,可以作為突破口

          準(zhǔn)備工作:部署nginx的deployment和service

          • 先準(zhǔn)備好kubernetes環(huán)境,部署好一些資源,再用這些資源來(lái)學(xué)習(xí)LabelSelector
          • 在kubernetes環(huán)境創(chuàng)建名為deployment-svc.yaml的文件,內(nèi)容如下
          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
          
          
          • 再執(zhí)行以下命令創(chuàng)建deployment和service資源
          kubectl apply -f deployment-svc.yaml
          
          • 訪問(wèn)kubernetes服務(wù)器的30000端口,可以看到nginx的首頁(yè),證明deployment和service都部署成功了



          了解標(biāo)簽選擇器LabelSelector

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



          • 上圖中的標(biāo)簽選擇器,就是咱們前面提到的LabelSelector,很顯然,yaml中的selector就是一種資源類型,在代碼中對(duì)應(yīng)的就是LabelSelector這個(gè)結(jié)構(gòu)體
          • kubernetes官方對(duì)標(biāo)簽選擇器的描述在這里:https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/labels/,這里面細(xì)說(shuō)了關(guān)于標(biāo)簽的各種寫法和約束,值得認(rèn)真看一遍

          什么時(shí)候該用labels.Selector

          • 從前面的操作可見:如果是編寫yaml文件,配置類似于deployment-svc.yaml這樣的內(nèi)容,那么用到的就是LabelSelector,因?yàn)樗鼘?duì)應(yīng)著yaml里的一個(gè)selector對(duì)象
          • 現(xiàn)在換個(gè)場(chǎng)景:如果在用client-go編寫代碼,對(duì)kubernetes的資源做過(guò)濾呢?例如查詢pod列表的時(shí)候,想要根據(jù)標(biāo)簽做過(guò)濾,那么用LabelSelector就不合適了,因?yàn)檫@是代碼,沒(méi)有yaml文件,自然也就沒(méi)有LabelSelector了
          • 此時(shí)labels.Selector就派上用場(chǎng)了:編碼時(shí),用labels.Selector可以對(duì)資源按照標(biāo)簽做過(guò)濾,接下來(lái)就寫代碼試試labels.Selector怎么用
          • 現(xiàn)在k8s環(huán)境里只有一個(gè)pod,就是前面創(chuàng)建的那個(gè),為了演示labels.Selector的效果,咱們?cè)偬砑右粋€(gè)deployment,腳本如下,這個(gè)pod的app標(biāo)簽值等于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
          
          • 現(xiàn)在有兩個(gè)pod了,接下來(lái)寫代碼,用選出其中一個(gè)
           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
          

          如何創(chuàng)建labels.Selector

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

          編碼

          • 為了后續(xù)文章的實(shí)戰(zhàn)代碼能統(tǒng)一管理,這里繼續(xù)使用前文《client-go實(shí)戰(zhàn)之七:準(zhǔn)備一個(gè)工程管理后續(xù)實(shí)戰(zhàn)的代碼
            》創(chuàng)建的
            client-go-tutorials工程,將代碼寫在這個(gè)工程中
          • client-go-tutorials工程中新增名label.go的文件,整個(gè)工程結(jié)構(gòu)如下圖所示
          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
          
          • 為了個(gè)整個(gè)系列保持一致,先新增一個(gè)結(jié)構(gòu)體
          type Lable struct{}
          
          • 然后是個(gè)輔助方法listPods,接收l(shuí)abels.Selector對(duì)象作為入?yún)ⅲ缓笤诓樵僷od列表的時(shí)候用這個(gè)labels.Selector來(lái)過(guò)濾
          // listPods 根據(jù)傳入的selector過(guò)濾
          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]個(gè)pod\n", prefix, nums)
          
          	// 如果沒(méi)有pod就返回了
          	if nums < 1 {
          		return
          	}
          
          	// 遍歷列表中的每個(gè)pod
          	for index, pod :=range pods.Items {
          		log.Printf("[%v] %v. pod : %v\n", prefix, index+1, pod.Name)
          	}
          }
          
          • 然后是主方法,里面展示了前面提到的四種labels.Selector對(duì)象的創(chuàng)建方式,以及如何用做匹配功能
          func (lable Lable) DoAction(clientset *kubernetes.Clientset) error {
          	// 第一種: 創(chuàng)建Requirement對(duì)象,指定類型是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)
          
          	// 驗(yàn)證,應(yīng)該只查到app等于other的pod
          	listPods(clientset, selector, "用Requirement創(chuàng)建,Equal操作")
          
          	// 第一種: 創(chuàng)建Requirement對(duì)象,指定類型是In,not_exists不會(huì)有任何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)
          
          	// 驗(yàn)證,應(yīng)該查到app=other的pod
          	listPods(clientset, selector, "用Requirement創(chuàng)建,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
          	}
          
          	// 驗(yàn)證,應(yīng)該查到app=other的pod
          	listPods(clientset, parsedSelector, "用Parse創(chuàng)建")
          
          	// 第三種:labels.SelectorFromSet方法
          	setSelector :=labels.SelectorFromSet(labels.Set(map[string]string{"app": "nginx"}))
          
          	// 驗(yàn)證,應(yīng)該查到app=nginx的pod
          	listPods(clientset, setSelector, "用SelectorFromSet創(chuàng)建")
          
          	// 第四種:metav1.LabelSelectorAsSelector方法
          	// 適用于當(dāng)前環(huán)境已有資源對(duì)象的場(chǎng)景,可以取出LabelSelector對(duì)象來(lái)轉(zhuǎn)換成labels.Selector
          	// 先創(chuàng)建一個(gè)LabelSelector
          	labelSelector :=&metav1.LabelSelector{
          		MatchLabels: map[string]string{"app": "other"},
          	}
          
          	// 將LabelSelector轉(zhuǎn)為labels.Selector
          	convertSelector, err :=metav1.LabelSelectorAsSelector(labelSelector)
          
          	if err !=nil {
          		log.Println("4. create equalRequirement fail, ", err)
          		return err
          	}
          
          	// 驗(yàn)證,應(yīng)該查到app=nginx的pod
          	listPods(clientset, convertSelector, "用LabelSelector轉(zhuǎn)換")
          
          	// labels.Selector的第五種用法:用labels.Selector匹配
          
          	// 準(zhǔn)備好一個(gè)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())
          	}
          
          	// 遍歷列表中的每個(gè)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文件,增加一個(gè)新的action的支持,如下圖黃框所示



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

          在List&Watch場(chǎng)景

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



          • 修改后的完整DoAction方法如下,構(gòu)造labels.Selector對(duì)象,改用NewFilteredListWatchFromClient方法,如此,只有匹配了這個(gè)labels.Selector的pod的事件,才會(huì)被apiserver發(fā)送到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)
          
          	// 創(chuàng)建ListWatch對(duì)象,指定要監(jiān)控的資源類型是pod,namespace是default
          	// podListWatcher :=cache.NewListWatchFromClient(clientset.CoreV1().RESTClient(), "pods", v1.NamespaceDefault, fields.Everything())
          
          	// 創(chuàng)建工作隊(duì)列
          	queue :=workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
          
          	// 創(chuàng)建informer,并將返回的存儲(chǔ)對(duì)象保存在變量indexer中
          	indexer, informer :=cache.NewIndexerInformer(podListWatcher, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
          		// 響應(yīng)新增資源事件的方法,可以按照業(yè)務(wù)需求來(lái)定制,
          		// 這里的做法比較常見:寫入工作隊(duì)列
          		AddFunc: func(obj interface{}) {
          			key, err :=cache.MetaNamespaceKeyFunc(obj)
          			if err==nil {
          				queue.Add(key)
          			}
          		},
          		// 響應(yīng)修改資源事件的方法,可以按照業(yè)務(wù)需求來(lái)定制,
          		// 這里的做法比較常見:寫入工作隊(duì)列
          		UpdateFunc: func(old interface{}, new interface{}) {
          			key, err :=cache.MetaNamespaceKeyFunc(new)
          			if err==nil {
          				queue.Add(key)
          			}
          		},
          		// 響應(yīng)修改資源事件的方法,可以按照業(yè)務(wù)需求來(lái)定制,
          		// 這里的做法比較常見:寫入工作隊(duì)列,注意刪除的時(shí)候生成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{})
          
          	// 創(chuàng)建Controller對(duì)象,將所需的三個(gè)變量對(duì)象傳入
          	controller :=NewController(queue, indexer, informer)
          
          	// Now let's start the controller
          	stop :=make(chan struct{})
          	defer close(stop)
          	// 在協(xié)程中啟動(dòng)controller
          	go controller.Run(1, stop)
          
          	// Wait forever
          	select {}
          	return nil
          }
          
          • 至此,labels.Selector的實(shí)戰(zhàn)就完成了,這是個(gè)重要的功能,在查找和監(jiān)聽的場(chǎng)景都會(huì)用到,希望本文能夠給給您一些參考,幫助您在client-go開發(fā)中做到精確過(guò)濾和選擇

          歡迎關(guān)注頭條號(hào):程序員欣宸

          • 學(xué)習(xí)路上,你不孤單,欣宸原創(chuàng)一路相伴...

          主站蜘蛛池模板: 一区二区精品在线| 视频一区在线免费观看| 久久4k岛国高清一区二区| 精品国产一区二区三区无码| 精品国产日韩亚洲一区| 无码国产精品一区二区免费虚拟VR | 一级特黄性色生活片一区二区| 亚洲一区二区三区日本久久九| 一区二区三区影院| 一区二区三区四区视频在线| 波多野结衣AV一区二区三区中文 | 亚洲AV一区二区三区四区| 鲁丝片一区二区三区免费| 国模无码人体一区二区| 农村人乱弄一区二区| 国产成人久久精品麻豆一区| 在线观看国产一区亚洲bd| 国产一区二区三区小说| 亚洲精品色播一区二区| 海角国精产品一区一区三区糖心| 3d动漫精品一区视频在线观看| 日韩在线不卡免费视频一区| 久久久久成人精品一区二区| 日本成人一区二区| 久久无码人妻一区二区三区| 日韩人妻无码一区二区三区久久| 无码av免费一区二区三区| 99精品国产一区二区三区不卡| 人妻少妇精品视频一区二区三区| 无码少妇一区二区三区浪潮AV | 亚洲另类无码一区二区三区| 成人欧美一区二区三区在线视频| 亚洲色一区二区三区四区| 免费高清在线影片一区| 国产成人av一区二区三区在线| 美女福利视频一区二区| 久久精品一区二区东京热| 91福利国产在线观看一区二区| 无码视频一区二区三区在线观看 | 动漫精品专区一区二区三区不卡| 精品一区二区三区电影|