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

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

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

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

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

海洋
國際新聞
鯊魚

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

【其他文章推薦】

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

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

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

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

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

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

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

問題

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

基礎

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

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

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

什麼是 Swagger ?

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

為什麼使用 Swagger ?

主要的優點:

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

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

如何使用 Swagger

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

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

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

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

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

@Configuration
@EnableSwagger2
public class SwaggerConfig {

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

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

第三板斧使用 Swagger 註解

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

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

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

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

@ApiModel
public class User {

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

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

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

    public String getUserName() {
        return userName;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public Long getUserId() {
        return userId;
    }

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

效果如下:

實體註解

接口描述

接口參數

執行接口

返回地址

條件裝配 @Conditional 介紹

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

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

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

}
使用方法

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

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

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

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

@Configuration
public class ConditionalConfig {

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

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

    @Autowired
    private ApplicationContext applicationContext;

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

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

@Conditional擴展

註解介紹

配置文件參數獲取 @ConfigurationProperties

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

比較好的方式

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

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

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

Maven包的引入

 

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

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

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

@ConfigurationProperties("swagger")
public class SwaggerProperties {

    /**
     * 子系統
     */

    private String title;

    /**
     * 描述
     */

    private String description;

    /**
     * 版本號
     */

    private String version;

    /**
     * api包路徑
     */

    private String basePackage;

    public String getTitle() {
        return title;
    }

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

    public String getDescription() {
        return description;
    }

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

    public String getVersion() {
        return version;
    }

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

