標籤: 貨運

  • 在採用K8S之前您必須了解的5件事情

    在採用K8S之前您必須了解的5件事情

    作者簡介

    Christopher Tozzi,自2008年來以自由職業者的身份對Linux、虛擬化、容器、數據存儲及其相關主題進行報道。

    時至今日,Kubernetes已然成為風靡一時的容器編排調度工具,許多IT分析師均提出了企業應當在何時採用Kubernetes的深刻建議。然而,和所有其他的軟件平台一樣,Kubernetes並非是一個適用於所有人的靈丹妙藥。我更傾向於認為人們在有些時候過分誇大了Kubernetes的作用,以至於他們產生了一種錯覺:他們無法離開Kubernetes,而實際上,Kubernetes比他們真正的需求要複雜得多。

    為了分析人們真正的需求與Kubernetes的匹配程度,我分析了企業在採用Kubernetes編排之前必須考慮的5個事情。

    Kubernetes是什麼?

    如果您關注容器,您可能會知道Kubernetes是一個用於容器編排的開源工具,它可以自動執行諸如啟動容器、停止容器以及在同一個容器的不同實例之間的負載均衡等重要任務。

    簡而言之,Kubernetes的主要目的是最大限度地減少工程師必須手動執行的管理工作量,並通過簡化容器操作,幫助企業大規模運行複雜的容器化應用程序。

    決定是否採用Kubernetes的關鍵要素

    基於Kubernetes的設立初衷,如果您喜歡自動化,討厭手動執行重複性的任務,那麼Kubernetes無疑是您的極佳選擇。

    這是您決定是否採用Kubernetes的重要前提,但是,您不能僅根據這一“前提”就決定是否採用Kubernetes。在採用Kubernetes之前,您還需要考慮並權衡其他重要的因素。

    1、Kubernetes的基礎設施規模

    您的基礎設施規模是其中一個決定Kubernetes是否能夠很好地為您所用的關鍵要素。

    Kubernetes的設計初衷是協調分佈在真正龐大的環境中的容器,這往往意味着企業應當擁有數十台主機服務器。根據過往的實施經驗,如果基礎架構中的服務器少於50個,那麼您可能沒有足夠的資源來利用Kubernetes的全部優勢。

    這並不是指Kubernetes無法在較小規模的基礎設施上運行。實際上,如果您願意,您可以在單個主機上運行Kubernetes。然而,由於Kubernetes其中的一個研發目的是:通過在數量龐大的集群中分佈容器化應用程序提供高可用性,因此,如果您只有少量服務器,則無法享受到Kubernetes的某些價值。

    除此之外,考慮到設置和維護Kubernetes的複雜性,如果您的基礎設施規模較小,無法完全實現Kubernetes的高可用性承諾,那麼或許您不應投入過多時間和精力在Kubernetes上。

    對於較小的基礎架構,您可以使用較為簡單的容器編排工具,或者使用如AWS ECS等具有內置編排的基於雲的容器服務。

    2、Kubernetes操作系統環境

    Kubernetes主要是一種Linux技術。儘管Kubernetes可以用於管理託管Windows服務器上的容器化應用程序,這些應用程序作為Kubernetes服務器集群內的所謂工作節點運行。但託管Kubernetes核心服務的主要服務器或者說主節點必須是Linux。

    因此,如果您的商店以Windows為中心,那麼Kubernetes並非您的最佳選擇。但是您可以選擇Rancher輕鬆將Kubernetes的優勢引入Windows,並且極大程度降低使用的複雜性。

    3、安裝和設置Kubernetes

    在決定採用Kubernetes之前,您還需要評估您可以在此項目上投入的工作時間。

    普通的開放源代碼版本的Kubernetes缺少內置的應用程序,也並未提供一種可以適用於所有默認配置的安裝方式。在集群正常運行之前,您需要投入大量的時間從頭開始編寫及調整配置文件。因此,安裝和配置Kubernetes的過程或許是一個令人生畏的過程,您需要投入大量的時間和精力。

    部分Kubernetes發行版提供了交互式安裝程序腳本,可以幫助您自動執行大部分設置過程。如果您選擇Rancher等Kubernetes發行版,則有望在一两天內輕鬆完成配置及安裝。

    第三種選擇是使用諸如Google Kubernetes Engine等雲供應商解決方案,將Kubernetes作為託管服務在雲上運行。在這種情況下,您可以自行選擇安裝及設置。但值得注意的一點是,在確定如何配置Kubernetes環境時,您的選擇可能會受到限制。

    您必須意識到最為關鍵的一點:不要低估配置Kubernetes的難度。在您真的要全身心投入Kubernetes之前,請確保您所付出的努力是值得的。另一方面,如果您無法確定為企業在生產集群上安裝和部署Kubernetes的難度,您可以嘗試使用K3s等輕量級Kubernetes發行版來進行測試,預估後續需要付出多少努力來進行Kubernetes的配置和設置。

    4、Kubernetes和聲明式配置管理

    Kubernetes採用了所謂的聲明式配置管理方法,這就意味着,您需要自行編寫配置文件來設置Kubernetes應用程序應當如何運行,而Kubernetes將自動指出如何使應用程序符合規範。

    聲明式配置管理與命令式配置管理相反,在命令式配置管理中,您可以自行配置應用程序的每個組件,並讓其按照您所想要的方式運行。

    聲明式配置是Kubernetes在許多用戶實例中如此強大和可伸縮的其中一個原因。您可以設置一次配置,並且根據需要多次應用它。

    但是,如果您的配置需求不斷變化,或者在工作負載或環境中的不同部分之間變化,那麼您應當如何處理呢?在這種情況下,聲明式配置管理將成為一個障礙,您將發現自己需要不斷地調整先前認為是“一勞永逸”的配置文件。

    因此,在您選擇採用Kubernetes之前,您需要考慮應用程序的配置需求。只有當您所需要的配置相對通用且靜態時,Kubernetes才是一個不錯的選項。

    5、Kubernetes和多雲

    Rancher等部分Kubernetes發行版的主要功能之一,是單個Kubernetes部署可以編排多個集群,無論集群位於在不同的公有雲還是私有雲上。這一功能使Kubernetes成為協助控制多雲架構複雜性的優秀工具。

    在跨多雲部署容器化應用程序,並且Kubernetes的設置和配置工作很合理時,多雲上的Kubernetes是十分有意義的。

    在這一因素中,您需要留意的是,在考慮是否以及何時採用Kubernetes時,應考慮您當前的多雲戰略以及多雲擴展計劃。

    結 語

    Kubernetes是一個非常棒的工具,在正確設置的情況下,它可以產生巨大的價值。但是,它並沒有達到殺手級應用程序的狀態,因為它無法在所有用戶實例中交付價值。在您被巨大的宣傳攻勢攻陷,並確定您無法離開Kubernetes之前,請清醒地對自己的需求進行評估,明確Kubernetes是否能在真正意義上幫助您更加有效、更加可靠地運行應用程序。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※教你寫出一流的銷售文案?

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※回頭車貨運收費標準

    ※別再煩惱如何寫文案,掌握八大原則!

    ※超省錢租車方案

  • Spring和Springboot相關知識點整理

    Spring和Springboot相關知識點整理

    簡介

    本文主要整理一些Spring & SpringBoot應用時和相關原理的知識點,對於源碼不做沒有深入的講解。

    1. 思維導圖

    右鍵新窗口打開可以放大。

    說明

    • 使用@Configuration在java代碼中聲明一個bean——而不是使用xml——實際上很早就有了(至少在《Spring實戰(第3版)》出版時,也就是Spring3.0時),我一直以為是SpringBoot的新特性。

    2. Spring

    2.1 AOP術語

    • 通知Advice —— 切面要做什麼,何時執行。何時,包括方法調用前、方法調用后、方法成功調用后、方法調用拋異常后、環繞(Around)。環繞允許提供一些需要跨越方法調用前後的功能,如計算調用耗時。
    • 連接點Joinpoint —— 應用執行時能插入切面的點。實際上是一個邏輯概念,不體現在配置中。
    • 切點Pointcut —— 執行通知的具體的連接點。
    • 切面Aspect —— 通知+切點
    • 引入Introduction —— 允許為類添加新的方法或屬性。(個人理解即應用使用切面中的方法和屬性,就好像這些方法和屬性是原生的一樣。可以參考<aop:declare-parents>元素)
    • 織入Weaving —— 將切面應用到目標對象創建新的代理對象的過程,Spring使用的是運行時。編譯期和類加載時是其他的方式。

    2.2 Bean的生命周期

    雖然被稱為生命周期,實際上指的是bean在初始化、回收期間調用了哪些方法。如果只看《Spring實戰》,可以看到類似下面的圖(圖源:參考文獻[3])

    具體哪一步做了什麼?其實這些方法都是可選的,自定義的bean可以實現,也可以不實現,實現里寫什麼似乎都沒問題,參考文獻[3]中的測試代碼展示了這些方法在bean生命周期中的執行順序。
    但是對於Spring框架的bean,就有必要的用途了。這裏沒有深入研究,有興趣可以自行讀源碼。

    2.3 Cglib和JdkProxy

    2.3.1 與Spring的關係

    這是Spring AOP的兩種實現方式。根據官方文檔:

    1. 默認使用JdkProxy
    2. 對於被代理對象沒有實現任何接口,使用Cglib
    3. 可以強制指定使用Cglib。
      這樣就可以解釋為什麼有的bean實現了接口,有的沒有,但是在同一個工程中可以並存了。

    2.3.2 示例代碼

    本節代碼改寫自參考文獻[5]。

    //用戶管理接口
    public interface UserManager {
        //新增用戶抽象方法
        void addUser(String userName,String password);
        //刪除用戶抽象方法
        void delUser(String userName);
    }
    
    //用戶管理實現類,實現用戶管理接口
    public class UserManagerImpl implements UserManager{
        @Override
        public void addUser(String userName) {
            System.out.println("調用了新增的方法!");
            System.out.println("傳入參數為 userName: "+userName+" password: "+password);
        }
        @Override
        public void delUser(String userName) {
            System.out.println("調用了刪除的方法!");
            System.out.println("傳入參數為 userName: "+userName);
        }   
    }
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import com.lf.shejimoshi.proxy.entity.UserManager;
    import com.lf.shejimoshi.proxy.entity.UserManagerImpl;
    //JDK動態代理實現InvocationHandler接口
    public class JdkProxy implements InvocationHandler {
        private Object target ;//需要代理的目標對象
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("JDK動態代理,監聽開始!");
            Object result = method.invoke(target, args);
            System.out.println("JDK動態代理,監聽結束!");
            return result;
        }
        //定義獲取代理對象方法
        // 因為只是在main()里測試,聲明為private了 
        private Object getJDKProxy(Object targetObject){
            this.target = targetObject;
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
        }
        
        public static void main(String[] args) {
            JdkProxy jdkProxy = new JdkProxy();
            UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//獲取代理對象
            user.addUser("admin");
        }   
    }
    
    import java.lang.reflect.Method;
    
    import com.lf.shejimoshi.proxy.entity.UserManager;
    import com.lf.shejimoshi.proxy.entity.UserManagerImpl;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    //Cglib動態代理,實現MethodInterceptor接口
    public class CglibProxy implements MethodInterceptor {
        private Object target;//需要代理的目標對象
        
        //重寫攔截方法
        @Override
        public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {
            System.out.println("Cglib動態代理,監聽開始!");
            Object invoke = method.invoke(target, arr);//方法執行,參數:target 目標對象 arr參數數組
            System.out.println("Cglib動態代理,監聽結束!");
            return invoke;
        }
        //定義獲取代理對象方法
        public Object getCglibProxy(Object objectTarget){
            //為目標對象target賦值
            this.target = objectTarget;
            Enhancer enhancer = new Enhancer();
            //設置父類,因為Cglib是針對指定的類生成一個子類,所以需要指定父類
            enhancer.setSuperclass(objectTarget.getClass());
            enhancer.setCallback(this);// 設置回調 
            Object result = enhancer.create();//創建並返回代理對象
            return result;
        }
        
        public static void main(String[] args) {
            CglibProxy cglib = new CglibProxy();
            UserManager user =  (UserManager) cglib.getCglibProxy(new UserManagerImpl());
            user.delUser("admin");
        }
        
    }
    

    2.3.3 比較

    比較一下兩者的區別,這也是常見的面試問題。

    JdkProxy Cglib
    依賴 被代理對象實現了接口(所有接口的方法數總和必須>0[4]) 引入asm、cglib jar ;不能是final類和方法
    原理 反射,實現被代理對象接口的匿名內部類,通過InvocationHandler.invoke()包圍被代理對象的方法 引入asm、cglib jar,代理類實現MethodInterceptor,通過底層重寫字節碼來實現
    效率 創建快,運行慢(見下方說明2) 創建慢,運行快

    說明

    1. Cglib是如何修改字節碼,從代碼上是看不出來的。使用的是ASM技術,修改class文件,可以自行查閱。
    2. JDK1.8及以後,JdkProxy的運行速度已經比Cglib快了(之前則是慢於Cglib),測試代碼可見參考文獻[6]。

    2.3.4 關於org.apoalliance.intercept.MethodInterceptor

    我讀了一下之前所用的日誌攔截器源碼,發現其實現的是這節標題的接口:

    class CommonInterceptor implements MethodInterceptor {
          @Override
          public Object invoke(MethodInvocation invocation) throws Throwable {
                try {
                       // 攔截器內部邏輯
                      result = invoication.proceed();
                catch(Throwable e) {
                      // 異常處理
                }
                return result;
          }
    }
    

    聲明代理鏈

    @Configuration
    public class InterceptorConfig {
    
          @Bean
          public CommonInterceptor serviceInterceptor() {
                CommonInterceptor bean = new CommonInterceptor();
                return bean;
          }
    
          // 代理名稱後綴為servie的實現類
          @Bean
          public BeanNameAutoProxyCreator servieBeanNameAutoProxyCreator() {
                BeanNameAutoProxyCreator creator = new BeanNameAutoProxyCreator();
                creator.setName("*ServieImpl");
                creator.setInterceptorNames("serviceInterceptor");
                creator.setProxyTargetClass(true);
                return creator;
          }
    }
    

    查了一些資料,apoalliance包下只是aop的接口規範,不是具體的實現,不要把這裏的MethodInterceptor和cglib的MethodInterceptor搞混。

    2.4 構造方法注入和設值注入的區別

    注:設值注入指的是通過setter注入。
    之前看參考文獻[7],感覺很難懂,試着改換了一種說法:

    1. 如果只設置基本類型(int、long等)的值,建議設置默認值而不是通過任何一種注入完成
    2. 構造注入不支持大部分的依賴注入。構造注入僅在創建時執行,設值注入的值在後續也可以變化。
    3. 設值注入可以支持尚未完整的被依賴的對象,構造注入則不行。可以通過構造注入決定依賴關係,因此如果依賴關係不會發生變更也可以選擇依賴注入。

    2.5 ApplicationContext事件

    可以通過實現ApplicationEvent類和ApplicationListener接口,進行ApplicationContext的事件處理。這是標準的發送者-監聽者的模型,可以用來處理業務邏輯,將代碼解耦。
    但是,發送和接收實際上是同步的,如果有事務,會在同一個事務內,並不能作為異步處理機制[8]
    示例代碼見參考文獻[9]。

    3. SpringBoot

    注:工作中我對SpringBoot是偏應用的,研究並不是很深入。

    3.1 如何快速搭建一個SpringBoot項目

    見參考文獻[10]。實際的過程是藉助Spring Initializer這個網絡應用程序來生成SpringBoot項目。

    3.2 SpringBoot的關鍵註解

    所謂核心註解,這裏指的是相對Spring本身新增的一些註解,來看看它們有什麼作用。
    恰好這裏提到的註解,都可以打在SpringBoot的啟動類(不限於啟動類),用下面的代碼片段來進行說明。

    3.2.1 代碼示例

    package com.example.demo;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.PropertySource;
    
    @PropertySource(value = "classpath:application.properties")
    @MapperScan("com.example.demo.dal")
    @SpringBootApplication(scanBasePackages = {"com.example.demo"})
    @Import({DemoConfig1.class, DemoConfig2.class,})
    public class DemoApplication {
    	public static void main(String[] args) {
    		SpringApplication.run(DemoApplication.class, args);
    	}
    }
    

    3.2.2 @PropertySource

    從指定的文件讀取變量。示例代碼會從resource目錄下讀取application.properties變量的值。文件的格式如下,既可以用常量,也可以用變量替換,來對不同環境的值作區分。

    name=value
    env.name=$env.value
    

    如何使用這個值?在要使用的地方獲取即可。

    @PropertySource(value = "classpath:application.properties")
    class TestClass {
    	@Resource
    	private Environment environment;
    
          @Test
          public void test() {
                String value = environment.getProperty("name"));
          }
    }
    

    3.2.2.1 與@Value配合使用

    使用@Value可以把配置文件的值直接注入到成員變量中。

    @PropertySource("classpath:application.properties")
    public class PropertyConfig {
    
        @Value("${name}")
        private String value;
    
         ...
    }
    

    3.2.2.2 通過@Import引用

    3.2.1的示例代碼中,如果類上沒有@PropertySource,但DemoConfig1或DemoConfig2中有@PropertySource,通過@Import可以將它們加載的變量也讀出來。
    @Import的作用在下文會繼續介紹。

    3.2.2.3 .properties和.yml配置文件

    @PropertySource只能導入.properties配置文件里的內容,對於.yml是不支持的。看了一些文章,得出結論是yml文件是不需要註解就能導入,但是需要路徑。
    Springboot有兩種核心配置文件,application和bootstrap,都可以用properties或yml格式。區別在於bootstrap比application優先加載,並且不可覆蓋。

    3.2.3 @MapperScan

    這實際上是一個mybatis註解,作用是為指定路徑下的DAO接口,通過sqlmapping.xml文件,生成實現類。

    3.2.4 @SpringBootApplication

    @SpringBootApplication是由多個註解組合成的。源碼如下:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
          // 略
    }
    

    簡單介紹一下這些註解。

    3.2.4.1 元註解

    最上面四行都是元註解。回憶一下它們的作用[12]

    • @Target 註解可以用在哪。TYPE表示類型,如類、接口、枚舉
    • @Retention 註解的保留時間期限。只有RUNTIME類型可以在運行時通過反射獲取其值
    • @Documented 該註解在生成javadoc文檔時是否保留
    • @Inherited 被註解的元素,是否具有繼承性,如子類可以繼承父類的註解而不必顯式的寫下來。

    3.2.4.2 @SpringBootConfiguration

    標註這是一個SpringBoot的配置類,和@Configuration功能是相通的,從源碼也可以看出它直接使用了@Configuration。

    3.2.4.3 @EnableAutoConfiguration

    這個註解是實現自動化配置的核心註解,定義如下

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
          // 略
    }
    

    藉助@Import引入的AutoConfigurationImportSelector,SpringBoot應用將所有符合條件的@Configuration配置都加載到當前SpringBoot創建並使用的IoC容器。具體的細節這裏不展開了。

    3.2.4.4 @ComponentScan

    掃描@Service、@Repository、@Component、@Controller等標註的類,創建bean。
    可以設置掃描範圍,決定類哪些生成bean,哪些類不生成。

    3.2.5 @Import

    將外部資源(bean、@Configuration配置類)導入到當前IOC容器中。
    使用@Import便可以實例化引用的jar中定義的bean了。

    3.3 Starter

    指的是在依賴中引用的各種starter包。starter可以看作是“依賴jar+配置”打包的結果,目的是降低開發者引入組件的成本,不用自己梳理依賴、編寫配置文件。
    starter遵循“約定大於配置”的原則,使用的組件的配置大部分都有默認值,不聲明也可以直接用。

    創建一個Spring boot的簡易步驟(完整的可以看參考文獻[14]):

    1. 創建maven項目
    2. 創建proterties類來保存配置信息
    3. 編寫業務功能類(包含會實例化為bean的類)
    4. 編寫Configuration類,定義bean
    5. 在resources 文件夾下新建目錄 META-INF,在目錄中新建 spring.factories 文件,並且在 spring.factories 中配置AutoConfiguration
    6. 打包

    3.4 war包

    3.4.1 和jar包的區別

    • jar 把類和相關的資源封裝
    • war 代表了一個可部署的Web應用程序

    3.4.2 SpringBoot項目打war包部署

    通用步驟如下,其中1可能需要移除內嵌tomcat,2有其他形式,因為我工作時都是拿線程腳本打包的,沒有實際操作過,下面步驟僅供參考。

    1. pom.xml修改為按war打包
    2. 修改main入口方法,繼承一個SpringBootServletInitializer類,並且覆蓋configure方法
    3. maven打包
    4. 複製到tomcat路徑下(tomcat需要預先配置),使用startup啟動

    3.5 Springboot面試題補充

    本節內容結合了參考文獻[16]進行補充,上面提到的知識不再重複。

    3.5.1 使用Springboot的兩種方式

    1. 繼承spring-boot-starter-parent項目
    <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>1.5.6.RELEASE</version>
    </parent>
    
    1. 導入spring-boot-dependencies項目依賴
    <dependencyManagement>
          <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>1.5.6.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
          </dependencies>
    </dependencyManagement>
    

    3.5.2 SpringBoot 需要獨立的容器運行嗎?

    可以不需要,內置了 Tomcat/Jetty等容器。
    如何使用Jetty?排除掉Tomcat依賴並引入Jetty,並更改一些application配置。兩種容器的比較和替換方式見參考文獻[17]。

    3.5.3 運行 SpringBoot 有哪幾種方式?

    1. 打包用命令或者放到容器中運行
    2. 用 Maven/ Gradle 插件運行
    3. 直接執行 main 方法運行

    3.5.4 SpringBoot啟動時運行特定代碼的方式

    Bean實現接口 ApplicationRunner或者CommandLineRunner即可。

    3.5.5 SpringBoot實現熱部署有哪幾種方式?

    主要有兩種

    • Spring Loaded —— 引用依賴(maven plugin)。對於註解和xml變動無法感知需要重啟
    • Spring-boot-devtools —— 引用依賴、更改配置(可選)、啟動idea的自動編譯。注意生產環境插件可能導致gc

    3.5.6 Spring Boot 可以兼容老 Spring 項目嗎,如何做?

    可以兼容,使用 @ImportResource 註解導入老 Spring 項目配置文件。

    參考文獻

    1.AOP -連接點和切點的區別
    2.Spring AOP術語:連接點和切點的區別。
    3.深究Spring中Bean的生命周期
    4.Spring AOP代理用的到底是CGLIB還是JDK動態代理
    5. Spring的兩種動態代理:Jdk和Cglib 的區別和實現
    6. Spring AOP中的JDK和CGLib動態代理哪個效率更高?
    7. 經典面試題-構造方法注入和設值注入有什麼區別?
    8. Spring的ApplicationEvent
    9. spring-第三篇之ApplicationContext的事件機制
    10. 使用IDEA搭建一個簡單的SpringBoot項目——詳細過程
    11. 淺析PropertySource 基本使用
    12. JAVA核心知識點–元註解詳解
    13. 簡單講講@SpringBootApplication
    14. Spring Boot Starters
    15. SpringBoot 打包成war
    16. 吐血整理 20 道 Spring Boot 面試題,我經常拿來面試別人!
    17. SpringBoot2使用Jetty容器(替換默認Tomcat)

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

    ※別再煩惱如何寫文案,掌握八大原則!

    ※回頭車貨運收費標準

    ※教你寫出一流的銷售文案?

  • 深入理解React:事件機制原理

    深入理解React:事件機制原理

    目錄

    • 序言
    • DOM事件流
      • 事件捕獲階段、處於目標階段、事件冒泡階段
      • addEventListener 方法
    • React 事件概述
    • 事件註冊
      • document 上註冊
      • 回調函數存儲
    • 事件分發
    • 小結
    • 參考

    1.序言

    React 有一套自己的事件系統,其事件叫做合成事件。為什麼 React 要自定義一套事件系統?React 事件是如何註冊和觸發的?React 事件與原生 DOM 事件有什麼區別?帶着這些問題,讓我們一起來探究 React 事件機制的原理。為了便於理解,此篇分析將盡可能用圖解代替貼 React 源代碼進行解析。

    2.DOM事件流

    首先,在正式講解 React 事件之前,有必要了解一下 DOM 事件流,其包含三個流程:事件捕獲階段、處於目標階段和事件冒泡階段。

    W3C協會早在1988年就開始了DOM標準的制定,W3C DOM標準可以分為 DOM1、DOM2、DOM3 三個版本。

    從 DOM2 開始,DOM 的事件傳播分三個階段進行:事件捕獲階段、處於目標階段和事件冒泡階段。

    (1)事件捕獲階段、處於目標階段和事件冒泡階段

    示例代碼:

    <html>
        <body>
            <div id="outer">
    	    <p id="inner">Click me!</p>
    	</div>
        </body>
    </html>
    

    上述代碼,如果點擊 <p>元素,那麼 DOM 事件流如下圖:

    (1)事件捕獲階段:事件對象通過目標節點的祖先 Window 傳播到目標的父節點。

    (2)處於目標階段:事件對象到達事件目標節點。如果阻止事件冒泡,那麼該事件對象將在此階段完成后停止傳播。

    (3)事件冒泡階段:事件對象以相反的順序從目標節點的父項開始傳播,從目標節點的父項開始到 Window 結束。

    (2)addEventListener 方法

    DOM 的事件流中同時包含了事件捕獲階段和事件冒泡階段,而作為開發者,我們可以選擇事件處理函數在哪一個階段被調用。

    addEventListener() 方法用於為特定元素綁定一個事件處理函數。addEventListener 有三個參數:

    element.addEventListener(event, function, useCapture)
    

    另外,如果一個元素(element)針對同一個事件類型(event),多次綁定同一個事件處理函數(function),那麼重複的實例會被拋棄。當然如果第三個參數capture值不一致,此時就算重複定義,也不會被拋棄掉。

    3.React 事件概述

    React 根據W3C 規範來定義自己的事件系統,其事件被稱之為合成事件 (SyntheticEvent)。而其自定義事件系統的動機主要包含以下幾個方面:

    (1)抹平不同瀏覽器之間的兼容性差異。最主要的動機。

    (2)事件”合成”,即事件自定義。事件合成既可以處理兼容性問題,也可以用來自定義事件(例如 React 的 onChange 事件)。

    (3)提供一個抽象跨平台事件機制。類似 VirtualDOM 抽象了跨平台的渲染方式,合成事件(SyntheticEvent)提供一個抽象的跨平台事件機制。

    (4)可以做更多優化。例如利用事件委託機制,幾乎所有事件的觸發都代理到了 document,而不是 DOM 節點本身,簡化了 DOM 事件處理邏輯,減少了內存開銷。(React 自身模擬了一套事件冒泡的機制)

    (5)可以干預事件的分發。V16引入 Fiber 架構,React 可以通過干預事件的分發以優化用戶的交互體驗。

    注:「幾乎」所有事件都代理到了 document,說明有例外,比如audiovideo標籤的一些媒體事件(如 onplay、onpause 等),是 document 所不具有,這些事件只能夠在這些標籤上進行事件進行代理,但依舊用統一的入口分發函數(dispatchEvent)進行綁定。

    4.事件註冊

    React 的事件註冊過程主要做了兩件事:document 上註冊、存儲事件回調。

    (1)document 上註冊

    在 React 組件掛載階段,根據組件內的聲明的事件類型(onclick、onchange 等),在 document 上註冊事件(使用addEventListener),並指定統一的回調函數 dispatchEvent。換句話說,document 上不管註冊的是什麼事件,都具有統一的回調函數 dispatchEvent。也正是因為這一事件委託機制,具有同樣的回調函數 dispatchEvent,所以對於同一種事件類型,不論在 document 上註冊了幾次,最終也只會保留一個有效實例,這能減少內存開銷。

    示例代碼:

    function TestComponent() {
      handleFatherClick=()=>{
    		// ...
      }
    
      handleChildClick=()=>{
    		// ...
      }
    
      return <div className="father" onClick={this.handleFatherClick}>
    	<div className="child" onClick={this.handleChildClick}>child </div>
      </div>
    }
    

    上述代碼中,事件類型都是onclick,由於 React 的事件委託機制,會指定統一的回調函數 dispatchEvent,所以最終只會在 document 上保留一個 click 事件,類似document.addEventListener('click', dispatchEvent),從這裏也可以看出 React 的事件是在 DOM 事件流的冒泡階段被觸發執行。

    (2)存儲事件回調

    React 為了在觸發事件時可以查找到對應的回調去執行,會把組件內的所有事件統一地存放到一個對象中(listenerBank)。而存儲方式如上圖,首先會根據事件類型分類存儲,例如 click 事件相關的統一存儲在一個對象中,回調函數的存儲採用鍵值對(key/value)的方式存儲在對象中,key 是組件的唯一標識 id,value 對應的就是事件的回調函數。

    React 的事件註冊的關鍵步驟如下圖:

    5.事件分發

    事件分發也就是事件觸發。React 的事件觸發只會發生在 DOM 事件流的冒泡階段,因為在 document 上註冊時就默認是在冒泡階段被觸發執行。

    其大致流程如下:

    1. 觸發事件,開始 DOM 事件流,先後經過三個階段:事件捕獲階段、處於目標階段和事件冒泡階段
    2. 當事件冒泡到 document 時,觸發統一的事件分發函數 ReactEventListener.dispatchEvent
    3. 根據原生事件對象(nativeEvent)找到當前節點(即事件觸發節點)對應的 ReactDOMComponent 對象
    4. 事件的合成
      1. 根據當前事件類型生成對應的合成對象
      2. 封裝原生事件對象和冒泡機制
      3. 查找當前元素以及它所有父級
      4. 在 listenerBank 中查找事件回調函數併合成到 events 中
    5. 批量執行合成事件(events)內的回調函數
    6. 如果沒有阻止冒泡,會將繼續進行 DOM 事件流的冒泡(從 document 到 window),否則結束事件觸發

    注:上圖中阻止冒泡是指調用stopImmediatePropagation 方法阻止冒泡,如果是調用stopPropagation阻止冒泡,document 上如果還註冊了同類型其他的事件,也將會被觸發執行,但會正常阻斷 window 上事件觸發。了解兩者之間的詳細區別

    示例代碼:

    class TestComponent extends React.Component {
    
      componentDidMount() {
        this.parent.addEventListener('click', (e) => {
          console.log('dom parent');
        })
        this.child.addEventListener('click', (e) => {
          console.log('dom child');
        })
        document.addEventListener('click', (e) => {
          console.log('document');
        })
        document.body.addEventListener('click', (e) => {
          console.log('body');
        })
        window.addEventListener('click', (e) => {
          console.log('window');
        })
      }
    
      childClick = (e) => {
        console.log('react child');
      }
    
      parentClick = (e) => {
        console.log('react parent');
      }
    
      render() {
        return (
          <div class='parent' onClick={this.parentClick} ref={ref => this.parent = ref}>
            <div class='child' onClick={this.childClick} ref={ref => this.child = ref}>
              Click me!
            </div>
          </div>)
      }
    }
    

    點擊 child div 時,其輸出如下:

    在 DOM 事件流的冒泡階段先後經歷的元素:child <div> -> parent <div> -> <body> -> <html> -> document -> window,因此上面的輸出符合預期。

    6.小結

    React 合成事件和原生 DOM 事件的主要區別:

    (1)React 組件上聲明的事件沒有綁定在 React 組件對應的原生 DOM 節點上。

    (2)React 利用事件委託機制,將幾乎所有事件的觸發代理(delegate)在 document 節點上,事件對象(event)是合成對象(SyntheticEvent),不是原生事件對象,但通過 nativeEvent 屬性訪問原生事件對象。

    (3)由於 React 的事件委託機制,React 組件對應的原生 DOM 節點上的事件觸發時機總是在 React 組件上的事件之前。

    7.參考

    javascript中DOM0,DOM2,DOM3級事件模型解析

    Event dispatch and DOM event flow

    EventTarget.addEventListener() – Web API 接口參考| MDN

    合成事件

    談談React事件機制和未來(react-events)

    React源碼解讀系列 – 事件機制

    一文吃透 react 事件機制原理

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • 一條update SQL語句是如何執行的

    一條update SQL語句是如何執行的

      一條更新語句的執行過程和查詢語句類似,更新的流程涉及兩個日誌:redo log(重做日誌)和binlog(歸檔日誌)。比如我們要將ID(主鍵)=2這一行的值加(c:字段)1,SQL語句如下:

    update T set c=c+1 where ID=2;
    • redo log

      重做日誌是InnoDB引擎特有的,是物理日誌,記錄在“某個數據頁上做了什麼修改“。大小是固定,可以進行配置大小。假如我們配置一組4個文件,圖中write pos是當前記錄的位置,往後推移並且循環;checkpoint是當前要擦除的位置,移動規律和前者一樣。兩者之間的位置可以記錄新的操作

      

      如果write pos 追上checkpoint,就移動checkpoint擦除一些記錄。所以即使數據可以發生異常重啟,InnoDB也可以保證之前提交的記錄不會丟,這就是MySQL的crash_safe能力。

    • binlog

       歸檔日誌是MySQL的server層的實現的,所有引擎都可以使用。binlog記錄的是sql語句的原始邏輯,比如根劇’id’字段查詢所有的信息;相比redo log的循環寫入,binlog是追加寫的,binlog文件寫到一定大小後會切換到下一個,不會覆蓋以前的日誌。

      Binlog有兩種模式,statement 格式的話是記sql語句, row格式會記錄行的內容,記兩條,更新前和更新后都有。

     文章開頭的更新語句在InnoDB中的執行流程如下:深色代表在執行器中執行的,淺色是在存儲引擎中。

      最後寫入redolog分為了prepare和commit兩步,用來保證兩個日誌寫入的一致性,這就是“兩階段提交”。比如我們執行“update T set status = 1“時:

    • 如果寫入redolog成功,但寫binlog失敗,重啟恢復時,數據庫發現沒有commit,那麼事務本身回滾;備份恢復時沒有binlog,數據庫里的status值不變。
    • 如果在commit失敗,重啟恢復時redolog和binlog一致,重新commit;備份恢復時有binlog,直接恢復。

      總的來說binlog記錄了對數據庫所有的邏輯操作,可以通過binlog來備份出一份完全相同的庫;因為redolog是InnoDB引擎特有的,如果使用其他引擎,那麼就要依賴binlog來記錄操作。

    Q定期全量備份的周期“取決於系統重要性,有的是一天一備,有的是一周一備”。那麼在什麼場景下,一天一備會比一周一備更有優勢呢?或者說,它影響了這個數據庫系統的哪個指標?

    A一天一備,那麼如果需要恢複數據的話,只要保證當天的binlog完整即可;一周一備的話就要保證一周的binlog完整;同時頻繁全量備份需要更多存儲空間,如何選擇取決於業務的重要性,對應的指標是RTO(目標恢復時間)。

     — 《MySQL實戰45講》筆記二

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※教你寫出一流的銷售文案?

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※回頭車貨運收費標準

    ※別再煩惱如何寫文案,掌握八大原則!

    ※超省錢租車方案

  • 畢業了,我的四年大學:平凡但不平庸(寫給每一位想要認真學習的小夥伴)

    畢業了,我的四年大學:平凡但不平庸(寫給每一位想要認真學習的小夥伴)

    去年十月份的時候,我分享了一篇關於我三年大學的文章:普普通通,我的三年大學,說實話,這篇文章還是激勵了不少小夥伴,給很多人帶來了動力。寫這篇那會,我剛結束了自己的秋招,結束秋招的那一刻,可以說是 2019 年心情最放鬆的時刻了,對於沒有讀研的學生來說,秋招那一戰,可以算是整個大學最重要的一戰了,在這裏,也希望各位小夥伴,也要早點做好準備。

    時間是過的真快,一不小心就在公司實習了差不多三個月了,大學最後的作業 —- 答辯和論文,也都處理完畢了,可以說我的大學,即將要畫上一個句號了。

    我的四年大學,可以說是普普通通,在校期間也沒啥輝煌的戰績,更準確的說是,0 戰績:沒有參加過什麼大賽,甚至沒有參加過比賽;在學校也沒拿過獎學金,連個三好學生都不給;也沒去用心融入一個社團,成為裏面的一員;更沒有去做過兼職,發過海報。

    但正是因為普普通通,我才要分享自己的四年大學。一個簡單的原因就是,我的公眾號里有 65% 是在校生,並且我相信大部分人和我一樣,都是普普通通,所以我希望我在大學的做法,或許可以給你們一個參考。不過說實話,雖然我的大學沒有什麼輝煌的戰績,但我還算滿意,至少是按照我自己的計劃走過來的,並且自己的目標也都實現了。

    大一

    大一第一學期這部分的事情最多了,所以會寫的多一些,因為我覺得,在大一,我的思維發生了很大的改變,大一過後,我的想法、計劃就基本有着落了,後面基本事按照自己的計劃,去做自己喜歡的事了。

    大一第一學期的第一要務:轉專業

    看過我之前文章的讀者可能都知道,我大一第一學期不是計算機專業的,讀的專業是木材科學與工程。當然,這不是我自己選擇的專業,而是高考分數不夠,被調劑的專業,我自己熱愛的專業是計算機類專業,還沒有進入大學之前,我就說我一定要讀編程相關的專業了。

    說實話,我很慶幸自己最後轉到了計算機類的專業,一個很現實的原因就是,學編程,真的能夠掙錢,而且你願意努力學的話,你一畢業就能夠拿到很高的工資,本科畢業,有好些可以拿到年薪 50 萬,你敢信?就算工資很低,也都能年薪十幾萬,當然,我不能暴露自己的工資,暴露了就要被公司勸退了。

    是的,我讀計算機專業,有兩個重要的原因,一個原因就是熱愛,一個是家裡窮,我想掙錢,改變家裡的現狀。所以在我大一第一學期那一會,我的目標就很明確,這學期的任務就是轉專業,所以我了解了轉專業的各種規則,了解到轉專業的一個最基本的要求就是數學和英語需要 85 分以上才能參与考試。

    85 分很難嗎?不難;但是如果需要你保證 85 分難嗎?說實話,有一些,因為萬一考不好呢?考不好就得大二轉了,所以我第一學期,英語的平時分,我給刷到了 100 分,也就是滿分。數學是我的強項,但我依然不敢掉以輕心,在考前把 2006-2015 年的試卷都刷了一兩遍。

    有這樣的準備,數學和英語的分數那必須杠杠,實不相瞞,大一學期的績點是我整個大學的巔峰,然而,三等獎依然沒有拿到!!!因為我只學了這兩門,其他隨意,不掛科即可。

    當然,85 分只是一個門檻,轉專業的競爭還是挺激烈。我選了軟件工程,參与人數大概是差不多 60 人,不過學院只接 15 個人(後來好像是錄取了 20 人),所以為了穩一些,我在第一學期就把 c 語言自學了一遍,把學校的 OJ 題庫,第一學期相關的編程題,給刷了一兩遍。

    說實話,有了這樣的準備,轉專業想不成功都難,考試 2 小時,我不到半個小時就離開考場了,然而我居然是第三個離開考場的,而不是第一個。第一個和第二個離開考場的,單挑嗎?

    有必要加入社團嗎?

    對於剛步入大學的同學來說,社團是一個比較有趣的玩意,絕大部分人都會去加入自己喜歡的社團吧。

    我進入大學之前,很多人說,在大學,要多交點人脈,多認識一些朋友,因為這些人會成為你後面很重要的資源,所以我去加入一些社團,其實是想多認識一些人,然而,我去面試了兩個社團,都在二面被刷了,說實話,對於面試,我還是有點恐懼的,反正就是挺緊張,後來我就乾脆不去面試了,不加入社團了。

    後來我就加入了他們的會員,之前面試那個,是成為他們的幹部。成為會員則不需要面試,不過需要交會員費用,所以我就順便成為了幾個社團的會員,例如羽毛球,愛心社團啥的。

    不過,我去參与了一兩次社團的活動之後,就沒在去了,一個簡單的原因就是,我不大喜歡,我還是喜歡去做自己喜歡的事。我是一個懶散,喜歡自由,不喜歡被束縛的人,所以我覺得,我還是不去參加這些活動了。

    回答剛才的問題,有必要加入社團嗎?,可能很多人會覺得,必須要加入社團,不然大學就不完整了,我的想法則是,加入社團不是一個必選項,加與不加,我覺得都沒事,看你自己的喜歡。加入社團能學到很多東西嗎?能學到一些,但這些沒啥的,對於以後找工作,我覺得屬於可有可無。如果你自己願意學習,學習能力比較強,在哪裡都可以學到很多東西。

    擺脫的社團之後,我基本就什麼組織也沒參加了,然後我宿舍也有一個和我一樣比較逗比+沙雕的,我倆就經常去外面溜達,看到有趣的活動,就去參与一波,反正完全看心情,報名了活動,交了錢,心情不好就不去參加了,美滋滋(感覺要被噴,哈哈)

    有必要多認識些人嗎?

    我剛才說了,我希望自己在大學多認識些人,多泛交些朋友,後來經過第一學期的感悟,我發現,這沒必要,在大學,大家基本都在忙各自的事情,我本來還想在大學找幾個摯友,以後一起干大事,但經過一學期的觀察,發現這很難,當然,很難不代表不存在。

    總之,對於現在還在大一大二的學生,如果你有這方面的疑問,那麼我給的建議是,沒必要刻意去交朋友,其實後面大家都各自去做自己的事情了,畢竟找到一些經的起時間考驗的志同道合的朋友,很不容易。

    第二學期:落差之后的折騰

    大一學期其實可以寫的還有非常非常多,因為第一學期,我的任務就兩門課程,還是非常閑,期間也發生了非常多改變我想法多事情,不過一不小心就兩千多字了,還是不繼續寫了,後面的時光可能就沒有第一學期那麼豐富了,相對比較枯燥了。

    經過了第一學期,成功進入了軟件工程,自己也沒參与什麼社團,並且大學想要干點事,例如創業之類的,因為沒讀大學之前,經常聽說創業這事……總之,進入大學的時候,感覺前途一片光明,我覺得我要干非常非常多的事,但進入大學之後,發現並沒有啥戰績。

    慶幸的事,我完成了一個非常堅定的目標,那便是轉專業。轉過來軟工之後,我也想干點大事,例如 acm 拿個牛逼的獎牌,或者寫個牛逼的軟件出來,因為經常聽到某某人開發了一個 xx,然後就成名了。

    然而,學了 c 語言,發現啥也寫不出來,學了算法,發現 acm 那些題也太難了,一道題做一天,還是沒做出來,答案也看不懂,發現自己並不是大神,腦子也並沒有大佬轉的快。

    後來,我就不打算參加 acm 了,感覺如果自己要拿到名次,肯定會花很多時間,並且不一定拿的到,加上我看到班裡也有人退出 acm 集訓隊了,這更加堅定了我的想法。

    聽說數據結構與算法很重要,所以我早早就把數據結構與算法這門課學完了,我第二學期學的最多的就是數據結構 + 算法這兩門課,雖然不參加 ACM,但算法還是得學,會點算法聽說會顯的牛逼一些。

    暑假的折騰

    大一暑假那會,雖然數據結構與算法學的還不錯,不過發現啥東西的做不出來,C 語言寫的程序都是黑乎乎的界面,然後我就學了 windows 程序編程,這樣我就可以寫個程序給身邊的人玩了,畢竟我是學編程的,至少得寫個作品出來給別人玩啊。

    在暑假花了十幾天,把那本 900 多頁的windows程序編程刷了 700 多頁,寫了個計算器,後來發現身邊的人還是玩不了,因為很多人沒電腦,於是我就對 windows 編程沒興趣了,想着寫個程序能夠在手機運行就好了,於是花了十幾天學了 android 編程,刷完了《第一行代碼》這本書,順便入門了 Java,跟着書寫了個天氣預報,還是挺開心。

    不過我又改變主意了,想着要是能掙到錢就更好了,於是我發現把 app 上傳到商店,然後植入廣告,就可以掙錢了,於是我買了一些實戰類 android 項目的書籍,寫了幾個 app,自己改版之後傳到了應用商店。

    然而,沒啥人下載,於是我又放棄搞安卓了,後來想寫個網站,然後放一些資源,讓別人來下載好像也有機會掙錢,於是我學習了 HTML,CSS,JavaScript,然後又不了了之……

    這兩個月的暑假,我感覺自己搞了好多東西,好多都是半途放棄,實不相瞞,驅動我去折騰這些事有兩個原因:掙錢 + 裝逼。說實話,我做什麼事情,都需要驅動力,我覺得驅動力對我來說太重要了,這個驅動力可以很虛,但必須得有,而我又是一個俗人,能夠掙錢,是我最大的驅動力。當初我玩斗地主,驅動力是掙 Q 幣,後來我發現這些 Q 幣好像不能充 QQ會 員還是怎麼的,我就放棄不玩了。

    這段折騰,我覺得讓我慢慢摸清了自己的方向,所以在這裏,對於大一或者大二的同學,如果你們有自己感興趣的,或許可以去嘗試一波。別人可能會說,這搞一下,那搞一下,會導致樣樣都會,但樣樣不精。而我的想法是:完全可以去嘗試,大學的學習,不存在精通這一說法,大一大二多嘗試,大三確定自己的方向來學習完全來得及。

    大二

    經過大一的洗禮,我覺得我的目標相對比較明確了,該玩的玩了,該折騰的折騰了,現在得好好規劃下自己的未來了。我了解到校招時大廠非常看重基礎,於是我大二就一直在學習基礎,例如計算機網絡,算法,操作系統這些,怎麼學?

    得有動力啊,於是我報名了中級軟考,這算是我一個動力,這門考試會考整個大學涉及到的知識,於是為了搞定這個軟考,我大二把很多課程都學了,後面軟考也順利通過了。

    中級軟考有必要參加嗎?答是隨便,這個證沒啥含金量的,我的目的是讓他督促我學習基礎知識,適合用來複習知識吧。

    總之,大一,我學了很多數據結構和算法相關知識,大二,我學了很多基礎知識 + Java 的知識,並且大二比較專心,啥比賽,啥活動也沒參加,我說了,我喜歡做自己喜歡做的事,喜歡跟着自己的步伐走,別人的建議,我可能會參考下,但我無論做什麼事,都有自己的想法和思考。

    有人也有問,有必要加入實驗室嗎?,我沒加過實驗室,但我想說的是,加與不加,都沒關係,重點是你想學習什麼,想成為什麼樣的人,實驗室,更多的是一種氣氛,但不一定適合你。要是我加入實驗室,我可能會把實驗室當作一個學習的場地,進而去學習自己喜歡的東西。

    也有人問,那些基礎知識很枯燥,有沒有什麼辦法?,答是沒有,有些本來就枯燥,但枯燥的東西,往往是決定你我之間的區別,如果都很有趣,那大家肯定也都學,正是因為困難,所以才有了人與人之間的區別。

    大三

    其實我大三基本就處於複習 + 寫作 + 運營公眾號了,關於寫作和運營公眾號這個事,我不想說太多,因為我覺得我可以再寫兩篇文章來說這些事了。我只能說,運營一個公眾號,很不容易,我希望你,好好積累,好好準備秋招或者考研。我之所以能夠在大三寫出那麼多原創文章,一個很重要的原因就是,我大一大二積累了很多,所以大三就輸入的很少,基本處於輸出和折騰公眾號的過程。

    大三的第二學期就是春招了,也就是找暑假實習,不過在大三,一般都面臨兩個選擇:讀研 還是 找工作?,實不相瞞,我從來沒想過讀研,讀研從來都沒在我的字典里。

    為什麼?

    之前也有挺多小夥伴問我為什麼沒讀研的,不過這個話題,我覺得我可以寫一篇關於我自己為何沒有讀研的原因了,如果你們感興趣,我後面寫一篇吧。

    總的來說就是,大三處於複習的過程,之前我也曬過自己的思維導圖:當初為了有機會進大廠,帥地狠心複習了這9門核心知識,熬夜整理成思維導圖送給大家

    不過說實話,其實我大三花在寫文章 + 弄公眾號的時間,佔比非常非常多,公眾號給我的學習狀態,帶來了很多負面影響,但幸運的是,我的公眾號做的不錯,給我帶來了不少收入,同時也幫忙了不少人,很多人都來感謝過我,這讓我很開心。

    大三,能說的太多,但更多的都是非技術學習,我這裏就不說了,這篇文章字數也挺多了,有機會後面再說。

    大四

    大三暑假,也就是 2019 年 9月份,我就結束了自己的秋招了,很幸運,找到了自己喜歡的公司與城市,2019 年這一年,真的可以說是非常幸運,找到了工作 + 有了自己的公眾號,所以大四,過的很輕鬆,畢竟沒有找工作的壓力,所以大四上學期,都是在 玩 + 為工作準備 + 運營公眾號

    到了大四第二學期,也就是 2020 年的 2 月份底,我就來公司實習了,一直實習到至今,關於實習到感悟,有機會再寫篇文章吧。

    總結

    說實話,我的大學,0 比賽 0 獎學金 0 證書,算是平凡但不平庸,但我始終都有一個明確目標支撐我去學習與探索,總的來說就是,我的大學做對了三件事,一是選擇了編程,二是學習正確的技能,三是入坑了寫作。

    說實話,如果你們願意學習,你們也是可以做到的,至少,你們畢業后的薪資會對的起你們平時的學習。

    我是帥地,一個即將畢業,步入社會大學的學生,希望在未來,我們共同成長,也歡迎大家見證我的成長!

    最後,獻上我備戰校招的思維導圖 + 提升內功的 PDF 吧

    九大思維導圖助你拿到心儀的 offer

    打開計算機網絡的思維導圖長這樣

    由於鏈接容易失效,不方便更新,大家可以在我的微信公眾號帥地玩編程回復思維導圖,即可獲取九大思維導圖,相信一定可以在面試時助你一臂之力。

    作者簡潔

    作者:大家好,我是帥地,從大學、自學一路走來,深知算法計算機基礎知識的重要性,所以申請了一個微星公眾號『帥地玩編程』,專業於寫這些底層知識,提升我們的內功,帥地期待你的關注,和我一起學習。 轉載說明:未獲得授權,禁止轉載

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

    ※別再煩惱如何寫文案,掌握八大原則!

    ※回頭車貨運收費標準

    ※教你寫出一流的銷售文案?

  • 雷諾與法國富豪攜手 搶進電動車市場

    法國雷諾集團(Renault SA)與法國富豪波洛黑(Vincent Bollore)決定合作,共同製造電動車,瞄準零排放汽車日益強勁的需求,及正於各地興起的環保汽車租用潮流。   雷諾汽車將於 2015 下半年開始生產波洛黑的電動車款「藍車(Bluecar)」,但並未透露生產目標。該車款自 2011 年起,被使用於巴黎名為「Autolib」的電動車租賃共享計畫中,此計畫同時也於里昂及波爾多等地運行。   此外,雷諾汽車與波洛黑亦宣布,將成立一策略聯盟,其中波洛黑擁有 70% 股權,雷諾股權則達 30%。該聯盟將於法國及歐洲其他地方提供車輛共享服務。另外並進行研究,以協助雷諾製造一款 3 人座、使用波洛黑所生產電池的電動車。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • 使用Kubernetes、K3s和Traefik2進行本地開發

    使用Kubernetes、K3s和Traefik2進行本地開發

    作者簡介

    Vyacheslav,擁有運維和項目管理經驗的軟件工程師

    這篇文章將承接我此前搭建的本地Docker開發環境,具體步驟已經放在在以下網址:

    https://github.com/Voronenko/traefik2-compose-template

    除了經典的docker化的項目之外,我還有其他的Kubernetes項目。儘管Kubernetes已經成為容器編排的事實標準,但是不得不承認Kubernetes是一個既消耗資源又消耗金錢的平台。由於我並不經常需要外部集群,因此我使用輕量級K3s發行版來進行Kubernetes本地開發。

    K3s是為IoT和邊緣計算而構建的經過認證的Kubernetes發行版之一,還能夠按產品規模部署到VM。

    我使用K3s的方式是這樣的:在我的工作筆記本上本地安裝K3s,儘管有時我需要在本地部署較重的測試工作負載,為此,我準備了兩個神器——兩個運行ESXi的外部Intel NUCs。

    默認情況下,K3s安裝Traefik 1.x作為ingress,如果你對此十分滿意,那麼無需往下繼續閱讀了。

    在我的場景中,我同時會牽涉到好幾個項目,特別是經典的docker和docker swarm,因此我經常遇到在獨立模式下部署Traefik的情況。

    因此,本文其餘部分將深入介紹如何將外部traefik2配置為K3s集群的ingress。

    安裝Kubernetes K3s系列集群

    你可以按照常規方式使用命令curl -sfL https://get.k3s.io | sh -安裝K3s,或者你可以使用輕量實用程序k3sup安裝(https://github.com/alexellis/k3sup)。具體步驟在之前的文章介紹過。

    與我們的設置不同的是,我們使用命令--no-deploy traefik專門安裝了不帶traefik組件的K3s。

    export CLUSTER_MASTER=192.168.3.100
    export CLUSTER_DEPLOY_USER=slavko
    k3sup install --ip $CLUSTER_MASTER --user $CLUSTER_DEPLOY_USER --k3s-extra-args '--no-deploy traefik'
    

    執行后,你將獲得使用kubectl所需的連接詳細信息。安裝K3s后,你可以快速檢查是否可以看到節點。

    # Test your cluster with - export path to k3s cluster kubeconfig:
    export KUBECONFIG=/home/slavko/kubeconfig
    kubectl get node -o wide
    

    注:這裏沒有固定的安裝模式,你甚至可以使用docker-compose自行啟動它。

    server:
      image: rancher/k3s:v0.8.0
      command: server --disable-agent --no-deploy traefik
      environment:
        - K3S_CLUSTER_SECRET=somethingtotallyrandom
        - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
        - K3S_KUBECONFIG_MODE=666
      volumes:
        # k3s will generate a kubeconfig.yaml in this directory. This volume is mounted
        # on your host, so you can then 'export KUBECONFIG=/somewhere/on/your/host/out/kubeconfig.yaml',
        # in order for your kubectl commands to work.
        - /somewhere/on/your/host/out:/output
        # This directory is where you put all the (yaml) configuration files of
        # the Kubernetes resources.
        - /somewhere/on/your/host/in:/var/lib/rancher/k3s/server/manifests
      ports:
        - 6443:6443
    
    node:
      image: rancher/k3s:v0.8.0
      privileged: true
      links:
        - server
      environment:
        - K3S_URL=https://server:6443
        - K3S_CLUSTER_SECRET=somethingtotallyrandom
      volumes:
        # this is where you would place a alternative traefik image (saved as a .tar file with
        # 'docker save'), if you want to use it, instead of the traefik:v2.0 image.
        - /sowewhere/on/your/host/custom-image:/var/lib/rancher/k3s/agent/images
    

    配置Traefik 2,與Kubernetes一起使用

    在文章開頭提到的鏈接中,我已經在我的系統中安裝了Traefik 2,並根據該鏈接內容,服務於一些需求。現在是時候配置Traefik 2 Kubernetes後端了。

    Traefik 2使用CRD(自定義資源定義)來完成這一點。定義的最新示例可以在以下鏈接中找到,但這些示例僅適用於Traefik 2也作為Kubernetes工作負載的一部分執行的情況:

    https://docs.traefik.io/reference/dynamic-configuration/kubernetes-crd/

    對於外部Traefik 2,我們僅需要以下描述的定義子集。

    我們引入一系列自定義資源定義,以允許我們來描述我們的Kubernetes服務將會如何暴露到外部,traefik-crd.yaml

    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: ingressroutes.traefik.containo.us
    
    spec:
      group: traefik.containo.us
      version: v1alpha1
      names:
        kind: IngressRoute
        plural: ingressroutes
        singular: ingressroute
      scope: Namespaced
    
    ---
    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: ingressroutetcps.traefik.containo.us
    
    spec:
      group: traefik.containo.us
      version: v1alpha1
      names:
        kind: IngressRouteTCP
        plural: ingressroutetcps
        singular: ingressroutetcp
      scope: Namespaced
    
    ---
    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: middlewares.traefik.containo.us
    
    spec:
      group: traefik.containo.us
      version: v1alpha1
      names:
        kind: Middleware
        plural: middlewares
        singular: middleware
      scope: Namespaced
    
    ---
    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: tlsoptions.traefik.containo.us
    
    spec:
      group: traefik.containo.us
      version: v1alpha1
      names:
        kind: TLSOption
        plural: tlsoptions
        singular: tlsoption
      scope: Namespaced
    
    ---
    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: traefikservices.traefik.containo.us
    
    spec:
      group: traefik.containo.us
      version: v1alpha1
      names:
        kind: TraefikService
        plural: traefikservices
        singular: traefikservice
      scope: Namespaced  
    

    同時,我們需要集群角色traefik-ingress-controller,以提供對服務、端點和secret的只讀訪問權限以及自定義的traefik.containo.us組,traefik-clusterrole.yaml

    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1beta1
    metadata:
      name: traefik-ingress-controller
    
    rules:
      - apiGroups:
          - ""
        resources:
          - services
          - endpoints
          - secrets
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - extensions
        resources:
          - ingresses
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - extensions
        resources:
          - ingresses/status
        verbs:
          - update
      - apiGroups:
          - traefik.containo.us
        resources:
          - middlewares
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - traefik.containo.us
        resources:
          - ingressroutes
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - traefik.containo.us
        resources:
          - ingressroutetcps
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - traefik.containo.us
        resources:
          - tlsoptions
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - traefik.containo.us
        resources:
          - traefikservices
        verbs:
          - get
          - list
          - watch
    

    最後,我們需要系統服務賬號traefik-ingress-controller與之前創建的集群角色traefik-ingress-controller相關聯。

    ---
    kind: ServiceAccount
    apiVersion: v1
    metadata:
      namespace: kube-system
      name: traefik-ingress-controller
    
    ---
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1beta1
    metadata:
      name: traefik-ingress-controller
    
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: traefik-ingress-controller
    subjects:
      - kind: ServiceAccount
        name: traefik-ingress-controller
        namespace: kube-system
    

    我們應用以上資源之後:

    apply:
      kubectl apply -f traefik-crd.yaml
      kubectl apply -f traefik-clusterrole.yaml
      kubectl apply -f traefik-service-account.yaml
    

    我們已經準備好開始調整Traefik 2

    將Traefik 2指向K3s集群

    根據Traefik文檔的建議,當Traefik部署到Kubernetes中時,它將讀取環境變量KUBERNETES_SERVICE_HOST和KUBERNETES_SERVICE_PORT或KUBECONFIG來構造端點。

    /var/run/secrets/kubernetes.io/serviceaccount/token中查找訪問token,而SSL CA證書將在/var/run/secrets/kubernetes.io/serviceaccount/ca.crt.中查找。當部署到Kubernetes內部時,兩者都會自動提供掛載。

    當無法找到環境變量時,Traefik會嘗試使用external-cluster客戶端連接到Kubernetes API server。這一情況下,需要設置endpoint。具體來說,可以將其設置為kubectl代理使用的URL,以使用相關的kubeconfig授予的身份驗證和授權連接到Kubernetes集群。

    Traefik 2可以使用任何受支持的配置類型來靜態配置-toml、yaml或命令行交換。

    [providers.kubernetesCRD]
      endpoint = "http://localhost:8080"
      token = "mytoken"
    
    providers:
      kubernetesCRD:
        endpoint = "http://localhost:8080"
        token = "mytoken"
        # ...
    
    --providers.kubernetescrd.endpoint=http://localhost:8080 
    --providers.kubernetescrd.token=mytoken
    

    第一次運行時,如果你在外部有Traefik,很有可能沒有traefik-ingress-controller訪問token來指定mytoken。那麼,你需要執行以下命令:

    # Check all possible clusters, as your .KUBECONFIG may have multiple contexts:
    kubectl config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'
    
    # Output kind of
    # Alias tip: k config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'
    # Cluster name  Server
    # default  https://127.0.0.1:6443
    
    # You are interested in: "default", if you did not name it differently
    
    # Select name of cluster you want to interact with from above output:
    export CLUSTER_NAME="default"
    
    # Point to the API server referring the cluster name
    export APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}")
    # usually https://127.0.0.1:6443
    
    # Gets the token value
    export TOKEN=$(kubectl get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='traefik-ingress-controller')].data.token}" --namespace kube-system|base64 --decode)
    
    # Explore the API with TOKEN
    

    如果成功了,你應該收到以下響應:

    {
      "kind": "APIVersions",
      "versions": [
        "v1"
      ],
      "serverAddressByClientCIDRs": [
        {
          "clientCIDR": "0.0.0.0/0",
          "serverAddress": "192.168.3.100:6443"
        }
      ]
    

    以及一些事實,如token:

    eyJhbGciOiJSUzI1NiIsImtpZCI6IjBUeTQyNm5nakVWbW5PaTRRbDhucGlPeWhlTHhxTXZjUDJsRmNacURjVnMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJ0cmFlZmlrLWluZ3Jlc3MtY29udHJvbGxlci10b2tlbi12emM3diIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJ0cmFlZmlrLWluZ3Jlc3MtY29udHJvbGxlciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImQ5NTc3ZTkxLTdlNjQtNGMwNi1iZDgyLWNkZTk0OWM4MTI1MSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTp0cmFlZmlrLWluZ3Jlc3MtY29udHJvbGxlciJ9.Mk8EBS4soO8uX-uSnV3o4qZKR6Iw6bgeSmPhHbJ2fjuqFgLnLh4ggxa-N9AqmCsEWiYjSi5oKAu986UEC-_kGQh3xaCYsUwlkM8147fsnwCbomSeGIct14JztVL9F8JwoDH6T0BOEjn-J9uY8-fUKYL_Y7uTrilhFapuILPsj_bFfgIeOOapRD0XshKBQV9Qzg8URxyQyfzl68ilm1Q13h3jLj8CFE2RlgEUFk8TqYH4T4fhfpvV-gNdmKJGODsJDI1hOuWUtBaH_ce9w6woC9K88O3FLKVi7fbvlDFrFoJ2iVZbrRALPjoFN92VA7a6R3pXUbKebTI3aUJiXyfXRQ
    

    根據上次響應的API server的外部地址:https://192.168.3.100:6443

    同樣,提供的token中沒有任何特殊之處:這是JWT的token,你可以使用https://jwt.io/#debugger-io,檢查它的內容。

    {
      "alg": "RS256",
      "kid": "0Ty426ngjEVmnOi4Ql8npiOyheLxqMvcP2lFcZqDcVs"
    }
    {
      "iss": "kubernetes/serviceaccount",
      "kubernetes.io/serviceaccount/namespace": "kube-system",
      "kubernetes.io/serviceaccount/secret.name": "traefik-ingress-controller-token-vzc7v",
      "kubernetes.io/serviceaccount/service-account.name": "traefik-ingress-controller",
      "kubernetes.io/serviceaccount/service-account.uid": "d9577e91-7e64-4c06-bd82-cde949c81251",
      "sub": "system:serviceaccount:kube-system:traefik-ingress-controller"
    }
    

    正確的配置非常重要,因此請確保對APISERVER的兩個調用均返回合理的響應。

    export APISERVER=YOURAPISERVER
    export TOKEN=YOURTOKEN
    
    curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
    
    curl -X GET $APISERVER/api/v1/endpoints --header "Authorization: Bearer $TOKEN" --insecure
    

    創建其他訪問token

    控制器循環確保每個服務賬戶都有一個帶有API token的secret,可以像我們之前那樣被發現。

    此外,你還可以為一個服務賬戶創建額外的token,創建一個ServiceAccountToken類型的secret,併為服務賬戶添加一個註釋,控制器會用生成的token來更新它。

    ---
    apiVersion: v1
    kind: Secret
    namespace: kube-system
    metadata:
      name: traefik-manual-token
      annotations:
        kubernetes.io/service-account.name: traefik-ingress-controller
    type: kubernetes.io/service-account-token
    
    # Any tokens for non-existent service accounts will be cleaned up by the token controller.
    
    # kubectl describe secrets/traefik-manual-token
    

    用以下命令創建:

    kubectl create -f ./traefik-service-account-secret.yaml
    kubectl describe secret traefik-manual-token
    

    刪除/無效:

    kubectl delete secret traefik-manual-token
    

    對外部traefik 2的更改構成定義

    我們需要在文章開頭給出的鏈接中獲得的traefik2配置進行哪些更改?

    https://github.com/Voronenko/traefik2-compose-template

    a) 我們在新文件夾kubernetes_data中存儲ca.crt文件,該文件用於驗證對Kubernetes授權的調用。這是可以在kubeconfig文件的clusters-> cluster-> certificate-authority-data下找到的證書。

    該volume將映射在/var/run/secrets/kubernetes.io/serviceaccount下以獲取官方Traefik 2鏡像

    volumes:
        ...
          - ./kubernetes_data:/var/run/secrets/kubernetes.io/serviceaccount
    

    b) 調整Traefik 2 kubernetescrd後端以提供3個參數:endpoint、證書路徑和token。請注意,作為外部Traefik作為docker容器,你需要指定正確的endpoint地址,並確保以安全的方式進行。

      - "--providers.kubernetescrd=true"
          - "--providers.kubernetescrd.endpoint=https://192.168.3.100:6443"
          - "--providers.kubernetescrd.certauthfilepath=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
          - "--providers.kubernetescrd.token=YOURTOKENWITHOUTANYQUOTES
    

    如果你都執行正確了,那麼你現在應該在Traefik UI上看到了一些希望。如果你沒有看到traefik,或者在運行Traefik時有問題,你可以查看之後的故障排除部分。

    現在是時候通過Trafik 2暴露一些Kubernetes服務了,以確保Traefik 2能夠作為ingress工作。讓我們來看經典案例whoami服務,whoami-service.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: whoami
    
    spec:
      ports:
        - protocol: TCP
          name: web
          port: 80
      selector:
        app: whoami
    
    ---
    kind: Deployment
    apiVersion: apps/v1
    metadata:
      namespace: default
      name: whoami
      labels:
        app: whoami
    
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: whoami
      template:
        metadata:
          labels:
            app: whoami
        spec:
          containers:
            - name: whoami
              image: containous/whoami
              ports:
                - name: web
                  containerPort: 80
    
    

    並且以http或https的方式暴露它,whoami.k.voronenko.net全限定域名下的whoami-ingress-route.yaml

    apiVersion: traefik.containo.us/v1alpha1
    kind: IngressRoute
    metadata:
      name: ingressroute-notls
      namespace: default
    spec:
      entryPoints:
        - web
      routes:
        - match: Host(`whoami.k.voronenko.net`)
          kind: Rule
          services:
            - name: whoami
              port: 80
    
    ---
    apiVersion: traefik.containo.us/v1alpha1
    kind: IngressRoute
    metadata:
      name: ingressroute-tls
      namespace: default
    spec:
      entryPoints:
        - websecure
      routes:
        - match: Host(`whoami.k.voronenko.net`)
          kind: Rule
          services:
            - name: whoami
              port: 80
      tls:
        certResolver: default
    

    然後應用它:

    kubectl apply -f whoami-service.yaml
      kubectl apply -f whoami-ingress-route.yaml
    

    應用后,你應該會在Traefik dashboard上看到一些希望,即KubernetesCRD後端。

    正如你所看到的,Traefik已經檢測到我們的K3s Kubernetes集群上運行的新工作負載,而且它與我們在同一個盒子上的經典Docker工作負載(如portainer)很好地共存。

    讓我們檢查一下Traefik 2是否將Traefik路由到了我們的Kubernetes工作負載:如你所見,你可以在http和https endpoint上成功地接觸到whoami工作負載,瀏覽器接受你的證書為可信任的“綠標籤”。

    我們的目標達到了!我們在本地筆記本上配置了Traefik 2。Traefik 2將你的docker或Kubernetes工作流暴露在http或https endpoint上。帶可選的 letsencrypt 的 Traefik 2 將負責 https。

    故障排查

    正如你所知,在配置過程可能存在多個問題,你可以考慮使用一些分析工具,如:

    https://github.com/Voronenko/dotfiles/blob/master/Makefile#L185

    我特別建議:

    a) VMWare octant:這是一個基於Web的功能強大的Kubernetes dashboard,你可以在上面使用你的kubeconfig

    b) Rakess:這是一個獨立工具也是一個kubectl插件,用於显示Kubernetes服務器資源的訪問矩陣(https://github.com/corneliusweig/rakkess)

    檢查系統賬戶的憑據

    rakkess --sa kube-system:traefik-ingress-controller
    

    c) kubectl

    檢查哪些角色與服務賬戶相關聯

    kubectl get clusterrolebindings -o json | jq -r '
      .items[] |
      select(
        .subjects // [] | .[] |
        [.kind,.namespace,.name] == ["ServiceAccount","kube-system","traefik-ingress-controller"]
      ) |
      .metadata.name'
    

    d) Traefik 文檔:例如kubernetescrd後端提供了更多配置開關的方式。

    --providers.kubernetescrd  (Default: "false")
            Enable Kubernetes backend with default settings.
        --providers.kubernetescrd.certauthfilepath  (Default: "")
            Kubernetes certificate authority file path (not needed for in-cluster client).
        --providers.kubernetescrd.disablepasshostheaders  (Default: "false")
            Kubernetes disable PassHost Headers.
        --providers.kubernetescrd.endpoint  (Default: "")
            Kubernetes server endpoint (required for external cluster client).
        --providers.kubernetescrd.ingressclass  (Default: "")
            Value of kubernetes.io/ingress.class annotation to watch for.
        --providers.kubernetescrd.labelselector  (Default: "")
            Kubernetes label selector to use.
        --providers.kubernetescrd.namespaces  (Default: "")
            Kubernetes namespaces.
        --providers.kubernetescrd.throttleduration  (Default: "0")
            Ingress refresh throttle duration
        --providers.kubernetescrd.token  (Default: "")
            Kubernetes bearer token (not needed for in-cluster client).
        --providers.kubernetesingress  (Default: "false")
            Enable Kubernetes backend with default settings.
        --providers.kubernetesingress.certauthfilepath  (Default: "")
            Kubernetes certificate authority file path (not needed for in-cluster client).
        --providers.kubernetesingress.disablepasshostheaders  (Default: "false")
            Kubernetes disable PassHost Headers.
        --providers.kubernetesingress.endpoint  (Default: "")
            Kubernetes server endpoint (required for external cluster client).
        --providers.kubernetesingress.ingressclass  (Default: "")
            Value of kubernetes.io/ingress.class annotation to watch for.
        --providers.kubernetesingress.ingressendpoint.hostname  (Default: "")
            Hostname used for Kubernetes Ingress endpoints.
        --providers.kubernetesingress.ingressendpoint.ip  (Default: "")
            IP used for Kubernetes Ingress endpoints.
        --providers.kubernetesingress.ingressendpoint.publishedservice  (Default: "")
            Published Kubernetes Service to copy status from.
        --providers.kubernetesingress.labelselector  (Default: "")
            Kubernetes Ingress label selector to use.
        --providers.kubernetesingress.namespaces  (Default: "")
            Kubernetes namespaces.
        --providers.kubernetesingress.throttleduration  (Default: "0")
            Ingress refresh throttle duration
        --providers.kubernetesingress.token  (Default: "")
            Kubernetes bearer token (not needed for in-cluster client).
    

    e) 確保Traefik有足夠的權限可以訪問apiserver endpoint

    如果你希望Traefik為你查詢信息:通過在配置中放置一些錯誤的apiserver地址,可以查看訪問的endpoint和查詢順序。有了這些知識和你的Traefik Kubernetes token,你就可以使用Traefik憑證檢查這些endpoint是否可以訪問。

    traefik_1    | E0421 12:30:12.624877       1 reflector.go:125] pkg/mod/k8s.io/client-go@v0.0.0-20190718183610-8e956561bbf5/tools/cache/reflector.go:98: Failed to list *v1.Endpoints: Get https://192.168.3.101:6443/api/v1/endpoints?limit=500&resourceVersion=0:
    traefik_1    | E0421 12:30:12.625341       1 reflector.go:125] pkg/mod/k8s.io/client-go@v0.0.0-20190718183610-8e956561bbf5/tools/cache/reflector.go:98: Failed to list *v1.Service: Get https://192.168.3.101:6443/api/v1/services?limit=500&resourceVersion=0:
    traefik_1    | E0421 12:30:12.625395       1 reflector.go:125] pkg/mod/k8s.io/client-go@v0.0.0-20190718183610-8e956561bbf5/tools/cache/reflector.go:98: Failed to list *v1beta1.Ingress: Get https://192.168.3.101:6443/apis/extensions/v1beta1/ingresses?limit=500&resourceVersion=0:
    traefik_1    | E0421 12:30:12.625449       1 reflector.go:125] pkg/mod/k8s.io/client-go@v0.0.0-20190718183610-8e956561bbf5/tools/cache/reflector.go:98: Failed to list *v1alpha1.Middleware: Get https://192.168.3.101:6443/apis/traefik.containo.us/v1alpha1/middlewares?limit=500&resourceVersion=0:
    traefik_1    | E0421 12:30:12.625492       1 reflector.go:125] pkg/mod/k8s.io/client-go@v0.0.0-20190718183610-8e956561bbf5/tools/cache/reflector.go:98: Failed to list *v1alpha1.IngressRoute: Get https://192.168.3.101:6443/apis/traefik.containo.us/v1alpha1/ingressroutes?limit=500&resourceVersion=0:
    traefik_1    | E0421 12:30:12.625531       1 reflector.go:125] pkg/mod/k8s.io/client-go@v0.0.0-20190718183610-8e956561bbf5/tools/cache/reflector.go:98: Failed to list *v1alpha1.TraefikService: Get https://192.168.3.101:6443/apis/traefik.containo.us/v1alpha1/traefikservices?limit=500&resourceVersion=0:
    traefik_1    | E0421 12:30:12.625572       1 reflector.go:125] pkg/mod/k8s.io/client-go@v0.0.0-20190718183610-8e956561bbf5/tools/cache/reflector.go:98: Failed to list *v1alpha1.TLSOption: Get https://192.168.3.101:6443/apis/traefik.containo.us/v1alpha1/tlsoptions?limit=500&resourceVersion=0:
    traefik_1    | E0421 12:30:12.625610       1 reflector.go:125] pkg/mod/k8s.io/client-go@v0.0.0-20190718183610-8e956561bbf5/tools/cache/reflector.go:98: Failed to list *v1alpha1.IngressRouteTCP: Get https://192.168.3.101:6443/apis/traefik.containo.us/v1alpha1/ingressroutetcps?limit=500&resourceVersion=0:
    

    f) 記錄K3s本身

    安裝腳本將自動檢測你的操作系統是使用systemd還是openrc並啟動服務。使用openrc運行時,將在/var/log/k3s.log中創建日誌。使用systemd運行時,將在/var/log/syslog中創建日誌,並使用journalctl -u k3s查看。

    在那裡,你可能會得到一些提示,例如:

    кві 21 15:42:44 u18d k3s[612]: E0421 15:42:44.936960     612 authentication.go:104] Unable to authenticate the request due to an error: invalid bearer token
    

    這將為你提供有關K8s Traefik起初使用時出現問題的線索,Enjoy your journey!

    相關代碼你可以在以下鏈接中找到:

    https://github.com/Voronenko/k3s-mini

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • Java 多線程基礎(十一)線程優先級和守護線程

     Java 多線程基礎(十一)線程優先級和守護線程

    一、線程優先級

    Java 提供了一個線程調度器來監控程序啟動後進去就緒狀態的所有線程。線程調度器通過線程的優先級來決定調度哪些線程執行。一般來說,Java的線程調度器採用時間片輪轉算法使多個線程輪轉獲得CPU的時間片。然而根據實際情況,每個線程的重要程序也不相同,有時候我們想讓一些線程優先執行,那麼我們可以將他的優先級調高一下,這樣它們獲得的時間片會多一些。

    多個線程處於就緒狀態時,若這些線程的優先級相同,則線程調度器會按時間片輪轉方式或獨佔方式來分配線程的執行時間。

    java 中的線程優先級的範圍是1~10,默認的優先級是5。“高優先級線程”會優先於“低優先級線程”執行。

    Java中線程優先級分為三個級別:

    • 低優先級:1~4,其中類變量 Thread.MIN_PRORITY 最低,數值為1;
    • 默認優先級:如果一個線程沒有指定優先級,默認優先級為5,由類變量 Thread.NORM_PRORITY表示;
    • 高優先級:6~10,類變量 Thread.MAX_PRORITY 最高,數值為10。

    注意:具有相同優先級的多個線程,若它們都為高優先級Thread.MAX_PRORITY,則每個線程都是獨佔式的,也就是這些線程將被順序執行;若它們優先級不是高優先級,則這些線程將被同時執行,可以說是無序執行。

    java 中有兩種線程:用戶線程和守護線程。可以通過 isDaemon() 方法來區別它們:如果返回 false,則說明該線程是“用戶線程”;否則就是“守護線程”。
    用戶線程一般用戶執行用戶級任務,而守護線程也就是“後台線程”,一般用來執行後台任務。需要注意的是:Java虛擬機在“用戶線程”都結束後會後退出。

    JDK中對用戶線程與守護線程的解釋:

    每個線程都有一個優先級。“高優先級線程”會優先於“低優先級線程”執行。每個線程都可以被標記為一個守護進程或非守護進程。在一些運行的主線程中創建新的子線程時,
    子線程的優先級被設置為等於“創建它的主線程的優先級”,當且僅當“創建它的主線程是守護線程”時“子線程才會是守護線程”。
    當Java虛擬機啟動時,通常有一個單一的非守護線程(該線程通過是通過main()方法啟動)。JVM會一直運行直到下面的任意一個條件發生,JVM就會終止運行:
    ①、調用了exit()方法,並且exit()有權限被正常執行。 ②、所有的“非守護線程”都死了(即JVM中僅僅只有“守護線程”)。
    每一個線程都被標記為“守護線程”或“用戶線程”。當只有守護線程運行時,JVM會自動退出。
     

    二、線程優先級示例

    public class Demo01 {
        private static Object obj = new Object();
        public static void main(String[] args) {
            
            Thread t1 = new ThreadA("t1");
            Thread t2 = new ThreadA("t2");
            
            t1.setPriority(1); // 設置優先級為1
            t2.setPriority(10);// 設置優先級為10
            
            t1.start();
            t2.start();
        }
    }
    class ThreadA extends Thread{
        public ThreadA(String name) {
            super(name);
        }
        public void run() {
            for(int i = 0;i < 3;i++)
                System.out.println(Thread.currentThread().getName() + " [ " 
                        + Thread.currentThread().getPriority() + " ] loop " + i);  
        }
    }
    // 運行結果
    t1 [ 1 ] loop 0
    t2 [ 10 ] loop 0
    t2 [ 10 ] loop 1
    t2 [ 10 ] loop 2
    t1 [ 1 ] loop 1
    t1 [ 1 ] loop 2

    說明:

    ①、主線程main的優先級是5。
    ②、t1的優先級被設為1,而t2的優先級被設為10。cpu在執行t1和t2的時候,根據時間片輪循調度,所以能夠併發執行。

    三、守護線程示例

    public class Demo {
        public static void main(String[] args) {
    
            System.out.println(Thread.currentThread().getName()
                    +" [ isDaemon = "+Thread.currentThread().isDaemon()+ " ]");
            Thread t1=new ThreadA("t1");    
            Thread t2=new MyDaemon("t2");    
            t2.setDaemon(true);// 設置t2為守護線程
            t1.start();                        
            t2.start();                        
        }
    }
    class ThreadA extends Thread{
        public ThreadA(String name) {
            super(name);
        }
    
        public void run(){
            try {
                for (int i=0; i<5; i++) {
                    Thread.sleep(3);
                    System.out.println(this.getName() +"[ isDaemon = "+this.isDaemon()+ " ] " + "loop " + i);
                }
            } catch (InterruptedException e) {
            }
        }
    };
    
    class MyDaemon extends Thread{
        public MyDaemon(String name) {
            super(name);
        }
    
        public void run(){
            try {
                for (int i=0; i<10000; i++) {
                    Thread.sleep(1);
                    System.out.println(this.getName() +"[ isDaemon = " + this.isDaemon() +  " ] " +"loop "+i);
                }
            } catch (InterruptedException e) {
            }
        }
    }
    // 運行結果
    main [ isDaemon = false ]
    t2[ isDaemon = true ] loop 0
    t2[ isDaemon = true ] loop 1
    t2[ isDaemon = true ] loop 2
    t1[ isDaemon = false ] loop 0
    t2[ isDaemon = true ] loop 3
    t2[ isDaemon = true ] loop 4
    t1[ isDaemon = false ] loop 1
    t2[ isDaemon = true ] loop 5
    t2[ isDaemon = true ] loop 6
    t2[ isDaemon = true ] loop 7
    t1[ isDaemon = false ] loop 2
    t2[ isDaemon = true ] loop 8
    t2[ isDaemon = true ] loop 9
    t2[ isDaemon = true ] loop 10
    t1[ isDaemon = false ] loop 3
    t2[ isDaemon = true ] loop 11
    t2[ isDaemon = true ] loop 12
    t2[ isDaemon = true ] loop 13
    t2[ isDaemon = true ] loop 14
    t1[ isDaemon = false ] loop 4
    t2[ isDaemon = true ] loop 15

    說明:

    ①、主線程main是用戶線程,它創建的子線程t1也是用戶線程。
    ②、t2 是守護線程。在“主線程main”和“子線程t1”(它們都是用戶線程)執行完畢,只剩t2這個守護線程的時候,JVM自動退出。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※教你寫出一流的銷售文案?

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※回頭車貨運收費標準

    ※別再煩惱如何寫文案,掌握八大原則!

    ※超省錢租車方案

  • Linux下9種優秀的代碼比對工具推薦

    Linux下9種優秀的代碼比對工具推薦

    大家好,我是良許。

    在我們編寫代碼的時候,我們經常需要知道兩個文件之間,或者同一個文件不同版本之間有什麼差異性。在 Windows 下有個很強大的工具叫作 BeyondCompare ,那在 Linux 下需要用到什麼工具呢?

    本文介紹 9 種 Linux 下常用的 9 種代碼比對工具,不僅有命令行工具,還有 GUI 界面工具,讓你輕鬆進行代碼比對。

    1. diff命令

    diff 命令是 Linux 下自帶的一個強大的文本比對工具,而且使用起來非常方便。對於它的使用,我之前也單獨寫過一篇文章介紹,點擊下方鏈接可以查看。

    教你一招Linux下文本比對方法

    diff 命令在大多數的 Linux 發行版里已經預裝了,它可以逐行比對兩個文本文件,並輸出它們的差異點。更多介紹可以直接查看它的 man 手冊。

    $ man diff
    

    但是,diff 命令雖然強大,但它的輸出結果實在是太感人了,不直觀也不清晰。於是,有大佬為了彌補這個缺點,基於 diff 開發了更強大的工具。這裏推薦兩個:colordiffwdiff

    colordiff命令

    colordiff 是一個 Perl 腳本工具,它的輸出結果和 diff 命令一樣,但是會給代碼着色,並且具有語法高亮功能。同時,你如果不喜歡它的默認顏色的話,還可以自定義主題。

    你可以自行安裝 colordiff 到你的電腦,根據不同的發行版選擇不同的安裝命令。

    $ yum install colordiff             [On CentOS/RHEL/Fedora]
    $ dnf install colordiff             [On Fedora 23+ version]
    $ sudo apt-get install colordiff    [On Debian/Ubuntu/Mint]
    

    同樣,你可以使用 man 命令查看它的幫助文檔:

    $ man colordiff
    
    wdiff命令

    diff 命令是逐行比較差異,而 wdiff 更變態,是逐字比較。所以如果你的文本只是修改了少數一些詞語的話,使用 wdiff 命令將更加高效。

    安裝命令如下:

    $ yum install wdiff             [On CentOS/RHEL/Fedora]
    $ dnf install wdiff             [On Fedora 23+ version]
    $ sudo apt-get install wdiff    [On Debian/Ubuntu/Mint]
    

    更詳細內容可以查看它的 man 手冊。

    $ man wdiff
    

    2. vimdiff命令

    vimdiff 等同於 vim -d 命令,即 Vim 編輯器的 diff 模式。

    該命令後面通常會接兩個或多個文件名作為參數,這些文件會同時在 Vim 編輯器的分割窗口中打開,並高亮显示文件中內容有差異的部分。

    它的中文主頁是:http://vimcdoc.sourceforge.net/doc/diff.html

    以上介紹的兩款是 Linux 命令行的對比工具,我們再來看一些 GUI 比對工具。

    3. Kompare

    Kompare 是基於 diff 的一個 GUI 工具,使用者可以很方便看到文件之間的差異,並且支持合併這些差異。

    Kompare 的特性有如下:

    • 支持多種 diff 格式;
    • 支持目錄之間的比對;
    • 支持讀取 diff 文件;
    • 自定義界面;
    • 創建及應用源文件的 patch 文件。

    該工具的主頁為:https://www.kde.org/applications/development/kompare/

    4. DiffMerge

    DiffMerge 是一個跨平台的 GUI 文本比對工具,具有 Linux ,Windows ,macOS 三大平台版本。我們知道,BeyondCompare 是一款收費軟件,所以如果你們公司的版權要求比較高的話,不妨考慮一下 DiffMerge工具。

    DiffMerge 具有兩大功能:1. 圖示化显示兩個文件之間的改變。包含內部行高亮和完整的編輯支持。2. 圖示化显示三個文件之間的改變。允許自動合併(當可以安全操作時)和對結果文件完全編輯控制。

    它具有以下特性:

    • 支持文件夾比對;
    • 集成文件瀏覽器;
    • 高度可配置。

    該工具的主頁為:https://sourcegear.com/diffmerge/

    5. Meld

    Meld 是一個輕量級 GUI 代碼比對工具,它支持用戶比對文件、目錄,並且高度集成版本控制軟件。但針對軟件開發人員,它的以下幾個特性尤為吸引人:

    • 執行雙向和三向差異併合並
    • 輕鬆地在差異和衝突之間導航
    • 逐個文件地比較兩個或三個目錄,显示新文件,缺失文件和更改文件
    • 支持許多版本控制系統,包括 Git,Mercurial,Bazaar 和 SVN 等。

    它的官網為:http://meldmerge.org/

    6. Diffuse

    Diffuse 是另外一款很受歡迎的,免費,小巧,也十分簡單的 GUI 文本差異比對合併工具,它是用 Python 寫成的,具有兩個主要功能:文件比對及版本控制,允許文件編輯、合併,並且輸出兩個文件的差異點。

    你可以使用它查看文本比對小結,使用鼠標選擇文件里的某行進行編輯。它的其它特性包括:

    • 語法高亮
    • 快捷鍵便於文本導航
    • 無限次撤銷
    • 支持 unicode 編碼文件
    • 支持許多版本控制系統,包括 Git,Mercurial,Bazaar 和 SVN 等。

    它的官網為:http://diffuse.sourceforge.net/

    7. XXdiff

    XXdiff 是一款免費、強大的文件及文件夾差異比對及合併工具,它可以運行在很多類 Unix 系統上。不過它有個限制就是它不支持 unicode 文件,也沒法辦法直接編輯文件。

    它具有以下特性:

    • 遞歸對比文件及文件夾
    • 高亮显示差異點
    • 合併差異點,導出結果
    • 支持外部 diff 工具,比如:GNU diff,SIG diff ,Cleareddiff ,以及其它更多工具
    • 支持腳本拓展

    8. KDiff3

    KDiff3 是另外一種很強大的跨平台差異比對及合併工具,它是由 KDevelop 開發而成,可以在所有類 Unix 平台上運行,包括 Linux ,Mac OS ,Windows 等。

    它可以比對或合併兩到三個文件或目錄,具有以下特性:

    • 可以逐句、逐字對比差異
    • 支持自動合併
    • 內置編輯器,可以手動解決衝突
    • 支持 unicode ,UTF-8 等各種編碼格式
    • 支持打印差異

    它的官網為: http://kdiff3.sourceforge.net/

    9. TkDiff

    TkDiff 是另外一種跨平台,易於使用的 GUI 文本比對工具,可以運行在 Linux ,Windows 及 MacOS 系統上。它同樣提供一個左右分開的界面,用於查看對比的兩個文件。

    但是,它也有一些其它文本對比工具沒有的功能,比如差異書籤,以及一個便於快速定位導航差異點的導航圖。

    它的官網為:https://sourceforge.net/projects/tkdiff/

    公眾號:良許Linux

    有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

    ※別再煩惱如何寫文案,掌握八大原則!

    ※回頭車貨運收費標準

    ※教你寫出一流的銷售文案?

  • 大陸代工給鴻海?特斯拉駁斥傳言

    美國電動車廠特斯拉交貨能力大幅提升,出現逾 7 成的成長,面對會否將代工業務拉回大陸時,特斯拉卻傾向冷處理,甚至還主動發出官方回應,強調「目前沒有在大陸代工生產的計畫」,看來先前外界傳出鴻海要在中國替 Tesla 代工電動車的合作計畫,恐怕還要再等一等。   特斯拉 2013 年共交付 2 萬輛 Model S,2014 年有機會上看 3.5 萬輛,市場將特斯拉交貨能力大增原因指向生產線的擴展,特斯拉美國生產基地最近又新增了一條生產線,周產能達 1,000 輛。   除了在美擴充生產線,會否將代工產線拉回大陸,也成了市場關注焦點,但對此,特斯拉卻傾向冷處理,還特別發出官方回應,強調「特斯拉現所有在華銷售的車輛都是在美國生產,再進口到中國,目前沒有在大陸代工生產的計畫。」  

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準