標籤: iphone維修

  • 這 10 行比較字符串相等的代碼給我整懵逼了,不信你也來看看

    這 10 行比較字符串相等的代碼給我整懵逼了,不信你也來看看

    抱歉用這種標題吸引你點進來了,不過你不妨看完,看看能否讓你有所收穫。​(有收穫,請評論區留個言,沒收穫,下周末我直播吃**,哈哈,這你也信)

    補充說明:微信公眾號改版,對各個號主影響還挺大的。目前從後台數據來看,對我影響不大,因為我這反正都是小號,閱讀量本身就少的可憐,真相了,狗頭(剛從交流群學會的表情)。

    先直接上代碼:

    boolean safeEqual(String a, String b) {
       if (a.length() != b.length()) {
           return false;
       }
       int equal = 0;
       for (int i = 0; i < a.length(); i++) {
           equal |= a.charAt(i) ^ b.charAt(i);
       }
       return equal == 0;
    }

    上面的代碼是我根據原版(Scala)翻譯成 Java的,Scala 版本(最開始吸引程序猿石頭注意力的代碼)如下:

    def safeEqual(a: String, b: String) = {
      if (a.length != b.length) {
        false
      } else {
        var equal = 0
        for (i <- Array.range(0, a.length)) {
          equal |= a(i) ^ b(i)
        }
        equal == 0
      }
    }

    剛開始看到這段源碼感覺挺奇怪的,這個函數的功能是比較兩個字符串是否相等,首先“長度不等結果肯定不等,立即返回”這個很好理解。

    再看看後面的,稍微動下腦筋,轉彎下也能明白這其中的門道:通過異或操作1^1=0, 1^0=1, 0^0=0,來比較每一位,如果每一位都相等的話,兩個字符串肯定相等,最後存儲累計異或值的變量equal必定為 0,否則為 1。

    再細想一下呢?

    for (i <- Array.range(0, a.length)) {
      if (a(i) ^ b(i) != 0// or a(i) != b[i]
        return false
    }

    我們常常講性能優化,從效率角度上講,難道不是應該只要中途發現某一位的結果不同了(即為1)就可以立即返回兩個字符串不相等了嗎?(如上所示)

    這其中肯定有……

    再再細想一下呢?

    結合方法名稱 safeEquals 可能知道些眉目,與安全有關。

    本文開篇的代碼來自playframewok 里用來驗證cookie(session)中的數據是否合法(包含簽名的驗證),也是石頭寫這篇文章的由來。

    以前知道通過延遲計算等手段來提高效率的手段,但這種已經算出結果卻延遲返回的,還是頭一回!

    我們來看看,JDK 中也有類似的方法,如下代碼摘自 java.security.MessageDigest

    public static boolean isEqual(byte[] digesta, byte[] digestb) {
       if (digesta == digestb) return true;
       if (digesta == null || digestb == null) {
           return false;
       }
       if (digesta.length != digestb.length) {
           return false;
       }

       int result = 0;
       // time-constant comparison
       for (int i = 0; i < digesta.length; i++) {
           result |= digesta[i] ^ digestb[i];
       }
       return result == 0;
    }

    看註釋知道了,目的是為了用常量時間複雜度進行比較。

    但這個計算過程耗費的時間不是常量有啥風險? (腦海里響起了背景音樂:“小朋友,你是否有很多問號?”)

    真相大白

    再深入探索和了解了一下,原來這麼做是為了防止計時攻擊(Timing Attack)。(也有人翻譯成時序攻擊​)​

    計時攻擊(Timing Attack)

    計時攻擊是邊信道攻擊(或稱”側信道攻擊”, Side Channel Attack, 簡稱SCA) 的一種,邊信道攻擊是一種針對軟件或硬件設計缺陷,走“歪門邪道”的一種攻擊方式。

    這種攻擊方式是通過功耗、時序、電磁泄漏等方式達到破解目的。在很多物理隔絕的環境中,往往也能出奇制勝,這類新型攻擊的有效性遠高於傳統的密碼分析的數學方法(某百科上說的)。

    這種手段可以讓調用 safeEquals("abcdefghijklmn", "xbcdefghijklmn") (只有首位不相同)和調用 safeEquals("abcdefghijklmn", "abcdefghijklmn") (兩個完全相同的字符串)的所耗費的時間一樣。防止通過大量的改變輸入並通過統計運行時間來暴力破解出要比較的字符串。

    舉個,如果用之前說的“高效”的方式來實現的話。假設某個用戶設置了密碼為 password,通過從a到z(實際範圍可能更廣)不斷枚舉第一位,最終統計發現 p0000000 的運行時間比其他從任意a~z的都長(因為要到第二位才能發現不同,其他非 p 開頭的字符串第一位不同就直接返回了),這樣就能猜測出用戶密碼的第一位很可能是p了,然後再不斷一位一位迭代下去最終破解出用戶的密碼。

    當然,以上是從理論角度分析,確實容易理解。但實際上好像通過統計運行時間總感覺不太靠譜,這個運行時間對環境太敏感了,比如網絡,內存,CPU負載等等都會影響。

    但安全問題感覺更像是 “寧可信其有,不可信其無”。為了防止(特別是與簽名/密碼驗證等相關的操作)被 timing attack,目前各大語言都提供了相應的安全比較函數。各種軟件系統(例如 OpenSSL)、框架(例如 Play)的實現也都採用了這種方式。

    例如 “世界上最好的編程語言”(粉絲較少,評論區應該打不起架來)—— php中的:

    // Compares two strings using the same time whether they're equal or not.
    // This function should be used to mitigate timing attacks; 
    // for instance, when testing crypt() password hashes.
    bool hash_equals ( string $known_string , string $user_string )

    //This function is safe against timing attacks.
    boolean password_verify ( string $password , string $hash )

    其實各種語言版本的實現方式都與上面的版本差不多,將兩個字符串每一位取出來異或(^)並用或(|)保存,最後通過判斷結果是否為 0 來確定兩個字符串是否相等。

    如果剛開始沒有用 safeEquals 去實現,後續的版本還會通過打補丁的方式去修復這樣的安全隱患。

    例如 JDK 1.6.0_17 中的Release Notes[1]中就提到了MessageDigest.isEqual 中的bug的修復,如下圖所示:

    MessageDigest timing attack vulnerabilities

    大家可以看看這次變更的的詳細信息openjdk中的 bug fix diff[2]為:

    MessageDigest.isEqual計時攻擊

    Timing Attack 真的可行嗎?

    我覺得各大語言的 API 都用這種實現,肯定還是有道理的,理論上應該可以被利用的。 這不,學術界的這篇論文就宣稱用這種計時攻擊的方法破解了 OpenSSL 0.9.7 的RSA加密算法了。關於 RSA 算法的介紹可以看看之前本人寫的這篇文章。

    這篇Remote Timing Attacks are Practical[3] 論文中指出(我大致翻譯下摘要,感興趣的同學可以通過文末鏈接去看原文):

    計時攻擊往往用於攻擊一些性能較弱的計算設備,例如一些智能卡。我們通過實驗發現,也能用於攻擊普通的軟件系統。本文通過實驗證明,通過這種計時攻擊方式能夠攻破一個基於 OpenSSL 的 web 服務器的私鑰。結果證明計時攻擊用於進行網絡攻擊在實踐中可行的,因此各大安全系統需要抵禦這種風險。

    最後,本人畢竟不是專研完全方向,以上描述是基於本人的理解,如果有不對的地方,還請大家留言指出來。感謝。

    補充說明2:感謝正在閱讀文章的你,讓我還有動力繼續堅持更新原創。

    本人發文不多,但希望寫的文章能達到的目的是:佔用你的閱讀時間,就盡量能夠讓你有所收穫。

    如果你覺得我的文章有所幫助,還請你幫忙轉發分享,另外請別忘了點擊公眾號右上角加個星標,好讓你別錯過後續的精彩文章(微信改版了,或許我發的文章都不能推送到你那了)。

    ​原創真心不易,希望你能幫我個小忙唄,如果本文內容你覺得有所啟發,有所收穫,請幫忙點個“在看”唄,或者轉發分享讓更多的小夥伴看到。 ​ 參考資料:

    • Timing Attacks on RSA: Revealing Your Secrets through the Fourth Dimension
    • Remote Timing Attacks are Practical

     

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 3dTiles 數據規範詳解[1] 介紹

    3dTiles 數據規範詳解[1] 介紹

    版權:轉載請帶原地址。https://www.cnblogs.com/onsummer/p/12799366.html @秋意正寒

    Web中的三維

    html5和webgl技術使得瀏覽器三維變成了可能。

    巧婦難為無米之炊,三維數據(三維模型)是三維可視化重要的一環,事實上就是:三維數據眾多,行業跨界廣。

    參考資料:http://www.bgteach.com/article/132

    three.js的各種加載器實現了大部分通用三維格式的加載,屏蔽了格式不同的數據結構差異。

    然而,這樣還是不能滿足日益增長的效果需求,比如場景一大,模型文件體積變大,解析所耗費的時間越來越長。

    webgl,包括所有gpu有關的圖形渲染編程,幾乎只認這樣的三維數據:頂點、頂點顏色、頂點法線、着色語言…

    所以,三維圖形界的通用格式:glTF應運而生,它面向終點,它按照圖形編程所需的格式來存儲數據,藉以二進制編碼提高傳輸速度。

    它不再使用面向對象的思維存儲三維模型、貼圖紋理,而是按顯卡的思維存儲,存的是頂點、法線、頂點顏色等最基礎的信息,只不過組織結構上進行了精心的設計。

    它面向終點,就意味着可編輯性差,因為渲染性能的提高犧牲了可編輯性,它不再像3ds、dae甚至是max、skp一樣容易編輯和轉換。

    事實上,大多數三維軟件提供了glTF格式的轉換,或多一步,或一步到位。

    地理真三維

    早年,地理的三維還處於地形三維上,即数字高程模型(DEM)提供地表的高度拉伸。柵格高程數據、等高線、不規則三角網等均是数字高程模型的具體案例。
    下圖是不規則三角網,也即所謂的三角面片(圖形渲染中很常見):

    隨着學科的融合、計算機技術和硬件的更新換代,使得有模型、有細節的真三維融入到GIS中成為了可能,或者說,計算機技術和硬件的升級,給GIS以更廣闊的視角觀察世界。

    cesium.js 號稱是 webgl 封裝的三維地理庫,是支持 gltf 模型的加載的。

    面對大規模精細三維數據的加載,還要照顧到GIS的各種坐標系統、分析計算,gltf這種單個模型的方案顯得力不從心。

    2016年,Cesium 團隊借鑒傳統2DGIS的地圖規範——WMTS,借鑒圖形學中的層次細節模型,打造出大規模的三維數據標準—— 3d-Tiles,中文譯名:三維瓦片。

    它在模型上利用了 gltf 渲染快的特點,對大規模的三維數據進行組織,包括層次細節模型、模型的屬性數據、模型的層級數據等。

    3dTiles的設計思想

    3dTiles 繼承了 gltf 的優點:貼合圖形渲染 API 的邏輯,討 GPU 喜愛,webgl 對其內部組織起來的三維模型數據,不需要轉換,可以直接渲染(glTF 的功勞)。

    關於 glTF 是如何嵌入到 3dTiles 中的,開篇不談,後續精講。

    我們區分一組概念:規範和實現。

    3dTiles 是一種規範,在規範的指導下,各種資源文件可以是獨立存在於硬盤中的目錄、文件,也可以以二進制形式寫入數據庫中。目前,3dTiles 的官方實現只有 “散列文件”,也就是文件、文件夾的形式存儲在硬盤中,有關如何存儲到數據庫中的討論,官方仍在進行中(截至發博客)。

    glTF 也是一種規範,它的數據文件不一定就是後綴名為 .gltf 的文件,也不一定只有一個文件(glTF 的文件還可以是二進制文件、紋理貼圖文件等,扯遠了哈)。
    在本文,會嚴格指明是數據還是數據標準,如果我說的是 “XXX文件(例如 Bird.glb 文件)” ,那就是在指特定的文件。

    3dTiles還有一個特點:那就是不記錄模型數據,只記錄各級“Tile”的邏輯關係,以及“Tile”自己的屬性信息。所謂的模型數據,是指三維模型的頂點、貼圖材質、法線、顏色等信息。邏輯關係是指,各級Tile是如何在空間中保持連續的,LOD是如何組織的。屬性信息就很簡單啦,門有門的生產商,窗戶有窗戶的使用年限等,往大了說,建築還有它自己的壽命、法人、施工單位等屬性信息。

    3dTiles的特點總結如下:

    • 三維模型使用了 glTF 規範,繼承它的渲染高性能
    • 除了嵌入的 glTF,3dTiles 自己 只記錄各級Tile的空間邏輯關係(如何構成整個3dtiles)和屬性信息,以及模型與屬性如何掛接在一起的信息

    我覺得你還是雲里霧裡的,下一節將展示3dTiles具體數據,說說3dTiles的組織結構,說說3dTiles中的”Tile”,也就是“三維瓦片數據”中的“瓦片”是什麼。

    3dTiles系列博客最終目錄:

    01 引入與博客目錄:3dTiles 數據規範詳解

    02 Tileset與Tile

    03 內嵌在瓦片文件中的兩大數據表

    04.1 B3dm 類型

    04.2 I3dm 類型

    04.3 Pnts 類型

    04.4 Cmpt 類型

    04.5 未發布的瓦片規範

    05 3dTiles強大的擴展能力

    06 優缺點

    07 與I3S比較

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 為疫苗研發犧牲? 保育團體憂:25萬尾鯊魚恐間接受害

    摘錄自2020年9月29日自由時報報導

    為盡速結束武漢肺炎(COVID-19)疫情所帶來的災難,各國無不卯足全力研發疫苗,然而近期有研發廠商為了讓疫苗的效果穩定,決定在疫苗內添加高效的醫學物質「鯊烯」(Squalene),然而鯊烯這種物質相當稀少,美國有鯊魚保育團體推估,光是製造提供給美國使用的疫苗量,恐就要殺死超過2.1萬條鯊魚,若是全世界範圍,「可能要殺害25萬條鯊魚」。

    綜合外媒報導,「鯊烯」主要從鯊魚的肝油中提煉而出,在醫學與美容方面都是極為重要的高效素質,不僅可以滋潤皮膚,也可以提高、增強免疫力,加在疫苗中可使疫苗的效力提升,然而鯊魚肝油中提煉出的鯊烯十分稀少,平均一噸的鯊烯大約需要3000條鯊魚。

    英國製藥大廠葛蘭素史克(GlaxoSmithKline)專門生產用於流感疫苗中的以鯊烯製成的佐劑,而葛蘭素史克曾表示,將在2021年生產10億個以鯊烯製成的「武漢肺炎疫苗用佐劑」,引起保育團體的憂慮;位在美國加州的鯊魚保育團體鯊魚同盟(Shark Allies)表示,如果全世界的人都會接種這種加入佐劑的疫苗,「那可能要殺害25萬條鯊魚」,如果再算上二次接種,數量還會翻倍到50萬條。

    海洋
    國際新聞
    鯊魚

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

  • 多應用下 Swagger 的使用,這可能是最好的方式!

    多應用下 Swagger 的使用,這可能是最好的方式!

    問題

    微服務化的時代,我們整個項目工程下面都會有很多的子系統,對於每個應用都有暴露 Api 接口文檔需要,這個時候我們就會想到 Swagger 這個優秀 jar 包。但是我們會遇到這樣的問題,假如說我們有5個應用,難道說我們每個模塊下面都要去引入這個 jar 包嗎?我作為一個比較懶的程序感覺這樣好麻煩,於是乎我思考了一種我認為比較好的方式,如果大家覺得有什麼不太好的地方希望指正,謝謝!

    基礎

    開始之前大家首先要了解一些基礎,主要有以下幾個方面:

    1. 單應用下 Swagger 的集成與使用
    2. 條件裝配 @Conditional 介紹
    3. 配置文件參數獲取 @ConfigurationProperties
    單體應用下 Swagger 集成與使用

    關於這部分從3方面講起分別是:什麼是、為什麼、如何用

    什麼是 Swagger ?

    Swagger 是一個規範且完整的框架,用於生成、描述、調用和可視化 RESTful 風格的 Web 服務。

    為什麼使用 Swagger ?

    主要的優點:

    1. 支持 API 自動生成同步的在線文檔:使用 Swagger 后可以直接通過代碼生成文檔,不再需要自己手動編寫接口文檔了,對程序員來說非常方便,可以節約寫文檔的時間去學習新技術。
    2. 提供 Web 頁面在線測試 API:光有文檔還不夠,Swagger 生成的文檔還支持在線測試。參數和格式都定好了,直接在界面上輸入參數對應的值即可在線測試接口。

    缺點的話就是但凡引入一個 jar 需要去了解下原理和使用,對於這個缺點我感覺相比於優點就是大巫見小巫,我簡單看了一下源碼,其實不算太難。

    如何使用 Swagger

    關於 Swagger 的使用其實也就是3板斧,大家一定很熟悉的;

    第一板斧就是引入 jar 包,這裏我使用的是2.9.2版本

            <!-- swagger 相關 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger2.version}</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger2.version}</version>
            </dependency>

    第二板斧就是SpringBoot自動掃描配置類

    /**
     * SwaggerConfig
     *
     * @author wangtongzhou
     * @since 2020-06-09 09:41
     */

    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {

        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    //生產環境的時候關閉 Swagger 比較安全
                    .apiInfo(apiInfo())
                    .select()
                    //Api掃描目錄
                    .apis(RequestHandlerSelectors.basePackage("com.springboot2.learning"))
                    .paths(PathSelectors.any())
                    .build();
        }

        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("learn")
                    .description("learn")
                    .version("1.0")
                    .build();
        }
    }

    第三板斧使用 Swagger 註解

    /**
     * 用戶相關接口
     *
     * @author wangtongzhou
     * @since 2020-06-12 07:35
     */

    @RestController
    @RequestMapping("/user")
    @Api(value = "用戶相關接口")
    public class UserController {

        @PostMapping("/")
        @ApiOperation("添加用戶的接口")
        @ApiImplicitParams({
                @ApiImplicitParam(name = "userName", value = "用戶名", defaultValue =
                        "wtz")
    ,
                @ApiImplicitParam(name = "age", value = "年齡", defaultValue = "20")
        })
        public User addUser(String userName, Integer age) {
            User user = new User();
            user.setAge(age);
            user.setUserName(userName);
            return user;
        }

        @GetMapping("/{userId}")
        @ApiOperation("根據用戶id查詢用戶信息")
        @ApiImplicitParam(name = "userId", value = "用戶id", defaultValue = "20")
        public User queryUserByUserId(@PathVariable Long userId) {
            User user = new User();
            user.setUserId(userId);
            return user;
        }
    }
    /**
     * 用戶實體
     *
     * @author wangtongzhou
     * @since 2020-06-12 07:45
     */

    @ApiModel
    public class User {

        @ApiModelProperty(value = "用戶名稱")
        private String userName;

        @ApiModelProperty(value = "年齡")
        private Integer age;

        @ApiModelProperty(value = "用戶id")
        private Long userId;

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        public Long getUserId() {
            return userId;
        }

        public void setUserId(Long userId) {
            this.userId = userId;
        }
    }

    效果如下:

    實體註解

    接口描述

    接口參數

    執行接口

    返回地址

    條件裝配 @Conditional 介紹

    @Conditional 是Spring4.0提供的註解,位於 org.springframework.context.annotation 包內,它可以根據代碼中設置的條件裝載不同的bean。比如說當一個接口有兩個實現類時,我們要把這個接口交給Spring管理時通常會只選擇實現其中一個實現類,這個時候我們總不能使用if-else吧,所以這個@Conditional的註解就出現了。

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPEElementType.METHOD})
    public @interface Conditional {

        Class<? extends Condition>[] value();

    }
    使用方法

    這裏介紹一個MySQL和Oracle選擇方式,開始之前首先在properties文件中增加sql.name=mysql的配置,接下來步驟如下

    1. 實現Conditional接口, 實現matches方法
    /**
     * mysql條件裝配
     *
     * @author wangtongzhou
     * @since 2020-06-13 08:01
     */

    public class MysqlConditional implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            String sqlName = context.getEnvironment().getProperty("sql.name");
            if ("mysql".equals(sqlName)){
                return true;
            }
            return false;
        }
    }
    /**
     * oracle條件裝配
     *
     * @author wangtongzhou
     * @since 2020-06-13 08:02
     */

    public class OracleConditional implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            String sqlName=context.getEnvironment().getProperty("sql.name");
            if ("oracle".equals(sqlName)){
                return true;
            }
            return false;
        }
    }
    1. 在需要判斷條件的bean上,加上@Conditional(***.class)即可在滿足條件的時候加載對應的類
    /**
     * conditional
     *
     * @author wangtongzhou
     * @since 2020-06-13 08:01
     */

    @Configuration
    public class ConditionalConfig {

        @Bean
        @Conditional(MysqlConditional.class)
        public Mysql mysql() {
            return new Mysql();
        }

        @Bean
        @Conditional(OracleConditional.class)
        public Oracle oracle() {
            return new Oracle();
        }
    }
    1. 調用測試
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ConditionalTests {

        @Autowired
        private ApplicationContext applicationContext;

        @Test
        public void test_conditional() {
            Mysql mysql = (Mysql) applicationContext.getBean("mysql");
            Assert.assertNotNull(mysql);
            Assert.assertTrue("mysql".equals(mysql.getSqlName()));
        }
    }
    其他擴展註解
    1. @@ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
      當存在Docket和ApiInfoBuilder類的時候才加載Bean;
    2. @ConditionalOnMissingClass不存在某個類的時候才會實例化Bean;
    3. @ConditionalOnProperty(prefix = “swagger”, value = “enable”, matchIfMissing = true)當存在swagger為前綴的屬性,才會實例化Bean;
    4. @ConditionalOnMissingBean當不存在某個Bean的時候才會實例化;

    這裏就介紹這幾個常用,org.springframework.boot.autoconfigure.condition這個下面包含全部的關於@Conditional相關的所有註解

    @Conditional擴展

    註解介紹

    配置文件參數獲取 @ConfigurationProperties

    @ConfigurationProperties是SpringBoot加入的註解,主要用於配置文件中的指定鍵值對映射到一個Java實體類上。關於這個的使用就在下面的方式引出。

    比較好的方式

    關於開篇中引入的問題,解題流程主要是以下3步:

    1. 抽象一個公共 Swagger jar;
    2. 如何定製化 Swagger jar;
    3. 使用定製化完成以後的 Swagger jar;
    抽象一個公共 Swagger jar

    主要是就是將 Swagger jar 和一些其他需要的 jar 進行引入,使其成為一個公共的模塊,軟件工程中把這個叫做單一原則;

    Maven包的引入

     

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
            </dependency>
        </dependencies>
    如何定製化 Swagger jar;

    定製化就是將 Swagger 相關的屬性進行配置化的處理,這裏也可以分為兩步;

    1. 將公共的屬性抽象成配置化的類,這裏就是關於@ConfigurationProperties的使用,將配置文件中的 swagger 開頭的屬性映射到配置類的屬性當中;
    /**
     * Swagger基本屬性
     *
     * @author wangtongzhou
     * @since 2020-05-24 16:58
     */

    @ConfigurationProperties("swagger")
    public class SwaggerProperties {

        /**
         * 子系統
         */

        private String title;

        /**
         * 描述
         */

        private String description;

        /**
         * 版本號
         */

        private String version;

        /**
         * api包路徑
         */

        private String basePackage;

        public String getTitle() {
            return title;
        }

        public SwaggerProperties setTitle(String title) {
            this.title = title;
            return this;
        }

        public String getDescription() {
            return description;
        }

        public SwaggerProperties setDescription(String description) {
            this.description = description;
            return this;
        }

        public String getVersion() {
            return version;
        }

        public SwaggerProperties setVersion(String version) {
            this.version = version;
            return this;
        }

        public String getBasePackage() {
            return basePackage;
        }

        public SwaggerProperties setBasePackage(String basePackage) {
            this.basePackage = basePackage;
            return this;
        }
    }
    1. 公共屬性賦值配置到 Swagger 的配置類中,該配置類中進行一些類條件的判斷和插件Bean是否已經注入過,然後就是將配置類中的屬性,賦值到 Swagger 的初始化工程中;
    /**
     * Swagger自動配置類
     *
     * @author wangtongzhou
     * @since 2020-05-24 16:35
     */

    @Configuration
    @EnableSwagger2
    @ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
    @ConditionalOnProperty(prefix = "swagger", value = "enable", matchIfMissing = true)
    @EnableConfigurationProperties(SwaggerProperties.class)
    public class SwaggerConfig {

        @Bean
        @ConditionalOnMissingBean
        public SwaggerProperties swaggerProperties() {
            return new SwaggerProperties();
        }

        @Bean
        public Docket createRestApi() {
            SwaggerProperties properties = swaggerProperties();
            return new Docket(DocumentationType.SWAGGER_2)
                    //生產環境的時候關閉 Swagger 比較安全
                    .apiInfo(apiInfo(properties))
                    .select()
                    //Api掃描目錄
                    .apis(RequestHandlerSelectors.basePackage(properties.getBasePackage()))
                    .paths(PathSelectors.any())
                    .build();
        }

        private ApiInfo apiInfo(SwaggerProperties properties) {
            return new ApiInfoBuilder()
                    .title(properties.getTitle())
                    .description(properties.getDescription())
                    .version(properties.getVersion())
                    .build();
        }

    }

    完成以上兩步,就完成了 Swagger 模塊的定製化開發,接下來還要做一件事情,作為一個公共的模塊,我們要讓他自己進行自動化裝配,解放我們的雙手,我們在 resources 目錄下增加一個 spring.factories 配置文件,內容如下:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.springcloud.study.swagger.config.SwaggerConfig

    到此我們完成所有的開發;

    使用定製化完成以後的 Swagger jar;

    關於使用也分為兩步,

    1. 引入 jar;
            <dependency>
                <groupId>com.springcloud.study</groupId>
                <artifactId>common-swagger</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    1. 定製化的屬性配置;
    Swagger 配置項
    swagger:
      title: 用戶模塊
      description: 用戶子系統
      version: 1.0.0
      base-packagecom.springcloud.study.user.controller

    完成這兩步就可以開啟正常的使用了;

    後續的規劃

    1. 註解整理
      後續會將 Spring 註解進行一個統一的整理,包含一些使用說明或者原理等等,希望到時候能幫助到大家吧,目前計劃兩周一個吧;
    2. 開源項目
      Spring Cloud 的學習過於碎片化,希望通過自己搞一個開源項目,提升對各個組件的掌握能力,同時也能產出一套通用化權限管理系統,具備很高的靈活性、擴展性和高可用性,並且簡單易用,這塊是和未來做企業数字化轉型相關的事是重合的,慢慢的會做一些企業級通用化的的功能開發;前端部分的話希望是採用Vue,但是這塊有一個學習成本,還沒有進行研究,目前還沒排上日程。整體的里程碑是希望在6.22離職之前完成整套後端的開發,7月中旬完成第一次Commit。

    點點關注

    這邊文章限於篇幅,過多的關注於使用了,後續會把上面幾個註解的原理分析講講,歡迎大家點點關注,點點贊,感謝!

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • RocketMQ系列(六)批量發送與過濾

    RocketMQ系列(六)批量發送與過濾

    今天我們再來看看RocketMQ的另外兩個小功能,消息的批量發送和過濾。這兩個小功能提升了我們使用RocketMQ的效率。

    批量發送

    以前我們發送消息的時候,都是一個一個的發送,這樣效率比較低下。能不能一次發送多個消息呢?當然是可以的,RocketMQ為我們提供了這樣的功能。但是它也有一些使用的條件:

    • 同一批發送的消息的Topic必須相同;
    • 同一批消息的waitStoreMsgOK 必須相同;
    • 批量發送的消息不支持延遲,就是上一節說的延遲消息;
    • 同一批次的消息,大小不能超過1MiB;

    好了,只要我們滿足上面的這些限制,就可以使用批量發送了,我們來看看發送端的代碼吧,

    @Test
    public void producerBatch() throws Exception {
    
        List<Message> messages = new ArrayList<>();
        for (int i = 0;i<3;i++) {
            MessageExt message = new MessageExt();
            message.setTopic("cluster-topic");
            message.setKeys("key-"+i);
            message.setBody(("this is batchMQ,my NO is "+i+"---"+new Date()).getBytes());
            messages.add(message);
        }
        SendResult sendResult = defaultMQProducer.send(messages);
        System.out.println("sendResult:" + sendResult.getSendStatus().toString());
    }
    
    • 其實批量發送很簡單,我們只是把消息放到一個List當中,然後統一的調用send方法發送就可以了。

    再來看看消費端的代碼,

    @Bean(initMethod = "start",destroyMethod = "shutdown")
    public DefaultMQPushConsumer pushConsumer()  {
        try {
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("DefaultMQPushConsumer");
            consumer.setNamesrvAddr("192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876;");
            consumer.subscribe("cluster-topic", "*");
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    System.out.println("msgs.size():"+msgs.size());
                    if (msgs != null && msgs.size() > 0) {
                        for (MessageExt msg : msgs) {
                            System.out.println(new String(msg.getBody()));
                        }
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            return consumer;
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    • 消費端的代碼沒有任何的變化,正常的接收消息就可以了,我們只是打印出了msgs.size(),看看一次接收一個消息,還是一次可以批量的接收多個消息。

    我們啟動項目,批量發送一下,看看效果吧,

    發送端的日誌如下:

    sendResult:SEND_OK
    

    發送成功,看來我們批量發送的3個消息都進入到了隊列中,再看看消費端,是一次消費一個,還是一次消費3個,如下:

    msgs.size():1
    this is batchMQ,my NO is 0---Mon Jun 15 09:31:04 CST 2020
    msgs.size():1
    this is batchMQ,my NO is 1---Mon Jun 15 09:31:04 CST 2020
    msgs.size():1
    this is batchMQ,my NO is 2---Mon Jun 15 09:31:04 CST 2020
    

    看樣子是一次只消費了一個消息,那麼能不能一次消費3個消息呢?當然是可以的,不過要進行特殊的設置,

    consumer.setConsumeMessageBatchMaxSize(5);
    

    在消費端,我們設置批量消費消息的數量是5,這個值默認是1。我們再看看消費端的日誌,

    msgs.size():3
    this is batchMQ,my NO is 0---Mon Jun 15 09:35:47 CST 2020
    this is batchMQ,my NO is 1---Mon Jun 15 09:35:47 CST 2020
    this is batchMQ,my NO is 2---Mon Jun 15 09:35:47 CST 2020
    

    這次一次消費了3個消息,如果消息比較多的話,最大一次能消費5個。這就是RocketMQ的批量發送和批量消費。

    消息過濾

    其實我們在大多數情況下,使用tag標籤就能夠很好的實現消息過濾。雖然tag標籤咱們並沒有過多的介紹,其實也很好理解,就是一個子Topic的概念,咱們在構建消息message的時候,message.setTags("xxx")。然後在消費的時候,訂閱Topic的時候,也可以指定訂閱的tag,

    consumer.subscribe("cluster-topic", "*");
    

    看到那個”*”了嗎?它就是訂閱的tag,”*”代表全部的tag,如果您想訂閱其中的一個或幾個,可以使用這種方式”tagA || tagB || tagC”,這是訂閱了cluster-topic下的3個tag,其他的tag是不會被消費的。

    這裏我們所說的消息過濾比tag要高級很多,是可以支持sql的,怎麼樣?高級吧。比如:我們訂閱”a > 5 and b = ‘abc’”的消息,如下圖:

    但是,RocketMQ畢竟不是數據庫,它只能支持一些基礎的SQL語句,並不是所有的SQL都支持,

    • 数字型的支持,>, >=, <, <=, BETWEEN, =

    • 字符串支持,=, <>, IN

    • IS NULL或者IS NOT NULL

    • 邏輯判斷,ANDORNOT

    字段的類型也只是簡單的幾種,

    • 数字型,支持123,543.123,整型、浮點都可以;
    • 字符串,必須使用單引號”括起來;
    • 空值,NULL;
    • 布爾型,TRUE或者FALSE;

    並且對消費者的類型也有一定的限制,只能使用push consumer才可以進行消息過濾。好了,說了這麼多了,我們看看怎麼使用吧,消費端和生產端都要進行相應的改造,先看看生產端吧,

    @Test
    public void producerBatch() throws Exception {
    
        List<Message> messages = new ArrayList<>();
        for (int i = 0;i<3;i++) {
            MessageExt message = new MessageExt();
            message.setTopic("cluster-topic");
            message.setKeys("key-"+i);
            message.setBody(("this is batchMQ,my NO is "+i+"---"+new Date()).getBytes());
    
            int a = i+4;
            message.putUserProperty("a",String.valueOf(a));
    
            messages.add(message);
        }
        SendResult sendResult = defaultMQProducer.send(messages);
        System.out.println("sendResult:" + sendResult.getSendStatus().toString());
    }
    

    我們在之前批量發送的基礎上進行了修改,定義了a的值,等於i+4,這樣循環3次,a的值就是4,5,6。然後調用message.putUserProperty("a",String.valueOf(a))注意,在使用消息過濾的時候,這些附加的條件屬性都是通過putUserProperty方法進行設置。這裏,我們設置了a的值。再看看消費端,

    consumer.subscribe("cluster-topic", MessageSelector.bySql("a > 5"));
    

    消費端,整體上沒有變化,只是在訂閱的方法中,使用MessageSelector.bySql("a > 5"),進行了條件的過濾。有的小夥伴可能會有疑問,我既想用sql過濾又想用tag過濾怎麼辦?當然也是可以,我們可以使用MessageSelector.bySql("a > 5").byTag("xx),byTag和bySql不分前後,怎麼樣,很強大吧。我們運行一下程序,看看效果吧。

    我們啟動一下服務,報錯了,怎麼回事?錯誤信息如下:

    The broker does not support consumer to filter message by SQL92
    

    隊列不支持過濾消息,我們查詢了RocketMQ源碼中的BrokerConfig類,這個類就是對broker的一些設置,其中發現了這兩個屬性,

    // whether do filter when retry.
    private boolean filterSupportRetry = false;
    private boolean enablePropertyFilter = false;
    
    • filterSupportRetry是在重試的時候,是否支持filter;
    • enablePropertyFilter,這個就是是否支持過濾消息的屬性;

    我們把這兩個屬性在broker的配置文件改為true吧,如下:

    filterSupportRetry=true
    enablePropertyFilter=true
    

    然後,再重新部署一下我們兩主兩從的集群環境。環境部署完以後,我們再重啟應用,沒有報錯。在生產端發送一下消息看看吧,

    sendResult:SEND_OK
    

    生產端發送消息沒有問題,說明3個消息都發送成功了。再看看消費端的日誌,

    msgs.size():1
    this is batchMQ,my NO is 2---Mon Jun 15 10:59:37 CST 2020
    

    只消費了一個消息,並且這個消息中i的值是2,那麼a的值就是2+4=6,它是>5的,滿足SQL的條件,所以被消費掉了。這完全符合我們的預期。

    總結

    今天的兩個小功能還是比較有意思的,但裡邊也有需要注意的地方,

    • 消息的批量發送,只要我們滿足它的條件,然後使用List發送就可以了;批量消費,默認的消費個數是1,我們可以調整它的值,這樣就可以一次消費多個消息了;
    • 過濾消息中,最大的坑就是隊列的配置里,需要設置enablePropertyFilter=true,否則消費端在啟動的時候報不支持SQL的錯誤;

    我們在使用的時候,多加留意就可以了,有問題,評論區留言吧~

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 10萬級最多人買的SUV,換代后還能不能讓你買單?

    10萬級最多人買的SUV,換代后還能不能讓你買單?

    新車還針對霧燈區域裝飾調整,而大燈光源也更換為LED光源,照明效果更出眾。煥然一新的全新哈弗H6無疑是耐看的。要知道 耐看 向來就是比較難以保證的,在面對不斷變化的消費者眼光,懂得適時微調車型造型是一件識時務的事。

    前幾天,奔赴北京,試駕了全新哈弗H6超豪型。與全新哈弗H6超豪型近距離接觸了幾天,哈弗H6已經是一款非常熟悉的SUV車型,對於它的了解也已經比較深刻;但是一款重點的全新換代,還是會讓人有不少的期待,更何況哈弗H6是一款在銷量上讓眾多同級別競品汗顏的熱銷產品,全新換代後會呈現怎樣的產品力,不得不說十分令人期待。

    畢竟這是一場試駕為主的活動,所以更多的是衝著這款車本身的試駕感受去的,那文章的開始就先聊聊全新哈弗H6超豪型的駕駛體驗好了。

    我從來不會擔心哈弗H6的底盤質感,在第一代哈弗H6上市后我就對它厚實穩重的底盤姿態感到十分的欣喜,而全新哈弗H6的底盤在此基礎上做了更加深度和全面的優化,底盤噪音降至一個很低的水平,在行駛過程中的舒適性得以充分保障;前麥弗遜,后多連桿的前後獨立懸架將路面振動過濾得比較徹底,而且避震響應動作同樣也比較利落,在面對常見顛簸路面的時候不會有晃晃悠悠的動作,行駛姿態非常從容。

    其次動力總成的變化同樣讓人印象深刻,新的1.5T渦輪增壓發動機新增了長城自己正向研發的可變氣門升程技術,新技術的引入不僅改善了油耗水平,更在功率和峰值扭矩上做了12.7%和35.7%的提升,讓哈弗H6的綜合性能有了明顯的提高。

    雙離合變速箱的標定水平也是有了長足的進步,這款濕式雙離合變速箱在實際駕駛中可以很明顯的感知到工程師在對它進行標定的時候極大程度上考慮到了行駛平順的重要性,換擋平順程度十分友好,不會有明顯突兀的頓挫感。

    除了駕駛感受給留下不錯的印象,對哈弗H6的外觀印象也越來越好了。

    全新哈弗H6超豪型藍標版的前臉有着比較大變化,新車用上了面積更大的進氣格柵,且鍍鉻裝飾條數量增加為五條;前保險杠處的通風口更換為網狀設計;而新車尾部整體造型變化不大。整車看起來更時尚、動感了。

    至於全新哈弗H6超豪型紅標版呢?外觀也是有着不容忽視的變化。與藍標版的改變相似,紅標版新車型的進氣格柵也稍微變大了,且有着5條鍍鉻裝飾條;新車還針對霧燈區域裝飾調整,而大燈光源也更換為LED光源,照明效果更出眾。

    煥然一新的全新哈弗H6無疑是耐看的。要知道 耐看 向來就是比較難以保證的,在面對不斷變化的消費者眼光,懂得適時微調車型造型是一件識時務的事。哈弗H6就很好地做到這一點,全新哈弗H6超豪型更符合當下年輕消費者的口味,潛在消費人群無疑會也進一步擴大。

    進入車內,可以感受到全新哈弗H6超豪型的座椅包裹性有了明顯改善,增強了乘坐舒適性。不僅如此,新車內飾是蒙皮經純植物提取的香料處理了,還配有pM2.5粉塵過濾系統,車內的乘坐環境更為舒適。此外,新車還在後排空調出風口下方設計了兩個USB接口,車內配備了0.82㎡的超大全景天窗、前排座椅加熱等配置,這一切都令全新哈弗H6超豪型的駕乘舒適性達到了新的高度。

    儲物空間方面,哈弗的工程師針對換擋桿前方置物盒造型進行優化,優化后的儲物盒能容納下更多更大的隨身物品。而新車的後備廂經優化后,整體容積增大近20L,實用性更強了。

    安全,也是全新哈弗H6超豪型所注重的。新車增加了四項主動安全配置,包含半自動泊車系統、ACC自適應巡航系統、360°環視系統、FCW前碰撞預警系統+AEB自動剎車系統。此外,全新哈弗H6超豪型配備了側氣簾。主/被動安全系統更加完善的哈弗H6有助於對乘客進行全方位智能防護。

    比你優秀的人不可怕,可怕的是比你優秀的人比你更努力。哈弗H6就是那個很努力的優秀“人”。作為國產緊湊型SUV市場的常勝將軍,哈弗H6一向都是走實力派路線的。試駕過很多版本的哈弗H6,可以說是看着它將自己的小毛病一個一個地改掉,到近年來接近完美的狀態。至於經多方升級的全新哈弗H6超豪型,我們有理由相信它未來將助力哈弗H6家族繼續在SUV市場叱吒風雲。

    本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 如何使用 Shell 腳本來查看多個服務器的端口是否打開?

    如何使用 Shell 腳本來查看多個服務器的端口是否打開?

    我們在進行服務器配置的時候,經常要查看服務器的某個端口是否已經開放。如果服務器只有一兩台的話,那很好辦,只需要使用 nc 命令一個個查看即可。

    但是,如果你的服務器是個集群,有很多台呢?那如果還一個個手動去檢查的話,效率肯定是無比低下的,年底裁員名單里肯定有你。

    在這種情況下,我們完全可以使用 Shell 腳本配合 nc 命令來達到我們的目的。而且,不管服務器有幾台,需要檢查的端口有幾個,都可以實現這樣的目標。

    在本文里,我們用 Shell 腳本來實現兩個需求:

    • 掃描多台服務器的一個端口是否打開
    • 掃描多台服務器的多個端口是否打開

    在開始之前,我們先來了解一下 nc 命令。

    nc 命令簡介

    nc 是英文單詞 netcat 的縮寫,它是通過使用 TCP 或 UDP 的網絡協議的連接來讀或寫數據,可以直接被第三方程序或腳本直接調用。

    同時,它是一款功能非常強大的網絡調試工具,因為它可以創建幾乎所有你所需要的連接方式。

    nc 工具主要有三種功能模式:連接模式、監聽模式、通道模式。它的一般使用格式如下:

    $ nc [-options] [HostName or IP] [PortNumber]
    

    接下來,我們就用 Shell 腳本結合 nc 命令來實現我們的兩個需求。

    1. 掃描多台服務器的一個端口是否打開

    在這裏,我們先把需要查詢的所有服務器地址全部放在一個 server-list.txt 文件里,每個地址單獨一行,如下:

    # cat server-list.txt
    192.168.1.2
    192.168.1.3
    192.168.1.4
    192.168.1.5
    192.168.1.6
    192.168.1.7
    

    然後,我們再用 for 循環依次掃描 server-list.txt 里對應服務器的端口是否打開。在這裏,我們掃描 22 端口是否打開。

    # vi port_scan.sh
    
    #!/bin/sh
    for server in `more server-list.txt`
    do
    #echo $i
    nc -zvw3 $server 22
    done
    

    最後,我們給這個腳本賦予可執行權限即可。

    $ chmod +x port_scan.sh
    

    之後,我們就可以用這個腳本來自動依次檢查多個服務器的 22 端口是否已打開。

    # sh port_scan.sh
    
    Connection to 192.168.1.2 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.3 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.4 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.5 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.6 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.7 22 port [tcp/ssh] succeeded!
    

    2. 掃描多台服務器的多個端口是否打開

    在這裏,我們同樣把需要查詢的所有服務器地址全部放在一個 server-list.txt 文件里,每個地址單獨一行。這裏就不重複演示了。

    與此同時,我們也把需要查詢的服務器端口放在另一個 port-list.txt 文件里,每個端口單獨一行,如下所示:

    # cat port-list.txt
    22
    80
    

    然後,我們再用 for 循環依次掃描 server-list.txt 里對應服務器 port-list.txt 所列的端口是否打開。注意,這裏用到了兩個 for 循環,第一層是服務器列表,第二層是端口列表。

    # vi multiple_port_scan.sh
    
    #!/bin/sh
    for server in `more server-list.txt`
    do
    for port in `more port-list.txt`
    do
    #echo $server
    nc -zvw3 $server $port
    echo ""
    done
    done
    

    最後,我們給這個腳本賦予可執行權限即可。

    $ chmod +x multiple_port_scan.sh
    

    之後,我們就可以用這個腳本來自動依次檢查多個服務器的多個端口是否已打開。

    # sh multiple_port_scan.sh
    Connection to 192.168.1.2 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.2 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.3 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.3 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.4 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.4 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.5 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.5 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.6 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.6 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.7 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.7 80 port [tcp/http] succeeded!
    

    公眾號:良許Linux

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

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 如何獲取Apollo上項目下的所有namespace?

    如何獲取Apollo上項目下的所有namespace?

    背景

    項目配置遷移到Apollo之後,通過統一的配置管理及配置監聽使得項目配置修改的成本大大降低。

    但是,在使用Apollo的過程中,強哥也遇到一個問題:如果我們要獲取Apollo下的namespace信息需要通過ConfigServer.getConfig(String namespace)方法來獲取,但是使用這個方法的前提是我們必須知道當前項目下有哪些namespace,或者說我們只能使用我們已知的namespace。這就對我們的代碼擴展性產生了限制,假如項目已經上線,而之後我們又要新增namespace或者修改已有namespace名稱,就必須更改代碼將對應的namespace加入或修改,然後重新發布。

    雖然我們不會經常修改namespace,但是,有這麼一個痛點,就讓人很不舒服。而且從官方文檔中,強哥“並沒有”找到:通過項目app_id獲取到Apollo上對應的該項目下的所有namespace的方法。

    那麼這個問題要怎麼解決呢?強哥今天就帶大家通過Apollo源碼來看看如何找到解決思路。

    入手點

    按常理出牌,我們先在Google中搜索一下我們的問題(這裏提一下,別用百度,他么的根本定位不到要搜的點):

    第一條搜索結果點進去看看,是其他開發者在github上提的issue:

    我們可以看到,作者的回復是:通過open api來獲取所有namespace。也就是官方文檔中的這塊內容:

    額,這個……其實,官方文檔中是有提到如何獲取項目下的所有namespace的方法的,那麼強哥上面為什麼說沒有找到呢?這不是啪啪啪打臉嗎?

    強哥這麼說是因為官網提供的方式比較雞肋。我們可以看到,需要獲取項目下所有的namespace,需要接入Apollo開放平台。操作步驟如下:

    註冊第三方應用
    給已註冊的第三方應用授權
    第三方應用通過獲取的Token調用Apollo Open API
    這尼瑪,坑爹啊,這麼麻煩,還要註冊授權拿Token才能搞,這對於強哥這種懶人來說簡直沒法接受。

    Token是不可能用Token的,這輩子都不會用Token來獲取這玩意的。於是,從官方提供的Api來看是沒法了,只能另謀出路啦。

    追根溯源

    雖然官方文檔中沒有直接提供解決問題的方法,可是我們從提供的開放平台API倒是也可以發現一些信息:

    根據官網配置后調用如下:

    發現確實可以獲取到項目下的所有namespace信息,可是,信息有點太多了,將namespace下的配置也都返回了回來,而且請求中不加入Authorization屬性的Token信息,調用會返回401沒有權限。果然強扭的瓜不甜。

    那麼我們怎麼從上面的信息找突破點呢?沒錯,如果有強哥一樣思路的同學,應該會想到:既然開放平台提供了調用接口,那麼我們就去源碼里看看這個接口的具體實現,沒準能夠有所收穫呢!

    從上圖中我們可以看到,接口地址是:http://{portal_address},那麼源碼就從apollo-portal入手啦:

    直接進到Controller目錄下(別問我為什麼知道是這個目錄,有點基礎的點開項目自然就會這麼去找了):

    可以定位到我們調用的開放平台的方法是這個:

    代碼很簡單,可以看到,獲取namespace走的是namespaceService.findNamespaceBOs()方法,進去實現看看(這裏為github點個贊,點擊方法能夠直接跳轉到對應的實現,真的是方便):

    第一行就獲取了namespace:
    namespaceAPI.findNamespaceByCluster(appId, env, clusterName);
    進去看看:

    吼吼,原來走的也是api調用,可是,這個api的服務地址是哪裡呢?這就要小夥伴們對Apollo的架構有點熟悉了,上大圖:

    我們調用的接口是Portal進去的,而底層走的是Admin Service,所以,上面代碼的restTemplate調用走的就是apollo-adminservice項目啦,話不多說,進apollo-adminservice看看:

    其實到這裏已經差不多了,因為再往細的研究已經沒有了意義。我們已經可以通過調用上圖提供的Api來獲取到我們需要的內容了,試一下:

    試驗發現,確實是可以獲取到項目下的所有namespace,且不需要註冊第三方平台應用,也不需要在調用接口時傳遞Authorization參數,返回的結果也剛好是簡單的所有namespace信息。完美的解決了我們的問題。

    當然有些小夥伴可能會說,這樣還是要調用http接口,還是有點不方便。強哥只想說,自己本地封裝一個方法,獲取應該還是比較簡單的。而且,Apollo Client提供給我們的Api,比如:ConfigService.getConfig(String namespace)其實底層也是走的socket網絡調用,只是client為我們做了一層封裝對用戶屏蔽了而已,同時還額外加入了緩存機制來提高效率。

    當然,你也可以自己下載apollo-client的源碼,然後在裏面封裝調用這個api的邏輯,然後maven部署到自己的私服,這樣就能和其他Api一樣調用啦!不過太麻煩了,強哥就不帶大家試了。

    總結

    先總結一下解決方法:
    直接越過portal,調用底層admin-service的api
    http://{adminservice}/apps/{appId}/clusters/{clusterName}/namespaces
    {adminservice}這個地址根據自己項目配置的地址及端口去設置哦,默認端口8090~

    其實,我們發現,對於開源項目,很多東西只要我們願意去找,還是能找到解決的思路的。不過,首先還是要了解其架構原理先,否則在查找源碼的過程中,可能會無從下手。

    就拿為什麼強哥上面會知道apollo-client獲取namespace信息的時候有使用了緩存機制呢?因為強哥當時找這個問題的解決方法時,也簡單的研究了下client的源碼,想要看看官方是否有提供對應的Api,結果沒有找到,但是也對apollo-client的部分實現有所熟悉。所以,有時候,走一些“該走的彎路”也不是壞事。

    希望這篇文章對大家有用,好啦,今天就到這~
    關注公眾號獲取更多內容,有問題也可在公眾號提問哦:強哥叨逼叨

    叨逼叨編程、互聯網的見解和新鮮事

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 月銷30000多輛,百公里6L油,這款10萬級家轎有這麼好?

    月銷30000多輛,百公里6L油,這款10萬級家轎有這麼好?

    在油耗方面,日產軒逸提供了1。6L以及1。8L兩款動力總成供消費者選擇,與之匹配的是一台CVT變速箱。而作為主銷車型的1。6L版本整體的動力表現並不會太過出色,但好在動力輸出平順,而且油耗水平做得相當出色,也是各大滴滴車主當中最火爆的車型之一。

    在10多萬的這個價位區間中,無論是合資品牌還是國產品牌都有着數十款車型供你選擇,而對於絕大多數消費者而言,都是希望能買到一台價格便宜、用得放心的車型,於是你會發現不少人都變得十分苦惱,究竟自己看上的車型到底值不值得購買呢?成為了當前最希望解決的一個難題。

    帶着問題,今天咱們先來討論一下日系三強當中熱度相當高的日產軒逸,究竟這一款車型的表現怎麼樣呢?

    在造型方面相信大家對其也是相當熟悉,也是採用當下最常規的日產家族式設計語言,造型設計較為年輕,符合到當下消費者的審美,而且每月均保持數萬台的銷量(3月份37672輛)也足以證明它受到眾多消費者所熱捧。

    有着沙發廠之稱的日產品牌,在軒逸上也做得相當出色,2700mm的軸距表現也賦予了它大空間舒適家用的亮點,而且車型本身也沒有明顯的短板,也是一款能夠滿足到各種家庭需求的車型。

    雖然很多人都認為合資車型總會出現價格高配置低的情況,而實際上隨着車型的更新換代,你會發現其實原本配置簡陋的合資車型已經變得更具競爭力;

    在配置水平方面日產軒逸雖說沒有過於突出的地方,在低配版本上配置可以說是相當低,所以也是不值得推薦的。但在主銷的中高配版本,豐富的配置水平也讓其性價比極高;而且在高配版本上頁新增加了併線輔助、車道偏離系統、主動剎車等科技含量比較高的配置,也是這個價位當中極其少有的存在。

    在油耗方面,日產軒逸提供了1.6L以及1.8L兩款動力總成供消費者選擇,與之匹配的是一台CVT變速箱。而作為主銷車型的1.6L版本整體的動力表現並不會太過出色,但好在動力輸出平順,而且油耗水平做得相當出色,也是各大滴滴車主當中最火爆的車型之一。

    而在保養方面,日系車最大的優勢便是保養費用不高,而且車型的耐久性相當高,開着不壞、用起來相當省心。

    優點:車型造型大氣、油耗出色、儲物和乘坐空間大,很適合家用

    缺點:沒有後排出風口、沒有標配倒車雷達、動力和操控較弱

    就像上文所說,軒逸更推薦考慮車價為13.78萬的1.6L尊享版或以上的車型版本,而車型本身的價格並不算太高,但是軒逸也是擁有着一個相當不錯的現金優惠,也幾乎可以用原車價去購車。

    關於軒逸而言,其實多年在市場上打滾也讓它在消費者心目當中有着非常不錯的口碑,舒適家用也是它的優勢之處,而車型本身並沒有明顯的短板,雖然表現平平但不失為一台及格的家用轎車。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準

  • 架構思考-業務快速增長時的容量問題

    架構思考-業務快速增長時的容量問題

    背景

    之前做過一個項目,數據庫存儲採用的是mysql。當時面臨着業務指數級的增長,存儲容量不足。當時採用的措施是

     

    1>短期解決容量的問題

    mysql從5.6升級5.7,因為數據核心且重要,數據庫主從同步採用的是全同步, 利用5.7并行複製新特性,減少了主從同步的延遲,提高了吞吐量。

     

    當時業務量高峰是2000TPS,5.6時可承受的最大TPS是3000,升級到5.7壓測可承受的最大TPD是5000.

     

    2>流量拆分,從根本上解決容量問題

    首先進行容量評估,通過對於業務開展規劃、活動預估,年底的容量會翻5倍。由於目前指數級增長的特性,數據庫要預留至少4倍的冗餘。

     

    要對數據庫進行擴容,因為我們已經使用的是最頂配的SSD物理機了,就算可以在linux內核層面對numa進行綁核和非綁核等測試調參優化性能,提升容量也很有限。注意:一般的業務系統numa綁核會提高性能,但是mysql等數據庫系統是相反的。

     

    所以垂直擴容不成功,就看看是否可以拆分流量。mysql流量拆分方式有x軸拆分(水平拆分)、y軸拆分(垂直拆分)、z軸拆分。

     

    其中y軸拆分(垂直拆分)就是目前都在說做垂直領域,就是在一個細分領域里做深入的意思。由此可以很容易的記住垂直拆分的意思就是按照業務領域進行拆分,專庫專用。實際上能按領域拆分是最理想的,因為這種拆分業務清晰;拆分規則明確;系統之間整合或擴展容易。但是因為當時的業務已經很簡單,y軸拆分已經沒有什麼空間,這種拆分不能達到擴容20倍的目的。

     

    z軸拆分近幾年沒有聽說過了,實際上大家也一直在用。這種方式是將一張大表拆分為子母表,就是分為概要信息和詳細信息。這種拆分方式對解決容量問題意義不大。

     

    比較可行的一個方案是水平拆分。就是常說的分庫分表。按照容量評估,數據庫水平拆分一拆十,根據業務特點找一個標準字段來進行取模。

     

    水平拆分一個技術點在於新老切換。

    採用的是數據庫雙寫的方式,採用異步確保性的補償型事務,發送實時和延遲兩個MQ,通過開關來控制以老數據為準還是新數據庫為準。開始時以老數據庫為準,觀察新老數據沒有一致性問題之後,在一個低峰期,關閉了系統入口,等數據庫沒有任何變更之後切換開關,再打開系統入口。

     

    問題

    對於容量問題,上面採用的是一次性拆分到位的方法。對於一個規模稍大的公司來講,10組物理機(1組包含1主N從)的成本還好。

    1>如果量級再次升級,需要每周增加10台數據庫才能支撐容量呢?

    2>並且對系統可用性還有強要求,1s的停機都不可以接受呢?

     

    解決方案分析

    垂直流量拆分

    首先我要分析的是每周增加10台數據庫這個容量是不是合理的。是否存在放大效應或者說可以減少對mysql這種昂貴資源的使用,轉為增加對HBase、Elasticsearch這種低成本高擴展性資源的使用呢?

     

    基於這個思路,我們需要梳理下是否有可垂直拆分的流量。比如正向流量和負向流量。所謂正向流量是指比如交易下單,負向流量就是取消訂單,包括已付款取消、未付款取消、已到貨取消、未到貨取消等等。實際上負向流量在總訂單里佔比很少,但是業務要比正向交易業務複雜。將正向和逆向拆分的一個主要優勢是分治思想,可以降低兩部分各自的複雜度。將流量拆分重心轉移到正向流量上。

     

    對於正向流量,一個業務比較常用的流量拆分思路是CQRS命令查詢分離,也就是常說的讀寫分離。如果讀流量大於寫流量。可以考慮能否將讀流量進一步拆分。拆分成實時和離線,將實時性要求不高的查詢走ES。ES的數據可以通過同步binlog變更獲得。

     

    另外一個思路是將數據庫按照歷史數據來拆分。就是數據庫里只保存一定時間內的實時數據。超過指定時間則進行數據歸檔。將數據歸檔到HBase等,一般對於歷史的查詢實時性要求也不是很高。

     

    垂直流量拆分可能遇到的問題

    以上方法都是只考慮問題1如果量級再次升級,需要每周增加10台數據庫才能支撐容量的方案。如果再考慮問題2並且對系統可用性還有強要求,1s的停機都不可以接受。就需要看上述方案可能會遇到的問題。

     

    拆分正向流量和負向流量、CQRS都需要改造,改造過程就需要過渡。過渡可以採用上面說的雙寫方式,觀察運行情況進行切換。切換過程中也可以不關閉流量。

     

    麻煩的是數據歸檔。因為數據歸檔后刪除數據庫的數據,變更生效時,針對innodb來說,意味着數據結構重建,頻繁IO。這會影響OLTP在線事務的處理。可以考慮按表來歸檔,控制操作頻率,控制單位時間內對IO的影響。

     

    分佈式關係型數據庫

    分佈式關係型數據庫本質上是通過增加代理等方式將分庫分表做的更加隱蔽。

     

    阿里巴巴分佈式關係數據庫(DRDS),前身是淘寶分佈式數據層(TDDL),核心就是用於分庫分表管理的代理層,宣稱可實現平滑擴容。

     

     

    擴容過程實際是物理數據遷移的過程,引擎層按照分庫遷移后的邏輯先在物理節點上建立新的分庫,然後保留一個時間點進行全量的數據遷移。完成全量遷移后,開始基於先前保留的時間點進行增量的數據追趕。當增量數據追趕到兩邊的數據幾乎一致時,對數據庫進行瞬時停寫,將最後的數據追平,引擎層進行分庫邏輯的路由切換,路由規則切換完成后就完成了核心的擴容邏輯,整個切換過程在毫秒級別完成。

     

    因為整個過程是毫秒級,所以可以做到業務層沒有感知,等多就是看到擴容過程中請求延時增加了不到1s。從原理上來說是可行的。

     

    NOSQL解決方案

    像這麼大的數據量一個很好的參考是12306。12306採用的是Geode。它是有數據庫功能的內存數據網格(In-Memory Data Grid,IMDG)。其重要特性是

     

    1)集群內存總容量,現在Geode可以實現單個節點200-300GB內存,總集群包含300個節點的大型集群,因此總容量可以達到90TB左右的級別。

     

    2)Geode集群功能非常強大,實現了內存中數據Shard分佈,自動管理,集群故障自動恢復,自動平均分佈等一系列企業級的功能,而且有自帶的集群間數據同步功能。

     

    3)在CAP原理下(不了解的話可以百度一下CAP不可能三角),Geode可以保證集群內數據的強一致性,注意是真正的強一致性而不是最終一致性,再加上分區可用性,因此是一個CP型的產品,可以提供統一的數據視圖,支持高併發下的acid事務。

     

    採用新的解決方案最大問題是平滑過渡,平滑過渡方面我還是覺得上面提到的數據庫雙寫方式安全可靠。

     

    系統共建的解決方案

    如果達到我所說的量級,基本上在一個行業中是處於垄斷地位的。並不是一家純的互聯網公司。這種公司可以採用和互聯網大廠合作的方式、用已經有這方面經驗的大廠,來根據自己內部系統的特性共建一套合適自己的定製化數據庫。

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心