分類: 3C資訊

  • 一行代碼引來的安全漏洞就讓我們丟失了整個服務器的控制權

    一行代碼引來的安全漏洞就讓我們丟失了整個服務器的控制權

    之前在某廠的某次項目開發中,項目組同學設計和實現了一個“引以為傲”,額,有點擴張,不過自認為還說得過去的 feature,結果臨上線前被啪啪打臉,因為實現過程中因為一行代碼(沒有標題黨,真的是一行代碼)帶來的安全漏洞讓我們丟失了整個服務器控制權(測試環境)。多虧了上線之前有公司安全團隊的人會對代碼進行掃描,才讓這個漏洞被扼殺在搖籃里。

    下面我們就一起來看看這個事故,啊,不對,是故事。

    背景說明

    我們的項目是一個面向全球用戶的 Web 項目,用 SpringBoot 開發。在項目開發過程中,離不開各種異常信息的處理,比如表單提交參數不符合預期,業務邏輯的處理時離不開各種異常信息(例如網絡抖動等)的處理。於是利用 SpringBoot 各種現成的組件支持,設計了一個統一的異常信息處理組件,統一管理各種業務流程中可能出現的錯誤碼和錯誤信息,通過國際化的資源配置文件進行統一輸出給用戶。

    統一錯誤信息配置管理

    我們的用戶遍布全球,為了給各個國家用戶比較好的體驗會進行不同的翻譯。具體而言,實現的效果如下,為了方便理解,以“找回登錄密碼”這樣一個業務場景來進行闡述說明。

    假設找回密碼時,需要用戶輸入手機或者郵箱驗證碼,假設這個時候用戶輸入的驗證碼通過後台數據庫(可能是Redis)對比發現已經過期。在業務代碼中,只需要簡單的 throw new ErrorCodeException(ErrorCodes.AUTHCODE_EXPIRED) 即可。具體而言,針對不同國家地區不同的語言看到的效果不一樣:

    • 中文用戶看到的提示就是“您輸入的驗證碼已過期,請重新獲取”;
    • 歐美用戶看到的效果是“The verification code you input is expired, …”;
    • 德國用戶看到的是:“Der von Ihnen eingegebene Verifizierungscode ist abgelaufen, bitte wiederholen” 。(我瞎找的翻譯,不一定準)
    • ……

    統一錯誤信息配置管理代碼實現

    關鍵信息其實就在於一個 GlobalExceptionHandler,對所有Controller 入口進行 AOP 攔截,根據不同的錯誤信息,獲取相應資源文件配置的 key,並從語言資源文件中讀取不同國家的錯誤翻譯信息。

    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(BadRequestException.class)
        @ResponseBody
        public ResponseEntity handle(HttpServletRequest request, BadRequestException e){
            String i18message = getI18nMessage(e.getKey(), request);
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Response.error(e.getCode(), i18message));
        }
        
        @ExceptionHandler(ErrorCodeException.class)
        @ResponseBody
        public ResponseEntity handle(HttpServletRequest request, ErrorCodeException e){
            String i18message = getI18nMessage(e.getKey(), request);
            return ResponseEntity.status(HttpStatus.OK).body(Response.error(e.getCode(), i18message));
        }
    }

     

    不同語言的資源文件示例

    private String getI18nMessage(String key, HttpServletRequest request) {
       try {
           return messageSource.getMessage(key, null, LanguaggeUtils.currentLocale(request));
       } catch (Exception e) {
           // log
           return key;
       }
    }

     

    詳細代碼實現可以參考本人之前寫的這篇文章一文教你實現 SpringBoot 中的自定義 Validator 和錯誤信息國際化配置,上面有附完整的代碼實現。

    基於註解的表單校驗(含自定義註解)

    還有一種常見的業務場景就是後端接口需要對用戶提交的表單進行校驗。以“註冊用戶”這樣的場景舉例說明, 註冊用戶時,往往會提交昵稱,性別,郵箱等信息進行註冊,簡單起見,就以這 3 個屬性為例。

    定義的表單如下:

    public class UserRegForm {
     private String nickname;
     private String gender;
     private String email;
    }

     

    對於表單的約束,我們有:

    • 昵稱字段:“nickname” 必填,長度必須是 6 到 20 位;
    • 性別字段:“gender” 可選,如果填了,就必須是“Male/Female/Other/”中的一種。說啥,除了男女還有其他?對,是的。畢竟全球用戶嘛,你去看看非死不可,還有更多。
    • 郵箱: “email”,必填,必須滿足郵箱格式。

    對於以上約束,我們只需要在對應的字段上添加如下註解即可。

    public class UserRegForm {
     @Length(min = 6, max = 20, message = "validate.userRegForm.nickname")
     private String nickname;
    
     @Gender(message="validate.userRegForm.gender")
     private String gender;
    
     @NotNull
     @Email(message="validate.userRegForm.email")
     private String email;
    }

     

    然後在各個語言資源文件中配置好相應的錯誤信息提示即可。其中, @Gender 就是一個自定義的註解。

    基於含自定義註解的表單校驗關鍵代碼

    自定義註解的實現主要的其實就是一個自定義註解的定義以及一個校驗邏輯。 例如定義一個自定義註解 CustomParam

    @Documented
    @Constraint(validatedBy = CustomValidator.class)
    @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CustomParam {
        String message() default "name.tanglei.www.validator.CustomArray.defaultMessage";
    
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default { };
    
        @Documented
        @Retention(RetentionPolicy.RUNTIME)
        @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
        @interface List {
            CustomParam[] value();
        }
    }

     

    校驗邏輯的實現 CustomValidator

    public class CustomValidator implements ConstraintValidator<CustomParam, String> {
        @Override
        public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
            if (null == s || s.isEmpty()) {
                return true;
            }
            if (s.equals("tanglei")) {
                return true;
            } else {
                error(constraintValidatorContext, "Invalid params: " + s);
                return false;
            }
        }
    
        @Override
        public void initialize(CustomParam constraintAnnotation) {
        }
    
        private static void error(ConstraintValidatorContext context, String message) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
        }
    }

     

    上面例子只為了闡述說明問題,其中校驗邏輯沒有實際意義,這樣,如果輸入參數不滿足條件,就會明確提示用戶輸入的哪個參數不滿足條件。例如輸入參數 xx,則會直接提示:Invalid params: xx

    這個跟第一部分的處理方式類似,因為現有的 validator 組件實現中,如果違反相應的約束也是一種拋異常的方式實現的,因此只需要在上述的 GlobalExceptionHandler中添加相應的異常信息即可,這裏就不詳述了。 這不是本文的重點,這裏就不詳細闡述了。 詳細代碼實現可以參考本人之前寫的這篇文章一文教你實現 SpringBoot 中的自定義 Validator 和錯誤信息國際化配置,上面有附完整的代碼實現。

    場景重現

    一切都顯得很完美,直到上線前代碼提交至安全團隊掃描,就被“啪啪打臉”,掃描報告反饋了一個嚴重的安全漏洞。而這個安全漏洞,屬於很高危的遠程代碼執行漏洞。

    用前文提到的自定義 Validator,輸入的參數用: “1+1=${1+1}”,看看效果:

    太 TM 神奇了,居然幫我運算出來了,返回 "message": "Invalid params: 1+1=2"

    問題就出現在實現自定義註解進行校驗的這行代碼(如下圖所示):

    其實,最開始的時候,這裏直接返回了“Invalid params”,當初為了更好的用戶體驗,要明確告訴用戶哪個參數沒有通過校驗,因此在輸出的提示上加上了用戶輸入的字段,也就是上面的"Invalid params: " + s,沒想到,這闖了大禍了(回過頭來想,感覺這裏沒必要這麼詳細啊,因為前端已經有相應的校驗了,正常情況下回攔住,針對不守規矩的用非常規手段來的接口請求,直接返回校驗不通過就行了,畢竟不是對外提供的 OpenAPI 服務)。

    仔細看,這個方法實際上是 ConstraintValidatorContext這個接口中聲明的,看方法名字其實能知道輸入參數是一個字符串模板,內部會進行解析替換的(這其實也符合“見名知意”的良好編程習慣)。(教訓:大家應該把握好自己寫的每一行代碼背後實際在做什麼。)

    /* ......
     * @param messageTemplate new un-interpolated constraint message
     * @return returns a constraint violation builder
     */
    ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate);

     

    這個 case,源碼調試進去之後,就能跟蹤到執行翻譯階段,在如下方法中: org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator.interpolateMessage

    再往後,就是表達式求值了。

    以為就這樣就完了嗎?

    剛開始感覺,能幫忙算簡單的運算規則也就完了吧,你還能把我怎麼樣?其實這個相當於暴露了一個入口,支持用戶輸入任意 EL 表達式進行執行。網上通過關鍵字 “SpEL表達式注入漏洞” 找找,就能發現事情並沒有想象中那麼簡單。

    我們構造恰當的 EL 表達式(注意各種轉義,下文的輸入參數相對比較明顯在做什麼了,實際上還有更多黑科技,比如各種二進制轉義編碼啊等等),就能直接執行輸入代碼,例如:可以直接執行命令,“ls -al”, 返回了一個 UNIXProcess 實例,命令已經被執行過了。

    比如,我們執行個打開計算器的命令,搞個計算器玩玩~

    我錄製了一個動圖,來個演示可能更生動一些。

    這還得了嗎?這相當於提供了一個 webshell 的功能呀,你看想運行啥命令就能運行啥命令,例如 ping 本人博客地址(ping www.tanglei.name),下面動圖演示一下整個過程(從運行 ping 到 kill ping)。

    我錄製了一個視頻,點擊這裏可以訪問。

    豈不是直接創建一個用戶,然後遠程登錄就可以了。後果很嚴重啊,別人想幹嘛就幹嘛了。

    我們跟蹤下對應的代碼,看看內部實現,就會“恍然大悟”了。

    經驗教訓

    幸虧這個漏洞被扼殺在搖籃里,否則後果還真的挺嚴重的。通過這個案例,我們有啥經驗和教訓呢?那就是作為程序員,我們要對每一行代碼都保持“敬畏”之心。也許就是因為你的不經意的一行代碼就帶來了嚴重的安全漏洞,要是不小心被壞人利用,輕則……重則……(自己想象吧)

    此外,我們也應該看到,程序員需要對常見的安全漏洞(例如XSS/CSRF/SQL注入等等)有所了解,並且要有足夠的安全意識(其實有時候研究一些安全問題還挺好玩的,比如這篇《RSA算法及一種”旁門左道”的攻擊方式》就比較有趣)。例如:

    • 用戶權限分離:運行程序的用戶不應該用 root,例如新建一個“web”或者“www”之類的用戶,並設置該用戶的權限,比如不能有可執行 xx 的權限之類的。本文 case,如果權限進行了分離(遵循最小權限原則),應該也不會這麼嚴重。(本文就剛好是因為是測試環境,所以沒有強制實施)
    • 任何時候都不要相信用戶的輸入,必須對用戶輸入的進行校驗和過濾,又特別是針對公網上的應用。
    • 敏感信息加密保存。退一萬步講,假設攻擊者攻入了你的服務器,如果這個時候,你的數據庫賬戶信息等配置都直接明文保存在服務器中。那數據庫也被脫走了。

    如果可能的話,需要對開發者的代碼進行漏洞掃描。一些常見的安全漏洞現在應該是有現成的工具支持的。另外,讓專業的人做專業的事情,例如要有安全團隊,可能你會說你們公司沒有不也活的好好的,哈哈,只不過可能還沒有被壞人盯上而已,壞人也會考慮到他們的成本和預期收益的,當然這就更加對我們開發者提高了要求。一些敏感權限盡量控制在少部分人手中,配合相應的流程來支撐(不得不說,大公司繁瑣的流程還是有一定道理的)。

    畢竟我不是專業研究Web安全的,以上說得可能也不一定對,如果你有不同意見或者更好的建議歡迎留言參与討論。

    這篇文章從寫代碼做實驗,到錄屏做視頻動圖等等耗時還蠻久的(好幾個周末的時間呢),原創真心不易,希望你能幫我個小忙唄,如果本文內容你覺得有所啟發,有所收穫,請幫忙點個“在看”唄,或者轉發分享讓更多的小夥伴看到。

    精彩推薦
    • 一個由跨平台產生的浮點數bug | 有你意想不到的結果。
    • RSA算法及一種”旁門左道”的攻擊方式。
    • 震驚! 阿里的程序員也不過如此,竟被一個簡單的 SQL 查詢難住。
    • 面了7輪 Google,最終還是逃不脫被掛的命運。

    文章首發於本人微信公眾號(ID:tangleithu),請感興趣的同學關注我的微信公眾號,及時獲取技術乾貨。

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

    【其他文章推薦】

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

    ※推薦台中搬家公司優質服務,可到府估價

  • 03 . Prometheus監控容器和HTTP探針應用

    03 . Prometheus監控容器和HTTP探針應用

    Eeporter是什麼及來源?

    是什麼?

    廣義上講所有可以向Prometheus提供監控樣本數據的程序都可以被稱為一個Exporter。而Exporter的一個實例稱為target,如下所示,Prometheus通過輪詢的方式定期從這些target中獲取樣本數據:

    來源有哪些?

    社區提供的

    Prometheus社區提供了豐富的Exporter實現,涵蓋了從基礎設施,中間件以及網絡等各個方面的監控功能。這些Exporter可以實現大部分通用的監控需求。下錶列舉一些社區中常用的Exporter:

    範圍 常用Exporter
    數據庫 MySQL Exporter, Redis Exporter, MongoDB Exporter, MSSQL Exporter等
    硬件 Apcupsd Exporter,IoT Edison Exporter, IPMI Exporter, Node Exporter等
    消息隊列 Beanstalkd Exporter, Kafka Exporter, NSQ Exporter, RabbitMQ Exporter等
    存儲 Ceph Exporter, Gluster Exporter, HDFS Exporter, ScaleIO Exporter等
    HTTP服務 Apache Exporter, HAProxy Exporter, Nginx Exporter等
    API服務 AWS ECS Exporter, Docker Cloud Exporter, Docker Hub Exporter, GitHub Exporter等
    日誌 Fluentd Exporter, Grok Exporter等
    監控系統 Collectd Exporter, Graphite Exporter, InfluxDB Exporter, Nagios Exporter, SNMP Exporter等
    其他 Blockbox Exporter, JIRA Exporter, Jenkins Exporter, Confluence Exporter等

    用戶自定義的

    除了直接使用社區提供的Exporter程序以外,用戶還可以基於Prometheus提供的Client Library創建自己的Exporter程序,目前Promthues社區官方提供了對以下編程語言的支持:Go、Java/Scala、Python、Ruby。同時還有第三方實現的如:Bash、C++、Common Lisp、Erlang,、Haskeel、Lua、Node.js、PHP、Rust等。

    Exporter的運行方式

    從Exporter的運行方式來講,又可以分為

    獨立使用的

    以我們已經使用過的Node Exporter為例,由於操作系統本身並不直接支持Prometheus,同時用戶也無法通過直接從操作系統層面上提供對Prometheus的支持。因此,用戶只能通過獨立運行一個程序的方式,通過操作系統提供的相關接口,將系統的運行狀態數據轉換為可供Prometheus讀取的監控數據。 除了Node Exporter以外,比如MySQL Exporter、Redis Exporter等都是通過這種方式實現的。 這些Exporter程序扮演了一个中間代理人的角色。

    集成到應用中的

    為了能夠更好的監控系統的內部運行狀態,有些開源項目如Kubernetes,ETCD等直接在代碼中使用了Prometheus的Client Library,提供了對Prometheus的直接支持。這種方式打破的監控的界限,讓應用程序可以直接將內部的運行狀態暴露給Prometheus,適合於一些需要更多自定義監控指標需求的項目。

    Exporter規範

    所有的Exporter程序都需要按照Prometheus的規範,返回監控的樣本數據。以Node Exporter為例,當訪問/metrics地址時會返回以下內容:

    # HELP node_cpu Seconds the cpus spent in each mode.
    # TYPE node_cpu counter
    node_cpu{cpu="cpu0",mode="idle"} 362812.7890625
    # HELP node_load1 1m load average.
    # TYPE node_load1 gauge
    node_load1 3.0703125
    

    這是一種基於文本的格式規範,在Prometheus 2.0之前的版本還支持Protocol buffer規範。相比於Protocol buffer文本具有更好的可讀性,以及跨平台性。Prometheus 2.0的版本也已經不再支持Protocol buffer。

    Exporter返回的樣本數據,主要由三個部分組成:樣本的一般註釋信息(HELP),樣本的類型註釋信息(TYPE)和樣本。Prometheus會對Exporter響應的內容逐行解析:

    如果當前行以# HELP開始,Prometheus將會按照以下規則對內容進行解析,得到當前的指標名稱以及相應的說明信息:

    # HELP <metrics_name> <doc_string>
    

    如果當前行以# TYPE開始,Prometheus會按照以下規則對內容進行解析,得到當前的指標名稱以及指標類型:

    # TYPE <metrics_name> <metrics_type>
    

    TYPE註釋行必須出現在指標的第一個樣本之前。如果沒有明確的指標類型需要返回為untyped。 除了# 開頭的所有行都會被視為是監控樣本數據。 每一行樣本需要滿足以下格式規範:

    metric_name [
      "{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}"
    ] value [ timestamp ]
    

    其中metric_name和label_name必須遵循PromQL的格式規範要求。value是一個float格式的數據,timestamp的類型為int64(從1970-01-01 00:00:00以來的毫秒數),timestamp為可選默認為當前時間。具有相同metric_name的樣本必須按照一個組的形式排列,並且每一行必須是唯一的指標名稱和標籤鍵值對組合。

    需要特別注意的是對於histogram和summary類型的樣本。需要按照以下約定返回樣本數據:

    1 . 類型為summary或者histogram的指標x,該指標所有樣本的值的總和需要使用一個單獨的x_sum指標表示

    2 . 類型為summary或者histogram的指標x,該指標所有樣本的總數需要使用一個單獨的x_count指標表示。

    3 . 對於類型為summary的指標x,其不同分位數quantile所代表的樣本,需要使用單獨的x{quantile=”y”}表示。

    4 . 對於類型histogram的指標x為了表示其樣本的分佈情況,每一個分佈需要使用x_bucket{le=”y”}表示,其中y為當前分佈的上位數。同時必須包含一個樣本x_bucket{le=”+Inf”},並且其樣本值必須和x_count相同。

    5 . 對於histogram和summary的樣本,必須按照分位數quantile和分佈le的值的遞增順序排序。

    以下是類型為histogram和summary的樣本輸出示例

    # A histogram, which has a pretty complex representation in the text format:
    # HELP http_request_duration_seconds A histogram of the request duration.
    # TYPE http_request_duration_seconds histogram
    http_request_duration_seconds_bucket{le="0.05"} 24054
    http_request_duration_seconds_bucket{le="0.1"} 33444
    http_request_duration_seconds_bucket{le="0.2"} 100392
    http_request_duration_seconds_bucket{le="+Inf"} 144320
    http_request_duration_seconds_sum 53423
    http_request_duration_seconds_count 144320
    
    # Finally a summary, which has a complex representation, too:
    # HELP rpc_duration_seconds A summary of the RPC duration in seconds.
    # TYPE rpc_duration_seconds summary
    rpc_duration_seconds{quantile="0.01"} 3102
    rpc_duration_seconds{quantile="0.05"} 3272
    rpc_duration_seconds{quantile="0.5"} 4773
    rpc_duration_seconds_sum 1.7560473e+07
    rpc_duration_seconds_count 2693
    

    指定樣式格式的版本
    在Exporter響應的HTTP頭信息中,可以通過Content-Type指定特定的規範版本,例如:

    HTTP/1.1 200 OK
    Content-Encoding: gzip
    Content-Length: 2906
    Content-Type: text/plain; version=0.0.4
    Date: Sat, 17 Mar 2018 08:47:06 GMT
    

    其中version用於指定Text-based的格式版本,當沒有指定版本的時候,默認使用最新格式規範的版本。同時HTTP響應頭還需要指定壓縮格式為gzip。

    容器監控

    Docker是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後發布到任何流行的Linux/Windows/Mac機器上。容器鏡像正成為一個新的標準化軟件交付方式。

    例如,可以通過一下命令快速在本地啟動一個Nginx服務:

    安裝docker
    # 安裝一些必要的系統工具
    sudo yum install -y yum-utils device-mapper-persistent-data lvm2
    # 添加軟件源信息
    # docker 官方源
    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    
    # 阿里雲源
    sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
    sudo yum makecache fast
    
    # CentOS7安裝 Docker-ce
    yum -y install docker-ce   
    
    
    mkdir /etc/docker
    vim /etc/docker/daemon.json
    {
    "registry-mirrors": ["https://registry.docker-cn.com"]
    }
    
    # 啟動Docker後台服務
    systemctl start docker && systemctl enable docker
    systemctl daemon-reload                 # 守護進程重啟
    
    # 運行一個nginx做測試
    docker run -itd nginx
    

    為了能夠獲取到Docker容器的運行狀態,用戶可以通過Docker的stats命令獲取到當前主機上運行容器的統計信息,可以查看容器的CPU利用率、內存使用量、網絡IO總量以及磁盤IO總量等信息。

    docker stats
    CONTAINER           CPU %      MEM USAGE / LIMIT     MEM %      NET I/O         BLOCK I/O   PIDS
    9a1648bec3b2        0.30%      196KiB / 3.855GiB     0.00%      828B / 0B       827kB / 0B  1
    # 除了使用命令以外,用戶還可以通過docker提供的http api查看容器的監控統計信息.
    

    使用CAdvisor

    CAdvisor是Google開源的一款用於展示和分析容器運行狀態的可視化工具。通過在主機上運行CAdvisor用戶可以輕鬆的獲取到當前主機上容器的運行統計信息,並以圖表的形式向用戶展示。

    在本地運行CAdvisor也非常簡單,直接運行一下命令即可:

    docker run \
      --volume=/:/rootfs:ro \
      --volume=/var/run:/var/run:rw \
      --volume=/sys:/sys:ro \
      --volume=/var/lib/docker/:/var/lib/docker:ro \
      --publish=8080:8080 \
      --detach=true \
      --name=cadvisor \
      google/cadvisor:latest
    # 通過訪問http://localhost:8080可以查看,當前主機上容器的運行狀態.
    

    CAdvisor是一個簡單易用的工具,相比於使用Docker命令行工具,用戶不用再登錄到服務器中即可以可視化圖表的形式查看主機上所有容器的運行狀態。

    而在多主機的情況下,在所有節點上運行一個CAdvisor再通過各自的UI查看監控信息顯然不太方便,同時CAdvisor默認只保存2分鐘的監控數據。好消息是CAdvisor已經內置了對Prometheus的支持。訪問http://localhost:8080/metrics即可獲取到標準的Prometheus監控樣本輸出:

    下面列舉了一些CAdvisor中獲取的典型監控指標

    指標名稱 類型 含義
    gauge 再過去10秒內容器CPU的平均負載
    container_cpu_usage_seconds_total
    指標名稱 類型 含義
    container_cpu_load_average_10s gauge 過去10秒內容器CPU的平均負載
    container_cpu_usage_seconds_total counter 容器在每個CPU內核上的累積佔用時間 (單位:秒)
    container_cpu_system_seconds_total counter System CPU累積佔用時間(單位:秒)
    container_cpu_user_seconds_total counter User CPU累積佔用時間(單位:秒)
    container_fs_usge_bytes gauge 容器中文件系統的使用量(單位:字節)
    container_network_receive_bytes_total counter 容器網絡累計接受數據總量(單位: 字節)
    container_network_transmit_bytes_total counter 容器網絡累計傳輸數據總量(單位: 字節)

    與Prometheus集成

    修改/etc/prometheus/prometheus.yml,將cAdvisor添加監控數據採集任務目標當中:

      - job_name: 'docker'
        static_configs:
        - targets: ['172.19.0.27:8080']
    
    systemctl restart prometheus
    

    啟動Prometheus服務,可以在Prometheus UI中看到當前所有的Target狀態:

    當能夠正常採集到cAdvisor的樣本數據后,可以通過一下錶達式計算容器的CPU使用率.

    sum(irate(container_cpu_usage_seconds_total{image!=""}[1m])) without (cpu)
    

    查詢容器內存使用量(單位: 字節)

    container_memory_usage_bytes{image!=""}
    

    查詢容器網絡接收量速率(單位: 字節/秒)

    sum(rate(container_network_receive_bytes_total{image!=""}[1m])) without (interface)
    

    查詢容器網絡傳輸量速率

    sum(rate(container_network_transmit_bytes_total{image!=""}[1m])) without (interface)
    

    查詢容器文件系統讀取速率

    sum(rate(container_fs_reads_bytes_total{image!=""}[1m])) without (device)
    
    # 為了方便看出效果,我們使用dd命令
    docker exec -it 628d /bin/bash
    dd if=/dev/zero of=test bs=1M count=1000
    

    • 查詢容器文件系統寫入速率(單位: 字節/秒)
    sum(rate(container_fs_writes_bytes_total{image!=""}[1m])) without (device)
    

    Prometheus網絡探測

    接下來我們主要介紹Prometheus下如何進行白盒監控,我們之前監控主機的資源用量、容器的運行狀態、數據庫中間件的運行數據。 這些都是支持業務和服務的基礎設施,通過白盒能夠了解其內部的實際運行狀態,通過對監控指標的觀察能夠預判可能出現的問題,從而對潛在的不確定因素進行優化。而從完整的監控邏輯的角度,除了大量的應用白盒監控以外,還應該添加適當的黑盒監控。
    黑盒監控即以用戶的身份測試服務的外部可見性,常見的黑盒監控包括HTTP探針、TCP探針等用於檢測站點或者服務的可訪問性,以及訪問效率等。

    黑盒監控相較於白盒監控最大的不同在於黑盒監控是以故障為導向當故障發生時,黑盒監控能快速發現故障,而白盒監控則側重於主動發現或者預測潛在的問題。一個完善的監控目標是要能夠從白盒的角度發現潛在問題,能夠在黑盒的角度快速發現已經發生的問題。

    安裝Blackbox Exporter

    Blackbox Exporter是Prometheus社區提供的官方黑盒監控解決方案,其允許用戶通過:HTTP、HTTPS、DNS、TCP以及ICMP的方式對網絡進行探測。用戶可以直接使用go get命令獲取Blackbox Exporter源碼並生成本地可執行文件:

    下載安裝blackbox_exporter

    wget https://github.com/prometheus/blackbox_exporter/releases/download/v0.16.0/blackbox_exporter-0.16.0.linux-amd64.tar.gz

    tar xvf blackbox_exporter-0.16.0.linux-amd64.tar.gz -C /usr/local/prometheus/
    mv blackbox_exporter-0.16.0.linux-amd64/ blackbox_exporter
    useradd prometheus
    chown -R prometheus:prometheus /usr/local/prometheus/
    
    vim /usr/lib/systemd/system/blackbox_exporter.service
    [Unit]
    Description=blackbox_exporter
    After=network.target
    
    [Service]
    Type=simple
    User=prometheus
    ExecStart=/usr/local/prometheus/blackbox_exporter/blackbox_exporter --config.file=/usr/local/prometheus/blackbox_exporter/blackbox.yml
    Restart=on-failure
    
    [Install]
    WantedBy=multi-user.target
    
    systemctl enable blackbox_exporter.service
    systemctl start blackbox_exporter.service
    

    運行Blackbox Exporter時,需要用戶提供探針的配置信息,這些配置信息可能是一些自定義的HTTP頭信息,也可能是探測時需要的一些TSL配置,也可能是探針本身的驗證行為。在Blackbox Exporter每一個探針配置稱為一個module,並且以YAML配置文件的形式提供給Blackbox Exporter。 每一個module主要包含以下配置內容,包括探針類型(prober)、驗證訪問超時時間(timeout)、以及當前探針的具體配置項:

    # 探針類型:http、 tcp、 dns、 icmp.
    prober: <prober_string>
    # 超時時間
    [ timeout: <duration> ]
    # 探針的詳細配置,最多只能配置其中的一個
    [ http: <http_probe> ]
    [ tcp: <tcp_probe> ]
    [ dns: <dns_probe> ]
    [ icmp: <icmp_probe> ]
    

    下面是一個簡化的探針配置文件blockbox.yml,包含兩個HTTP探針配置項

    modules:
      http_2xx:
        prober: http
        http:
          method: GET
      http_post_2xx:
        prober: http
        http:
          method: POST
    

    通過運行一下命令,並指定使用的探針設置文件啟動Blockbox Exporter實例:

    blackbox_exporter --config.file=/etc/prometheus/blackbox.yml
    or
    systemctl restart blackbox_exporter.service
    

    啟動成功后,就可以通過訪問http://172.19.0.27:9115/probe?module=http_2xx&target=baidu.com對baidu.com進行探測。這裏通過在URL中提供module參數指定了當前使用的探針,target參數指定探測目標,探針的探測結果通過Metrics的形式返回:

    # HELP probe_dns_lookup_time_seconds Returns the time taken for probe dns lookup in seconds
    # TYPE probe_dns_lookup_time_seconds gauge
    probe_dns_lookup_time_seconds 0.004359875
    # HELP probe_duration_seconds Returns how long the probe took to complete in seconds
    # TYPE probe_duration_seconds gauge
    probe_duration_seconds 0.046153996
    # HELP probe_failed_due_to_regex Indicates if probe failed due to regex
    # TYPE probe_failed_due_to_regex gauge
    probe_failed_due_to_regex 0
    # HELP probe_http_content_length Length of http content response
    # TYPE probe_http_content_length gauge
    probe_http_content_length 81
    # HELP probe_http_duration_seconds Duration of http request by phase, summed over all redirects
    # TYPE probe_http_duration_seconds gauge
    probe_http_duration_seconds{phase="connect"} 0.00105657
    probe_http_duration_seconds{phase="processing"} 0.039457402
    probe_http_duration_seconds{phase="resolve"} 0.004359875
    probe_http_duration_seconds{phase="tls"} 0
    probe_http_duration_seconds{phase="transfer"} 0.000337184
    # HELP probe_http_last_modified_timestamp_seconds Returns the Last-Modified HTTP \
    response header in unixtime
    # TYPE probe_http_last_modified_timestamp_seconds gauge
    probe_http_last_modified_timestamp_seconds 1.26330408e+09
    # HELP probe_http_redirects The number of redirects
    # TYPE probe_http_redirects gauge
    probe_http_redirects 0
    # HELP probe_http_ssl Indicates if SSL was used for the final redirect
    # TYPE probe_http_ssl gauge
    probe_http_ssl 0
    # HELP probe_http_status_code Response HTTP status code
    # TYPE probe_http_status_code gauge
    probe_http_status_code 200
    # HELP probe_http_uncompressed_body_length Length of uncompressed response body
    # TYPE probe_http_uncompressed_body_length gauge
    probe_http_uncompressed_body_length 81
    # HELP probe_http_version Returns the version of HTTP of the probe response
    # TYPE probe_http_version gauge
    probe_http_version 1.1
    # HELP probe_ip_protocol Specifies whether probe ip protocol is IP4 or IP6
    # TYPE probe_ip_protocol gauge
    probe_ip_protocol 4
    # HELP probe_success Displays whether or not the probe was a success
    # TYPE probe_success gauge
    probe_success 1
    

    從返回的樣本中,用戶可以獲取站點的DNS解析耗時,站點響應時間,HTTP響應狀態碼等等和站點訪問質量相關的監控指標,從而幫助管理員主動的發現故障和問題.

    Prometheus集成

    接下來,只需要在Prometheus下配置對Blockbox Exporter實例的採集任務即可、最直觀的配置方式.

      - job_name: 'baidu_http2xx_probe'
        params:
          module:
          - http_2xx
          target:
          - baidu.com
        metrics_path: /probe
        static_configs:
        - targets: ['172.19.0.27:9115']
    
      - job_name: 'prometheus_http2xx_probe'
        params:
          module:
          - http_2xx
          target:
          - prometheus.io
        metrics_path: /probe
        static_configs:
        - targets: ['172.19.0.27:9115']
    
    systemctl restart prometheus
    

    這裏分別配置了名為baidu_http2x_probe和prometheus_http2xx_probe的採集任務,並且通過params指定使用的探針(module)以及探測目標(target).

    那問題就來了,假如我們有N個目標站點且都需要M種探測方式,那麼Prometheus中將包含N * M個採集任務,從配置管理的角度來說顯然是不可接受的。這裏我們也可以採用Relabling的方式對這些配置進行簡化,配置方式如下:

      - job_name: 'blackbox'
        metrics_path: /probe
        params:
          module: [http_2xx]
        static_configs:
          - targets:
            - http://prometheus.io    # Target to probe with http.
            - https://prometheus.io   # Target to probe with https.
            - http://example.com:8080 # Target to probe with http on port 8080.
        relabel_configs:
          - source_labels: [__address__]
            target_label: __param_target
          - source_labels: [__param_target]
            target_label: instance
          - target_label: __address__
            replacement: 172.19.0.27:9115
    

    這裏針對每一個探針服務(如http_2xx)定義一個採集任務,並且直接將任務的採集目標定義為我們需要探測的站點,在採集樣本數據之前通過relabel_configs對採集任務進行動態配置.

    * 第一步, 根據Target實例的地址,寫入__param_target標籤中,__param_<name>形式的標籤來表示,
    	# 在採集任務時會在請求目標地址中添加<name>參數,等同於params的設置.
    * 第二步,  獲取__param_target的值,並覆寫到instance標籤中.
    * 第三步,  覆寫Target實例的__address__標籤值為BlockBox Exporter實例的訪問地址.
    

    HTTP探針

    HTTP探針是進行黑盒監控時最常用的探針之一,通過HTTP探針能夠網站或者HTTP服務建立有效的監控,包括其本身的可用性,以及用戶體驗相關的如響應時間等等。除了能夠在服務出現異常的時候及時報警,還能幫助系統管理員分析和優化網站體驗。

    Blockbox Exporter中所有的探針均是以Module的信息進行配置。如下所示,配置了一個最簡單的HTTP探針:

    modules:
      http_2xx_example:
        prober: http
        http:
    

    通過prober配置項指定探針類型。配置項http用於自定義探針的探測方式,這裡有沒對http配置項添加任何配置,表示完全使用HTTP探針的默認配置,該探針將使用HTTP GET的方式對目標服務進行探測,並且驗證返回狀態碼是否為2XX,是則表示驗證成功,否則失敗。

    自定義HTTP請求

    HTTP服務通常會以不同的形式對外展現,有些可能就是一些簡單的網頁,而有些則可能是一些基於REST的API服務。 對於不同類型的HTTP的探測需要管理員能夠對HTTP探針的行為進行更多的自定義設置,包括:HTTP請求方法、HTTP頭信息、請求參數等。對於某些啟用了安全認證的服務還需要能夠對HTTP探測設置相應的Auth支持。對於HTTPS類型的服務還需要能夠對證書進行自定義設置。

    如下所示,這裏通過method定義了探測時使用的請求方法,對於一些需要請求參數的服務,還可以通過headers定義相關的請求頭信息,使用body定義請求內容:

    http_post_2xx:
        prober: http
        timeout: 5s
        http:
          method: POST
          headers:
            Content-Type: application/json
          body: '{}'
    

    如果HTTP服務啟用了安全認證,Blockbox Exporter內置了對basic_auth的支持,可以直接設置相關的認證信息即可:

    http_basic_auth_example:
        prober: http
        timeout: 5s
        http:
          method: POST
          headers:
            Host: "login.example.com"
          basic_auth:
            username: "username"
            password: "mysecret"
    

    對於使用了Bear Token的服務也可以通過bearer_token配置項直接指定令牌字符串,或者通過bearer_token_file指定令牌文件。

    對於一些啟用了HTTPS的服務,但是需要自定義證書的服務,可以通過tls_config指定相關的證書信息:

     http_custom_ca_example:
        prober: http
        http:
          method: GET
          tls_config:
            ca_file: "/certs/my_cert.crt"
    
    • 自定義探針行為
    • 在默認情況下HTTP探針只會對HTTP返回狀態碼進行校驗,如果狀態碼為2XX(200 <= StatusCode < 300)則表示探測成功,並且探針返回的指標probe_success值為1。
    • 如果用戶需要指定HTTP返回狀態碼,或者對HTTP版本有特殊要求,如下所示,可以使用valid_http_versions和valid_status_codes進行定義:
      http_2xx_example:
        prober: http
        timeout: 5s
        http:
          valid_http_versions: ["HTTP/1.1", "HTTP/2"]
          valid_status_codes: []
    

    默認情況下,Blockbox返回的樣本數據中也會包含指標probe_http_ssl,用於表明當前探針是否使用了SSL:

    # HELP probe_http_ssl Indicates if SSL was used for the final redirect
    # TYPE probe_http_ssl gauge
    probe_http_ssl 0
    

    而如果用戶對於HTTP服務是否啟用SSL有強制的標準。則可以使用fail_if_ssl和fail_if_not_ssl進行配置。fail_if_ssl為true時,表示如果站點啟用了SSL則探針失敗,反之成功。fail_if_not_ssl剛好相反。

      http_2xx_example:
        prober: http
        timeout: 5s
        http:
          valid_status_codes: []
          method: GET
          no_follow_redirects: false
          fail_if_ssl: false
          fail_if_not_ssl: false
    

    除了基於HTTP狀態碼,HTTP協議版本以及是否啟用SSL作為控制探針探測行為成功與否的標準以外,還可以匹配HTTP服務的響應內容。使用fail_if_matches_regexp和fail_if_not_matches_regexp用戶可以定義一組正則表達式,用於驗證HTTP返回內容是否符合或者不符合正則表達式的內容。

      http_2xx_example:
        prober: http
        timeout: 5s
        http:
          method: GET
          fail_if_matches_regexp:
            - "Could not connect to database"
          fail_if_not_matches_regexp:
            - "Download the latest version here"
    

    最後需要提醒的時,默認情況下HTTP探針會走IPV6的協議。 在大多數情況下,可以使用preferred_ip_protocol=ip4強制通過IPV4的方式進行探測。在Bloackbox響應的監控樣本中,也會通過指標probe_ip_protocol,表明當前的協議使用情況:

    # HELP probe_ip_protocol Specifies whether probe ip protocol is IP4 or IP6
    # TYPE probe_ip_protocol gauge
    probe_ip_protocol 6
    

    除了支持對HTTP協議進行網絡探測以外,Blackbox還支持對TCP、DNS、ICMP等其他網絡協議![]

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

    【其他文章推薦】

    ※回頭車貨運收費標準

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

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

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

    台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

    台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

  • 最新研究:大氣二氧化碳總量逼近1500萬年來高峰

    環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

    【其他文章推薦】

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

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

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

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

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

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

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

  • 工業轉型與綠色振興聲明 就政策、技術、金融三個領域提出建言

    文:台大風險政策中心 RSPRC;林怡均翻譯;趙家緯審校

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

    【其他文章推薦】

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

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

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

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

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

    網頁設計最專業,超強功能平台可客製化

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

  • 海洋最大謎團——沒人看過鯨鯊生小孩 科學家取得腹部超音波仍無果

    環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準

    網頁設計最專業,超強功能平台可客製化

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

  • 亞馬遜6月毀林創紀錄 巴西太空署總督導下台

    摘錄自2020年7月14日中央社報導

    巴西政府免去國家太空署(INPE)地球觀測站總督導溫海斯(Lubia Vinhas)的職位,免職書由科技部長龐特斯(Marcos Pontes)簽署,刊登在今(13日)的國家公報。

    溫海斯接受環球電視(TV Globo)訪問時說,她通過高考進入國家太空署服務23年了,所以就算被免去管理職位,也將繼續待在機關。溫海斯也表示不曉得為何被免職,說自己是通過國家公報才知道被調職。

    上週巴西國家太空署公布報告,今年6月份的毀林警報數量創下2015年以來當月最高紀錄。今年上半年累計的警報顯示,亞馬遜遭破壞面積達3069.57平方公里,與2019年上半年相比增加25%。僅6月的毀林警報範圍就達1034.4平方公里。

    溫海斯遭免職後,國際環保團體綠色和平組織(Greenpeace)發表聲明表示,基於巴西總統波索納洛(Jair Bolsonaro)政府先前的決定,溫海斯被免職並不意外,只是再次顯示「巴西政府是真理的敵人」。

    巴西經濟部長葛德斯(Paulo Guedes)今天在經濟合作暨發展組織(OECD)線上會議中表示,巴西願意配合和幫助保護環境;如果巴西的環境政策出現錯誤或過度行為,也將予以糾正。

    生物多樣性
    國際新聞
    巴西
    亞馬遜雨林
    森林

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

    ※回頭車貨運收費標準

  • 俄羅斯計畫2032年前清除七艘廢棄核潛艦

    摘錄自2020年7月14日自由時報報導

    為解決海域核污染問題,俄羅斯正在設計建造能夠打撈沉沒在大海中的核動力潛艦,整個工作預計要進行到2032年。

    《Defense World》報導,在第23屆俄羅斯-挪威聯合委員會會議中,改善核能與輻射安全成為主要議題。俄羅斯計畫清除七艘沉沒在北極海域的核潛艇,「K-159」和「K-27」從打撈到拆解預計各自花費四年時間,分別為2025至2028年、2028至2031年,另外5艘則在2029至2032年完成。

    報導指出,在1990年代,超過100艘除役的核潛艇被放置在九個不同地點,目前有兩個地點的核潛艇已被清除,自2004年起,放射性廢料減少了一半以上,從1110萬居禮下降至509萬居禮。

    能源轉型
    國際新聞
    俄羅斯
    核子潛艦
    核污染
    核能

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

    【其他文章推薦】

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

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

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

    網頁設計最專業,超強功能平台可客製化

    ※回頭車貨運收費標準

  • 邁向碳中合歐盟 擬設統一能源部門

    摘錄自2020年7月13日台灣醒報報導

    歐洲議會推設立「統一能源部門」,促歐盟各國實現碳中合目標!奧地利的歐洲議員嘉夢日前在一場會議中,提出設立「歐洲統一能源部門」,將協助整合各國現有的能源法規、打造歐盟再生能源中心及促進工業及交通運輸轉型為使用綠色能源,獲得多數議員支持。

    歐盟許多成員國包括芬蘭、丹麥已設下2050年以前要實現「碳中合」的目標,即便是將脫歐的英國也曾承諾跟進。根據歐盟調查,為了實現碳中合目標,歐盟整體的用電量將從25%提升到50%,嘉夢說:「實現碳中合,非常需要各國政府機關朝使用綠色能源邁進,因此,我們需要一個部門負責協調整合。」

    除了統一各國現有的能源政策及法規外,該部門也將負責打造歐盟再生能源中心、提升各國的太陽能、生物能發電等基礎設施的普及率、研究並部署綠色能源科技,以及協助將乾淨能源的使用範圍擴張到傳統上依賴化石燃料的領域中,例如提倡綠色建築、工業和交通運輸。

    能源議題
    再生能源
    能源轉型
    國際新聞
    歐盟
    碳中和

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

    【其他文章推薦】

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

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

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

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

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

    ※回頭車貨運收費標準

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

  • 大話計算機網絡一 聊聊UDP

    大話計算機網絡一 聊聊UDP

    引言

    UDP是一個簡單的面向數據報運輸層協議

    UDP不提供可靠性,它把應用程序傳給IP層得數據發送出去,不保證它們能達到目的地

    UDP首部

    端口號表示發送進程和接受進程

    UDP長度字段指的是UDP首部和UDP數據的字節長度,該字段最小值為8字節

    UDP長度是全長減去IP首部的長度

    UDP檢驗和是一個端到端的檢驗和。它由發送端計算,然後由接收端驗證。其目的是為了發現UDP首部和數據在發送端到接收端之間發生的任何改動。

     

    最大UDP數據報長度

    理論上,IP數據報的最大長度是65535字節,這是由IP首部(圖3-1)16比特總長度字段所限制的。去除20字節的IP首部和8個字節的UDP首部,UDP數據報中用戶數據的最長長度為65507字節。但是,大多數實現所提供的長度比這個最大值小。

     

    UDP校驗和

     

    UDP和TCP在首部中都有覆蓋它們首部和數據的檢驗和。UDP的檢驗和是可選的,而TCP的檢驗和是必需的。

    儘管UDP檢驗和的基本計算方法與我們在3.2節中描述的IP首部檢驗和計算方法相類似(16 bit字的二進制反碼和),但是它們之間存在不同的地方。首先,UDP數據報的長度可以為奇数字節,但是檢驗和算法是把若干個16 bit字相加。解決方法是必要時在最後增加填充字節0,這隻是為了檢驗和的計算(也就是說,可能增加的填充字節不被傳送)。

    其次,UDP數據報和TCP段都包含一個12字節長的偽首部,它是為了計算檢驗和而設置的。偽首部包含IP首部一些字段。其目的是讓UDP兩次檢查數據是否已經正確到達目的地(例如,IP沒有接受地址不是本主機的數據報,以及IP沒有把應傳給另一高層的數據報傳給UDP)。UDP數據報中的偽首部格式如圖11-3所示。

     

    在該圖中,我們特地舉了一個奇數長度的數據報例子,因而在計算檢驗和時需要加上填充字節。注意,UDP數據報的長度在檢驗和計算過程中出現兩次。

    如果檢驗和的計算結果為0,則存入的值為全1(65535),這在二進制反碼計算中是等效的。如果傳送的檢驗和為0,說明發送端沒有計算檢驗和。

    如果發送端沒有計算檢驗和而接收端檢測到檢驗和有差錯,那麼UDP數據報就要被悄悄地丟棄。不產生任何差錯報文(當IP層檢測到IP首部檢驗和有差錯時也這樣做)。

    UDP檢驗和是一個端到端的檢驗和。它由發送端計算,然後由接收端驗證。其目的是為了發現UDP首部和數據在發送端到接收端之間發生的任何改動。

     

     

    這個系列主要是對自己讀TCP/IP詳解 卷一 協議的筆記,推薦看完以後去閱讀一下這本又臭又厚的書

    电子書的鏈接地址http://www.52im.net/topic-tcpipvol1.html

    感謝這位站長的開源 

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

    【其他文章推薦】

    ※回頭車貨運收費標準

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

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

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

  • 重學 Java 設計模式:實戰裝飾器模式(SSO單點登錄功能擴展,增加攔截用戶訪問方法範圍場景)

    重學 Java 設計模式:實戰裝飾器模式(SSO單點登錄功能擴展,增加攔截用戶訪問方法範圍場景)

    作者:小傅哥
    博客:https://bugstack.cn

    沉澱、分享、成長,讓自己和他人都能有所收穫!

    一、前言

    對於代碼你有編程感覺嗎

    很多人寫代碼往往是沒有編程感覺的,也就是除了可以把功能按照固定的流程編寫出流水式的代碼外,很難去思考整套功能服務的擴展性和可維護性。尤其是在一些較大型的功能搭建上,比較缺失一些駕馭能力,從而導致最終的代碼相對來說不能做到盡善盡美。

    江洋大盜與江洋大偷

    兩個本想描述一樣的意思的詞,只因一字只差就讓人覺得一個是好牛,一個好搞笑。往往我們去開發編程寫代碼時也經常將一些不恰當的用法用於業務需求實現中,當卻不能意識到。一方面是由於編碼不多缺少較大型項目的實踐,另一方面是不思進取的總在以完成需求為目標缺少精益求精的工匠精神。

    書從來不是看的而是用的

    在這個學習資料幾乎爆炸的時代,甚至你可以輕易就獲取幾個T的視頻,小手輕輕一點就收藏一堆文章,但卻很少去看。學習的過程從不只是簡單的看一遍就可以,對於一些實操性的技術書籍,如果真的希望學習到知識,那麼一定是把這本書用起來而絕對不是看起來。

    二、開發環境

    1. JDK 1.8
    2. Idea + Maven
    3. 涉及工程三個,可以通過關注公眾號bugstack蟲洞棧,回復源碼下載獲取(打開獲取的鏈接,找到序號18)
    工程 描述
    itstack-demo-design-9-00 場景模擬工程;模擬單點登錄類
    itstack-demo-design-9-01 使用一坨代碼實現業務需求
    itstack-demo-design-9-02 通過設計模式優化改造代碼,產生對比性從而學習

    三、裝飾器模式介紹

    初看上圖感覺裝飾器模式有點像俄羅斯套娃、某眾汽車,而裝飾器的核心就是再不改原有類的基礎上給類新增功能。不改變原有類,可能有的小夥伴會想到繼承、AOP切面,當然這些方式都可以實現,但是使用裝飾器模式會是另外一種思路更為靈活,可以避免繼承導致的子類過多,也可以避免AOP帶來的複雜性。

    你熟悉的場景很多用到裝飾器模式

    new BufferedReader(new FileReader(""));,這段代碼你是否熟悉,相信學習java開發到字節流、字符流、文件流的內容時都見到了這樣的代碼,一層嵌套一層,一層嵌套一層,字節流轉字符流等等,而這樣方式的使用就是裝飾器模式的一種體現。

    四、案例場景模擬

    在本案例中我們模擬一個單點登錄功能擴充的場景

    一般在業務開發的初期,往往內部的ERP使用只需要判斷賬戶驗證即可,驗證通過後即可訪問ERP的所有資源。但隨着業務的不斷髮展,團隊里開始出現專門的運營人員、營銷人員、數據人員,每個人員對於ERP的使用需求不同,有些需要創建活動,有些只是查看數據。同時為了保證數據的安全性,不會讓每個用戶都有最高的權限。

    那麼以往使用的SSO是一個組件化通用的服務,不能在裏面添加需要的用戶訪問驗證功能。這個時候我們就可以使用裝飾器模式,擴充原有的單點登錄服務。但同時也保證原有功能不受破壞,可以繼續使用。

    1. 場景模擬工程

    itstack-demo-design-9-00
    └── src
        └── main
            └── java
                └── org.itstack.demo.design
                    ├── HandlerInterceptor.java
                    └── SsoInterceptor.java
    
    • 這裏模擬的是spring中的類:HandlerInterceptor,實現起接口功能SsoInterceptor模擬的單點登錄攔截服務。
    • 為了避免引入太多spring的內容影響對設計模式的閱讀,這裏使用了同名的類和方法,盡可能減少外部的依賴。

    2. 場景簡述

    2.1 模擬Spring的HandlerInterceptor

    public interface HandlerInterceptor {
    
        boolean preHandle(String request, String response, Object handler);
    
    }
    
    • 實際的單點登錄開發會基於;org.springframework.web.servlet.HandlerInterceptor 實現。

    2.2 模擬單點登錄功能

    public class SsoInterceptor implements HandlerInterceptor{
    
        public boolean preHandle(String request, String response, Object handler) {
            // 模擬獲取cookie
            String ticket = request.substring(1, 8);
            // 模擬校驗
            return ticket.equals("success");
        }
    
    }
    
    • 這裏的模擬實現非常簡單隻是截取字符串,實際使用需要從HttpServletRequest request對象中獲取cookie信息,解析ticket值做校驗。
    • 在返回的裏面也非常簡單,只要獲取到了success就認為是允許登錄。

    五、用一坨坨代碼實現

    此場景大多數實現的方式都會採用繼承類

    繼承類的實現方式也是一個比較通用的方式,通過繼承后重寫方法,併發將自己的邏輯覆蓋進去。如果是一些簡單的場景且不需要不斷維護和擴展的,此類實現並不會有什麼,也不會導致子類過多。

    1. 工程結構

    itstack-demo-design-9-01
    └── src
        └── main
            └── java
                └── org.itstack.demo.design
                    └── LoginSsoDecorator.java
    
    • 以上工程結構非常簡單,只是通過 LoginSsoDecorator 繼承 SsoInterceptor,重寫方法功能。

    2. 代碼實現

    public class LoginSsoDecorator extends SsoInterceptor {
    
        private static Map<String, String> authMap = new ConcurrentHashMap<String, String>();
    
        static {
            authMap.put("huahua", "queryUserInfo");
            authMap.put("doudou", "queryUserInfo");
        }
    
        @Override
        public boolean preHandle(String request, String response, Object handler) {
            // 模擬獲取cookie
            String ticket = request.substring(1, 8);
            // 模擬校驗
            boolean success = ticket.equals("success");
    
            if (!success) return false;
    
            String userId = request.substring(9);
            String method = authMap.get(userId);
    
            // 模擬方法校驗
            return "queryUserInfo".equals(method);
        }
    
    }
    
    • 以上這部分通過繼承重寫方法,將個人可訪問哪些方法的功能添加到方法中。
    • 以上看着代碼還算比較清晰,但如果是比較複雜的業務流程代碼,就會很混亂。

    3. 測試驗證

    3.1 編寫測試類

    @Test
    public void test_LoginSsoDecorator() {
        LoginSsoDecorator ssoDecorator = new LoginSsoDecorator();
        String request = "1successhuahua";
        boolean success = ssoDecorator.preHandle(request, "ewcdqwt40liuiu", "t");
        System.out.println("登錄校驗:" + request + (success ? " 放行" : " 攔截"));
    }
    
    • 這裏模擬的相當於登錄過程中的校驗操作,判斷用戶是否可登錄以及是否可訪問方法。

    3.2 測試結果

    登錄校驗:1successhuahua 攔截
    
    Process finished with exit code 0
    
    • 從測試結果來看滿足我們的預期,已經做了攔截。如果你在學習的過程中,可以嘗試模擬單點登錄並繼承擴展功能。

    六、裝飾器模式重構代碼

    接下來使用裝飾器模式來進行代碼優化,也算是一次很小的重構。

    裝飾器主要解決的是直接繼承下因功能的不斷橫向擴展導致子類膨脹的問題,而是用裝飾器模式后就會比直接繼承顯得更加靈活同時這樣也就不再需要考慮子類的維護。

    在裝飾器模式中有四個比較重要點抽象出來的點;

    1. 抽象構件角色(Component) – 定義抽象接口
    2. 具體構件角色(ConcreteComponent) – 實現抽象接口,可以是一組
    3. 裝飾角色(Decorator) – 定義抽象類並繼承接口中的方法,保證一致性
    4. 具體裝飾角色(ConcreteDecorator) – 擴展裝飾具體的實現邏輯

    通過以上這四項來實現裝飾器模式,主要核心內容會體現在抽象類的定義和實現上。

    1. 工程結構

    itstack-demo-design-9-02
    └── src
        └── main
            └── java
                └── org.itstack.demo.design
                    ├── LoginSsoDecorator.java
                    └── SsoDecorator.java
    

    裝飾器模式模型結構

    • 以上是一個裝飾器實現的類圖結構,重點的類是SsoDecorator,這個類是一個抽象類主要完成了對接口HandlerInterceptor繼承。
    • 當裝飾角色繼承接口後會提供構造函數,入參就是繼承的接口實現類即可,這樣就可以很方便的擴展出不同功能組件。

    2. 代碼實現

    2.1 抽象類裝飾角色

    public abstract class SsoDecorator implements HandlerInterceptor {
    
        private HandlerInterceptor handlerInterceptor;
    
        private SsoDecorator(){}
    
        public SsoDecorator(HandlerInterceptor handlerInterceptor) {
            this.handlerInterceptor = handlerInterceptor;
        }
    
        public boolean preHandle(String request, String response, Object handler) {
            return handlerInterceptor.preHandle(request, response, handler);
        }
    
    }
    
    • 在裝飾類中有兩個重點的地方是;1)繼承了處理接口、2)提供了構造函數、3)覆蓋了方法preHandle
    • 以上三個點是裝飾器模式的核心處理部分,這樣可以踢掉對子類繼承的方式實現邏輯功能擴展。

    2.2 裝飾角色邏輯實現

    public class LoginSsoDecorator extends SsoDecorator {
    
        private Logger logger = LoggerFactory.getLogger(LoginSsoDecorator.class);
    
        private static Map<String, String> authMap = new ConcurrentHashMap<String, String>();
    
        static {
            authMap.put("huahua", "queryUserInfo");
            authMap.put("doudou", "queryUserInfo");
        }
    
        public LoginSsoDecorator(HandlerInterceptor handlerInterceptor) {
            super(handlerInterceptor);
        }
    
        @Override
        public boolean preHandle(String request, String response, Object handler) {
            boolean success = super.preHandle(request, response, handler);
            if (!success) return false;
            String userId = request.substring(8);
            String method = authMap.get(userId);
            logger.info("模擬單點登錄方法訪問攔截校驗:{} {}", userId, method);
            // 模擬方法校驗
            return "queryUserInfo".equals(method);
        }
    }
    
    • 在具體的裝飾類實現中,繼承了裝飾類SsoDecorator,那麼現在就可以擴展方法;preHandle
    • preHandle的實現中可以看到,這裏只關心擴展部分的功能,同時不會影響原有類的核心服務,也不會因為使用繼承方式而導致的多餘子類,增加了整體的靈活性。

    3. 測試驗證

    3.1 編寫測試類

    @Test
    public void test_LoginSsoDecorator() {
        LoginSsoDecorator ssoDecorator = new LoginSsoDecorator(new SsoInterceptor());
        String request = "1successhuahua";
        boolean success = ssoDecorator.preHandle(request, "ewcdqwt40liuiu", "t");
        System.out.println("登錄校驗:" + request + (success ? " 放行" : " 攔截"));
    }
    
    • 這裏測試了對裝飾器模式的使用,通過透傳原有單點登錄類new SsoInterceptor(),傳遞給裝飾器,讓裝飾器可以執行擴充的功能。
    • 同時對於傳遞者和裝飾器都可以是多組的,在一些實際的業務開發中,往往也是由於太多類型的子類實現而導致不易於維護,從而使用裝飾器模式替代。

    3.2 測試結果

    23:50:50.796 [main] INFO  o.i.demo.design.LoginSsoDecorator - 模擬單點登錄方法訪問攔截校驗:huahua queryUserInfo
    登錄校驗:1successhuahua 放行
    
    Process finished with exit code 0
    
    • 結果符合預期,擴展了對方法攔截的校驗性。
    • 如果你在學習的過程中有用到過單點登陸,那麼可以適當在裏面進行擴展裝飾器模式進行學習使用。
    • 另外,還有一種場景也可以使用裝飾器。例如;你之前使用某個實現某個接口接收單個消息,但由於外部的升級變為發送list集合消息,但你又不希望所有的代碼類都去修改這部分邏輯。那麼可以使用裝飾器模式進行適配list集合,給使用者依然是for循環后的單個消息。

    七、總結

    • 使用裝飾器模式滿足單一職責原則,你可以在自己的裝飾類中完成功能邏輯的擴展,而不影響主類,同時可以按需在運行時添加和刪除這部分邏輯。另外裝飾器模式與繼承父類重寫方法,在某些時候需要按需選擇,並不一定某一個就是最好。
    • 裝飾器實現的重點是對抽象類繼承接口方式的使用,同時設定被繼承的接口可以通過構造函數傳遞其實現類,由此增加擴展性並重寫方法里可以實現此部分父類實現的功能。
    • 就像夏天熱你穿短褲,冬天冷你穿棉褲,雨天挨澆你穿雨衣一樣,你的根本本身沒有被改變,而你的需求卻被不同的裝飾而實現。生活中往往比比皆是設計,當你可以融合這部分活靈活現的例子到代碼實現中,往往會創造出更加優雅的實現方式。

    八、推薦閱讀

    • 1. 重學 Java 設計模式:實戰工廠方法模式(多種類型商品發獎場景)
    • 2. 重學 Java 設計模式:實戰抽象工廠模式(替換Redis雙集群升級場景)
    • 3. 重學 Java 設計模式:實戰建造者模式(裝修物料組合套餐選配場景)
    • 4. 重學 Java 設計模式:實戰原型模式(多套試每人題目和答案亂序場景)
    • 5. 重學 Java 設計模式:實戰橋接模式(多支付渠道「微信、支付寶」與多支付模式「刷臉、指紋」場景)
    • 6. 重學 Java 設計模式:實戰組合模式(營銷差異化人群發券,決策樹引擎搭建場景)

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

    ※回頭車貨運收費標準