標籤: 網頁設計

  • 特斯拉為電池進軍礦業 放棄併購決定自己挖鋰礦

    摘錄自2020年9月29日聯合新聞報導

    彭博資訊引述知情人士報導,電動車大廠特斯拉(Tesla)本想以併購方式取得在美國內華達州的一處鋰礦,但是和礦商Cypress開發公司的收購談判沒能成功,現在改以自行取得採礦權的方式,準備自己開採,以確保鋰礦供應源。

    上周特斯拉舉行「電池日」時,執行長馬斯克仍宣布,已經確保了礦權,而且將要自己來挖礦。馬斯克告訴投資人,特斯拉已經確定取得1萬英畝有著鋰蘊藏豐富泥岩的區域,將以「極為永續的方法」來提取出鋰。

    特斯拉決定自己生產電池並且目標要將電池成本砍一半,進軍礦業已經成為此計畫的中心。

    能源轉型
    國際新聞
    特斯拉
    礦業

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • 多應用下 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維修中心

  • 【設計模式】如何用組合替代繼承

    如果問面向對象的三大特性是什麼,多數人都能回答出來:封裝、繼承、多態。

    繼承 作為三大特性之一,近來卻越來越不推薦使用,更有極端的語言,直接語法中就不支持繼承,例如 Go。這又是為什麼呢?

    為什麼不推薦使用繼承?

    假設我們要設計一個關於鳥的類。

    我們將“鳥類”定義為一個抽象類 AbstractBird。所有更細分的鳥,比如麻雀、鴿子、烏鴉等,都繼承這個抽象類。

    大部分鳥都會飛,那我們可不可以在 AbstractBird 抽象類中,定義一個 Fly() 方法呢?

    答案是否定的。儘管大部分鳥都會飛,但也有特例,比如鴕鳥就不會飛。鴕鳥繼承具有 Fly() 方法的父類,那鴕鳥就具有“飛”這樣的行為,這顯然不符合我們對現實世界中事物的認識。

    解決方案一

    在鴕鳥這個子類中重寫 Fly() 方法,讓它拋出異常。

    public class AbstractBird
    {
        public virtual void Fly()
        {
            Console.WriteLine("I'm flying.");
        }
    }
    
    //鴕鳥
    public class Ostrich : AbstractBird
    {
        public override void Fly()
        {
            throw new NotImplementedException("I can't fly.");
        }
    }
    

    這種設計思路雖然可以解決問題,但不夠優美。因為除了鴕鳥之外,不會飛的鳥還有很多,比如企鵝。對於這些不會飛的鳥來說,我們都需要重寫 Fly() 方法,拋出異常。

    這違背了迪米特法則(也叫最少知識原則),暴露不該暴露的接口給外部,增加了類使用過程中被誤用的概率。

    解決方案二

    通過 AbstractBird 類派生出兩個更加細分的抽象類:會飛的鳥類 AbstractFlyableBird 和不會飛的鳥類 AbstractUnFlyableBird,讓麻雀、烏鴉這些會飛的鳥都繼承 AbstractFlyableBird,讓鴕鳥、企鵝這些不會飛的鳥,都繼承 AbstractUnFlyableBird 類。

    此時,繼承關係變成了三層,還行得通。

    如果要再添加一個游泳 Swim() 的方法,那情況就複雜了,要分為四中情況:

    • 會飛會游泳
    • 會飛不會游泳
    • 不會飛會游泳
    • 不會飛不會游泳

    如果再有其他行為加入,抽象類的數量就會幾何級數增長。

    我們要搞清楚某個類具有哪些方法、屬性,必須閱讀父類的代碼、父類的父類的代碼……一直追溯到最頂層父類的代碼。

    使用組合

    針對“會飛”這樣一個行為特性,我們可以定義一個 Flyable 接口,只讓會飛的鳥去實現這個接口。針對會游泳,定義一個 Swimable 接口,會叫定義一個 Tweetable 接口。

    public interface Flyable
    {
        void Fly();
    }
    
    public interface Swimable
    {
        void Swim();
    }
    
    public interface Tweetable
    {
        void Tweet();
    }
    
    //麻雀
    public class Sparrow : Flyable, Tweetable
    {
        public void Fly() => Console.WriteLine("I am flying.");
    
        public void Tweet() => Console.WriteLine("!@#$%^&*……");
    }
    
    //企鵝
    public class Penguin : Swimable, Tweetable
    {
        public void Swim() => Console.WriteLine("I am swimming.");
    
        public void Tweet() => Console.WriteLine("!@#$%^&*……");
    }
    

    麻雀和企鵝都會叫,Tweet 實現了兩遍,這是壞味道。我們可以用組合來消除這個壞味道。

    public interface Flyable
    {
        void Fly();
    }
    
    public interface Swimable
    {
        void Swim();
    }
    
    public interface Tweetable
    {
        void Tweet();
    }
    
    public class FlyAbility : Flyable
    {
        public void Fly() => Console.WriteLine("I am flying.");
    }
    
    public class SwimAbility : Swimable
    {
        public void Swim() => Console.WriteLine("I am swimming.");
    }
    
    public class TweetAbility : Tweetable
    {
        public void Tweet() => Console.WriteLine("!@#$%^&*……");
    }
    
    //麻雀
    public class Sparrow : Flyable, Tweetable
    {
        FlyAbility flyAbility = new FlyAbility();
        TweetAbility tweetAbility = new TweetAbility();
    
        public void Fly() => flyAbility.Fly();
    
        public void Tweet() => tweetAbility.Tweet();
    }
    
    //企鵝
    public class Penguin : Swimable, Tweetable
    {
        SwimAbility swimAbility = new SwimAbility();
        TweetAbility tweetAbility = new TweetAbility();
    
        public void Swim() => swimAbility.Swim();
    
        public void Tweet() => tweetAbility.Tweet();
    }
    

    雖然現在主流的思想都是多用組合少用繼承,但是從上面的例子可以看出,繼承改寫成組合意味着要做更細粒度的類的拆分,要定義更多的類和接口。類和接口的增多也就或多或少地增加代碼的複雜程度和維護成本。所以,在實際的項目開發中,我們還是要根據具體的情況,來具體選擇該用繼承還是組合。

    本文出自極客時間 王爭 老師的課程《設計模式之美》。原文示例為 java,因為我是做 C# 的,所以本文示例代碼我改成了 C# 。

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • Golang簡單入門教程——函數進階篇

    Golang簡單入門教程——函數進階篇

    本文始發於個人公眾號:TechFlow,原創不易,求個關注

    今天是golang專題的第八篇,我們來聊聊golang當中的函數。

    我們在之前的時候已經介紹過了函數的基本用法,知道了怎麼樣設計或者是定義一個函數,以及怎麼樣調用一個函數,還了解了defer的用法。今天這篇文章我們來繼續深入這個話題,來看看golang當中關於函數的一些進階的用法。

    返回error

    前文當中我們曾經提到過,在golang當中並沒有try catch捕獲異常的機制。在其他語言當中異常只有一種,可以通過try catch語句進行捕獲,而golang當中做了區分,將異常分為兩種,一種是可以在函數當中返回的error,另外一種是嚴重的會引起程序崩潰的panic

    在golang中,error也是一個數據類型,由於golang支持函數的多值返回,所以我們可以設置一個返回值是error。我們通過對這個error的判斷來獲取運行函數的情況。

    舉個例子,比如說,假設我們實現一個Divide函數實現兩個int相除。那麼顯然我們需要除數不能為0,當除數為0的時候我們需要返回一個異常。這個時候我們可以把代碼寫成這樣:

    // Divide test
    func Divide(a, b int) (ret int, err error) {
     if b == 0 {
      err = errors.New("divisor is zero")
      return
     }
     return a / b, nil
    }
    

    當我們調用函數的時候,我們用兩個變量去接收這個函數返回的結果,第二個變量的類型是error。當這個函數成功執行的時候第二個變量的結果為nil,我們只需要判斷它是否等於nil,就可以知道函數執行是否成功。如果不成功,我們還可以記錄失敗的原因。

    func main() {
     ret, err := Divide(5, 2)
     if err == nil {
      fmt.Println(ret)
     } else {
      fmt.Println(err)
     }
    }
    

    這種用法在golang當中非常常見,我們之前在介紹字符串相關操作的時候也介紹過返回error的用法。我們在設計函數的時候如果需要判斷輸入的合法性可以使用error,這樣就可以保證handle住非法的情況,並且也能讓下游感知到。

    不定參數

    不定參數的用法在很多語言當中都有,比如在Python當中,不定參數是*args。通過*args我們可以接受任何數量的參數,由於Python是弱變量類型的語言,所以args這些參數的類型可以互不相同。但是golang不行,golang嚴格限制類型,不定參數必須要保證類型一樣。除此之外,其他的用法和Python一樣,不定參數會以數組的形式傳入函數內部,我們可以使用數組的api進行訪問。

    我們來看一個例子,我們通過…來定義不定參數。比如我們可以實現一個sum函數,可以將任意個int進行累加。

    func Sum(nums ... int) int{
        ret := 0
        for _, num := range nums {
            ret += num
        }
        return ret
    }
    

    我們來仔細研究一下上面這個例子,在這個例子當中,我們通過…傳入了一個不定參數,我們不定參數的類型只寫一次,寫在…的後面。從底層實現的機制上來說,不定參數本質上是將傳入的參數轉化成數組的切片。但是這就有了一個問題,既然傳入的是一個數組的切片,我們為什麼要專門設置一個關鍵字,而不是規定傳入一個切片呢?

    比如上面的代碼我們完全可以寫成這樣:

    func Sum(nums []int) int{
        ret := 0
        for _, num := range nums {
            ret += num
        }
        return ret
    }
    

    無論從代碼的閱讀還是編寫上來看相差並不大,好像這樣做完全沒有意義,其實不是這樣的。這個關鍵字簡化的並不是函數的設計方,而是函數的使用方。如果我們規定了函數的輸入是一個切片,那麼當我們在傳入數據的時候,必須要使用強制轉化,將我們的數據轉化成切片,比如這樣:

    Sum([]int(3, 4, 6, 8))
    

    而使用…關鍵字我們則可以省略掉強制轉化的過程,上面的代碼我們寫成這樣就可以了:

    Sum(3, 4, 6, 8)
    

    很明顯可以看出差異,使用不定參數的話調用方會輕鬆很多,不需要再進行額外的轉換。如果我們要傳入的也是一個數組,那麼在傳遞的時候也需要用…符號將它展開

    a := make([]int)
    a = append(a, 3)
    a = append(a, 4)
    Sum(a...)
    Sum(a[1:]...)
    

    既然聊到不定參數的傳遞,那麼又涉及到了一個問題,當我們想要像Python那樣傳遞多個類型不同的參數的時候,應該怎麼辦呢?按照道理golang是靜態類型的語言,限制死了參數的類型,是不能隨便轉換的才對。但是偏偏這樣操作是可以的,因為golang當中有一個特殊的類型,叫做interface

    interface的用法很多,一個很重要的用法是用在面向對象當中充當結構體的接口。這裏我們不做過多深入,我們只需要知道,interface的一個用法是可以用來代替所有類型的變量。我們來看一個例子:

    func testInterface(args ...interface{}) {
        for _, arg := range args {
            switch arg.(type) {
                case int:
                 fmt.Println("it's a int")
             case string:
                 fmt.Println("it's a string")    
                case float32:
                 fmt.Println("it's a float")
                default:
                 fmt.Println("it's an unknown type")
            }
        }
    }
    
    
    func main() {
        testInterface(3, 4.5, "abc")
    }
    

    我們可以用.(type)獲取一個interface變量實際的類型,這樣我們就實現了任意類型任意數量參數的傳入。

    匿名函數和閉包

    匿名函數我們在Python當中經常使用到,其實這個概念出現已久,最早可以追溯到1958年Lisp語言。所以這並不是一個新鮮的概念,只是傳統的C、C++等語言沒有支持匿名函數的功能,所以顯得好像是一個新出現的概念一樣。golang當中也支持匿名函數,但是golang當中匿名函數的使用方式和Python等語言稍稍有些不同。

    在Python當中我們是通過lambda關鍵字來定義匿名函數,它可以被傳入另一個函數當中,也可以賦值給一個變量。golang當中匿名函數的定義方式和普通函數基本是一樣的,只是沒有函數名而已,不過它也可以被傳入函數或者是賦值給另一個變量。

    比如:

    s := func(a, b int) int {
        return a + b
    }
    
    c := s(3, 4)
    

    除了匿名函數之外,golang還支持閉包。閉包的概念我們在之前Python閉包的介紹當中曾經提到過,我們之前也用過好幾次,閉包的本質不是一個包,而是一個函數,是一個持有外部環境變量的函數。比如在Python當中,我們經常可以看到這樣的寫法:

    def outside(x):
        def inside(y):
            print(x, y)
        return inside
    
    
    ins = outside(3)
    ins(5) #3, 5
    

    我們可以看到outside這個函數返回了inside這個函數,對於inside這個函數而言,它持有了x這個變量。x這個變量並不是屬於它的,而是定義在它的外部域的。並且我們在調用inside的時候是無法干涉這個變量的,這就是一個閉包的典型例子。根據輪子哥的說法,閉包的閉的意思並不是封閉內部,而是封閉外部。當外部scope失效的時候,函數仍然持有一份外部的環境的值。

    golang當中閉包的使用方法大同小異,我們來看一個類似的例子:

    func main() {
        a := func(x int) (func(int)) {
            return func(y int){
                fmt.Println(x, y)
            }
        }
        b := a(4)
        b(5)
    }
    

    這個閉包的例子和剛才上面Python那個例子是一樣的,唯一不同的是由於golang是強類型的語言,所以我們需要在定義閉包的時候將輸入和輸出的類型定義清楚。

    總結

    關於golang當中函數的高級用法就差不多介紹完了,這些都是實際編程當中經常使用的方法,如果想要學好golang這門語言的話,這些是基本功。如果你之前有其他語言的基礎,來寫go的話,整體上手的難度還是不大的,很多設計都可以在其他的語言當中找到影子,有了參照來學會簡單得多。

    我很難描述實際工作當中寫golang的體驗,和我寫任何一門其他的語言都不一樣,有一種一開始期望很低,慢慢慢慢總能發現驚喜的感覺。我強烈建議大家去實際感受一下。

    如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

    本文使用 mdnice 排版

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

    【其他文章推薦】

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

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

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

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

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

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

  • 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維修中心

  • 養生吧,程序員!

    由於寫作的原因,我認識了蠻多天南海北的朋友。空下來的時候,我就會主動找他們聊一聊,一呢,從他們那吸取成功的經驗,開拓一下眼界;二呢,讓自己的社交圈擴大一些,要知道,多一個朋友就多一條路;三呢,看看朋友們有沒有自己可以幫得上的忙;四呢,保持年輕的心態,不至於落伍。

    老讀者都知道了,我在三線城市洛陽,雖然幸福感很足,但整體社交的圈子是有限的,只通過互聯網,很難得到一些不便於公開的信息,和朋友們聊一聊,不僅能夠加深感情,還能夠讓自己和一線城市保持同步。

    昨天,我和一個非常優秀的年輕人聊天,他給我說,身體上出了一些狀況。他比我小七八歲,按理說身體素質倍棒才對,但大城市的生活壓力,讓他只能快馬加鞭。這就導致身體一直處於高負荷的運轉狀態,很容易出問題。

    提起程序員,總免不了和一些段子關聯上,比如說“要變強,必變禿”,再比如說:

    零基礎學編程→某編程語言入門→某編程語言進階→技術專家→頸椎病

    以前,我對這些段子總是一笑而過,但回想起朋友說的那些話,總免不了一陣心酸。就讓我來寫一篇“程序員養生指南”吧,希望能給讀者朋友們一些警醒或者說參考吧。

    心學鼻祖王陽明曾提出過一個觀點,叫做“知行合一”,我的理解就是,當你要做一件事情的話,你必須得對它有一定的認知,否則的話,你根本就不會去做這件事。

    舉個例子來說,如果你不知道學習很重要,哪怕你天資聰慧,你也不可能去學習,你會把心思花在其他地方。

    再比如說,如果你認知不到英語對於程序員的重要性,你永遠也擺脫不了對翻譯軟件的依賴症。

    對吧?如果在你的認知里,意識不到健康的重要性,那你對待身體的態度可就會大有不同。

    大二結束后,我要去蘇州參加培訓,臨走之前,同學們給我送行,結果我一下子喝大了。畢竟一起玩了兩年,又要去人生地不熟的地方,傷感在所難免,於是我把自己喝到了醫院。

    喝完酒感覺沒啥事,結果睡覺前嘔吐的厲害,感覺整個人就要死翹翹了。我的一個好兄弟看我情況不妙,背着我就跑,大晚上也沒啥車,幸好醫院就在我們學校旁邊,算是保住了一條小命。

    從那以後,我就特別討厭喝酒,儘管有的時候不得不喝一些,但都會盡量控制到不上頭的狀態。

    我身邊有不少人,總是把鍛煉掛在嘴上,聊起來感覺特別注重身體。但過一段時間再問他,“夥計,你健身卡辦了沒?”“最近忙,過两天就去辦,感覺身體有點撐不住了。”

    生存的壓力確實大,但如果身體跨了,生存的意義又是什麼呢?

    改變一個人的認知,最壞的方式就是徹底摧毀他之前的認知。極端的例子就是大病初愈,那時候,人會迫切地想要鍛煉身體。

    最好的方式,當然是聽得進去勸,把別人糟糕的經歷當做是自己前進的動力。認識到健康的重要性,然後一點一滴地去改善生活的習慣,最終達到知行合一的境界。

    我曾看過一本書,裏面提到一個觀點,養成習慣的最好方式就是,連續不間斷地做一件事,周期為 21 天。這件事最好是一件小事,比如說,起床后疊被子這件事,它一點也不難,很容易就能做到。

    但能不能堅持 21 天不間斷就需要一點點耐力了,21 天不算長,但絕對算得上是富有意義的考驗了,它對你培養飲食、作息、運動方面的良好行為習慣是一道開胃菜。

    接下來說說飲食吧,我覺得一定不要暴飲暴食,或者餓着肚子。每天的三餐一定要及時,哪怕你早上起晚了,一定也要記得吃點東西,可以準備一些麵包或者牛奶,不要餓着肚子挺到中午,那樣的話,對身體不好,另外,餓的時候,工作狀態也會大打折扣。

    現在天氣熱了,很多讀者的食慾都不會特彆強烈,尤其是到了中午的時候,可以選擇一些清淡的,比如說,我作為北方人,最喜歡的就是甜面片,伴着一點鹹菜吃,再喝點麵湯,感覺特別舒服。

    到了晚上,不要因為餓就要找場子,覺得必須得吃回來,然後睡覺的時候就感覺特別撐,很難入睡,反而影響了睡眠質量。

    我一般是十點半入睡,然後一覺睡到早上五六點。這個作息還是非常規律的,中午如果困的話,就再補個覺。最好不要超過半個小時,否則越睡越困,如果實在睡不着,就閉目養神會,就算是休息休息眼睛吧,畢竟做程序員的,每天對着電腦屏幕和手機屏幕,對眼睛的傷害還是蠻大的。

    有不少讀者都睡得特別晚,一方面是公司上下班時間上的問題,一方面就是好不容易有點自己的自由時間,舍不得睡覺,於是熬一會,打一把遊戲,就到了凌晨一兩點。

    我是怎麼知道的?通過留言的時間就知道了。讓我很心疼的一個事實是,有些留言的時間竟然是凌晨三四點。真的,看到這個時間點的留言,我都會勸讀者能早點睡就早點睡,不要熬夜。

    二十五六歲之前,熬個夜,很快就恢復了,但再往後,真的是非常難,我以前熬夜看英超,結果第二天整個人都處於懵逼的狀態,效率特別差,要兩三天才能恢復正常,索性後來就戒了。

    最後再說說鍛煉吧,年前我辦了一張健身卡,一周能堅持去兩到三次,但後來就中斷了。最近兩個月,我開始了騎自行車,感覺效果還不錯,瘦了七八斤的樣子,有一種返老還童的感覺,真的。

    五六點起來,騎一個小時左右,回到家不耽誤上班。我在朋友圈曬過兩張圖,老讀者應該看到了。清晨,空氣特別好,路上人也不多,車也不多,氣溫也剛剛好,耳邊再聽着周杰倫或者王力宏,感覺真的非常愜意。

    作為程序員,絕大部分時間都要坐在椅子上,再加上有些公司加班特別厲害,就導致久坐的時間特別長,於是頸椎病、肩周炎等等職業病隨着而來。

    除了及時地走動,還是要盡量騰出一些時間去鍛煉一下,哪怕走走路,跑幾步,對身體的調整都卓有成效,業績是公司的,身體是自己的。

    最後,就是堅持了。生活需要用心去對待,如果我們用心去愛她,她就會加倍地愛我們——養生吧,程序員!

    如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀。

    本文已收錄 GitHub,傳送門~ ,裏面更有大廠面試完整考點,歡迎 Star。

    我是沉默王二,一枚有顏值卻靠才華苟且的程序員。關注即可提升學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,嘻嘻

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

    【其他文章推薦】

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

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

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

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

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

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

  • 【SEED Labs】Public-Key Infrastructure (PKI) Lab

    【SEED Labs】Public-Key Infrastructure (PKI) Lab

    Lab Overview

    公鑰加密是當今安全通信的基礎,但當通信的一方向另一方發送其公鑰時,它會受到中間人的攻擊。根本的問題是,沒有簡單的方法來驗證公鑰的所有權,即,給定公鑰及其聲明的所有者信息,如何確保該公鑰確實屬於聲明的所有者?公鑰基礎設施(PKI)是解決這一問題的一種切實可行的方法。

    通過這個實驗,我們應該能夠更好地了解PKI是如何工作的,PKI是如何用來保護網絡,以及PKI如何打敗中間人攻擊。此外,我們將能夠了解在公鑰基礎設施中信任的根源,以及如果這種根源信任被破壞會出現什麼問題。本實驗所涵蓋的主題如下:

    • Public-key encryption

    • Public-Key Infrastructure (PKI)

    • Certificate Authority (CA) and root CA

    • X.509 certificate and self-signed certificate

    • Apache, HTTP, and HTTPS

    • Man-in-the-middle attacks

    Lab Environment

    這個實驗在我kali VM和Ubuntu 16.04 VM上進行了測試.在這個實驗中,我們將使用openssl命令和庫。

    Lab Tasks

    Task1: Becoming a Certificate Authority(CA) 

    證書頒發機構(CA)是發布数字證書的受信任實體。数字證書通過證書的命名主體來驗證公鑰的所有權。一些商業性的CAs被視為根類CAs;在撰寫本文時,VeriSign是最大的CA。想要獲得商業核證機關發出的数字證書的用戶需要向這些核證機關支付費用。

    在這個實驗室,我們需要創建数字證書,但是我們不會支付任何商業CA,我們自己會成為一個根CA,然後用這個CA為其他人(比如服務器)頒發證書。在這個任務中,我們將使自己成為一個根CA,併為這個CA生成一個證書。RootCA的認證通常預裝在大多數操作系統、web瀏覽器和其他依賴於PKI的軟件中。根CA的證書是無條件信任的。

    The Configuration File openssl.conf  

    為了使用OpenSSL創建證書,必須有一個配置文件。配置文件通常有一個extension.cnf。它由三個OpenSSL命令使用:ca、req和x509。可以使用谷歌搜索找到openssl.conf的手冊頁。還可以從/usr/lib/ssl/openssl.cnf獲得配置文件的副本。將此文件複製到當前目錄后,需要按照配置文件中指定的方式創建子目錄(查看[CA default]部分): 

     

     

     對於index.txt文件,只需創建一個空文件。對於serial文件,在文件中放入一個字符串格式的数字(例如1000)。設置好配置文件openssl.cnf之後,就可以創建和頒發證書了。

    CertificateAuthority(CA).

    如前所述,我們需要為我們的CA生成一個自簽名證書,這意味着這個CA是完全可信的,它的證書將作為根證書。運行以下命令為CA生成自簽名證書:

    系統將提示您輸入信息和密碼。不要丟失此密碼,因為每次要使用此CA為其他人簽名證書時,都必須鍵入口令。您還將被要求填寫一些信息,如國家名稱、常用名稱等。該命令的輸出存儲在兩個文件中:ca.key和ca.crt。文件CA .key包含CA的私鑰,而CA .crt包含公鑰證書。

     

     

     Task2: Creating a Certificate for SEEDPKILab2018.com 

    現在,我們成為一個根CA,我們準備為我們的客戶簽署数字證書。我們的第一個客戶是一家名為SEEDPKILab2018.com的公司。該公司要從CA獲得数字證書,需要經過三個步驟。

    Step 1: Generate public/private key pair  。公司需要首先創建自己的公鑰/私鑰對。可以運行以下命令來生成RSA密鑰對(私有和公共密鑰)。您還需要提供一個密碼來加密私鑰(使用AES-128加密算法,在命令選項中指定)。密鑰將存儲在文件服務器中。

     

    server.key是一個編碼的文本文件(也是加密的),因此無法看到實際內容,例如模數、私有指數等。要查看這些,可以運行以下命令:

     

     

     

     

     Step 2: Generate a Certificate Signing Request (CSR). 一旦公司擁有了密鑰文件,它應該生成一個證書籤名請求(CSR),它基本上包括公司的公鑰。CSR將被發送到CA, CA將為密鑰生成證書(通常在確保CSR中的身份信息與服務器的真實身份匹配之後)。使用SEEDPKILab2018.com作為證書請求的通用名稱。

    需要注意的是,上面的命令與我們為CA創建自簽名證書時使用的命令非常相似,唯一的區別是使用了-x509選項。沒有它,命令生成一個請求;使用它,該命令生成一個自簽名證書。

    Step 3: Generating Certificates . CSR文件需要有CA的簽名才能形成證書。在現實世界中,CSR文件通常被發送到受信任的CA進行簽名。在這個實驗中,我們將使用我們自己的可信CA來生成證書。以下命令使用CA的CA .crt和CA .key將證書籤名請求(server.csr)轉換為X509證書(server.crt):

    如果OpenSSL拒絕生成證書,您請求中的名稱很可能與ca的名稱不匹配。匹配規則在配置文件中指定(查看[policy match]部分)。您可以更改請求的名稱以符合策略,也可以更改策略。配置文件還包括另一個限制較少的策略(稱為任何策略)。您可以通過更改以下行來選擇該策略:

    “policy = policy_match” change to “policy = policy_anything”.

    Task3: Deploying Certificate in an HTTPS Web Server

    在這個實驗中,我們將探索網站如何使用公鑰證書來保護web瀏覽。我們將使用openssl的內置web服務器建立一個HTTPS網站。

    Step1: Configuring DNS .我們選擇SEEDPKILab2018.com作為我們的網站名稱。為了讓我們的計算機識別這個名稱,讓我們將下面的條目添加到/etc/hosts;這個條目基本上將主機名SEEDPKILab2018.com映射到本地主機(即127.0.0.1):

     

    Step 2: Configuring the web server.  讓我們使用在上一個任務中生成的證書啟動一個簡單的web服務器。OpenSSL允許我們使用s_server命令啟動一個簡單的web服務器:

     

    默認情況下,服務器將監聽端口4433。您可以使用-accept選項進行更改。現在,您可以使用以下URL訪問服務器:https://SEEDPKILab2018.com:4433/。最有可能的是,您將從瀏覽器中得到一條錯誤消息。在Firefox中,您將看到如下消息:“seedpkilab2018.com:4433使用了無效的安全證書。該證書不受信任,因為發布者證書未知”.

     

    Step3: Getting the browser to accept our CA certificate. 如果我們的證書是由VeriSign分配的,就不會出現這樣的錯誤消息,因為VeriSign的證書很可能已經預加載到Firefox的證書存儲庫中了。很遺憾,SEEDPKILab2018.com的證書是由我們自己的CA,並且Firefox無法識別此CA。有兩種方法可以讓Firefox接受CA的自簽名證書。

    • 我們可以請求Mozilla將我們的CA證書包含在Firefox軟件中,這樣每個使用Firefox的人都可以識別我們的CA。不幸的是,我們自己的CA沒有足夠大的市場讓Mozilla包含我們的證書,所以我們不會追求這個方向。
    • 加載CA .crt到Firefox中:我們可以手動添加我們的CA證書到Firefox瀏覽器通過點擊下面的菜單序列:

                 Edit -> Preference -> Privacy & Security -> View Certificates

    您將看到一個已經被Firefox接受的證書列表。從這裏,我們可以“導入”我們自己的證書。請導入CA .crt,並選擇以下選項:“信任此CA識別網站”。您將看到我們的CA證書現在位於Firefox接受的證書列表中。

     Step4. Testing our HTTPS website . 現在,將瀏覽器指向https://SEEDPKILab2018.com: 4433,可以正常訪問

    Task4: Deploying Certificate in an Apache-Based HTTPS Website

    使用openssl的s_server命令設置HTTPS服務器主要用於調試和演示目的。在這個實驗中,我們基於Apache建立了一個真正的HTTPS web服務器。Apache服務器(已經安裝在我們的VM中)支持HTTPS協議。要創建一個HTTPS網站,我們只需要配置Apache服務器,這樣它就知道從哪裡獲得私鑰和證書。

    一個Apache服務器可以同時託管多個網站。它需要知道網站文件存儲的目錄。這是通過它的VirtualHost文件完成的,該文件位於/etc/apache2/sites-available目錄中。要添加一個HTTP網站,我們需要在文件000-default.conf中添加一個虛擬主機條目。而要添加一個HTTPS網站,我們則需要在同一個文件夾的default-ssl.conf文件中添加一個VirtualHost條目。

     

     

    ServerName條目指定網站的名稱,而DocumentRoot條目指定網站文件存儲的位置。在設置中,我們需要告訴Apache服務器證書和私鑰存儲在哪裡。

    修改了default-ssl.conf文件之後,我們需要運行一系列命令來啟用SSL。Apache將要求我們輸入用於加密私鑰的密碼。一旦一切都設置好了,我們就可以瀏覽網站了,瀏覽器和服務器之間的所有流量都被加密了。

     

     Task5: Launching a Man-In-The-Middle Attack 

     

     在這個任務中,我們將展示PKI如何擊敗中間人(MITM)攻擊。下圖描述了MITM攻擊的工作原理。假設Alice想通過HTTPS協議訪問example.com。她需要從example.com服務器獲取公鑰;Alice將生成一個密鑰,並使用服務器的公鑰對該密鑰進行加密,然後將其發送到服務器。如果攻擊者可以攔截Alice和服務器之間的通信,則攻擊者可以用自己的公鑰替換服務器的公鑰。因此,Alice的秘密實際上是用攻擊者的公鑰加密的,因此攻擊者將能夠讀取秘密。攻擊者可以使用服務器的公鑰將密鑰轉發給服務器。秘密用於加密Alice和服務器之間的通信,因此攻擊者可以解密加密的通信。

     

     

    Step 1: Setting up the malicious website. 在Task 4中,我們已經為SEEDPKILab2018.com建立了一個HTTPS網站。我們將使用相同的Apache服務器模擬example.com 。為此,我們將按照任務4中的指令向Apache的SSL配置文件添加一個VirtualHost條目:ServerName應該是example.com,但配置的其餘部分可以與任務4中使用的相同。我們的目標如下:當用戶試圖訪問example.com時,我們將讓用戶登陸我們的服務器,其中為example.com託管一個假網站。如果這是一個社交網站,虛假網站可以显示一個登錄頁面類似於目標網站。如果用戶不能分辨出兩者的區別,他們可能會在假網頁中輸入他們的帳戶憑據,本質上就是向攻擊者披露憑據。

    Step2: Becoming the man in the middle 。有幾種方法可以讓用戶的HTTPS請求到達我們的web服務器。一種方法是攻擊路由,將用戶的HTTPS請求路由到我們的web服務器。另一種方法是攻擊DNS,當受害者的機器試圖找到目標網絡服務器的IP地址時,它會得到我們的網絡服務器的IP地址。在此任務中,我們使用“攻擊”DNS。我們只需修改受害者機器的/etc/hosts文件,以模擬DNS緩存設置攻擊的結果,而不是啟動實際的DNS緩存中毒攻擊。

    Step3: Browse the target website  。一切都設置好了,現在訪問目標真實的網站。

    可以看到,我們訪問到的是kali攻擊機的默認目錄。

    訪問https服務的時候,由於kali攻擊機的證書不被信任,所以會有安全警告

     

     

     

    Task6: Launching a Man-In-The-Middle Attack with a Compromised CA 

    設計一個實驗來證明攻擊者可以在任何HTTPS網站上成功發起MITM攻擊。可以使用在Task 5中創建的相同設置,但是這一次,需要演示MITM攻擊是成功的。即,當受害者試圖訪問一個網站而進入MITM攻擊者的假網站時,瀏覽器不會引起任何懷疑。

     

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

    【其他文章推薦】

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

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

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

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

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

  • CPU性能分析工具原理

    轉載請保留以下聲明

      作者: 趙宗晟

      出處: https://www.cnblogs.com/zhao-zongsheng/p/13067733.html

    很多軟件都要做性能分析和性能優化。很多語言都會有他的性能分析工具,例如如果優化C++的性能,我們可以用Visual Studio自帶的性能探測器,或者使用Intel VTune Profiler。了解性能分析工具的原理有助於了解工具給出的數據與結果,也能幫助我們在遇到異常結果時排查哪裡出了問題。這篇博客簡單總結一下常見的性能分析工具原理。

    性能分析器原理分類

    性能分析工具大部分都可以分為下面幾類

    1. 基於採樣(Sampling)
    2. 基於插裝(Instrumentation)
    3. 基於事件(Event-based)

    1. 基於採樣

    基於採樣的分析器會每隔一個固定時間間隔暫停所有線程的執行,然後分析有哪些線程正在執行,那些線程處於就緒狀態。對於這類線程,我們記錄每個線程的調用堆棧,以及其他需要的信息。我們稱這個行為為一次採樣,記錄下來的每個堆棧為一個樣本。然後在結束性能分析的時候我們統計記錄下載的堆棧,看每個堆棧被記錄了多少次,或者每個函數被記錄了多少次。統計學告訴我們,那些執行時間比較長的函數、堆棧,被記錄的次數會更多。如果堆棧A被記錄了200次,堆棧B被記錄了100次,那麼堆棧B的執行時間是堆棧A的2倍。我們可以計算某個堆棧樣本的數量佔總樣本數的比例,這個比例就是堆棧執行時間的佔比。用Visual Studio的性能探測器我們看到的百分比和数字就是值樣本的佔比(也就是時間佔比)和樣本次數。

    很多性能分析工具都是基於採樣的方式。運行性能分析器是會影響被測程序的性能的,而基於採樣的有點是對性能影響比較小,不需要重新編譯程序,非常方便。

    2.基於插裝

    插裝是指通過修改程序,往裡面加入性能分析相關代碼,來收集、監控相關性能指標。例如我們可以在一個函數的開頭寫下計數代碼,我們就可以知道在運行中這個函數被執行了多少次。一般來說基於插裝的性能分析更為準確,但是對性能影響比較大。因為需要修改代碼,所以也不是很方便。另外,基於插裝的分析也可能會引起海森堡bug(heisenbug)。海森堡bug是指當你再運行程序的時候能遇到這個bug,但是試圖定位這個bug時卻遇不到這個bug。這個bug往往是因為在定位bug時修改了軟件的運行環境/流程,導致軟件執行和生產時不一樣,於是就遇不到這個bug了。這個命名的來源很有意思,海森堡是量子力學的著名科學家,他提出了不確定性原理,以及觀察者理論。這個理論認為,觀察會影響例子的狀態,導致觀察粒子和不觀察粒子會導致不同的結果,這個和海森堡bug的情形非常相似。關於觀察者理論,有興趣的人可以再了解一下。

    回到正題,基於插裝也可以再進行劃分:

    • 人手修改源碼:這個是我們非常常用的性能分析方法。我們做性能分析有時候就會直接修改源碼,計算某一段代碼的執行時長,或者計算函數調用次數,分析哪段代碼耗時。
    • 工具自動修改源碼
    • 工具自動修改彙編/中間碼
    • 運行時注入
    • ……

    3.基於事件

    在軟件執行過程中,可能會拋出某些事件。我們通過統計事件出現的次數,事件出現的時機,可以得到軟件的某些性能指標。事件又可以分為軟件事件和硬件事件。軟件事件比如Java可以在class-load的時候拋出事件。硬件事件則是使用專門的性能分析硬件。現在很多CPU裏面都有用於分析性能的性能監控單元(PMU),PMU本身是一個寄存器,在某個事件發生時寄存器裏面的值會+1。例如我們可以設置為當運行中發生memory cache miss的時候PMU寄存器+1,這樣我們就知道一段時間內發生了多少次memory cache miss。性能分析器使用PMU時,會給PMU設置需要統計的事件類型,以及Sample After Value (SAV)。SAV是指寄存器達到什麼值之後出發硬件中斷。例如,我們可以給PMU設置SAV為2000,統計事件為memory cache miss。那麼當cache miss發生2000次時,發生硬件中斷。這個時候性能分析器就可以收集CPU的IP,調用堆棧,進程ID等等信息,分析器結束時進行統計分析。

    基於硬件事件的優點是,對被測程序的影響非常小,比基於採樣還小,可以使用更短的時間間隔收集信息,而且可以收集CPU的非常重要的性能指標,例如cache miss,分支預測錯誤,等等。

    但是基於硬件事件的分析器也有它的一些問題,導致數據上的誤差:

    • SAV一般會設置成很大的數值:
      像是Intel VTune Profiler一般會把SAV設置成10,000到2,000,000,發生中斷時我們能知道最後一次觸發該事件是哪段代碼引起的,但是在這之前的9,999到1,999,999次事件我們是不知道的。他會認為所有10,000到2,000,000次事件都是由同一處代碼引起的。如果發生了很多次中斷,收集了很多次信息,而某一行代碼出現了很多次,那麼根據統計學,我們能知道這行代碼觸發了多少次事件。但是如果某一行代碼只出現了一兩次,那麼我們很難知道這行代碼到底出發了多少次時間。
    • CPU一個核只有幾個PMU,但是可以統計的事件有幾十種:
      一個PMU可以統計一個事件,但是我們分析一個軟件可能需要統計幾十種事件。一般的處理方法是多路復用(Multiplexing)。比如說前10ms記錄事件A,后10ms記錄事件B,再后10ms由記錄事件A,……,這樣輪流記錄事件A和事件B,那麼一個PMU就可以同時統計兩個事件。多路復用可以解決少量PMU統計大量事件的問題,但是也會導致每種事件記錄的樣本數減少,倒是最後統計計算的結果誤差更大。
    • Intel® Hyper-Threading Technology導致記錄不準:
      Hyper-Threading技術可以讓一個CPU物理核變成兩個邏輯核,但是這兩個邏輯核會共享很多硬件,包括PMU。這會出現什麼問題呢?例如我們有兩個線程再兩個CPU核同時運行。我們覺得實在兩個核上執行,但是實際上是在同一個物理核上。所以PMU會同時統計兩個程序觸發的事件,我們很難區分到底是哪個邏輯核出發了多少事件,所以PMU記錄的數據就會不準確。另外,性能分析器計算性能指標時會使用一些硬件參數,Hyper-Threading技術會讓這些參數不準確。例如一般個CPU核能再一個clock執行4個uop,所以CPI(Cycle Per Instruction,每個clock執行的指令數)是0.25。但是如果使用了Hyper-Threading技術,實際的CPI會是0.5
    • Event Skid(事件打滑)會導致記錄的IP不準確:
      PMU記錄有些事件會出現一定延時,所以在執行分析器的中斷處理代碼時,可能被測程序已經又執行了好多指令,IP已經向後滑動了很遠了。一般如果我們只是統計函數的話不會太大問題,但是我們想統計指令時就會有很大問題了。比如我們可能會看到某個add指令導致了大量的分支預測錯誤,顯然這個是不可能的。往往這種時候我們可以看看前面一點的指令。
    • Interrupt Masking(中斷屏蔽),導致統計出來的空閑狀態比正常的高
      不同的中斷有不同的優先級,為了高優先級的中斷處理程序不被打斷,我們可以選擇屏蔽一部分中斷事件。但是PMU的事件也是一个中斷,如果系統中有大量的中斷屏蔽,那麼會有PMU的中斷被屏蔽掉一部分,導致統計出來的數據不準確。表現出來的效果就是,統計出來的處於空閑狀態的時間比實際的要高。

    總結

    這幾個就是比較常見的性能分析方法。我們知道了性能分析的原理,可以更好地理解性能分析器給出的結果,也可以在出現明顯異常的結果時,分析判斷可能的原因,針對性的解決。

    參考

    https://en.wikipedia.org/wiki/Profiling_(computer_programming)
    https://software.intel.com/content/www/us/en/develop/articles/understanding-how-general-exploration-works-in-intel-vtune-amplifier-xe.html
    https://software.intel.com/content/www/us/en/develop/documentation/vtune-help/top/analyze-performance/user-mode-sampling-and-tracing-collection.html
    https://software.intel.com/content/www/us/en/develop/documentation/vtune-help/top/analyze-performance/hardware-event-based-sampling-collection.html

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

    【其他文章推薦】

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

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

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

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

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

  • 循序漸進VUE+Element 前端應用開發(10)— 基於vue-echarts處理各種圖表展示,循序漸進VUE+Element 前端應用開發(5)— 表格列表頁面的查詢,列表展示和字段轉義處理,循序漸進VUE+Element 前端應用開發(9)— 界面語言國際化的處理

    循序漸進VUE+Element 前端應用開發(10)— 基於vue-echarts處理各種圖表展示,循序漸進VUE+Element 前端應用開發(5)— 表格列表頁面的查詢,列表展示和字段轉義處理,循序漸進VUE+Element 前端應用開發(9)— 界面語言國際化的處理

    在我們做應用系統的時候,往往都會涉及圖表的展示,綜合的圖表展示能夠給客戶帶來視覺的享受和數據直觀體驗,同時也是增強客戶認同感的舉措之一。基於圖表的處理,我們一般往往都是利用對應第三方的圖表組件,然後在這個基礎上為它的數據模型提供符合要求的圖表數據即可,VUE+Element 前端應用也不例外,我們這裏使用了基於vue-echarts組件模塊來處理各種圖表vue-echarts是對echarts圖表組件的封裝。

    1、圖表組件的安裝使用

    首先使用npm 安裝vue-echarts組件。

    git地址:https://github.com/ecomfe/vue-echarts

    NPM安裝命令

    npm install echarts vue-echarts

    然後在對應模塊頁面裏面引入對應的組件對象,如下代碼所示。

    <script>
    import ECharts from 'vue-echarts' // 主圖表對象
    import 'echarts/lib/chart/line' // 曲線圖表
    import 'echarts/lib/chart/bar' // 柱狀圖
    import 'echarts/lib/chart/pie' // 餅狀圖
    import 'echarts/lib/component/tooltip' // 提示信息

    接着在Vue組件裏面對象中加入對象即可。

    export default {
      components: {
        'v-chart': ECharts
      },

    如果是全局註冊使用,那麼可以在main.js裏面進行加載

    // 註冊組件后即可使用
    Vue.component('v-chart', VueECharts)

    我們來看看圖表展示的效果圖

    柱狀圖效果

     

    餅狀圖

      

     曲線圖

      

     其他類型,極坐標和散點圖形

      

     或者曲線和柱狀圖組合的圖形

     更多的案例可以參考官網的展示介紹:https://echarts.apache.org/examples/zh/index.html

      

    2、各種圖表的展示處理

    對於我們需要的各種常規的柱狀圖、餅狀圖、折線圖(曲線圖)等,我下來介紹幾個案例代碼,其他的一般我們根據官方案例提供的data數據模型,構造對應的數據即可生成,就不再一一贅述。

    另外,我們也可以參考Vue-echarts封裝的處理demo:https://github.com/ecomfe/vue-echarts/tree/master/src/demo 

    對於柱狀圖,效果如下

    在Vue模塊頁面的Template 裏面,我們定義界面代碼如下即可。

    <v-chart
      ref="simplebar"
      :options="simplebar"
      autoresize
    />

    然後在data裏面為它準備好數據即可,如下代碼所示。

     data() {
        return {
          simplebar: {
            title: { text: '柱形圖Demo' },
            tooltip: {},
            xAxis: {
              data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子']
            },
            yAxis: {},
            series: [{
              name: '銷量',
              type: 'bar',
              data: [5, 20, 36, 10, 10, 20]
            }]
          }
        }
      }

    當然我們也可以把這些構造對應數據的邏輯放在單獨的JS文件裏面,然後導入即可。

    例如對於餅圖,它的界面效果如下所示。

    它的vue視圖下,Template裏面的代碼如下所示。

    <v-chart
      ref="pie"
      :options="pie"
      autoresize />

    一般對於圖表的數據,由於處理代碼可能不少,建議是獨立放在一個JS文件裏面,然後我們通過import導入即可使用。

      然後在data裏面引入對應的對象即可,如下所示。

    <script>
    import ECharts from 'vue-echarts' // 主圖表對象
    import 'echarts/lib/chart/line' // 曲線圖表
    import 'echarts/lib/chart/bar' // 柱狀圖
    import 'echarts/lib/chart/pie' // 餅狀圖
    import 'echarts/lib/component/tooltip' // 提示信息
    
    // 導入報表數據
    import getBar from './chartdata/bar' import pie from './chartdata/pie'
    import scatter from './chartdata/scatter'
    import lineChart from './chartdata/lineChart'
    import incomePay from './chartdata/incomePay'
    
    export default {
      components: {
        'v-chart': ECharts
      },
       return {
          pie,
          scatter,,
          lineChart,
          incomePay,
          simplebar: {
            title: { text: '柱形圖Demo' },
            tooltip: {},
            xAxis: {
              data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子']
            },
            yAxis: {},
            series: [{
              name: '銷量',
              type: 'bar',
              data: [5, 20, 36, 10, 10, 20]
            }]
          }
        }
      },

    其中pie.js裏面放置的是處理餅圖數據的邏輯,如下代碼所示。

    export default {
      title: {
        text: '餅圖程序調用高亮示例',
        x: 'center'
      },
      tooltip: {
        trigger: 'item',
        formatter: '{a} <br/>{b} : {c} ({d}%)'
      },
      legend: {
        orient: 'vertical',
        left: 'left',
        data: ['直接訪問', '郵件營銷', '聯盟廣告', '視頻廣告', '搜索引擎']
      },
      series: [
        {
          name: '訪問來源',
          type: 'pie',
          radius: '55%',
          center: ['50%', '60%'],
          data: [
            { value: 335, name: '直接訪問' },
            { value: 310, name: '郵件營銷' },
            { value: 234, name: '聯盟廣告' },
            { value: 135, name: '視頻廣告' },
            { value: 1548, name: '搜索引擎' }
          ],
          itemStyle: {
            emphasis: {
              shadowBlur: 10,
              shadowOffsetX: 0,
              shadowColor: 'rgba(0, 0, 0, 0.5)'
            }
          }
        }
      ]
    }

    在界面處理的時候,值得注意的時候,有時候Vue頁面處理正常,但是圖表就是沒有出來,可能是因為高度或者寬度為0的原因,需要對對應的樣式進行處理設置,以便能夠正常显示出來。

    如下是我 對圖表的設置的樣式處理,使得圖表在一個卡片的位置能夠显示正常。

    <style scoped>
      .echarts { width: 100%; height: 400px;}
    
      .el-row {
        margin-bottom: 20px;
      }
      .el-col {
        border-radius: 4px;
        margin-bottom: 20px;
      }
    </style>

    最後幾個界面組合一起的效果如下所示。

     以上就是基於vue-echarts處理各種圖表展示,其中常規的引入組件很容易的,主要就是需要根據對應的圖表案例,參考數據組成的規則,從而根據我們實際情況構建對應的數據,賦值給對應的模型變量即可。

    列出以下前面幾篇隨筆的連接,供參考:

    循序漸進VUE+Element 前端應用開發(1)— 開發環境的準備工作

    循序漸進VUE+Element 前端應用開發(2)— Vuex中的API、Store和View的使用

    循序漸進VUE+Element 前端應用開發(3)— 動態菜單和路由的關聯處理

    循序漸進VUE+Element 前端應用開發(4)— 獲取後端數據及產品信息頁面的處理

    循序漸進VUE+Element 前端應用開發(5)— 表格列表頁面的查詢,列表展示和字段轉義處理

    循序漸進VUE+Element 前端應用開發(6)— 常規Element 界面組件的使用

    循序漸進VUE+Element 前端應用開發(7)— 介紹一些常規的JS處理函數

    循序漸進VUE+Element 前端應用開發(8)— 樹列表組件的使用

    循序漸進VUE+Element 前端應用開發(9)— 界面語言國際化的處理

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

  • 一起玩轉微服務(1)——概念

    一起玩轉微服務(1)——概念

    一、什麼是微服務

    隨着各行各業公司的快速發展,業務規模的不斷擴大,不可避免的造成原有架構不能夠適應快速的增長和變化。這時,微服務就進入大家的視野,其實在微服務之前,很多的公司已經做過服務化的改造,並且取得了一定的成果,但是對於整體流程的標準化還有一定有差距。那麼,什麼是微服務呢?
    準確的說,微服務是一種軟件架構模式,將大型系統或者複雜的應用分割成多個服務的架構,服務之間互相協調、互相配合,為用戶提供最終價值。每個服務都有獨立的生命周期,可以單獨的維護和部署,各個業務模塊之間是松耦合的,比傳統的應用程序更有效地利用計算資源,應用的擴展更加靈活,能夠通過擴展組件來處理功能瓶頸問題。這樣一來,開發人員只需要為額外的組件部署計算資源,而不需要部署一個完整的應用程序的全新迭代。
    一個微服務的架構如圖所示,單體應用被拆分成多個微小的服務:

    也有人將微服務的開發比喻成搭積木,每個服務都是一個零件,使用這些不同的服務可以搭建出不同的形狀。簡單的說,微服務架構就是把一個大系統按業務功能分解成多個職責單一的小系統,並利用簡單的方法使多個小系統相互協作,組合成一個大系統。
    其實這裏蘊含着自古以來的真理,就是分而治之,當一件事情大到不能處理的時候,就使用一定的切分方法,將其變成很多微小的類,然後分門別類的進行處理,以達到最好的效果,最終實現1+1>2的功效。
    微服務的優點和缺點(或者說挑戰)一樣明顯。
    優點

    • 開發簡單,每個服務完成獨立的功能

    • 技術棧靈活,可以選擇不同的語言完成不同的服務,發揮各種語言的最大優勢

    • 服務獨立無依賴,每個服務都可以單獨部署,一個服務出現問題不會導致整個系統癱瘓

    • 獨立按需擴展,以應對高併發以及大流量

    • 可用性高,當其中一個點出現問題時,能夠及時切換,不影響業務正常運行

    • 複雜應用解耦為小而眾的服務,拆分可以基於一定的原則,將耗時的應用解耦

    • 各服務精而專,也就是我們常說的專人干專事,映射到微服務中就是專服務干專事

    • 服務間通信通過API完成,選擇輕量的API通信

      缺點(挑戰)

    • 多服務運維難度,服務的增加意味着運維的困難,如何有效的管理是一個挑戰

    • 系統部署依賴,當業務複雜是,系統之間的耦合關係高度耦合,如何高效部署是一個挑戰

    • 服務間通信成本,包括網絡延遲,接口不可用性等,保證服務的高可用性是一個挑戰

    • 數據一致性,再各個服務間如何有效的共享數據,確保相應服務的數據需求一致性是一個挑戰

    • 系統集成測試,拆分后,原本需要測試的內容成倍的增加,如何高效的降低測試成本是一個挑戰

    • 重複工作,服務拆分之後,由於信息的不對稱導致的重複性工作,如何有效抽象是一個挑戰

    • 性能監控,原本只需要一個監控的部分,現在需要分開監控,如何快速定位問題是一個挑戰

    • 溝通成本的成倍增加,服務拆分后,各個服務由單獨的人來維護,如何高效的溝通是一個挑戰

    二、為什麼微服務

    從一般的平台遇到的問題說起,平台的問題包括:

    • 服務配置複雜。基礎服務多,服務的資源配置複雜。傳統方式管理服務複雜。
    • 服務之間調用複雜。檢索服務、用戶中心服務等,服務之間的調用複雜,依賴多。
    • 服務監控難度大。服務比較多,機器部署複雜,服務存活監控、業務是否正常監控尤為重要。
    • 服務化測試問題。服務依賴性比較大,測試一個小的功能,周邊服務也需要啟動。

    那麼微服務的架構有什麼優勢,大家為什麼都懷着極高的熱情來應對微服務呢。

    1. 區別

    首先看一下兩者的區別,單體應用和分佈式應用都有哪些優點和缺點呢,通過下面的表格來做一下分析和對比?

     

     

    從上面的表格我們可以看到,分佈式系統雖然有一些優勢,但也存在一些問題。

    • 架構設計變得複雜(尤其是其中的分佈式事務)。
    • 部署單個服務會比較快,但是如果一次部署需要多個服務,部署會變得複雜。
    • 系統的吞吐量會變大,但是響應時間會變長。
    • 運維複雜度會因為服務變多而變得很複雜。
    • 架構複雜導致學習曲線變大。
    • 測試和查錯的複雜度增大。
    • 技術可以很多樣,這會帶來維護和運維的複雜度。
    • 管理分佈式系統中的服務和調度變得困難和複雜。

    也就是說,分佈式系統架構的難點在於系統設計,以及管理和運維。

    接下來我們就一起來看一下架構的演變過程。

    2. 從單體應用說起

    下圖是我們非常熟悉的單體應用,或者說是非常傳統的應用架構。
    從圖中可以看到,應用通過瀏覽器進行訪問,當訪問到訂單服務的時候,分單服務提供相應的功能,然後去訪問應用的數據層,數據層負責對數據的解析,這是一個極其典型的單體應用結構,這種結構所帶來的問題顯而易見,數據庫存在單點,所有服務都耦合在一個應用中,當其中一個服務出現問題的時候,整個工程都需要重新發布,從而導致整體業務不能提供響應。這種結構在小項目中是沒有什麼問題的,而且操作起來非常靈活,但當業務量爆增的時候,就無法靈活應對了。

     

     

    3. 第一步切分

    為了應對單體應對無法滿足業務增漲,需要對數據庫進行進一步的切分,以提高擴展性,下圖就是一個切分后的架構圖。

    業務之間通過進程間的服務進行相互調用,數據庫之間沒有耦合性,不會存在單點故障。前端只需要調用相應的服務,返回自身需要的數據,然後與用戶進行交互。可以看到,這種方式比傳統的單體應用已經前進了一步,但是數據庫層面仍然存在着問題,根據數據量需要評估是否使用讀寫分離的設計,服務層面也增加了相應的複雜性。前端調用隨着調用接口數量的增加也急需治理。

    4. 服務化所帶來的問題

    隨着服務的拆分,新的問題應運而生。客戶端如何訪問這些服務?這些服務的調用情況,切分是否合理,安全問題,如果受到攻擊應該如何應對,是否可以使用限流或者降級的方式來及時解決。
    應對這些問題,API網關是一個不錯的解決方案。當有新的設備需要調用這些接口時,可以復用原有接口,不需要進行二次開發。接口的維護也會更有條理性,對於訪問次數,安全等問題,都可以在這一層進行解決。

    解決了應用前端訪問的問題后,服務之間的相互調用也是一個問題,如果整個系統內部調用關係混亂,就會帶來非常多的不必要的問題。所以約定好服務之間的通信方式是非常有必要的。不管是開源還是公司內部研發,都有非常多的解決方案。最簡單的方式,是通過rpc的調用,傳輸json或者XML,雙方定義好協議格式,通過報文的方式進行數據傳輸。
    當多種服務需要互相調用的時候,服務的數量會急劇的增加。服務的治理就成為新的問題。不同的服務的版本問題。如果不能通過一個單獨的註冊地址,像書的目錄一樣來管理整個服務結構,服務的調用就侍显示非常混亂。
    一般的分佈式服務,都有一個註冊中心,例如dubbo是基於zookeeper進行的二次開發,自身提供管理控制台,可以對服務進行註冊和查找,Spring Cloud有Eureka,還有etcd,consul等可供選擇。
    經過上面的處理,整體上來看應用已經達到了一個非常好的狀態,但是每個應用服務仍然存在着單點問題,當一個服務出現問題的時候,有可能導致連鎖的反應,使用整個系統癱瘓。這時候需要除了監控告警之外的一種容錯機制保障整體服務的可運行性。
    通過網關層的反向代理來實現高可用是一個不錯的解決方案,訪問層無需要了解整個體系有多少應用提供,只需要關心服務是否能夠提供服務,並且對必要的接口進行監測,當發現接口無法正常提供服務時,提供相應的告警機制,以微信、短信或者郵件的方式通知相關人及時進行處理。
    隨着服務的切分,業務的擴展,數據量的激增也是一個非常大的問題,如果採用傳統的方案來應對,各種關係型數據庫都有瓶頸,把運算量比較大,比較耗資源的運算,跨庫統計查詢的需求。
    再下一步隨着數據量的不斷增漲,需要業務和統計分離。
    統計一般都是比較耗時的應用,比如計算用戶的留存情況,需要分析一周甚至是更長周期內的用戶數據,如果使用在線的方式分析顯然不太現實,所以,對於大數據量的分析和數據挖掘,需要從業務中抽取數據進行離線分析,然後將分析的結果進行展現。

    5. 微服務的可擴展性

    針對以上的分析,可以看到,微服務需要具備極強的可擴展性,這些擴展性包含以下幾個方面:
    • 性能可擴展:性能無法完全實現線性擴展,但要盡量使用具有併發性和異步性的組件。具備完成通知功能的工作隊列要優於同步連接到數據庫。
    • 可用性可擴展:CAP理論表明,分佈式系統無法同時提供一致性、可用性和分區容錯性保證。許多大規模Web應用程序都為了可用性和分區容錯性而犧牲了強一致性,而後者則有賴於最終一致性來保證。
    • 維護可擴展:軟件和服務器都需要維護。在使用平台的工具監控和更新應用程序時,要盡可能地自動化。
    • 成本可擴展:總成本包括開發、維護和運營支出。在設計一個系統時,要在重用現有組件和完全新開發組件之間進行權衡。現有組件很少能完全滿足需求,但修改現有組件的成本還是可能低於開發一個完全不同的方案。另外,使用符合行業標準的技術使組織更容易聘到專家,而發布獨有的開源方案則可能幫助組織從社區中挖掘人才。

    6. 微服務與SOA的區別

    面向服務的架構(SOA)是一個組件模型,它將應用程序的不同功能單元(稱為服務)通過這些服務之間定義良好的接口和契約聯繫起來。接口是採用中立的方式進行定義的,它應該獨立於實現服務的硬件平台、操作系統和編程語言。這使得構建在各種各樣的系統中的服務可以以一種統一和通用的方式進行交互。
    都是做服務化,那麼微服務與SOA的異同有哪些呢?
    相同點:

    • 需要Registry,實現動態的服務註冊發現機制;
    • 需要考慮分佈式下面的事務一致性,CAP原則下,兩段式提交不能保證性能,事務補償機制需要考慮;
    • 同步調用還是異步消息傳遞,如何保證消息可靠性?SOA由ESB來集成所有的消息;
    • 都需要統一的Gateway來匯聚、編排接口,實現統一認證機制,對外提供APP使用的RESTful接口;
    • 同樣的要關注如何再分佈式下定位系統問題,如何做日誌跟蹤,就像我們電信領域做了十幾年的信令跟蹤的功能;

    差異點:

    • 是持續集成、持續部署?對於CI、CD(持續集成、持續部署),這本身和敏捷、DevOps是交織在一起的,所以這更傾向於軟件工程的領域而不是微服務技術本身;
    • 使用不同的通訊協議是不是區別?微服務的標杆通訊協議是RESTful,而傳統的SOA一般是SOAP,不過目前來說採用輕量級的RPC框架Dubbo、Thrift、gRPC非常多,在Spring Cloud中也有Feign框架將標準RESTful轉為代碼的API這種仿RPC的行為,這些通訊協議不應該是區分微服務架構和SOA的核心差別;
    • 是流行的基於容器框架還是虛擬機為主?Docker和虛擬機還是物理機都是架構實現的一種方式,不是核心區別;

    SOA和微服務的一個主要不同點就是自動化程度上的不同。大部分的SOA實現只達到服務級別的抽象,而微服務走的更遠,它達到了對實現和運行環境的抽象級別。
    而在一個規範的微服務中,每個微服務應該被構建成胖jar(fat Jar)其中內置了所有的依賴,然後作為一個單獨的java進程存在。

    三、常見的微服務組件

    既然談到了微服務架構,就說一下通用的微服務都包括哪些組件:

    • 服務註冊

    服務註冊是一個記錄當前可用的微服務實例的網絡信息數據庫,是服務發現機制的主要核心,服務註冊表查詢api、管理api,使用查詢api獲得可用服務的實例,使用管理api實現註冊、註銷。

    • 服務發現

    服務調用方從服務註冊中心找到自己需要調用的服務的地址。可以選擇客戶端服務發現,也可以選擇服務端服務發現。

    • 負載均衡

    服務提供方一般以多實例的形式提供服務,負載均衡功能能夠讓服務調用方連接到合適的服務節點。並且,節點選擇的工作對服務調用方來說是透明的。可以選擇服務端的負載均衡也可以選擇客戶端的負載均衡。

    • 服務網關

    服務網關是服務調用的唯一入口,可以在這個組件是實現用戶鑒權、動態路由、灰度發布、A/B測試、負載限流等功能。根據公司流量規模的大小網關可以是一個,也可以是多個。

    • 配置中心

    將本地化的配置信息(properties, XML, yaml等)註冊到配置中心,實現程序包在開發、測試、生產環境的無差別性,方便程序包的遷移。配置部分可以單獨使用高可用的分佈式配置中心,確保一個配置服務出現問題是,其它服務也能夠提供配置服務。

    • API管理

    以方便的形式編寫及更新API文檔,並以方便的形式供調用者查看和測試。通常需要加入版本控制的概念,以確保服務的不同版本在升級過程中都能夠提供服務。

    • 集成框架

    微服務組件都以職責單一的程序包對外提供服務,集成框架以配置的形式將所有微服務組件集成到統一的界面框架下,讓用戶能夠在統一的界面中使用系統。

    • 分佈式事務

    對於重要的業務,需要通過分佈式事務技術(TCC、高可用消息服務、最大努力通知)保證數據的一致性。根據業務的不同適當的需要犧牲一些數據的一致性要求,確保數據的最終一致性。

    • 調用鏈

    記錄完成一個業務邏輯時調用到的微服務,並將這種串行或并行的調用關係展示出來。在系統出錯時,可以方便地找到出錯點。同時統計各個服務的調用次數,確保比較熱的服務能夠分配更多的資源。

    • 支撐平台

    系統微服務化后,系統變得更加碎片化,系統的部署、運維、監控等都比單體架構更加複雜,那麼,就需要將大部分的工作自動化。現在,可以通過Docker等工具來中和這些微服務架構帶來的弊端。 例如持續集成、藍綠髮布、健康檢查、性能健康等等。可以這麼說,如果沒有合適的支撐平台或工具,就不要使用微服務架構。

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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