標籤: 租車

  • 我去,你竟然還不會用 Java final 關鍵字

    我去,你竟然還不會用 Java final 關鍵字

    寫一篇文章容易嗎?太不容易了,首先,需要一個安靜的環境,這一點就非常不容易。很多小夥伴的辦公室都是開放式的,非常吵,況且上班時間寫的話,領導就不高興了;只能抽時間寫。其次,環境有了,還要有一顆安靜的心,如果心裏裝着其他揮之不去的事,那就糟糕了,呆坐着電腦前一整天也不會有結果。

    我十分佩服一些同行,他們寫萬字長文,這在我看來,幾乎不太可能完成。因為我要日更,一萬字的長文,如果走原創的話,至少需要一周時間,甚至一個月的時間。

    就如小夥伴們看到的,我寫的文章大致都能在五分鐘內閱讀完,並且能夠保證小夥伴們在閱讀完學到或者溫習到一些知識。這就是我的風格,通俗易懂,輕鬆幽默。

    好了,又一篇我去系列的文章它來了:你竟然還不會用 final 關鍵字。

    已經晚上 9 點半了,我還沒有下班,因為要和小王一塊修復一個 bug。我訂了一份至尊披薩,和小王吃得津津有味的時候,他突然問了我一個問題:“老大,能給我詳細地說說 final 關鍵字嗎,總感覺對這個關鍵字的認知不夠全面。”

    一下子我的火氣就來了,儘管小王問的態度很謙遜,很卑微,但我還是忍不住破口大罵:“我擦,小王,你丫的竟然不會用 final,我當初是怎麼面試你進來的!”

    發火歸發火,我這個人還是有原則的,等十點半回到家后,我決定為小王專門寫一篇文章,好好地講一講 final 關鍵字,也希望給更多的小夥伴一些幫助。

    儘管繼承可以讓我們重用現有代碼,但有時處於某些原因,我們確實需要對可擴展性進行限制,final 關鍵字可以幫助我們做到這一點。

    01、final 類

    如果一個類使用了 final 關鍵字修飾,那麼它就無法被繼承。如果小夥伴們細心觀察的話,Java 就有不少 final 類,比如說最常見的 String 類。

    public final class String
        implements java.io.SerializableComparable<String>, CharSequence,
                   ConstableConstantDesc 
    {}

    為什麼 String 類要設計成 final 的呢?原因大致有以下三個:

    • 為了實現字符串常量池
    • 為了線程安全
    • 為了 HashCode 的不可變性

    更詳細的原因,可以查看我之前寫的一篇文章。

    任何嘗試從 final 類繼承的行為將會引發編譯錯誤,為了驗證這一點,我們來看下面這個例子,Writer 類是 final 的。

    public final class Writer {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    嘗試去繼承它,編譯器會提示以下錯誤,Writer 類是 final 的,無法繼承。

    不過,類是 final 的,並不意味着該類的對象是不可變的。

    Writer writer = new Writer();
    writer.setName("沉默王二");
    System.out.println(writer.getName()); // 沉默王二

    Writer 的 name 字段的默認值是 null,但可以通過 settter 方法將其更改為“沉默王二”。也就是說,如果一個類只是 final 的,那麼它並不是不可變的全部條件。

    如果,你想了解不可變類的全部真相,請查看我之前寫的文章這次要說不明白immutable類,我就怎麼地。突然發現,寫系列文章真的妙啊,很多相關性的概念全部涉及到了。我真服了自己了。

    把一個類設計成 final 的,有其安全方面的考慮,但不應該故意為之,因為把一個類定義成 final 的,意味着它沒辦法繼承,假如這個類的一些方法存在一些問題的話,我們就無法通過重寫的方式去修復它。

    02、final 方法

    被 final 修飾的方法不能被重寫。如果我們在設計一個類的時候,認為某些方法不應該被重寫,就應該把它設計成 final 的。

    Thread 類就是一個例子,它本身不是 final 的,這意味着我們可以擴展它,但它的 isAlive() 方法是 final 的:

    public class Thread implements Runnable {
        public final native boolean isAlive();
    }

    需要注意的是,該方法是一個本地(native)方法,用於確認線程是否處於活躍狀態。而本地方法是由操作系統決定的,因此重寫該方法並不容易實現。

    Actor 類有一個 final 方法 show()

    public class Actor {
        public final void show() {

        }
    }

    當我們想要重寫該方法的話,就會出現編譯錯誤:

    如果一個類中的某些方法要被其他方法調用,則應考慮事被調用的方法稱為 final 方法,否則,重寫該方法會影響到調用方法的使用。

    一個類是 final 的,和一個類不是 final,但它所有的方法都是 final 的,考慮一下,它們之間有什麼區別?

    我能想到的一點,就是前者不能被繼承,也就是說方法無法被重寫;後者呢,可以被繼承,然後追加一些非 final 的方法。沒毛病吧?看把我聰明的。

    03、final 變量

    被 final 修飾的變量無法重新賦值。換句話說,final 變量一旦初始化,就無法更改。之前被一個小夥伴問過,什麼是 effective final,什麼是 final,這一點,我在之前的文章也有闡述過,所以這裏再貼一下地址:

    http://www.itwanger.com/java/2020/02/14/java-final-effectively.html

    1)final 修飾的基本數據類型

    來聲明一個 final 修飾的 int 類型的變量:

    final int age = 18;

    嘗試將它修改為 30,結果編譯器生氣了:

    2)final 修飾的引用類型

    現在有一個普通的類 Pig,它有一個字段 name:

    public class Pig {
       private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    在測試類中聲明一個 final 修飾的 Pig 對象:

     final Pig pig = new Pig();

    如果嘗試將 pig 重新賦值的話,編譯器同樣會生氣:

    但我們仍然可以去修改 Pig 的字段值:

    final Pig pig = new Pig();
    pig.setName("特立獨行");
    System.out.println(pig.getName()); // 特立獨行

    3)final 修飾的字段

    final 修飾的字段可以分為兩種,一種是 static 的,另外一種是沒有 static 的,就像下面這樣:

    public class Pig {
       private final int age = 1;
       public static final double PRICE = 36.5;
    }

    非 static 的 final 字段必須有一個默認值,否則編譯器將會提醒沒有初始化:

    static 的 final 字段也叫常量,它的名字應該為大寫,可以在聲明的時候初始化,也可以通過 static [代碼塊初始化]()。

    4) final 修飾的參數

    final 關鍵字還可以修飾參數,它意味着參數在方法體內不能被再修改:

    public class ArgFinalTest {
        public void arg(final int age) {
        }

        public void arg1(final String name) {
        }
    }

    如果嘗試去修改它的話,編譯器會提示以下錯誤:

    04、總結

    親愛的讀者朋友,我應該說得很全面了吧?我想小王看到了這篇文章后一定會感謝我的良苦用心的,他畢竟是個积極好學的好同事啊。

    如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀,回復「併發」更有一份阿里大牛重寫的 Java 併發編程實戰,從此再也不用擔心面試官在這方面的刁難了。

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

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

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

  • SpringBoot 2.3 整合最新版 ShardingJdbc + Druid + MyBatis 實現分庫分表

    SpringBoot 2.3 整合最新版 ShardingJdbc + Druid + MyBatis 實現分庫分表

      今天項目不忙,想搞一下shardingJDBC分庫分表看看,主要想實現以下幾點:

    1. 捨棄xml配置,使用.yml或者.properties文件+java的方式配置spring。
    2. 使用 Druid 作為數據庫連接池,同時開啟監控界面,並支持監控多數據源。
    3. 不依賴 com.dangdangsharding-jdbc-core 包。此包過於古老,最後一次更新在2016年。目測只是封裝了一層,意義不大。感覺如果不是dangdang公司內部開發,沒必要用這個包。(且本人實測不能和最新的Druid包一起用,insert語句報錯)

      折騰了半天,網上找的例子大部分跑不通。直接自己從零開搞,全部組件直接上當前最新版本。

      SpringBoot: 2.3.0

      mybatis: 2.1.3

      druid: 1.1.22

      sharding-jdbc: 4.1.1

      注意:這裏因為是自己邊看源碼邊配置,(sharding官網的例子可能是版本問題基本沒法用,GitHub 我這裏網絡基本打不開),所以數據源和sharding大部分用java代碼配置。了解配置原理后,也可以簡化到 .yml / .properties 文件中。

    Sharding-JDBC簡介

      Apache ShardingSphere 是一套開源的分佈式數據庫中間件解決方案組成的生態圈,它由 JDBC、Proxy 和 Sidecar(規劃中)這 3 款相互獨立,卻又能夠混合部署配合使用的產品組成。

      Sharding-JDBC定位為輕量級 Java 框架,在 Java 的 JDBC 層提供的額外服務。 它使用客戶端直連數據庫,以 jar 包形式提供服務,無需額外部署和依賴,可理解為增強版的 JDBC 驅動,完全兼容 JDBC 和各種 ORM 框架。

    • 適用於任何基於 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。
    • 支持任何第三方的數據庫連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
    • 支持任意實現JDBC規範的數據庫。目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 標準的數據庫。

    Sharding配置示意圖

      簡單的理解如下圖,對sharding-jdbc進行配置,其實就是對所有需要進行分片的表進行配置。對錶的配置,則主要是對分庫的配置和分表的配置。這裏可以只分庫不分表,或者只分表不分庫,或者同時包含分庫和分表邏輯。

     

      先看一下我的項目目錄結構整體如下:

      

    一、POM依賴配置

      完整的pom表如下,其中主要是對 mysql-connector-java、mybatis-spring-boot-starter、druid-spring-boot-starter、sharding-jdbc-core 的依賴。

      注意:sharding-jdbc-core 我用的4.0+的版本,因為已經晉陞為 apache 基金會的頂級項目,其 groupId 變為了 org.apache.shardingsphere,之前是io.shardingsphere。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.0.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>shardingjdbc</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>shardingjdbc</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <!--<sharding.jdbc.version>3.0.0</sharding.jdbc.version>-->
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.3</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.22</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>sharding-jdbc-core</artifactId>
                <version>4.1.1</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.16</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.5</version>
            </dependency>
        </dependencies>
    
        <build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                    </includes>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

    pom.xml

    二、application.properties

      這裏配置了兩個數據源,為避免和自動裝配產生衝突,屬性前綴要和自動裝配掃描的前綴區分開,這裏我用 datasource0datasource1

      下面 spring.datasource.druid 開頭的配置,會被 druid 的代碼自動掃描裝配。

    #################################### common config : ####################################
    spring.application.name=shardingjdbc
    # 應用服務web訪問端口
    server.port=8080
    
    # mybatis配置
    mybatis.mapper-locations=classpath:com/example/shardingjdbc/mapper/*.xml
    mybatis.type-aliases-package=com.example.shardingjdbc.**.entity
    
    datasource0.url=jdbc:mysql://localhost:3306/test0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    datasource0.driver-class-name=com.mysql.cj.jdbc.Driver
    datasource0.type=com.alibaba.druid.pool.DruidDataSource
    datasource0.username=root
    datasource0.password=852278
    
    datasource1.url=jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    datasource1.driver-class-name=com.mysql.cj.jdbc.Driver
    datasource1.type=com.alibaba.druid.pool.DruidDataSource
    datasource1.username=root
    datasource1.password=852278
    
    #
    ##### 連接池配置 #######
    # 過濾器設置(第一個stat很重要,沒有的話會監控不到SQL)
    spring.datasource.druid.filters=stat,wall,log4j2
    
    ##### WebStatFilter配置 #######
    #啟用StatFilter
    spring.datasource.druid.web-stat-filter.enabled=true
    #添加過濾規則
    spring.datasource.druid.web-stat-filter.url-pattern=/*
    #排除一些不必要的url
    spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
    #開啟session統計功能
    spring.datasource.druid.web-stat-filter.session-stat-enable=true
    #缺省sessionStatMaxCount是1000個
    spring.datasource.druid.web-stat-filter.session-stat-max-count=1000
    #spring.datasource.druid.web-stat-filter.principal-session-name=
    #spring.datasource.druid.web-stat-filter.principal-cookie-name=
    #spring.datasource.druid.web-stat-filter.profile-enable=
    
    ##### StatViewServlet配置 #######
    #啟用內置的監控頁面
    spring.datasource.druid.stat-view-servlet.enabled=true
    #內置監控頁面的地址
    spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
    #關閉 Reset All 功能
    spring.datasource.druid.stat-view-servlet.reset-enable=false
    #設置登錄用戶名
    spring.datasource.druid.stat-view-servlet.login-username=admin
    #設置登錄密碼
    spring.datasource.druid.stat-view-servlet.login-password=123
    #白名單(如果allow沒有配置或者為空,則允許所有訪問)
    spring.datasource.druid.stat-view-servlet.allow=127.0.0.1
    #黑名單(deny優先於allow,如果在deny列表中,就算在allow列表中,也會被拒絕)
    spring.datasource.druid.stat-view-servlet.deny=

    三、數據源和分片配置

      如下代碼,先從配置文件讀取數據源的所需要的屬性,然後生成 Druid 數據源。注意這裏配置語句中的 setFilters,如果不添加 filters,則 Duird 監控界面無法監控到sql。另外,其他諸如最大連接數之類的屬性這裏沒有配,按需配置即可。數據源創建好后,添加到 dataSourceMap 集合中。

      再往下註釋比較清楚,構造 t_user 表的分片規則(包括分庫規則 + 分表規則),然後將所有表的分片規則組裝成 ShardingRuleConfiguration

      最後,將前兩步配好的 dataSourceMapshardingRuleConfiguration 交給 ShardingDataSourceFactory,用來構造數據源。

      到這裏,sharding 、druid 的配置代碼就都寫好了。剩下基本都是業務代碼了。

    package com.example.shardingjdbc.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.example.shardingjdbc.sharding.UserShardingAlgorithm;
    import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
    import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
    import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
    import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    
    @Configuration
    public class DataSourceConfig {
        @Value("${datasource0.url}")
        private String url0;
        @Value("${datasource0.username}")
        private String username0;
        @Value("${datasource0.password}")
        private String password0;
        @Value("${datasource0.driver-class-name}")
        private String driverClassName0;
    
        @Value("${datasource1.url}")
        private String url1;
        @Value("${datasource1.username}")
        private String username1;
        @Value("${datasource1.password}")
        private String password1;
        @Value("${datasource1.driver-class-name}")
        private String driverClassName1;
    
        @Value(("${spring.datasource.druid.filters}"))
        private String filters;
    
        @Bean("dataSource")
        public DataSource dataSource() {
            try {
                DruidDataSource dataSource0 = new DruidDataSource();
                dataSource0.setDriverClassName(this.driverClassName0);
                dataSource0.setUrl(this.url0);
                dataSource0.setUsername(this.username0);
                dataSource0.setPassword(this.password0);
                dataSource0.setFilters(this.filters);
    
                DruidDataSource dataSource1 = new DruidDataSource();
                dataSource1.setDriverClassName(this.driverClassName1);
                dataSource1.setUrl(this.url1);
                dataSource1.setUsername(this.username1);
                dataSource1.setPassword(this.password1);
                dataSource1.setFilters(this.filters);
    
                //分庫設置
                Map<String, DataSource> dataSourceMap = new HashMap<>(2);
                //添加兩個數據庫database0和database1
                dataSourceMap.put("ds0", dataSource0);
                dataSourceMap.put("ds1", dataSource1);
    
                // 配置 t_user 表規則
                TableRuleConfiguration userRuleConfiguration = new TableRuleConfiguration("t_user", "ds${0..1}.t_user${0..1}");
                // 配置分表規則
                userRuleConfiguration.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("id", UserShardingAlgorithm.tableShardingAlgorithm));
                // 配置分庫規則
                userRuleConfiguration.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("id", UserShardingAlgorithm.databaseShardingAlgorithm));
                // Sharding全局配置
                ShardingRuleConfiguration shardingRuleConfiguration = new ShardingRuleConfiguration();
                shardingRuleConfiguration.getTableRuleConfigs().add(userRuleConfiguration);
                // 創建數據源
                DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfiguration, new Properties());
                return dataSource;
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
    }

    DataSourceConfig.java

      上面構造分片規則的時候,我定義了User表的分片算法類 UserShardingAlgorithm,並定義了兩個內部類分別實現了數據庫分片和表分片的邏輯。代碼如下:

    package com.example.shardingjdbc.sharding;
    
    import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
    import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
    
    import java.util.Collection;
    
    public class UserShardingAlgorithm {
        public static final DatabaseShardingAlgorithm databaseShardingAlgorithm = new DatabaseShardingAlgorithm();
        public static final TableShardingAlgorithm tableShardingAlgorithm = new TableShardingAlgorithm();
    
        static class DatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
            @Override
            public String doSharding(Collection<String> databaseNames, PreciseShardingValue<Long> shardingValue) {
                for (String database : databaseNames) {
                    if (database.endsWith(String.valueOf(shardingValue.getValue() % 2))) {
                        return database;
                    }
                }
    
                return "";
            }
        }
    
        static class TableShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
            @Override
            public String doSharding(Collection<String> tableNames, PreciseShardingValue<Long> shardingValue) {
                for (String table : tableNames) {
                    if (table.endsWith(String.valueOf(shardingValue.getValue() % 2))) {
                        return table;
                    }
                }
    
                return "";
            }
        }
    }

    UserShardingAlgorithm.java

      這裏實現分片規則時,實現的接口是 PreciseShardingAlgorithm,即精確分片,將指定的鍵值記錄映射到指定的1張表中(最多1張表)。這個接口基本上能滿足80%的需求了。

      其他的還有 Range、ComplexKey、Hint分片規則,這3種都可以將符合條件的鍵值記錄映射到多張表,即可以將記錄 a 同時插入A、B 或 B、C多張表中。

      其中,

        Range 是範圍篩選分片。我個人理解,比如id尾數1-5插入A表,6-0插入B表,這種情況,使用Range作為篩選條件更方便。也可以根據時間範圍分片。(如有誤請指正)。

        ComplexKey 看名字就是組合鍵分片,可以同時根據多個鍵,制定映射規則。

        Hint 看名字沒看懂,但看源碼其實也是組合鍵分片,但僅支持對組合鍵進行精確篩選。

        而 ComplexKey 支持對組合鍵進行範圍篩選。所以可以理解為 ComplexKey 是 Hint 的高級版本。  

      不管實現哪種分片算法,都要確保算法覆蓋所有可能的鍵值。

    四、使用行表達式配置分片策略(對第三步優化,可略過)

        上面第三步,我們通過實現 PreciseShardingValue 接口,來定義分片算法。這樣每有一張表需要分片,都要重新定義一個類,太麻煩。

      Sharding 提供了行表達式配置的方式,對簡單的分片邏輯,直接定義一個行表達式即可。(這種方式其實就是直接在 .yml 文件中配置分片策略的解析方式)

      和上面的代碼類似,這裏之改動了6、8行,直接 new 一個 InlineShardingStrategyConfiguration,省去了定義分片算法類的繁瑣步驟。

     

     1              // .....省略其他代碼
     2  
     3             // 配置 t_user 表規則
     4             TableRuleConfiguration userRuleConfiguration = new TableRuleConfiguration("t_user", "ds${0..1}.t_user${0..1}");
     5             // 行表達式分表規則
     6             userRuleConfiguration.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "t_user${id % 2}"));
     7             // 行表達式分庫規則
     8             userRuleConfiguration.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "ds${id % 2}"));
     9 
    10             // Sharding全局配置
    11             ShardingRuleConfiguration shardingRuleConfiguration = new ShardingRuleConfiguration();
    12             shardingRuleConfiguration.getTableRuleConfigs().add(userRuleConfiguration);
    13             // 創建數據源
    14             DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfiguration, new Properties());
    15             return dataSource;

    五、分佈式主鍵(雪花算法)

      分庫后,不能再使用 mysql 的自增主鍵,否則會產生重複主鍵。自定義主鍵,主要需要解決兩個問題:

    1. 主鍵唯一(必須)
    2. 主鍵單調遞增(可選)(提升索引效率,減少索引重排產生的空間碎片)

      Sharding 內部提供了2個主鍵生成器,一個使用雪花算法 SnowflakeShardingKeyGenerator,一個使用 UUID(考慮上面第2條,因此不使用 UUID)。

      雪花算法的主要原理:用一個 64 bit 的 long 型数字做主鍵。其中,

        第 1 位,1 bit 作為符號位永遠為 0,表示是正數。

        第 2 – 42 位, 41 個 bit 填充時間戳。

        第 43 – 52 位,10 個 bit 填充機器唯一id。舉個例子,可以用前4位標識機房號,后6位標識機器號。

        第 53 – 64 位,12 個 bit 填充id序號。範圍 0 – 4095,即每台機器每 1 毫秒最多生成 4096 個不同的主鍵id。

      雪花算法的主要實現代碼如下

    1. 先判斷時鐘是否回調。這裏默認容忍回調時間為0,如有回調則會產生異常。可以通過配置 max.tolerate.time.difference.milliseconds 屬性,讓其自旋等待時鐘回到上一次執行時間。
    2. 按當前毫秒數,遞增生成id序號。如果時鐘進入了下一毫秒,則從0開始重新生成id序號,範圍 0 – 4095。
    3. 將 時間戳 + 機器序號 + id序號 拼裝成 主鍵id。這裏機器序號默認為0,可以通過 worker.id 屬性進行配置。不同的服務器需要配置成不同的数字,範圍 0 – 1023。

      其中 EPOCH 是時鐘起點,sharding中設置的是2016年11月1日,那麼41位的時間戳差不多可以用70年,一直到2086年。

        public synchronized Comparable<?> generateKey() {
            long currentMilliseconds = timeService.getCurrentMillis();
            if (this.waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) {
                currentMilliseconds = timeService.getCurrentMillis();
            }
    
            if (this.lastMilliseconds == currentMilliseconds) {
                if (0L == (this.sequence = this.sequence + 1L & 4095L)) {
                    currentMilliseconds = this.waitUntilNextTime(currentMilliseconds);
                }
            } else {
                this.vibrateSequenceOffset();
                this.sequence = (long)this.sequenceOffset;
            }
    
            this.lastMilliseconds = currentMilliseconds;
            return currentMilliseconds - EPOCH << 22 | this.getWorkerId() << 12 | this.sequence;
        }

    六、業務代碼

      使用分佈式的主鍵ID生成器,需要給不同的表注入不同的ID生成器,在config包下加一個KeyIdConfig類,如下:

      (為了保持時鐘的統一,可以專門找一台機器作為時鐘服務,然後給所有主鍵生成器配置統一的時鐘服務。下圖中未配置,如需配置,直接調用setTimeService方法即可)

    @Configuration
    public class KeyIdConfig {
        @Bean("userKeyGenerator")
        public SnowflakeShardingKeyGenerator userKeyGenerator() {
            return new SnowflakeShardingKeyGenerator();
        }
    
        @Bean("orderKeyGenerator")
        public SnowflakeShardingKeyGenerator orderKeyGenerator() {
            return new SnowflakeShardingKeyGenerator();
        }
    }

      其他業務代碼,整體如下:

    package com.example.shardingjdbc.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.util.Date;
    
    @Data
    public class User implements Serializable {
        private Long id;
        private String name;
        private String phone;
        private String email;
        private String password;
        private Integer cityId;
        private Date createTime;
        private Integer sex;
    }

    User.java

    package com.example.shardingjdbc.mapper;
    
    import com.example.shardingjdbc.entity.User;
    import org.apache.ibatis.annotations.Mapper;
    
    import java.util.List;
    
    public interface UserMapper {
        /**
         * 保存
         */
        void save(User user);
    
        /**
         * 查詢
         * @param id
         * @return
         */
        User get(Long id);
    }

    UserMapper.java

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.shardingjdbc.mapper.UserMapper">
        <resultMap id="resultMap" type="com.example.shardingjdbc.entity.User">
            <id column="id" property="id" />
            <result column="name" property="name" />
            <result column="phone" property="phone"  />
            <result column="email" property="email"  />
            <result column="password" property="password"  />
            <result column="city_id" property="cityId"  />
            <result column="create_time" property="createTime"  />
            <result column="sex" property="sex"  />
        </resultMap>
    
        <insert id="save">
            insert into t_user (id, name, phone, email, password, city_id, create_time, sex)
            values (#{id}, #{name}, #{phone}, #{email}, #{password}, #{cityId}, #{createTime}, #{sex})
        </insert>
    
        <select id="get" resultMap="resultMap">
            select *
            from t_user
            where id = #{id}
        </select>
    </mapper>

    UserMapper.xml

     1 package com.example.shardingjdbc.controller;
     2 
     3 import com.example.shardingjdbc.entity.User;
     4 import com.example.shardingjdbc.mapper.UserMapper;
     5 import org.apache.shardingsphere.core.strategy.keygen.SnowflakeShardingKeyGenerator;
     6 import org.springframework.beans.factory.annotation.Autowired;
     7 import org.springframework.stereotype.Controller;
     8 import org.springframework.web.bind.annotation.PathVariable;
     9 import org.springframework.web.bind.annotation.RequestMapping;
    10 import org.springframework.web.bind.annotation.ResponseBody;
    11 
    12 import javax.annotation.Resource;
    13 import java.util.Date;
    14 
    15 @Controller
    16 public class UserController {
    17     @Autowired
    18     private UserMapper userMapper;
    19 
    20     @Resource
    21     SnowflakeShardingKeyGenerator userKeyGenerator;
    22 
    23     @RequestMapping("/user/save")
    24     @ResponseBody
    25     public String save() {
    26         for (int i = 0; i < 50; i++) {
    27             Long id = (Long)userKeyGenerator.generateKey();
    28             User user = new User();
    29             user.setId(id);
    30             user.setName("test" + i);
    31             user.setCityId(i);
    32             user.setCreateTime(new Date());
    33             user.setSex(i % 2 == 0 ? 1 : 2);
    34             user.setPhone("11111111" + i);
    35             user.setEmail("xxxxx");
    36             user.setCreateTime(new Date());
    37             user.setPassword("eeeeeeeeeeee");
    38             userMapper.save(user);
    39         }
    40 
    41         return "success";
    42     }
    43 
    44     @RequestMapping("/user/get/{id}")
    45     @ResponseBody
    46     public User get(@PathVariable Long id) {
    47         User user = userMapper.get(id);
    48         return user;
    49     }
    50 }

    UserController.java

     1 CREATE TABLE `t_user` (
     2   `id` bigint(20) NOT NULL,
     3   `name` varchar(64) DEFAULT NULL COMMENT '名稱',
     4   `city_id` int(12) DEFAULT NULL COMMENT '城市',
     5   `sex` tinyint(1) DEFAULT NULL COMMENT '性別',
     6   `phone` varchar(32) DEFAULT NULL COMMENT '電話',
     7   `email` varchar(32) DEFAULT NULL COMMENT '郵箱',
     8   `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '創建時間',
     9   `password` varchar(32) DEFAULT NULL COMMENT '密碼',
    10   PRIMARY KEY (`id`)
    11 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    t_user.sql

      啟動類如下:

     1 package com.example.shardingjdbc;
     2 
     3 import org.mybatis.spring.annotation.MapperScan;
     4 import org.springframework.boot.SpringApplication;
     5 import org.springframework.boot.autoconfigure.SpringBootApplication;
     6 
     7 @MapperScan("com.example.shardingjdbc.mapper")
     8 @SpringBootApplication
     9 public class ShardingjdbcApplication {
    10     public static void main(String[] args) {
    11         SpringApplication.run(ShardingjdbcApplication.class, args);
    12     }
    13 }

    ShardingjdbcApplication .java

      注意,這裏我在啟動類上加了 @MapperScan 註解。可能是因為引用依賴的問題,.properties 配置的 mybatis 包掃描目錄不管用了,後面有時間再研究。

    七、其他

      除了基本的分庫分表規則以外,還有一些其他的配置,比如綁定表。這裏先不詳細解釋了,舉個簡單的例子:

      現在有 order, order_detail兩張表,1 : 1的關係。

      在配置的時候,應該將相同 order_id 的 order 記錄 和 order_detail 記錄 映射到相同尾號的表中,方便連接查詢。

      比如 id % 2 = 1的,都插入到  order0, order_detail0 中。

      如果配置了綁定關係,那麼查找 id = 1 的記錄,只會產生一次查詢 select * from order0 as o join order_detail0 as d  on o.order_id = d.order_id where o.oder_id = 1。

      否則會產生笛卡兒積查詢, 

        select * from order0 as o join order_detail0 as d  on o.order_id = d.order_id where o.order_id = 1

        select * from order0 as o join order_detail1 as d  on o.order_id = d.order_id where o.order_id = 1

        select * from order1 as o join order_detail0 as d  on o.order_id = d.order_id where o.order_id = 1

        select * from order1 as o join order_detail1 as d  on o.order_id = d.order_id where o.order_id = 1

    八、總結

      項目啟動前,先創建數據庫 test0, test1, 然後分別建表 t_user0, t_user1。 可以全部在同一台機器。

      項目啟動后,訪問 http://localhost:8080/user/save, id 是 偶數的都插入到了 test0 庫的 t_user0 表中, 奇數的都插入到了 test1 庫中的 t_user1 表中。

      druid 的後台監控頁面地址: http://localhost:8080/druid/。

      項目啟動后,sharding日誌會將配置已 yml 格式的形式打印出來,也可以省去 java 配置,將其優化到 .yml 配置文件中去,如下圖:

      

      本文原文地址:https://www.cnblogs.com/lyosaki88/p/springboot_shardingjdbc_druid_mybatis.html

      源碼下載地址:https://474b.com/file/14960372-448059323

      作者QQ: 116269651

     

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • Java規則引擎 Easy Rules

    Java規則引擎 Easy Rules

    1.  Easy Rules 概述

    Easy Rules是一個Java規則引擎,靈感來自一篇名為《Should I use a Rules Engine?》的文章 

    規則引擎就是提供一種可選的計算模型。與通常的命令式模型(由帶有條件和循環的命令依次組成)不同,規則引擎基於生產規則系統。這是一組生產規則,每條規則都有一個條件(condition)和一個動作(action)———— 簡單地說,可以將其看作是一組if-then語句。

    精妙之處在於規則可以按任何順序編寫,引擎會決定何時使用對順序有意義的任何方式來計算它們。考慮它的一個好方法是系統運行所有規則,選擇條件成立的規則,然後執行相應的操作。這樣做的好處是,很多問題都很自然地符合這個模型:

    if car.owner.hasCellPhone then premium += 100;
    if car.model.theftRating > 4 then premium += 200;
    if car.owner.livesInDodgyArea && car.model.theftRating > 2 then premium += 300;

    規則引擎是一種工具,它使得這種計算模型編程變得更容易。它可能是一個完整的開發環境,或者一個可以在傳統平台上工作的框架。生產規則計算模型最適合僅解決一部分計算問題,因此規則引擎可以更好地嵌入到較大的系統中。

    你可以自己構建一個簡單的規則引擎。你所需要做的就是創建一組帶有條件和動作的對象,將它們存儲在一個集合中,然後遍歷它們以評估條件並執行這些動作。 

    Easy Rules它提供Rule抽象以創建具有條件和動作的規則,並提供RuleEngine API,該API通過一組規則運行以評估條件並執行動作。 

    Easy Rules簡單易用,只需兩步:

    首先,定義規則,方式有很多種

    方式一:註解

    @Rule(name = "weather rule", description = "if it rains then take an umbrella")
    public class WeatherRule {
    
        @Condition
        public boolean itRains(@Fact("rain") boolean rain) {
            return rain;
        }
        
        @Action
        public void takeAnUmbrella() {
            System.out.println("It rains, take an umbrella!");
        }
    }

    方式二:鏈式編程

    Rule weatherRule = new RuleBuilder()
            .name("weather rule")
            .description("if it rains then take an umbrella")
            .when(facts -> facts.get("rain").equals(true))
            .then(facts -> System.out.println("It rains, take an umbrella!"))
            .build();

    方式三:表達式

    Rule weatherRule = new MVELRule()
            .name("weather rule")
            .description("if it rains then take an umbrella")
            .when("rain == true")
            .then("System.out.println(\"It rains, take an umbrella!\");");

    方式四:yml配置文件

    例如:weather-rule.yml

    name: "weather rule"
    description: "if it rains then take an umbrella"
    condition: "rain == true"
    actions:
      - "System.out.println(\"It rains, take an umbrella!\");"
    MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
    Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml"));

    接下來,應用規則

    public class Test {
        public static void main(String[] args) {
            // define facts
            Facts facts = new Facts();
            facts.put("rain", true);
    
            // define rules
            Rule weatherRule = ...
            Rules rules = new Rules();
            rules.register(weatherRule);
    
            // fire rules on known facts
            RulesEngine rulesEngine = new DefaultRulesEngine();
            rulesEngine.fire(rules, facts);
        }
    }

    入門案例:Hello Easy Rules

    <dependency>
        <groupId>org.jeasy</groupId>
        <artifactId>easy-rules-core</artifactId>
        <version>4.0.0</version>
    </dependency>

    通過骨架創建maven項目:

    mvn archetype:generate \
        -DarchetypeGroupId=org.jeasy \
        -DarchetypeArtifactId=easy-rules-archetype \
        -DarchetypeVersion=4.0.0

    默認給我們生成了一個HelloWorldRule規則,如下:

    package com.cjs.example.rules;
    
    import org.jeasy.rules.annotation.Action;
    import org.jeasy.rules.annotation.Condition;
    import org.jeasy.rules.annotation.Rule;
    
    @Rule(name = "Hello World rule", description = "Always say hello world")
    public class HelloWorldRule {
    
        @Condition
        public boolean when() {
            return true;
        }
    
        @Action
        public void then() throws Exception {
            System.out.println("hello world");
        }
    
    }

    2.  規則定義

    2.1.  定義規則

    大多數業務規則可以用以下定義表示:

    • Name : 一個命名空間下的唯一的規則名稱
    • Description : 規則的簡要描述
    • Priority : 相對於其他規則的優先級
    • Facts : 事實,可立即為要處理的數據
    • Conditions : 為了應用規則而必須滿足的一組條件
    • Actions : 當條件滿足時執行的一組動作 

    Easy Rules為每個關鍵點提供了一個抽象來定義業務規則。

    在Easy Rules中,Rule接口代表規則

    public interface Rule {
    
        /**
        * This method encapsulates the rule's conditions.
        * @return true if the rule should be applied given the provided facts, false otherwise
        */
        boolean evaluate(Facts facts);
    
        /**
        * This method encapsulates the rule's actions.
        * @throws Exception if an error occurs during actions performing
        */
        void execute(Facts facts) throws Exception;
    
        //Getters and setters for rule name, description and priority omitted.
    
    }

    evaluate方法封裝了必須計算結果為TRUE才能觸發規則的條件。execute方法封裝了在滿足規則條件時應該執行的動作。條件和操作由Condition和Action接口表示。

    定義規則有兩種方式:

    • 通過在POJO類上添加註解
    • 通過RuleBuilder API編程

    可以在一個POJO類上添加@Rule註解,例如:

    @Rule(name = "my rule", description = "my rule description", priority = 1)
    public class MyRule {
    
        @Condition
        public boolean when(@Fact("fact") fact) {
            //my rule conditions
            return true;
        }
    
        @Action(order = 1)
        public void then(Facts facts) throws Exception {
            //my actions
        }
    
        @Action(order = 2)
        public void finally() throws Exception {
            //my final actions
        }
    
    }

    @Condition註解指定規則條件
    @Fact註解指定參數
    @Action註解指定規則執行的動作

    RuleBuilder支持鏈式風格定義規則,例如:

    Rule rule = new RuleBuilder()
                    .name("myRule")
                    .description("myRuleDescription")
                    .priority(3)
                    .when(condition)
                    .then(action1)
                    .then(action2)
                    .build();

    組合規則

    CompositeRule由一組規則組成。這是一個典型地組合設計模式的實現。

    組合規則是一個抽象概念,因為可以以不同方式觸發組合規則。

    Easy Rules自帶三種CompositeRule實現:

    • UnitRuleGroup : 要麼應用所有規則,要麼不應用任何規則(AND邏輯)
    • ActivationRuleGroup : 它觸發第一個適用規則,並忽略組中的其他規則(XOR邏輯)
    • ConditionalRuleGroup : 如果具有最高優先級的規則計算結果為true,則觸發其餘規則

    複合規則可以從基本規則創建並註冊為常規規則:

    //Create a composite rule from two primitive rules
    UnitRuleGroup myUnitRuleGroup = new UnitRuleGroup("myUnitRuleGroup", "unit of myRule1 and myRule2");
    myUnitRuleGroup.addRule(myRule1);
    myUnitRuleGroup.addRule(myRule2);
    
    //Register the composite rule as a regular rule
    Rules rules = new Rules();
    rules.register(myUnitRuleGroup);
    
    RulesEngine rulesEngine = new DefaultRulesEngine();
    rulesEngine.fire(rules, someFacts);

    每個規則都有優先級。它代表觸發註冊規則的默認順序。默認情況下,較低的值表示較高的優先級。可以重寫compareTo方法以提供自定義優先級策略。

    2.2.  定義事實

    在Easy Rules中,Fact API代表事實

    public class Fact<T> {
         private final String name;
         private final T value;
    }

    舉個栗子:

    Fact<String> fact = new Fact("foo", "bar");
    Facts facts = new Facts();
    facts.add(fact);

    或者,也可以用這樣簡寫形式

    Facts facts = new Facts();
    facts.put("foo", "bar");

    用@Fact註解可以將Facts注入到condition和action方法中

    @Rule
    class WeatherRule {
    
        @Condition
        public boolean itRains(@Fact("rain") boolean rain) {
            return rain;
        }
    
        @Action
        public void takeAnUmbrella(Facts facts) {
            System.out.println("It rains, take an umbrella!");
            // can add/remove/modify facts
        }
    
    }

    2.3.  定義規則引擎

    Easy Rules提供兩種RulesEngine接口實現:

    • DefaultRulesEngine : 根據規則的自然順序應用規則
    • InferenceRulesEngine : 持續對已知事實應用規則,直到不再適用任何規則為止 

    創建規則引擎:

    RulesEngine rulesEngine = new DefaultRulesEngine();
    
    // or
    
    RulesEngine rulesEngine = new InferenceRulesEngine();

    然後,註冊規則

    rulesEngine.fire(rules, facts);

    規則引擎有一些可配置的參數,如下圖所示:

    舉個栗子:

    RulesEngineParameters parameters = new RulesEngineParameters()
        .rulePriorityThreshold(10)
        .skipOnFirstAppliedRule(true)
        .skipOnFirstFailedRule(true)
        .skipOnFirstNonTriggeredRule(true);
    
    RulesEngine rulesEngine = new DefaultRulesEngine(parameters);

    2.4. 定義規則監聽器

    通過實現RuleListener接口

    public interface RuleListener {
    
        /**
         * Triggered before the evaluation of a rule.
         *
         * @param rule being evaluated
         * @param facts known before evaluating the rule
         * @return true if the rule should be evaluated, false otherwise
         */
        default boolean beforeEvaluate(Rule rule, Facts facts) {
            return true;
        }
    
        /**
         * Triggered after the evaluation of a rule.
         *
         * @param rule that has been evaluated
         * @param facts known after evaluating the rule
         * @param evaluationResult true if the rule evaluated to true, false otherwise
         */
        default void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) { }
    
        /**
         * Triggered on condition evaluation error due to any runtime exception.
         *
         * @param rule that has been evaluated
         * @param facts known while evaluating the rule
         * @param exception that happened while attempting to evaluate the condition.
         */
        default void onEvaluationError(Rule rule, Facts facts, Exception exception) { }
    
        /**
         * Triggered before the execution of a rule.
         *
         * @param rule the current rule
         * @param facts known facts before executing the rule
         */
        default void beforeExecute(Rule rule, Facts facts) { }
    
        /**
         * Triggered after a rule has been executed successfully.
         *
         * @param rule the current rule
         * @param facts known facts after executing the rule
         */
        default void onSuccess(Rule rule, Facts facts) { }
    
        /**
         * Triggered after a rule has failed.
         *
         * @param rule the current rule
         * @param facts known facts after executing the rule
         * @param exception the exception thrown when attempting to execute the rule
         */
        default void onFailure(Rule rule, Facts facts, Exception exception) { }
    
    }

    3.  示例

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.cjs.example</groupId>
        <artifactId>easy-rules-quickstart</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <packaging>jar</packaging>
        <dependencies>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-core</artifactId>
                <version>4.0.0</version>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-support</artifactId>
                <version>4.0.0</version>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-mvel</artifactId>
                <version>4.0.0</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.7.30</version>
            </dependency>
        </dependencies>
    </project>

    4.  擴展

    規則本質上是一個函數,如y=f(x1,x2,..,xn)

    規則引擎就是為了解決業務代碼和業務規則分離的引擎,是一種嵌入在應用程序中的組件,實現了將業務決策從應用程序代碼中分離。

    還有一種常見的方式是Java+Groovy來實現,Java內嵌Groovy腳本引擎進行業務規則剝離。

    https://github.com/j-easy/easy-rules/wiki

     

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

  • EAS:基於網絡轉換的神經網絡結構搜索 | AAAI 2018

    EAS:基於網絡轉換的神經網絡結構搜索 | AAAI 2018

    論文提出經濟實惠且高效的神經網絡結構搜索算法EAS,使用RL agent作為meta-controller,學習通過網絡變換進行結構空間探索。從指定的網絡開始,通過function-preserving transformation不斷重用其權重,EAS能夠重用之前學習到的知識進行高效地探索新的結構,僅需要10 GPU days即可

    來源:曉飛的算法工程筆記 公眾號

    論文: Efficient Architecture Search by Network Transformation

    • 論文地址:https://arxiv.org/abs/1707.04873

    Introduction

      為了加速神經網絡搜索過程,論文提出EAS(Efficient Architecture Search),meta-controller通過網絡轉化(network transformation)操作進行結構空間探索,操作包含拓寬層,插入層,增加skip-connections等。為了繼續使用學習到的權重,基於function-preserving transformation來初始化新的不同參數的網絡,再進一步訓練來提高性能,能夠顯著地加速訓練過程。對於meta-controller,則結合了最近的強化學習方法

    Architecture Search by Net Transformation

      整體算法邏輯如圖1,meta-controller學習如何對當前網絡中進行網絡轉換,為了學習多種網絡轉換操作以及不增加meta-contreoller複雜性,使用encoder network來學習當前網絡的低維表達,然後傳到actor netowrk來生成一個確定的模型轉換動作。為了處理不定長的網絡結構輸入以及考慮整體網絡結構,使用bidrectional recurrent network以及input embedding layer

    Actor Networks

      給予輸入結構的低維表達,每個actor network給予特定的網絡轉換動作,共有兩種actor network,分別是Net2Wider actor和Net2Depper

    • Net2Wider Actor

      Net2Wider在保持網絡功能下替換網絡的某一層為更寬的層,例如對於全連接層是增加unit數,對於卷積層是增加卷積核數。對於卷積層的卷積核$K_l$,shape為$(k_wl,k_hl,f_il,f_ol)$,分別代表卷積核寬高以及輸入和輸出的維度數,將當前層替換成更寬的層即$\hat {f}_ol>f_ol$

      首先介紹隨機映射函數$G_l$,可以獲得新卷積核$\hat{K}_l[k_wl,k_hl,f_il,f_jl]$,第一個$f_ol$直接從$K_l$中獲得,剩餘的$\hat{f}_ol-f_o^l$維根據$G_l$從$K_l$中隨機選擇一維,因此,更寬的新層的輸出特徵$\hat{O}_l=O_l(G_l(j))$

      為了保持原有的功能,由於輸入多了複製的部分,下一層的卷積核$K_{l+1}$需要修改,新卷積核$\hat{K}_{l+1}$的shap維$(k_w{l+1},k_h{l+1},\hat{f}_i{l+1}=\hat{f}_ol,f_o^{l+1})$,公式3的意思大概是,權重要除以前一層對應維度複製的次數,以保證$l+1$層輸出跟之前一樣

      為了方便,論文使用的Net2Wider actor同時決定處理的層,對於encoder netowrk輸出的每一層對應的hidden state使用shared sigmoid分類器,另外將卷積的核數或全連接的unit數進行分區,直接將決定的層的對應參數升至下一個區間,例如$32\to 64$

    • Net2Deeper Actor

      Net2DeeperNet操作向網絡中插入新的層,然後將其初始化成跟插入identity mapping一樣,保持其原來的功能。對於新的卷積層,將其卷積核設為identity卷積核,對於全連接層,則將權重矩陣設為identiy矩陣,因此,新層必須與前一層有一樣的核數或unit。另外,想要保持原來網絡的性能,對於激活函數$\phi$,必須滿足$\phi(I\phi(v))=\phi(v)$,ReLU能滿足,而Sigmoid和thnh不能,但仍然可以重用帶sigmoid或tanh激活的網絡的權重,畢竟這樣總比隨機初始化要好。另外,當使用BN時,要設置其scale和bias為undo normalization,而不是初始化為1和0

      Net2Deeper actor的結構如圖3,為一個循環神經網絡,hidden state初始化為encoder network的最後一個hidden state。將CNN結構根據pooling的位置分成多個block,首先確定插入的block,然後再確定插入層的下標,對於新的卷積網絡,agent還需要確定卷積核大小和步長,而對於全連接層則不需要。在卷積網絡中,全連接層需要在網絡的頂層,如果插入位置在全局池化或全連接後面,新層則指定為全連接層

    Function-preserving Transformation for DenseNet

      原始的Net2Net設定網絡是layer-to-layer的,沒有并行層,但目前的網絡大都將單層的輸入應用到多個後續的多個層中,直接應用Net2Net會出現問題,因此論文對其進行了改進。對於DenseNet,$l{th}$層會將所有前面的層concatenate成輸入$[O_0,O_1,…,O_{l-1}]$,標記$l{th}$層的卷積核為$K_l$,shape為$(k_wl,k_hl,f_il,f_ol)$
      假設需要拓寬層並保持其功能,首先根據Net2WiderNet操作按公式1和公式2生成新層$\hat{K}_l$,這樣新輸出為$\hat{O}_l=O_l(G_l(j))$,由於$l^{th}$的輸出會傳遞到多層,$\hat{O}_l$的複製會傳遞到後面的所有層,所以要修改所有後續的層

      對於$m{th}>l$層,輸入變為$[O_0,…,O_{l-1},\hat{O}_l,O_{l+1},…,O_{m-1}]$,將隨機映射函數改為公式4,$f_o{0:l}=\sum_{v=0}{l-1}f_ov$為$l^{th}$層的所有輸入數量,公式4的第一部分為$[O_0,…,O_{l-1}]$,第二部分為$\hat{O}l$,第三部分為$[O{l+1},…,O_{m-1}]$

      $\hat{G}m$的簡單示意如上,前面的為新層的index,後面為對應的舊的維度index,然後$m^{th}$層的新權重直接使用替換成$\hat{G}m$的公式3獲得
      假設要在DenseNet的$l{th}$層插入新層,新層輸入為$O_{new}$,輸出為$[O_0,O_1,…,O_l]$。因此,對於$m{th}>l$層,插入后的輸入為$[O_0,…,O
    {l},O
    {new},O_{l+1},…,O_{m-1}]$,為了按照類似Net2WiderNet那樣保持性能,$O_{new}$應該為$[O_0,O_1,…,O_l]$中的一個複製

      新層的每個卷積核可以表示為tensor$\hat{F}$,shape為$(k_w{new},k_h{new},f_i{new}=f_o{0:l+1})$,第三項為輸入channel數。為了讓$\hat{F}$的輸入為$[O_0,O_1,…,O_l]$的$n^{th}$項,按照類似公式5的方式進行設置(假設卷積寬高為3),其它卷積核設為0,當新層的的輸出設定好后。建立一個特定的隨機映射(這裡是按照層建立映射,前面是按照映射建立層),然後按照公式4和公式3來修改後續的層的卷積核

    Experiments and Results

      EAS使用5 GPU進行搜索,有兩種設定,第一種為普通卷積網絡空間,僅包含卷積、池化和全連接,第二種為DenseNet結構空間

    Training Details

      meta-controller為單層雙向LSTM,50個hidden units,embedding size為16,使用ADAM進行訓練。每輪通過網絡轉換採樣10個模型,由於重用了特徵,每個模型只訓練20輪(原來為50輪),初始學習率調低為0.02,使用SGD訓練,對準確率$acc$進行線性變化來放大高準確率的收益,例如$tanh(acc_v\times \pi/2)$,另外,每個卷積和全連接後面接ReLU和BN

    Explore Plain CNN Architecture Space

    • Start with Small Network

      初始網絡如表1,卷積核大小為${1,3,5 {}}$,卷積核數量為${16,32,64,96,128,192,256,320,384,448,512 }$,全卷積的unit數為${64,128,256,384,512,640,768,896,1024 }$

      訓練分為兩階段,第一階段每輪將起始網絡進行5次Net2Deeper和4次Net2Wider,採樣夠300個網絡后,選取表現最好的網絡訓練100輪作為下階段的輸入。第二階段也是進行5次Net2Deeper和4次Net2Wider,採樣150個網絡后停止,取最好模型進行300輪迭代。結果如表2的depth=16,整體需要10 GPU days,共450個網絡

    • Further Explore Larger Architecture Space

      將上一個實驗的最好模型作為這次實驗的起點,結果如表2的depth=20,另外與SOTA進行了對比,結果如表3

    • Comparison Between RL and Random Search

    Explore DenseNet Architecture Space

      將DenseNet-BC(L=40,k=40)作為起點,結果如表4

    CONCLUSION

      論文提出經濟實惠且高效的神經網絡結構搜索算法EAS,使用RL agent作為meta-controller,學習通過網絡變換進行結構空間探索。從指定的網絡開始,通過function-preserving transformation不斷重用其權重,EAS能夠重用之前學習到的知識進行高效地探索新的結構,僅需要10 GPU days即可



    如果本文對你有幫助,麻煩點個贊或在看唄~
    更多內容請關注 微信公眾號【曉飛的算法工程筆記】

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

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

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

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

  • 我敢說這幾款是2016年最成功的自主車型!你同不同意?

    我敢說這幾款是2016年最成功的自主車型!你同不同意?

    如果要盤點2016年自主品牌中最有設計感的車型,寶駿510必定首當其沖。這款定位僅在6萬左右的SUV,居然擁有如此前衛和新潮的設計,不得不佩服上汽高層的決策。叫獸在後台大家對510的評論中可以預料到,這必將又是一款未上市已成爆款的車型。

    又到了辭舊迎新的日子,我們即將迎來嶄新的2017年。回望2016,汽車圈內發生了不少大事,也湧現出了非常多的好車。其中不少中國品牌的表現尤其搶眼,叫獸似乎從它們身上看到了正在崛起的“Made in China”,讓我對2017充滿期待。

    如今GS4甚至成了傳祺的代名詞,目前傳祺絕大部分的銷量和產能均來自GS4。廣汽同樣也審時度勢,在“SUV熱”持續不降的情形下推出了GS8。這款以中大型7座SUV身份登場的GS8,憑藉非常符合國人審美的大氣造型迅速俘獲了眾多青睞。

    據說現在GS8也是一車難求的狀態,訂單量已經排到了明年開年以後。傳祺現在的當務之急是儘快擴大產量,以保證GS4和GS8的銷量需求。對了,傳祺全新的MpV – GM8也將在明年初上市,2017將是廣汽傳祺非常有挑戰和希望的一年。

    SUV市場已經成為兵家必爭之地,幾乎所有中國品牌都在這看到了“彎道超車”的希望。最典型的如哈弗H6成了月銷超7萬的“無敵神車”(叫獸的印象中只有五菱達到過這樣的成績)。

    在殺得“頭破血流”的SUV爭奪中,想成為爆款可沒那麼簡單。榮威旗下的最新SUV – RX5,通過全新的面貌以及有馬雲“加持”的首款量產互聯網SUV身份登場,從北京車展亮相便收穫了極大關注。上市兩個月後,成為月銷破2萬的“黑馬”,風頭甚至蓋過了博越。

    廣州車展亮相的首款量產互聯網轎車 – 榮威i6和MG 推出的最新互聯網SUV – ZS再次成為焦點。以上3款車型雖然都打着互聯網的旗號,但叫獸相信互聯網只是錦上添花,真正讓它們成為“網紅”的是上汽旗下產品設計以及品質的全面提升。要知道,這些才是一款車成功的根本。

    寶駿是諸多中國品牌中比較特殊的存在,它身上流淌着上汽、通用以及五菱三家的血液。自730熱銷以及SUV 560成為“黑馬”后,寶駿也頻頻進入大眾的視線。

    這都不算啥,真正讓寶駿走向公眾焦點的是310。這款擁有超高顏值的入門小車,以3.68萬的起售價贏得了無數年輕消費者的關注和喜愛。要知道如今入門級轎車市場已經沒有幾款拿得出手的車型了。而310的推出將成為不少第一次買車的消費者的最新選擇,叫獸甚至認為它還影響到部分二手車的銷量。事實上,310已經連續兩個月月銷破萬,這樣的成績對早已被“邊緣化”的A0級市場無疑是一劑強心針。

    如果要盤點2016年自主品牌中最有設計感的車型,寶駿510必定首當其沖。這款定位僅在6萬左右的SUV,居然擁有如此前衛和新潮的設計,不得不佩服上汽高層的決策。叫獸在後台大家對510的評論中可以預料到,這必將又是一款未上市已成爆款的車型。

    2016是吉利最“吉利”的一年,去年上市的首款“3.0精品”車型 – 博瑞成了吉利集團成立近20年最重要的分水嶺之一。收購沃爾沃之後,吉利“蟄伏”5年終於放出了“大招”,以一款中高級轎車博瑞拉開了“3.0精品”的序幕。

    吉利的選擇很大膽,以高開高走的策略打造全新的精品之路。幸運的是博瑞成功了,甚至成為中國品牌汽車發展多年來首款真正成功的進軍高端的轎車。家族旗艦的成功,也將吉利帶入了一個新的高度,隨後以“3.0精品”身份誕生的博越、帝豪GS、帝豪GL以及僅僅只能算是改款的遠景SUV無一不成為月銷過萬的爆款車型。

    銷量說明一切。11月,吉利月銷量首次突破10萬大關,不僅老李笑得合不攏嘴,對整个中國品牌來說都是一件值得驕傲的事情。

    10月20號,吉利正“春風得意”的時候,總裁安聰慧在德國召開了一場“舉世奪目”的發布會,推出了全新高端品牌- LYNK & CO並亮相兩款概念車。LYNK & CO的目標對手為大眾、豐田等,從此吉利集團旗下品牌將覆蓋到各個檔次。

    我想說老李的野心可真不小。無論如何,吉利為中國品牌起了個好頭,值得我們肯定,加油吧。

    以上是2016年裡關注度最高的幾个中國品牌。誠然從銷量上它們已經達到和超越與外國品牌競爭的水平,甚至不少車型在價格上比大眾等一線合資品牌還要“堅挺”。這是好事,一來反映出中國品牌已經逐漸被廣大消費者認可,再也不用靠低價吸引顧客;二來說明中國品牌車型的造車水準和品牌影響有了質的提升,這才是最根本也是最重要的。

    誇了這麼多,叫獸也不得不提醒一句,雖然中國品牌看似在這兩年集體爆發,但細心的你一定會發現成功的品牌和車型大部分都靠的是SUV。迎合市場潮流成功固然可取,但全面均衡的發展更重要。偏科可不是優秀生,只有SUV、轎車、MpV多領域的成功才是真正的“三好學生”。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 教授為你盤點去年3-30萬最受關注車型

    教授為你盤點去年3-30萬最受關注車型

    3英寸液晶儀錶盤,看起來很有科技感。至於動力方面,第三代EA888發動機搭配7速雙離合變速箱,滿滿的德味,行駛品質依舊出色。在售價沒有大漲的前提下進行了這麼大的提升,全新邁騰總算沒有讓車迷失望。小型SUV:上汽通用五菱-寶駿510關注點:和寶駿310一樣有了前面310的成功經驗,那麼上汽通用五菱做起寶駿510這款的時候就更有信心了。

    不知不覺已經到2017年了,像這種文藝肌肉男,每年到這個時候總是難免感嘆時間流逝之快。正當我們停下來傷春悲秋的時候,汽車圈卻依然像幻變的風雲一樣,不斷髮展。在逝去的2016年,我們聽到最多的是哪些車的名字?是加價提車一車難求的思域?還是被吐槽萬遍依然銷量爆炸的H6?今天就盤點了這些2016年你們一定關注過的幾款車,看看誰是今年的“大明星”。

    小型轎車:上汽通用五菱-寶駿310

    關注點:配置齊全、外觀帥氣、價格超低

    從今年4月份北京車展的發布之後,時尚靚麗的造型、懸浮式的中控屏、以及比大眾polo還要大的車身尺寸,讓寶駿310受到不少年輕人的關注,再加上傳聞3萬多的起售價,那更是把寶駿310推到了關注的新高度。

    不出所料,在上市之後,憑藉3.68-4.98萬元的超低售價,迅速吸引了不少買家,在11月份的銷量已經突破了1.5W輛。其實寶駿310的成功也是不無道理,細想一下,同尺寸的競爭對手就是大眾polo、本田飛度等車,但它們對比310 卻毫無價格優勢,而同價格的自主品牌,卻只有比亞迪F0或者奇瑞QQ這樣的產品,產品力根本就不能和310相提並論。所以寶駿310 的成功,是離不開寶駿對消費者需求的正確解讀的。

    緊湊型轎車:東風本田-思域

    關注點:高性能、大空間、運動化的外觀

    認為思域確實是年度最受關注的緊湊型車,1.5T的發動機,爆發出177ps的馬力,單是這一點,已經讓很多對手都望塵莫及了,更何況它有溜背造型的車尾以及后多連桿獨立懸挂,試問在十五萬級別的車裡面,論產品力,誰能跟思域抗衡?

    然而,加價提車卻成了車迷心裏面永恆的痛,小則三四千,多則上萬,從思域上市至今,該車在各大經銷商就一直處於加價提車並且一車難求的狀態。而這一點也是制約着思域銷量的障礙,雖然月銷量才剛剛破萬,但是依然沒有降低廣大車迷對思域的關注,希望有天思域能夠不再加價提車,以回報車迷們的熱情吧。

    中型轎車:一汽大眾-邁騰

    關注點:新平台、第三代EA888發動機

    良久以來,帕薩特車系都一直是國內平民商務轎車的典範,親民的價格,大氣的外觀,舒適的駕乘體驗,想起買中型車,不買日系的話,基本就是往帕薩特/邁騰這方向考慮的了。然而,上一代的邁騰採用的是國外帕薩特B6的平台,而帕薩特B8早在2015年初就已經發布,所以國內的大眾車迷一直都在翹首以盼換代邁騰的到來,因此全新邁騰自然受到不少關注。

    實際上,該車的實力是值得肯定的,首先在外觀上改變了圓頭圓腦的造型,採用了扁平硬朗的設計,既時尚又穩重。同時內在的變化也不少,如360度全景攝像、以及跟奧迪類似的12.3英寸液晶儀錶盤,看起來很有科技感。至於動力方面,第三代EA888發動機搭配7速雙離合變速箱,滿滿的德味,行駛品質依舊出色。在售價沒有大漲的前提下進行了這麼大的提升,全新邁騰總算沒有讓車迷失望。

    小型SUV:上汽通用五菱-寶駿510

    關注點:和寶駿310一樣

    有了前面310的成功經驗,那麼上汽通用五菱做起寶駿510這款的時候就更有信心了。雖然該車尚未上市,但是在廣州車展發布以來,其漂亮的外觀以及精緻的內飾對於本來購車預算不大的消費者來說的確是很大的誘惑,可謂是國內車市在今年的一顆重磅炸彈。

    從動力系統上看,採用了通用的L2B發動機,排量為1.5L,前期推出的三款車型均搭載手動變速箱,期待後續有自動擋或者AMT車型的加入,畢竟寶駿的AMT其實做得不錯。說發動機的編號大家可能覺得陌生,但說起凱越和英朗這兩款別克車,大家就很有親切感了,沒錯,這個L2B發動機同樣搭載在凱越和英朗身上,在燃油經濟性和可靠性方面的現都很出色,相信這個發動機會在寶駿510身上繼續發光發熱。

    緊湊型SUV:哈弗H6

    關注點:銷量王、可選車型多

    油耗高、動力平庸、底盤也是逆向研發CRV得出來的H6,雖然被不少人吐槽,但是他卻依然熱銷,就在剛過去的11月,月銷量突破7萬輛,這讓古今中外多少車企都望塵莫及啊。

    H6的車型確實很多,想購買這輛車的朋友可能一時間真的不知道該怎麼選,其實可以用排除法選,首先在售的有1.5T汽油、2.4L汽油和2.0T柴油三種發動機,然後在根據自己的需要選擇手動或者自動變速箱,再來就是根據可以接受的價錢選擇一款車型即可。實際上,想長安CS75、吉利博越和傳祺GS4這些車型在看來都比哈弗H6要好,值得大家考慮。

    中型SUV:眾泰SR9

    關注點:十萬元就能買到保時捷……的外觀

    眾泰SR9一經推出就引起了熱議,但實際上其實都是調侃多於肯定的,甚至有人說這輩子能不能開上蘭博基尼就要看眾泰了。正當我們以為這輛車會被輿論推上風口浪尖的時候,11月11日上市的SR9當月就賣出了3286輛,不得不承認,眾泰是有他自己一套生存法則的。

    撇開外觀來談談這輛車的話,首先內飾上面來看,布局是明顯抄保時捷的了,特別是變速箱周圍密密麻麻的按鍵,遠看像極了保時捷,但是近看之後,粗糙的做工就會展露無遺。動力方面,採用三菱的4G63T發動機搭配手動變速箱或者雙離合變速箱,動力匹配的火候明顯未到家,中低速行駛的平順性乏善可陳,動力調校的功底多年來都沒有明顯的提高,難以形成自身的核心競爭力。但無論怎樣,這輛車在2016年的尾巴的確掀起了一個不小的“十萬買保時捷”風波。

    總結

    在我們關注的這些車裡面,要麼就是在性能方面有建樹,如新思域、新邁騰,要麼就是自帶賣點,如銷量王H6、十萬保時捷SR9。但相信,想要走得更遠,核心技術始終是最強有力的支撐,希望2017年將會有更多優秀的自主品牌汽車出現吧。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

  • 從7萬到25萬,這些車為了衝量竟然最高降3.7萬!

    從7萬到25萬,這些車為了衝量竟然最高降3.7萬!

    6L自動擋版本綜合油耗在5毛錢以內。別克君威指導價:17。89-27。99萬降價幅度:3。7萬推薦車型:2015款1。6T領先技術型君威作為別克的一款中高檔轎車,定位介於英朗和君越之間,屬於一款20萬級別的中型車,在乘坐空間,配置,做工用料上,都比代步車上了一個層次,而這款車的優惠幅度也是同級別車型中最大的,最高達到了3。

    許多經銷商們為了年底衝量,在7萬-25萬這個區間的車型,許多都放出了最大降價的優惠,從小型車,緊湊型車再到中級車,SUV,羅列了幾款降價幅度很大的好品質車型,供大家參考參考。

    雪鐵龍愛麗舍

    指導價:8.38-12.08萬元

    降價幅度:1.5萬元

    車型:2016款1.6L自動豪華型

    愛麗舍是法國雪鐵龍推出的一款家用轎車,自2002年誕生以來,用戶好口碑一直不斷積累着,為了針對中國人的審美觀,新款愛麗舍擁有更加動感的車身線條和造型設計,廠家似乎正通過這款車和消費者說:其實我們要造好造型還是不差於別人的。

    愛麗舍內飾的整體風格簡潔,清新,高雅,且空調出風口設計得很大,製造出風最好的效率,每個區域分隔清晰,操作非常方便,車型上推薦買自動豪華版,因為配置豐富帶ESp,真皮座椅,倒車影像,安全側氣囊等,優惠完車身價大概在10萬元左右。動力則全系搭載1.6L自然吸氣發動機,匹配5擋手動和6擋手自一體變速器。

    車主評價:動力充足,配置夠用,外形沉穩時尚,底盤舒服有韌性,剎車感覺非常線性。

    大眾高爾夫

    指導價:12.19-23.99萬

    降價幅度:2.8萬

    推薦車型:2016款 1.6L自動時尚型

    從1974年開始,大眾高爾夫就誕生了,直至今年,已經在全球市場推出七代車型,也是大眾最暢銷的車型之一,在中國,兩廂的高爾夫非常受人們歡迎,比起六代,七代高爾夫在外觀造型上更加犀利運動。

    內飾風格同樣也是大眾風格,簡約實用,對於家用代步來說,車型版本選擇1.6L自然吸氣排量就夠了,如果不需要天窗的可以選自動時尚型,匹配6擋手自一體,配置安全側氣囊,ESp車身穩定控制,上坡輔助,自動駐車等等,而如果是喜歡追尋動力的年輕人,可以考慮1.4T渦輪增壓發動機版本,匹配7擋雙離合變速器,更有操控和駕駛的樂趣。

    車主評價:外觀好看,舒適度滿意,轉向精準,隔音做得蠻好,油耗低,1.6L自動擋版本綜合油耗在5毛錢以內。

    別克君威

    指導價:17.89-27.99萬

    降價幅度:3.7萬

    推薦車型:2015款1.6T領先技術型

    君威作為別克的一款中高檔轎車,定位介於英朗和君越之間,屬於一款20萬級別的中型車,在乘坐空間,配置,做工用料上,都比代步車上了一個層次,而這款車的優惠幅度也是同級別車型中最大的,最高達到了3.7萬元。

    推薦的這款1.6T領先技術型,匹配6速手自一體變速箱,算是各方面相對均衡的一個版本,動力夠用,配置齊全,帶有胎壓監測,ESp,上坡輔助,電動天窗,自動空調等等,再加上美系車蠻好的隔音效果,優質的底盤響應,用料十分厚道,總有一種讓人們覺得這個價格買得值的感覺。

    車主口碑:隔音好,外觀公認好看,車身穩重,安全感強烈。

    現代ix35

    指導價:14.98-22.28萬

    降價幅度:2.8萬元

    推薦車型:2015款 2.0L自動兩驅智能型國IV

    現代ix35的外形設計由德國法蘭克福研發中心完成,帶有明顯歐式風格,時尚前衛,動感強韌,韓系車都喜歡將錢花在大家都看得到的地方,同樣的價格,假如這台車外形更好看的話,中國老百姓就喜歡,就覺得值,同樣車內空間的拓展上做得非常好,後備箱也一樣寬大。

    內飾方面,營造了一種溫馨家用的風格,做工細膩手感順滑,並採用大面積的銀色裝飾,方向盤採用真皮縫製,配置非常貼心,配備有安全側氣囊,無鑰匙進入與啟動,ESp,上坡輔助,電動天窗等等,同等價位里這個配置挺高分的了,搭載有2.0L和2.4L自然吸氣發動機,匹配6速手動和6速手自一體變速器。

    車主評價:同等級對比后,內部空間大,配置高,性價比強。

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • 汽車的頭枕為什麼怎麼調都不舒服?

    汽車的頭枕為什麼怎麼調都不舒服?

    這一后一前兩個猛烈彎折動作,足以導致頸椎折斷。這是重傷或者致死的主要原因。正因為被追尾的主要死/傷原因是揮鞭效應,所以汽車製造商就推出了相關的硬件安全設計。最常見的現行設計就是主動頭枕,防止頸部受傷。主動安全頭枕就是在碰撞的瞬間讓頭部得到支撐,也就是把頭枕往前移動。

    很多朋友買來新車以後會裝很多配件,比如座椅套,方向盤套。

    而細心的朋友會發現,不管頭枕怎麼調,手動調節還是電動調節都不能調到一個很舒服的位置,總覺得頭靠上去脖子部分空空的,所以很多朋友一般都會額外加裝一個托住脖子的軟墊。難道汽車廠商從來都沒有發現這個問題嗎?

    其實汽車頭枕的全稱是汽車座椅安全頭枕,在早些年的時候汽車都沒有頭枕這個東西。

    他的出現主要是為提高汽車安全性而設置的一種輔助裝置。

    因為在被追尾時, 安全帶是不會發揮作用的.追尾帶來的最大傷害的原因是揮鞭效應, 撞擊一瞬, 被撞者頭部猛烈後仰, 頸椎在巨大的頭部加速作用下,嚴重的頸椎會當場折斷,頭部後仰,在巨大慣性作用下,然後頭部猛烈想前運動,頸椎前折. 這一后一前兩個猛烈彎折動作,足以導致頸椎折斷。

    這是重傷或者致死的主要原因.正因為被追尾的主要死/傷原因是揮鞭效應,所以汽車製造商就推出了相關的硬件安全設計。最常見的現行設計就是主動頭枕,防止頸部受傷。

    主動安全頭枕就是在碰撞的瞬間讓頭部得到支撐,也就是把頭枕往前移動。其机械結構也不複雜,只要在椅背內設計了一個連桿,發生來自後部的撞擊時,身體重量擠壓在這個連桿上,在聯動機構的作用下,頭枕向前移動,最大限度地防止頭部猛烈後仰,從而保護人員頭部和頸部的安全。

    雖然現在也有些廠商將頭枕設計得很舒服,但是汽車的頭枕在絕大多數情況下都只是一項安全配置。

    最後祝所有TV的粉絲元旦快樂,在新的一年能開超跑。

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

  • 我買中國品牌的車不只是因為便宜!

    我買中國品牌的車不只是因為便宜!

    觀致耗資1600萬與超跑製造商柯尼塞克共同研發的無凸輪軸發動機(代號QamFree),同等排量下,要比傳統的發動機的功率大幅提升45%,最大扭矩大幅提高47%。假如測試通過,進行量產,將成為未來發動機技術的主要發展方向,彷彿為全世界指明了一條比混合動力更為通暢的道路。

    自主品牌亮瞎眼睛的地方還多着!

    當然了,自主品牌能夠亮瞎眼的地方遠不止這兩點。力壓奔馳、寶馬,在歐洲新車安全評鑒協會Euro-NCAp所進行的2013年最佳碰撞車型評選(小型家用車類)一舉奪得第一名的觀致3,讓一眾歪果仁瞠目結舌。觀致耗資1600萬與超跑製造商柯尼塞克共同研發的無凸輪軸發動機(代號QamFree),同等排量下,要比傳統的發動機的功率大幅提升45%,最大扭矩大幅提高47%。假如測試通過,進行量產,將成為未來發動機技術的主要發展方向,彷彿為全世界指明了一條比混合動力更為通暢的道路。吧唧這麼多,我想說的其實很簡單:自主品牌比你想象中牛逼多了!趕緊去4S店下訂吧!

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

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

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

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

  • 宇宙第一IDE是誰?

    宇宙第一IDE是誰?

    更多精彩文章,盡在碼農翻身

    微服務把我坑了

    如何降低程序員的工資?

    程序員,你得選准跑路的時間!

    兩年,我學會了所有的編程語言!

    一直CRUD,一直996,我煩透了,我要轉型

    字節碼萬歲!

    上帝託夢給我說:一切皆文件

    Javascript: 一個屌絲的逆襲

    Node.js :我只需要一個店小二

    我是一個線程

    TCP/IP之大明郵差

    一個故事講完Https

    CPU 阿甘

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案