    public String getBasePackage() {
        return basePackage;
    }

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

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

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

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

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

}

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

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

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

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

關於使用也分為兩步,

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

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

後續的規劃

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

點點關注

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

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

【其他文章推薦】

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

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

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

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

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

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

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

批量發送

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

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

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

@Test
public void producerBatch() throws Exception {

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

再來看看消費端的代碼,

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

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

發送端的日誌如下:

sendResult:SEND_OK

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

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

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

consumer.setConsumeMessageBatchMaxSize(5);

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

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

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

消息過濾

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

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

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

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

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

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

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

  • IS NULL或者IS NOT NULL

  • 邏輯判斷,ANDORNOT

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

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

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

@Test
public void producerBatch() throws Exception {

    List<Message> messages = new ArrayList<>();
    for (int i = 0;i<3;i++) {
        MessageExt message = new MessageExt();
        message.setTopic("cluster-topic");
        message.setKeys("key-"+i);
        message.setBody(("this is batchMQ,my NO is "+i+"---"+new Date()).getBytes());

        int a = i+4;
        message.putUserProperty("a",String.valueOf(a));

        messages.add(message);
    }
    SendResult sendResult = defaultMQProducer.send(messages);
    System.out.println("sendResult:" + sendResult.getSendStatus().toString());
}

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

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

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

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

The broker does not support consumer to filter message by SQL92

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

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

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

filterSupportRetry=true
enablePropertyFilter=true

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

sendResult:SEND_OK

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

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

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

總結

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

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

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

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

【其他文章推薦】

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

【其他文章推薦】

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

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

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

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

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

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

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

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

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

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

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

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

nc 命令簡介

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

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

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

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

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

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

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

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

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

# vi port_scan.sh

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

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

$ chmod +x port_scan.sh

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

# sh port_scan.sh

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

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

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

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

# cat port-list.txt
22
80

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

# vi multiple_port_scan.sh

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

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

$ chmod +x multiple_port_scan.sh

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

# sh multiple_port_scan.sh
Connection to 192.168.1.2 22 port [tcp/ssh] succeeded!
Connection to 192.168.1.2 80 port [tcp/http] succeeded!

Connection to 192.168.1.3 22 port [tcp/ssh] succeeded!
Connection to 192.168.1.3 80 port [tcp/http] succeeded!

Connection to 192.168.1.4 22 port [tcp/ssh] succeeded!
Connection to 192.168.1.4 80 port [tcp/http] succeeded!

Connection to 192.168.1.5 22 port [tcp/ssh] succeeded!
Connection to 192.168.1.5 80 port [tcp/http] succeeded!

Connection to 192.168.1.6 22 port [tcp/ssh] succeeded!
Connection to 192.168.1.6 80 port [tcp/http] succeeded!

Connection to 192.168.1.7 22 port [tcp/ssh] succeeded!
Connection to 192.168.1.7 80 port [tcp/http] succeeded!

公眾號:良許Linux

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

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

【其他文章推薦】

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

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

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

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

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

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

背景

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

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

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

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

入手點

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

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

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

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

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

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

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

追根溯源

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

總結

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

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

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

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

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

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

【其他文章推薦】

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

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

背景

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

 

1>短期解決容量的問題

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

 

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

 

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

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

 

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

 

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

 

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

 

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

 

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

 

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

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

 

問題

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

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

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

 

解決方案分析

垂直流量拆分

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

 

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

 

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

 

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

 

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

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

 

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

 

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

 

分佈式關係型數據庫

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

 

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

 

 

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

 

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

 

NOSQL解決方案

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

 

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

 

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

 

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

 

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

 

系統共建的解決方案

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

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

【其他文章推薦】

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

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

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

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

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

上次被目擊是1913年!消失百年「沃茲考氏變色龍」現蹤

摘錄自2020年11月1日自由時報報導

這真的是百年一見!科學家在非洲島國馬達加斯加西北部,發現難為世人所見的變色龍「沃茲考氏變色龍」(Voeltzkow’s chameleon),上一次這種變色龍被目擊的紀錄,是在100多年前。

馬達加斯加和德國的研究人員10月30日宣佈這項發現,他們2018年在馬達加斯加西北部探察時,發現了幾隻活「沃茲考氏變色龍」。巴伐利亞自然歷史陳列館(ZSM)的科學家,在期刊「蠑螈」(Salamandra)發表的報告說,基因分析確認,這些物種是拉波德氏變色龍(Labord’s chameleon)的近親。

上一次發現沃茲考氏變色龍是在1913年,而且,之前也從未有關於雌性「沃茲考氏變色龍」的紀錄。研究人員說,雄性的沃茲考氏變色龍外觀為綠色,相比之下,母變色龍就繽紛得多。研究人員相信,這兩種變色龍都只在雨季生活:孵化、迅速成長、與競爭者較勁,然後交配、死亡,這些過程都在短短幾個月內完成。ZSM爬蟲和兩棲動物典藏研究員格勞(Frank Glaw)說,「這些動物基本上是脊椎動物中的浮游類」。現今大規模森林砍伐正威脅牠們的棲地。

生物多樣性
國際新聞
變色龍
瀕危物種

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

【其他文章推薦】

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

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

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

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

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

有德系有美系,這幾款大7座SUV比漢蘭達還牛得多

98-63。98萬細心的朋友可能會發現,探險者的外觀設計有幾分“路虎車”的影子,如果你真的有這樣的直覺,那就對了,探險者的確和“路虎”之間有點不為人知的故事,感興趣的話可以上網找找哦。超大的尺寸仍然是這個級別車型的亮點,外觀大氣是無可厚非的,探險者車身上的設計即帶有都市的時尚感,亦有硬派的越野風,這也是路虎車的風格。

要說大七座SUV,我想不少人第一時間就想到漢蘭達,但是對於大眾粉來說,第一時間想到的或許是大眾的途昂。途昂作為一款比途觀L 定位更高的SUV,售價區間為30.89-51.89萬,比起漢蘭達23.98-42.28萬的售價,雖然重疊度很高,但也能明顯看出途昂的定位要高於漢蘭達。

如果宏觀市面上的所有合資中大型SUV,途昂可是說是定價非常低的一款,以至於它沒有多少直接的競爭對手,這也是他能在最近一年時間里月銷量能到達1萬輛左右的原因之一,那實際上到底在這個價位還有誰能與之匹敵?我們把終端優惠的價格也考慮進去,發現豐田的普拉多和福特的探險者似乎也很吸引人,對比一波看看。

上汽大眾-途昂

指導價:30.89-51.89萬

很多人第一眼看到途昂的時候可能都會以為是途觀L再加長后的SUV,因為他們套用的是同一套設計語言,呈現的效果很一致。途昂除了尺寸更大了,其實也有其他明顯的區別,就是途昂前後翼子板的腰線呈隆起的形狀,使其整體看起來更霸氣。當時話說回來,其實大眾車最終還是講究由簡潔的設計營造高級感,所以其實這款途昂的外觀也就一般般吧,不太高級,只是比較霸氣而已。

一汽豐田-普拉多

指導價:46.48-63.68萬

普拉多的外觀造型可是說是整個SUV界最獨居一格的,在如今的SUV設計風格都以精緻化、年輕化的方向靠攏的環境下,普拉多依舊以“粗曠”的設計營造出硬派的風格,最獨特的當然是“兇悍”的前臉,還有就是滿身肌肉感的車身,雖然沒有什麼平直硬朗的線條,但整體車身外觀也彰顯出很強的力量感。

福特(進口)-探險者

指導價:44.98-63.98萬

細心的朋友可能會發現,探險者的外觀設計有幾分“路虎車”的影子,如果你真的有這樣的直覺,那就對了,探險者的確和“路虎”之間有點不為人知的故事,感興趣的話可以上網找找哦。超大的尺寸仍然是這個級別車型的亮點,外觀大氣是無可厚非的,探險者車身上的設計即帶有都市的時尚感,亦有硬派的越野風,這也是路虎車的風格。

尺寸對比

尺寸方面,三車的長度很接近,探險者的車寬最寬;普拉多的車高最高;而途昂的軸距是最長的,而且比另外兩部車都要長不少,長軸距的車型更利於車內空間的布置,不過由於三輛車都已經達到了中大型SUV級別,而且都是7座車型,所以空間方面其實不必多慮。

內飾對比

雖然三輛車都不是豪華品牌,但是從售價來看,三輛車都超越了普通品牌的水平,所以內飾方面也絕對不能落伍。途昂的內飾還是很“大眾”,滿滿的家族氣息,只不過比起其他大眾車型,橫向面積更舒展,而且用料也更高級。普拉多的內飾一直都飽受好評,坐進車內后很容易就被其舒適性所征服,軟材質應用到位。但探險者的內飾設計就比較中庸,這也是福特車的特點,設計一向以來都比較平淡,不過用料方面還是很厚道。

動力對比

途昂的動力配備,由於選擇比較多,而且還應用了2.0T低功率發動機,以此將其門檻拉得很低,186匹馬力,這麼大的車,其實還是有點勉強吧。普拉多則全系搭載3.5L自然吸氣發動機,配備6AT的變速箱,這套動力總成無論是在鋪裝路面的日常駕駛,還是在一般的越野道路都可以說游刃有餘吧。而探險者的最低動力是2.3T的渦輪增壓發動機,276匹,是肯定夠用的,這個無需擔心。

總結

從以上對比就可以看出,其實途昂的起步價能這麼低,主要還是入門車型搭載了一款功率比較一般的發動機。但是我們綜合各方面表現,特別是配置方面(由於途昂配置領先很多,所以這次沒作對比),途昂還是很有優勢的,所以結論是,大七座SUV,途昂非常值得推薦。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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