標籤: 電動車

  • 5種常見Bean映射工具的性能比對

    5種常見Bean映射工具的性能比對

    本文由 JavaGuide 翻譯自 https://www.baeldung.com/java-performance-mapping-frameworks 。轉載請註明原文地址以及翻譯作者。

    1. 介紹

    創建由多個層組成的大型 Java 應用程序需要使用多種領域模型,如持久化模型、領域模型或者所謂的 DTO。為不同的應用程序層使用多個模型將要求我們提供 bean 之間的映射方法。手動執行此操作可以快速創建大量樣板代碼並消耗大量時間。幸運的是,Java 有多個對象映射框架。在本教程中,我們將比較最流行的 Java 映射框架的性能。

    綜合日常使用情況和相關測試數據,個人感覺 MapStruct、ModelMapper 這兩個 Bean 映射框架是最佳選擇。

    2. 常見 Bean 映射框架概覽

    2.1. Dozer

    Dozer 是一個映射框架,它使用遞歸將數據從一個對象複製到另一個對象。框架不僅能夠在 bean 之間複製屬性,還能夠在不同類型之間自動轉換。

    要使用 Dozer 框架,我們需要添加這樣的依賴到我們的項目:

    <dependency>
        <groupId>net.sf.dozer</groupId>
        <artifactId>dozer</artifactId>
        <version>5.5.1</version>
    </dependency>

    更多關於 Dozer 的內容可以在官方文檔中找到: http://dozer.sourceforge.net/documentation/gettingstarted.html ,或者你也可以閱讀這篇文章:https://www.baeldung.com/dozer 。

    2.2. Orika

    Orika 是一個 bean 到 bean 的映射框架,它遞歸地將數據從一個對象複製到另一個對象。

    Orika 的工作原理與 Dozer 相似。兩者之間的主要區別是 Orika 使用字節碼生成。這允許以最小的開銷生成更快的映射器。

    要使用 Orika 框架,我們需要添加這樣的依賴到我們的項目:

    <dependency>
        <groupId>ma.glasnost.orika</groupId>
        <artifactId>orika-core</artifactId>
        <version>1.5.2</version>
    </dependency>

    更多關於 Orika 的內容可以在官方文檔中找到:https://orika-mapper.github.io/orika-docs/,或者你也可以閱讀這篇文章:https://www.baeldung.com/orika-mapping。

    2.3. MapStruct

    MapStruct 是一個自動生成 bean mapper 類的代碼生成器。MapStruct 還能夠在不同的數據類型之間進行轉換。Github 地址:https://github.com/mapstruct/mapstruct。

    要使用 MapStruct 框架,我們需要添加這樣的依賴到我們的項目:

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>1.2.0.Final</version>
    </dependency>

    更多關於 MapStruct 的內容可以在官方文檔中找到:https://mapstruct.org/,或者你也可以閱讀這篇文章:https://www.baeldung.com/mapstruct。

    要使用 MapStruct 框架,我們需要添加這樣的依賴到我們的項目:

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>1.2.0.Final</version>
    </dependency>

    2.4. ModelMapper

    ModelMapper 是一個旨在簡化對象映射的框架,它根據約定確定對象之間的映射方式。它提供了類型安全的和重構安全的 API。

    更多關於 ModelMapper 的內容可以在官方文檔中找到:http://modelmapper.org/ 。

    要使用 ModelMapper 框架,我們需要添加這樣的依賴到我們的項目:

    <dependency>
      <groupId>org.modelmapper</groupId>
      <artifactId>modelmapper</artifactId>
      <version>1.1.0</version>
    </dependency>

    2.5. JMapper

    JMapper 是一個映射框架,旨在提供易於使用的、高性能的 Java bean 之間的映射。該框架旨在使用註釋和關係映射應用 DRY 原則。該框架允許不同的配置方式:基於註釋、XML 或基於 api。

    更多關於 JMapper 的內容可以在官方文檔中找到:https://github.com/jmapper-framework/jmapper-core/wiki。

    要使用 JMapper 框架,我們需要添加這樣的依賴到我們的項目:

    <dependency>
        <groupId>com.googlecode.jmapper-framework</groupId>
        <artifactId>jmapper-core</artifactId>
        <version>1.6.0.1</version>
    </dependency>
    

    3.測試模型

    為了能夠正確地測試映射,我們需要有一個源和目標模型。我們已經創建了兩個測試模型。

    第一個是一個只有一個字符串字段的簡單 POJO,它允許我們在更簡單的情況下比較框架,並檢查如果我們使用更複雜的 bean 是否會發生任何變化。

    簡單的源模型如下:

    public class SourceCode {
        String code;
        // getter and setter
    }
    

    它的目標也很相似:

    public class DestinationCode {
        String code;
        // getter and setter
    }

    源 bean 的實際示例如下:

    public class SourceOrder {
        private String orderFinishDate;
        private PaymentType paymentType;
        private Discount discount;
        private DeliveryData deliveryData;
        private User orderingUser;
        private List<Product> orderedProducts;
        private Shop offeringShop;
        private int orderId;
        private OrderStatus status;
        private LocalDate orderDate;
        // standard getters and setters
    }

    目標類如下圖所示:

    public class Order {
        private User orderingUser;
        private List<Product> orderedProducts;
        private OrderStatus orderStatus;
        private LocalDate orderDate;
        private LocalDate orderFinishDate;
        private PaymentType paymentType;
        private Discount discount;
        private int shopId;
        private DeliveryData deliveryData;
        private Shop offeringShop;
        // standard getters and setters
    }

    整個模型結構可以在這裏找到:https://github.com/eugenp/tutorials/tree/master/performance-tests/src/main/java/com/baeldung/performancetests/model/source。

    4. 轉換器

    為了簡化測試設置的設計,我們創建了如下所示的轉換器接口:

    public interface Converter {
        Order convert(SourceOrder sourceOrder);
        DestinationCode convert(SourceCode sourceCode);
    }

    我們所有的自定義映射器都將實現這個接口。

    4.1. OrikaConverter

    Orika 支持完整的 API 實現,這大大簡化了 mapper 的創建:

    public class OrikaConverter implements Converter{
        private MapperFacade mapperFacade;
    
        public OrikaConverter() {
            MapperFactory mapperFactory = new DefaultMapperFactory
              .Builder().build();
    
            mapperFactory.classMap(Order.class, SourceOrder.class)
              .field("orderStatus", "status").byDefault().register();
            mapperFacade = mapperFactory.getMapperFacade();
        }
    
        @Override
        public Order convert(SourceOrder sourceOrder) {
            return mapperFacade.map(sourceOrder, Order.class);
        }
    
        @Override
        public DestinationCode convert(SourceCode sourceCode) {
            return mapperFacade.map(sourceCode, DestinationCode.class);
        }
    }

    4.2. DozerConverter

    Dozer 需要 XML 映射文件,有以下幾個部分:

    <mappings xmlns="http://dozer.sourceforge.net"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://dozer.sourceforge.net
      http://dozer.sourceforge.net/schema/beanmapping.xsd">
    
        <mapping>
            <class-a>com.baeldung.performancetests.model.source.SourceOrder</class-a>
            <class-b>com.baeldung.performancetests.model.destination.Order</class-b>
            <field>
                <a>status</a>
                <b>orderStatus</b>
            </field>
        </mapping>
        <mapping>
            <class-a>com.baeldung.performancetests.model.source.SourceCode</class-a>
            <class-b>com.baeldung.performancetests.model.destination.DestinationCode</class-b>
        </mapping>
    </mappings>

    定義了 XML 映射后,我們可以從代碼中使用它:

    public class DozerConverter implements Converter {
        private final Mapper mapper;
    
        public DozerConverter() {
            DozerBeanMapper mapper = new DozerBeanMapper();
            mapper.addMapping(
              DozerConverter.class.getResourceAsStream("/dozer-mapping.xml"));
            this.mapper = mapper;
        }
    
        @Override
        public Order convert(SourceOrder sourceOrder) {
            return mapper.map(sourceOrder,Order.class);
        }
    
        @Override
        public DestinationCode convert(SourceCode sourceCode) {
            return mapper.map(sourceCode, DestinationCode.class);
        }
    }

    4.3. MapStructConverter

    Map 結構的定義非常簡單,因為它完全基於代碼生成:

    @Mapper
    public interface MapStructConverter extends Converter {
        MapStructConverter MAPPER = Mappers.getMapper(MapStructConverter.class);
    
        @Mapping(source = "status", target = "orderStatus")
        @Override
        Order convert(SourceOrder sourceOrder);
    
        @Override
        DestinationCode convert(SourceCode sourceCode);
    }

    4.4. JMapperConverter

    JMapperConverter 需要做更多的工作。接口實現后:

    public class JMapperConverter implements Converter {
        JMapper realLifeMapper;
        JMapper simpleMapper;
    
        public JMapperConverter() {
            JMapperAPI api = new JMapperAPI()
              .add(JMapperAPI.mappedClass(Order.class));
            realLifeMapper = new JMapper(Order.class, SourceOrder.class, api);
            JMapperAPI simpleApi = new JMapperAPI()
              .add(JMapperAPI.mappedClass(DestinationCode.class));
            simpleMapper = new JMapper(
              DestinationCode.class, SourceCode.class, simpleApi);
        }
    
        @Override
        public Order convert(SourceOrder sourceOrder) {
            return (Order) realLifeMapper.getDestination(sourceOrder);
        }
    
        @Override
        public DestinationCode convert(SourceCode sourceCode) {
            return (DestinationCode) simpleMapper.getDestination(sourceCode);
        }
    }

    我們還需要向目標類的每個字段添加@JMap註釋。此外,JMapper 不能在 enum 類型之間轉換,它需要我們創建自定義映射函數:

    @JMapConversion(from = "paymentType", to = "paymentType")
    public PaymentType conversion(com.baeldung.performancetests.model.source.PaymentType type) {
        PaymentType paymentType = null;
        switch(type) {
            case CARD:
                paymentType = PaymentType.CARD;
                break;
    
            case CASH:
                paymentType = PaymentType.CASH;
                break;
    
            case TRANSFER:
                paymentType = PaymentType.TRANSFER;
                break;
        }
        return paymentType;
    }

    4.5. ModelMapperConverter

    ModelMapperConverter 只需要提供我們想要映射的類:

    public class ModelMapperConverter implements Converter {
        private ModelMapper modelMapper;
    
        public ModelMapperConverter() {
            modelMapper = new ModelMapper();
        }
    
        @Override
        public Order convert(SourceOrder sourceOrder) {
           return modelMapper.map(sourceOrder, Order.class);
        }
    
        @Override
        public DestinationCode convert(SourceCode sourceCode) {
            return modelMapper.map(sourceCode, DestinationCode.class);
        }
    }
    

    5. 簡單的模型測試

    對於性能測試,我們可以使用 Java Microbenchmark Harness,關於如何使用它的更多信息可以在 這篇文章:https://www.baeldung.com/java-microbenchmark-harness 中找到。

    我們為每個轉換器創建了一個單獨的基準測試,並將基準測試模式指定為 Mode.All。

    5.1. 平均時間

    對於平均運行時間,JMH 返回以下結果(越少越好):

    這個基準測試清楚地表明,MapStruct 和 JMapper 都有最佳的平均工作時間。

    5.2. 吞吐量

    在這種模式下,基準測試返回每秒的操作數。我們收到以下結果(越多越好):

    在吞吐量模式中,MapStruct 是測試框架中最快的,JMapper 緊隨其後。

    5.3. SingleShotTime

    這種模式允許測量單個操作從開始到結束的時間。基準給出了以下結果(越少越好):

    這裏,我們看到 JMapper 返回的結果比 MapStruct 好得多。

    5.4. 採樣時間

    這種模式允許對每個操作的時間進行採樣。三個不同百分位數的結果如下:

    所有的基準測試都表明,根據場景的不同,MapStruct 和 JMapper 都是不錯的選擇,儘管 MapStruct 對 SingleShotTime 給出的結果要差得多。

    6. 真實模型測試

    對於性能測試,我們可以使用 Java Microbenchmark Harness,關於如何使用它的更多信息可以在 這篇文章:https://www.baeldung.com/java-microbenchmark-harness 中找到。

    我們為每個轉換器創建了一個單獨的基準測試,並將基準測試模式指定為 Mode.All。

    6.1. 平均時間

    JMH 返回以下平均運行時間結果(越少越好):

    該基準清楚地表明,MapStruct 和 JMapper 均具有最佳的平均工作時間。

    6.2. 吞吐量

    在這種模式下,基準測試返回每秒的操作數。我們收到以下結果(越多越好):

    在吞吐量模式中,MapStruct 是測試框架中最快的,JMapper 緊隨其後。

    6.3. SingleShotTime

    這種模式允許測量單個操作從開始到結束的時間。基準給出了以下結果(越少越好):

    6.4. 採樣時間

    這種模式允許對每個操作的時間進行採樣。三個不同百分位數的結果如下:

    儘管簡單示例和實際示例的確切結果明顯不同,但是它們的趨勢相同。在哪種算法最快和哪種算法最慢方面,兩個示例都給出了相似的結果。

    6.5. 結論

    根據我們在本節中執行的真實模型測試,我們可以看出,最佳性能顯然屬於 MapStruct。在相同的測試中,我們看到 Dozer 始終位於結果表的底部。

    7. 總結

    在這篇文章中,我們已經進行了五個流行的 Java Bean 映射框架性能測試:ModelMapper MapStruct Orika ,Dozer, JMapper。

    示例代碼地址:https://github.com/eugenp/tutorials/tree/master/performance-tests。

    開源項目推薦

    作者的其他開源項目推薦:

    1. :【Java學習+面試指南】 一份涵蓋大部分Java程序員所需要掌握的核心知識。
    2. : 適合新手入門以及有經驗的開發人員查閱的 Spring Boot 教程(業餘時間維護中,歡迎一起維護)。
    3. : 我覺得技術人員應該有的一些好習慣!
    4. :從零入門 !Spring Security With JWT(含權限驗證)後端部分代碼。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

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

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

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

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

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

  • EFK教程 – ElasticSearch高性能高可用架構

    EFK教程 – ElasticSearch高性能高可用架構

    通過將elasticsearch的data、ingest、master角色進行分離,搭建起高性能+高可用的ES架構

    作者:“發顛的小狼”,歡迎轉載與投稿

    目錄

    ▪ 用途
    ▪ 架構
    ▪ 步驟說明
    ▪ elasticsearch-data部署
    ▪ elasticsearch-ingest部署
    ▪ elasticsearch-master部署

    用途

    在第一篇《EFK教程 – 快速入門指南》中,闡述了EFK的安裝部署,其中ES的架構為三節點,即master、ingest、data角色同時部署在三台服務器上。

    在本文中,將進行角色分離部署,並且每個角色分別部署三節點,在實現性能最大化的同時保障高可用。

    ▷ elasticsearch的master節點:用於調度,採用普通性能服務器來部署
    ▷ elasticsearch的ingest節點:用於數據預處理,採用性能好的服務器來部署
    ▷ elasticsearch的data節點:用於數據落地存儲,採用存儲性能好的服務器來部署

    若不知道去哪找《EFK教程 - 快速入門指南》,可在主流搜索引擎里搜索:
    小慢哥 EFK教程 快速入門指南
    或者
    小慢哥 EFK教程 基於多節點ES的EFK安裝部署配置

    架構

    服務器配置

    注意:此處的架構是之前的文章《EFK教程 – 快速入門指南》的拓展,因此請先按照《EFK教程 – 快速入門指南》完成部署

    步驟說明

    1️⃣ 部署3台data節點,加入原集群
    2️⃣ 部署3台ingest節點,加入原集群
    3️⃣ 將原有的es索引遷移到data節點
    4️⃣ 將原有的es節點改造成master節點

    elasticsearch-data部署

    之前已完成了基礎的elasticsearch架構,現需要新增三台存儲節點加入集群,同時關閉master和ingest功能

    elasticsearch-data安裝:3台均執行相同的安裝步驟

    tar -zxvf elasticsearch-7.3.2-linux-x86_64.tar.gz
    mv elasticsearch-7.3.2 /opt/elasticsearch
    useradd elasticsearch -d /opt/elasticsearch -s /sbin/nologin
    mkdir -p /opt/logs/elasticsearch
    chown elasticsearch.elasticsearch /opt/elasticsearch -R
    chown elasticsearch.elasticsearch /opt/logs/elasticsearch -R
    # 數據盤需要elasticsearch寫權限
    chown elasticsearch.elasticsearch /data/SAS -R
    
    # 限制一個進程可以擁有的VMA(虛擬內存區域)的數量要超過262144,不然elasticsearch會報max virtual memory areas vm.max_map_count [65535] is too low, increase to at least [262144]
    echo "vm.max_map_count = 655350" >> /etc/sysctl.conf
    sysctl -p

    elasticsearch-data配置

    ▷ 192.168.1.51 /opt/elasticsearch/config/elasticsearch.yml

    cluster.name: my-application
    node.name: 192.168.1.51
    # 數據盤位置,如果有多個硬盤位置,用","隔開
    path.data: /data/SAS
    path.logs: /opt/logs/elasticsearch
    network.host: 192.168.1.51
    
    discovery.seed_hosts: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    cluster.initial_master_nodes: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    # 關閉master功能
    node.master: false
    # 關閉ingest功能
    node.ingest: false
    # 開啟data功能
    node.data: true

    ▷ 192.168.1.52 /opt/elasticsearch/config/elasticsearch.yml

    cluster.name: my-application
    node.name: 192.168.1.52
    # 數據盤位置,如果有多個硬盤位置,用","隔開
    path.data: /data/SAS
    path.logs: /opt/logs/elasticsearch
    network.host: 192.168.1.52
    
    discovery.seed_hosts: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    cluster.initial_master_nodes: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    # 關閉master功能
    node.master: false
    # 關閉ingest功能
    node.ingest: false
    # 開啟data功能
    node.data: true

    ▷ 192.168.1.53 /opt/elasticsearch/config/elasticsearch.yml

    cluster.name: my-application
    node.name: 192.168.1.53
    # 數據盤位置,如果有多個硬盤位置,用","隔開
    path.data: /data/SAS
    path.logs: /opt/logs/elasticsearch
    network.host: 192.168.1.53
    
    discovery.seed_hosts: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    cluster.initial_master_nodes: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    # 關閉master功能
    node.master: false
    # 關閉ingest功能
    node.ingest: false
    # 開啟data功能
    node.data: true

    elasticsearch-data啟動

    sudo -u elasticsearch /opt/elasticsearch/bin/elasticsearch

    elasticsearch集群狀態

    curl "http://192.168.1.31:9200/_cat/health?v"

    elasticsearch-data狀態

    curl "http://192.168.1.31:9200/_cat/nodes?v"

    elasticsearch-data參數說明

    status: green  # 集群健康狀態
    node.total: 6  # 有6台機子組成集群
    node.data: 6  # 有6個節點的存儲
    node.role: d  # 只擁有data角色
    node.role: i  # 只擁有ingest角色
    node.role: m  # 只擁有master角色
    node.role: mid  # 擁master、ingest、data角色

    elasticsearch-ingest部署

    現需要新增三台ingest節點加入集群,同時關閉master和data功能

    elasticsearch-ingest安裝:3台es均執行相同的安裝步驟

    tar -zxvf elasticsearch-7.3.2-linux-x86_64.tar.gz
    mv elasticsearch-7.3.2 /opt/elasticsearch
    useradd elasticsearch -d /opt/elasticsearch -s /sbin/nologin
    mkdir -p /opt/logs/elasticsearch
    chown elasticsearch.elasticsearch /opt/elasticsearch -R
    chown elasticsearch.elasticsearch /opt/logs/elasticsearch -R
    
    # 限制一個進程可以擁有的VMA(虛擬內存區域)的數量要超過262144,不然elasticsearch會報max virtual memory areas vm.max_map_count [65535] is too low, increase to at least [262144]
    echo "vm.max_map_count = 655350" >> /etc/sysctl.conf
    sysctl -p

    elasticsearch-ingest配置

    ▷ 192.168.1.41 /opt/elasticsearch/config/elasticsearch.yml

    cluster.name: my-application
    node.name: 192.168.1.41
    path.logs: /opt/logs/elasticsearch
    network.host: 192.168.1.41
    
    discovery.seed_hosts: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    cluster.initial_master_nodes: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    # 關閉master功能
    node.master: false
    # 開啟ingest功能
    node.ingest: true
    # 關閉data功能
    node.data: false

    ▷ 192.168.1.42 /opt/elasticsearch/config/elasticsearch.yml

    cluster.name: my-application
    node.name: 192.168.1.42
    path.logs: /opt/logs/elasticsearch
    network.host: 192.168.1.42
    
    discovery.seed_hosts: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    cluster.initial_master_nodes: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    # 關閉master功能
    node.master: false
    # 開啟ingest功能
    node.ingest: true
    # 關閉data功能
    node.data: false

    ▷ 192.168.1.43 /opt/elasticsearch/config/elasticsearch.yml

    cluster.name: my-application
    node.name: 192.168.1.43
    path.logs: /opt/logs/elasticsearch
    network.host: 192.168.1.43
    
    discovery.seed_hosts: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    cluster.initial_master_nodes: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    # 關閉master功能
    node.master: false
    # 開啟ingest功能
    node.ingest: true
    # 關閉data功能
    node.data: false

    elasticsearch-ingest啟動

    sudo -u elasticsearch /opt/elasticsearch/bin/elasticsearch

    elasticsearch集群狀態

    curl "http://192.168.1.31:9200/_cat/health?v"

    elasticsearch-ingest狀態

    curl "http://192.168.1.31:9200/_cat/nodes?v"

    elasticsearch-ingest參數說明

    status: green  # 集群健康狀態
    node.total: 9  # 有9台機子組成集群
    node.data: 6  # 有6個節點的存儲
    node.role: d  # 只擁有data角色
    node.role: i  # 只擁有ingest角色
    node.role: m  # 只擁有master角色
    node.role: mid  # 擁master、ingest、data角色

    elasticsearch-master部署

    首先,將上一篇《EFK教程 – 快速入門指南》中部署的3台es(192.168.1.31、192.168.1.32、192.168.1.33)改成只有master的功能, 因此需要先將這3台上的索引數據遷移到本次所做的data節點中

    1️⃣ 索引遷移:一定要做這步,將之前的索引放到data節點上

    curl -X PUT "192.168.1.31:9200/*/_settings?pretty" -H 'Content-Type: application/json' -d'
    {
      "index.routing.allocation.include._ip": "192.168.1.51,192.168.1.52,192.168.1.53"
    }'

    2️⃣ 確認當前索引存儲位置:確認所有索引不在192.168.1.31、192.168.1.32、192.168.1.33節點上

    curl "http://192.168.1.31:9200/_cat/shards?h=n"

    elasticsearch-master配置

    注意事項:修改配置,重啟進程,需要一台一台執行,要確保第一台成功后,再執行下一台。重啟進程的方法:由於上一篇文章《EFK教程 – 快速入門指南》里,是執行命令跑在前台,因此直接ctrl – c退出再啟動即可,啟動命令如下

    sudo -u elasticsearch /opt/elasticsearch/bin/elasticsearch

    ▷ 192.168.1.31 /opt/elasticsearch/config/elasticsearch.yml

    cluster.name: my-application
    node.name: 192.168.1.31
    path.logs: /opt/logs/elasticsearch
    network.host: 192.168.1.31
    
    discovery.seed_hosts: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    cluster.initial_master_nodes: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    #開啟master功能
    node.master: true
    #關閉ingest功能
    node.ingest: false
    #關閉data功能
    node.data: false

    ▷ 192.168.1.32 /opt/elasticsearch/config/elasticsearch.yml

    cluster.name: my-application
    node.name: 192.168.1.32
    path.logs: /opt/logs/elasticsearch
    network.host: 192.168.1.32
    
    discovery.seed_hosts: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    cluster.initial_master_nodes: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    #開啟master功能
    node.master: true
    #關閉ingest功能
    node.ingest: false
    #關閉data功能
    node.data: false

    ▷ 192.168.1.33 /opt/elasticsearch/config/elasticsearch.yml

    cluster.name: my-application
    node.name: 192.168.1.33
    path.logs: /opt/logs/elasticsearch
    network.host: 192.168.1.33
    
    discovery.seed_hosts: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    cluster.initial_master_nodes: ["192.168.1.31","192.168.1.32","192.168.1.33"]
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    #開啟master功能
    node.master: true
    #關閉ingest功能
    node.ingest: false
    #關閉data功能
    node.data: false

    elasticsearch集群狀態

    curl "http://192.168.1.31:9200/_cat/health?v"

    elasticsearch-master狀態

    curl "http://192.168.1.31:9200/_cat/nodes?v"

    至此,當node.role里所有服務器都不再出現“mid”,則表示一切順利完成。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

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

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

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

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

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

  • 科學家發現新型態鈾 恐影響核廢料處理計畫

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

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

    【其他文章推薦】

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

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

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

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

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

  • 擴大中國電動車佈局 鴻海今年據點數可能突破 10 個

    鴻海集團擬擴大在大陸電動車租車佈局,繼成功搶佔北京、杭州、常州後,7 月即將前進貴陽。鴻海指出,去年北京成功試點後,已累積相當經驗,今年將加速複製,內部規畫今年據點數可望突破 10 個,相較去年僅有北京 1 個據點,年成長逾 10 倍。   鴻海進軍電動車市場動作頻頻,繼去年與大陸北汽集團合作,共同組成新能源汽車租賃公司北京恆譽,搶進分時租車市場後,歷經一年,目前北京恆譽共設有 200 個充電椿,並提供 1,000 多輛北汽「E150EV」電動車供租賃。   今年鴻海集團還將大刀闊斧展開擴點,光是上半年就已順利進駐杭州、常州,貴陽也預定 7 月進駐,屆時將成立黔譽的子公司負責管理。據了解,目前鴻海手中還有幾個新點在考慮中,包括上海、深圳、梧州,目標是今年底前總據點數突破 10 個的基本門檻。   法人指出,鴻海透過電動車租賃,提前卡位電動車市場,目標不單是電動車租賃市場,更重要的是後面的代工市場,將電動車市場大餅做大,最大獲益者還是鴻海集團。

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

    【其他文章推薦】

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

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

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

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

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

  • MySQL 執行計劃詳解

    MySQL 執行計劃詳解

    MySQL 原理篇

    我們經常使用 MySQL 的執行計劃來查看 SQL 語句的執行效率,接下來分析執行計劃的各個显示內容。

    EXPLAIN SELECT * FROM users 
    WHERE id IN (SELECT userID FROMuser_address WHERE address = "湖南長沙麓谷") ;

    執行計劃的 id

    select 查詢的序列號,標識執行的順序

    • id 相同,執行順序由上至下
    • id 不同,如果是子查詢,id 的序號會遞增,id 值越大優先級越高,越先被執行

    執行計劃的 select_type

    查詢的類型,主要是用於區分普通查詢、聯合查詢、子查詢等。

    • SIMPLE:簡單的 select 查詢,查詢中不包含子查詢或者 union
    • PRIMARY:查詢中包含子部分,最外層查詢則被標記為 primary
    • SUBQUERY/MATERIALIZED:SUBQUERY 表示在 select 或 where 列表中包含了子查詢,MATERIALIZED表示 where 後面 in 條件的子查詢
    • UNION:表示 union 中的第二個或後面的 select 語句
    • UNION RESULT:union 的結果

    對於 UNION 和 UNION RESULT 可以通過下面的例子展現:

    EXPLAIN
    SELECT * FROM users WHERE id IN(1, 2)
    UNION
    SELECT * FROM users WHERE id IN(3, 4);

    執行計劃的 table

    查詢涉及到的表。

    • 直接顯示錶名或者表的別名
    • <unionM,N> 由 ID 為 M,N 查詢 union 產生的結果
    • <subqueryN> 由 ID 為 N 查詢產生的結果

    執行計劃的 type 

    訪問類型,SQL 查詢優化中一個很重要的指標,結果值從好到壞依次是:system > const > eq_ref > ref > range > index > ALL。

    • system:系統表,少量數據,往往不需要進行磁盤IO
    • const:常量連接
    • eq_ref:主鍵索引(primary key)或者非空唯一索引(unique not null)等值掃描
    • ref:非主鍵非唯一索引等值掃描
    • range:範圍掃描
    • index:索引樹掃描
    • ALL:全表掃描(full table scan)

    下面通過舉例說明。

    system

    explain select * from mysql.time_zone;

    上例中,從系統庫 MySQL 的系統表 time_zone 里查詢數據,訪問類型為 system,這些數據已經加載到內存里,不需要進行磁盤 IO,這類掃描是速度最快的。

    explain select * from (select * from user where id=1) tmp;

    再舉一個例子,內層嵌套(const)返回了一個臨時表,外層嵌套從臨時表查詢,其掃描類型也是 system,也不需要走磁盤 IO,速度超快。

    const 

    數據準備:

    CREATE TABLE `user` (
      `id` int(11) NOT NULL,
      `NAME` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into user values(1,'shenjian');
    insert into user values(2,'zhangsan');
    insert into user values(3,'lisi');
    explain select * from user where id=1;

    const 掃描的條件為: 

    1. 命中主鍵(primary key)或者唯一(unique)索引
    2. 被連接的部分是一個常量(const)值 

    如上例,id 是 主鍵索引,連接部分是常量1。

    eq_ref

    數據準備:

    CREATE TABLE `user` (
      `id` int(11) NOT NULL,
      `NAME` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into user values(1,'shenjian');
    insert into user values(2,'zhangsan');
    insert into user values(3,'lisi');
    
    CREATE TABLE `user_ex` (
      `id` int(11) NOT NULL,
      `age` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into user_ex values(1,18);
    insert into user_ex values(2,20);
    insert into user_ex values(3,30);
    insert into user_ex values(4,40);
    insert into user_ex values(5,50);
    EXPLAIN SELECT * FROM USER,user_ex WHERE user.id=user_ex.id;

    eq_ref 掃描的條件為,對於前表的每一行(row),后表只有一行被掃描。 

    再細化一點:  

    1. join 查詢
    2. 命中主鍵(primary key)或者非空唯一(unique not null)索引
    3. 等值連接;

    如上例,id 是主鍵,該 join 查詢為 eq_ref 掃描。

    ref

    數據準備:

    CREATE TABLE `user` (
      `id` int(11) DEFAULT NULL,
      `name` varchar(20) DEFAULT NULL,
      KEY `id` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into user values(1,'shenjian');
    insert into user values(2,'zhangsan');
    insert into user values(3,'lisi');
    
    CREATE TABLE `user_ex` (
      `id` int(11) DEFAULT NULL,
      `age` int(11) DEFAULT NULL,
      KEY `id` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into user_ex values(1,18);
    insert into user_ex values(2,20);
    insert into user_ex values(3,30);
    insert into user_ex values(4,40);
    insert into user_ex values(5,50);
    EXPLAIN SELECT * FROM USER,user_ex WHERE user.id=user_ex.id;

    如果把上例 eq_ref 案例中的主鍵索引,改為普通非唯一(non unique)索引。就由 eq_ref 降級為了 ref,此時對於前表的每一行(row),后表可能有多於一行的數據被掃描。

    select * from user where id=1;

    當 id 改為普通非唯一索引后,常量的連接查詢,也由 const 降級為了 ref,因為也可能有多於一行的數據被掃描。

    ref 掃描,可能出現在 join 里,也可能出現在單表普通索引里,每一次匹配可能有多行數據返回,雖然它比 eq_ref 要慢,但它仍然是一個很快的 join 類型。

    range

    數據準備:

    CREATE TABLE `user` (
      `id` int(11) NOT NULL,
      `name` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into user values(1,'shenjian');
    insert into user values(2,'zhangsan');
    insert into user values(3,'lisi');
    insert into user values(4,'wangwu');
    insert into user values(5,'zhaoliu');
    explain select * from user where id between 1 and 4;
    explain select * from user where id in(1,2,3);
    explain select * from user where id > 3;

    range 掃描就比較好理解了,它是索引上的範圍查詢,它會在索引上掃碼特定範圍內的值。

    像上例中的 between,in,> 都是典型的範圍(range)查詢。

    index

    explain count (*) from user;

    如上例,id 是主鍵,該 count 查詢需要通過掃描索引上的全部數據來計數,它僅比全表掃描快一點。

    ALL 

    數據準備:

    CREATE TABLE `user` (
      `id` int(11) DEFAULT NULL,
      `name` varchar(20) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into user values(1,'shenjian');
    insert into user values(2,'zhangsan');
    insert into user values(3,'lisi');
    
    CREATE TABLE `user_ex` (
      `id` int(11) DEFAULT NULL,
      `age` int(11) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    insert into user_ex values(1,18);
    insert into user_ex values(2,20);
    insert into user_ex values(3,30);
    insert into user_ex values(4,40);
    insert into user_ex values(5,50);
    explain select * from user,user_ex where user.id=user_ex.id;

    如果 id 上不建索引,對於前表的每一行(row),后表都要被全表掃描。

    文章中,這個相同的 join 語句出現了三次:

    1. 掃描類型為 eq_ref,此時 id 為主鍵
    2. 掃描類型為 ref,此時 id 為非唯一普通索引
    3. 掃描類型為 ALL,全表掃描,此時id上無索引

    有此可見,建立正確的索引,對數據庫性能的提升是多麼重要。

    總結 

    1. explain 結果中的 type 字段,表示(廣義)連接類型,它描述了找到所需數據使用的掃描方式;
    2. 常見的掃描類型有:system>const>eq_ref>ref>range>index>ALL,其掃描速度由快到慢;
    3. 各類掃描類型的要點是:
      1. system 最快:不進行磁盤 IO
      2. const:PK 或者 unique 上的等值查詢
      3. eq_ref:PK 或者 unique 上的 join 查詢,等值匹配,對於前表的每一行,后表只有一行命中
      4. ref:非唯一索引,等值匹配,可能有多行命中
      5. range:索引上的範圍掃描,例如:between、in、>
      6. index:索引上的全集掃描,例如:InnoDB 的 count
      7. ALL 最慢:全表掃描
    1. 建立正確的索引,非常重要;
    2. 使用 explain 了解並優化執行計劃,非常重要;

    執行計劃 possible_keys

    查詢過程中有可能用到的索引。

    執行計劃 key

    實際使用的索引,如果為 NULL ,則沒有使用索引。

    執行計劃 rows

    根據表統計信息或者索引選用情況,大致估算出找到所需的記錄所需要讀取的行數。

    執行計劃 filtered 

    表示返回結果的行數占需讀取行數的百分比, filtered 的值越大越好。

    執行計劃 Extra 

    十分重要的額外信息。

    • Using filesort:MySQL 對數據使用一個外部的文件內容進行了排序,而不是按照表內的索引進行排序讀取。
    • Using temporary:使用臨時表保存中間結果,也就是說 MySQL 在對查詢結果排序時使用了臨時表,常見於order by 或 group by。
    • Using index:表示 SQL 操作中使用了覆蓋索引(Covering Index),避免了訪問表的數據行,效率高。
    • Using index condition:表示 SQL 操作命中了索引,但不是所有的列數據都在索引樹上,還需要訪問實際的行記錄。
    • Using where:表示 SQL 操作使用了 where 過濾條件。
    • Select tables optimized away:基於索引優化 MIN/MAX 操作或者 MyISAM 存儲引擎優化 COUNT(*) 操作,不必等到執行階段再進行計算,查詢執行計劃生成的階段即可完成優化。
    • Using join buffer (Block Nested Loop):表示 SQL 操作使用了關聯查詢或者子查詢,且需要進行嵌套循環計算。

    下面通過舉例說明。

    數據準備:

    CREATE TABLE `user` (
      `id` int(11) NOT NULL,
      `name` varchar(20) DEFAULT NULL,
      `sex` varchar(5) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `name` (`name`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    
    insert into user values(1, 'shenjian','no');
    insert into user values(2, 'zhangsan','no');
    insert into user values(3, 'lisi', 'yes');
    insert into user values(4, 'lisi', 'no');

    數據說明:

    用戶表:id 主鍵索引,name 普通索引(非唯一),sex 無索引。

    四行記錄:其中 name 普通索引存在重複記錄 lisi。

    Using filesort

    explain select * from user order by sex;

     

    Extra 為 Using filesort 說明,得到所需結果集,需要對所有記錄進行文件排序。

    這類 SQL 語句性能極差,需要進行優化。

    典型的,在一個沒有建立索引的列上進行了 order by,就會觸發 filesort,常見的優化方案是,在 order by 的列上添加索引,避免每次查詢都全量排序。

    Using temporary

    explain select * from user group by name order by sex;

    Extra 為 Using temporary 說明,需要建立臨時表(temporary table)來暫存中間結果。

    這類 SQL 語句性能較低,往往也需要進行優化。

    典型的 group by 和 order by 同時存在,且作用於不同的字段時,就會建立臨時表,以便計算出最終的結果集。

    臨時表存在兩種引擎,一種是 Memory 引擎,一種是 MyISAM 引擎,如果返回的數據在 16M 以內(默認),且沒有大字段的情況下,使用 Memory 引擎,否則使用 MyISAM 引擎。 

    Using index

    EXPLAIN SELECT id FROM USER;

    Extra 為 Using index 說明,SQL 所需要返回的所有列數據均在一棵索引樹上,而無需訪問實際的行記錄。

    這類 SQL 語句往往性能較好。

    Using index condition

    explain select id, name, sex from user where name='shenjian';

    Extra 為 Using index condition 說明,確實命中了索引,但不是所有的列數據都在索引樹上,還需要訪問實際的行記錄。

    這類 SQL 語句性能也較高,但不如 Using index。

    Using where

    explain select * from user where sex='no';

    Extra 為 Using where 說明,查詢的結果集使用了 where 過濾條件,比如上面的 SQL 使用了 sex = 'no' 的過濾條件

    Select tables optimized away

    EXPLAIN SELECT MAX(id) FROM USER;

     

    比如上面的語句查詢 id 的最大值,因為 id 是主鍵索引,根據 B+Tree 的結構,天然就是有序存放的,所以不需要等到執行階段再進行計算,查詢執行計劃生成的階段即可完成優化。

    Using join buffer (Block Nested Loop)

    explain select * from user where id in (select id from user where sex='no');

    Extra 為 Using join buffer (Block Nested Loop) 說明,需要進行嵌套循環計算。內層和外層的 type 均為 ALL,rows 均為4,需要循環進行4*4次計算。

    這類 SQL 語句性能往往也較低,需要進行優化。

    典型的兩個關聯表 join,關聯字段均未建立索引,就會出現這種情況。常見的優化方案是,在關聯字段上添加索引,避免每次嵌套循環計算。

    參考 

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

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

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

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

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

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

  • 澳洲動物園抗野火 園長帶猴子老虎回家避難

    摘錄自2020年1月2日中央社雪梨報導

    在毀滅性野火肆虐澳洲各地的情況下,新南威爾斯省(New South Wales)一家小型動物園,成功保障園區所有動物的安全。

    英國廣播公司(BBC)報導,摩哥動物園(Mogo Zoo)的靈長類動物數量在澳洲數一數二,園區內還有斑馬、犀牛與長頸鹿等大型動物。但當動物園處在野火第一線時,飼養人員仍成功保護200隻動物全數未受傷害。

    新南威爾斯省12月31日對動物園所在區域下達疏散命令,但職員仍決定留下來保護動物。動物園園長史戴普斯表示,動物園能倖存下來,都是因為事先做好確實規劃。飼養人員先把區內所有可燃物都搬走,接著再遷移動物。

    諸如獅子、老虎與紅毛猩猩等大型動物都被趕進夜間圍欄內,以確保牠們安全、情緒平靜。但小型動物就需要特殊庇護,所以園長史戴普斯決定把牠們通通帶回自家,至於長頸鹿和斑馬則留在自己的圍欄裡,因為牠們體型夠大,可以避開火源。

    史戴普斯說,長頸鹿和斑馬是園內唯一承受壓力的動物,但壓力來源並非火災,而是飼養人員匆忙開車四處滅火,動物園員工事先準備數十萬公升的水,並把水置於車上的小型水槽四處移動,看到火就予以撲滅。他形容自己的團隊日以繼夜投入工作,若非員工的英勇作為,動物園早就陷入火海。

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

    【其他文章推薦】

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

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

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

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

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

  • 北京解讀純電動客車補貼政策

    今天(10日)上午,北京市對購買純電動客車有關財政政策進行解讀。北京提供1∶1市級財政補貼,兩級財政補貼總額最高不超過車輛銷售價格的60%。  
    補貼範圍   行政事業單位使用財政資金購買純電動客車不納入通知規定的補助範圍。地方純電動客車補貼,直接撥付到汽車生產企業,消費者按銷售價格扣減補助後支付。  
    按車長補貼   2015年純電動客車推廣應用補助標準按車長執行,車長超過10米的車補助標準最高,達50萬元 2016年補助標準中引入車長在10米-12米之間的“標準車”概念,在單位載品質能量消耗量小於0.25、續駛里程超過250公里時,補貼金額最多,可達50萬 12米以上、雙層客車參照標準車1.2倍給予補助 8-10米客車,按照標準車0.8倍給予補助 6-8米客車,參照標準車的0.5倍給予補貼 6米及以下客車參照標準車的0.2倍給予補助 2017年補助標準在2016年基礎上下調20%   面向全國開放純電動客車市場,北京市採用備案制管理,只要納入國家《節能與新能源汽車示範推廣應用工程推薦車型目錄》和《免征車輛購置稅的新能源汽車車型目錄》,完成企業和產品備案,即可在京銷售。   凡是滿足交通行業相關規定的運營企業均可購置、運營純電動客車。鼓勵公交、公路客運、旅遊等領域的企業積極開展純電動客車示範推廣工作;積極鼓勵開展各種純電動客車車型示範應用。

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

    【其他文章推薦】

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

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

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

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

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

  • mpvue+小程序雲開發,純前端實現婚禮邀請函(相冊小程序)

    mpvue+小程序雲開發,純前端實現婚禮邀請函(相冊小程序)

    請勿使用本文章及源碼作為商業用途!

    前言

    當初做這個小程序是為了婚禮前的需要,結婚之後,希望這個小程序能夠留存下來,特地花了一些空閑時間將小程序轉化成為“相冊類小程序”

    體驗碼

    準備工作

    1. mpvue框架 
    2. 小程序·雲開發 

    注意:使用mpvue前,首先你得先熟悉vue框架的基本使用 

    項目結構介紹

    注意:接下來展示的代碼,有幾個對比,分別是本人優化前和優化后的代碼對比,感興趣的可以着重看一下優化后的成熟寫法。

    • common目錄: 放一些公共資源,如js,css,json
    • components目錄:組件相關的.vue文件都放在這裏
    • pages目錄:所有頁面都放在這個目錄
    • utils目錄:使用mpvue時自動生成,可忽略
    • app.json文件:
    {
      "pages": [ "pages/index/main", "pages/photo/main", "pages/map/main", "pages/greet/main", "pages/message/main" ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "WeChat", "navigationBarTextStyle": "black" }, "tabBar": { "color": "#ccc", "selectedColor": "#ff4c91", "borderStyle": "white", "backgroundColor": "#ffffff", "list": [ { "pagePath": "pages/index/main", "iconPath": "static/images/1-1.png", "selectedIconPath": "static/images/1-2.png", "text": "邀請函" }, { "pagePath": "pages/photo/main", "iconPath": "static/images/2-1.png", "selectedIconPath": "static/images/2-2.png", "text": "甜蜜相冊" }, { "pagePath": "pages/map/main", "iconPath": "static/images/3-1.png", "selectedIconPath": "static/images/3-2.png", "text": "酒店導航" }, { "pagePath": "pages/greet/main", "iconPath": "static/images/4-1.png", "selectedIconPath": "static/images/4-2.png", "text": "好友祝福" }, { "pagePath": "pages/message/main", "iconPath": "static/images/5-1.png", "selectedIconPath": "static/images/5-2.png", "text": "留言評論" } ] }, "requiredBackgroundModes": ["audio"] }
    • App.vue文件 (本人主要是為了增加項目更新后的提醒),所以在這個文件加了些相關內容,內容如下:
    <script>
    export default { onLaunch () { // 檢測小程序是否有新版本更新 if (wx.canIUse('getUpdateManager')) { const updateManager = wx.getUpdateManager() updateManager.onCheckForUpdate(function (res) { // 請求完新版本信息的回調 if (res.hasUpdate) { updateManager.onUpdateReady(function () { wx.showModal({ title: '更新提示', content: '新版本已經準備好,是否重啟應用?', success: function (res) { if (res.confirm) { // 新的版本已經下載好,調用 applyUpdate 應用新版本並重啟  updateManager.applyUpdate() } } }) }) // 小程序有新版本,會主動觸發下載操作(無需開發者觸發)  wx.getUpdateManager().onUpdateFailed(function () { // 當新版本下載失敗,會進行回調  wx.showModal({ title: '提示', content: '檢查到有新版本,下載失敗,請檢查網絡設置', showCancel: false }) }) } }) } else { // 版本過低則無法使用該方法  wx.showModal({ title: '提示', confirmColor: '#5BB53C', content: '當前微信版本過低,無法使用該功能,請升級到最新微信版本后重試。' }) } } } </script> <style lang="stylus"> page height 100% image display block </style>
    • main.js文件:
    import Vue from 'vue'
    import App from './App' Vue.config.productionTip = false App.mpType = 'app' wx.cloud.init({ env: '雲開發環境ID' }) const app = new Vue(App) app.$mount()
    • functions目錄:主要放一些雲函數,這裏不清楚雲函數的文章後面會提及
    • images目錄:主要放一些靜態資源圖片

    頁面介紹

    首頁——邀請函

    首頁着重和大家講解下背景音樂的實現方法

    const audioCtx = wx.createInnerAudioContext()

     接口獲取實例

    接着,通過實例的相關方法來實現音樂的播放與暫停功能

    具體代碼如下:

    <script>
    import IndexSwiper from 'components/indexSwiper' import tools from 'common/js/h_tools' const audioCtx = wx.createInnerAudioContext() export default { name: 'Index', components: { IndexSwiper }, data () { return { isPlay: true, list: [] } }, onShow () { const that = this that.isPlay = true that.getMusicUrl() }, methods: { audioPlay () { const that = this if (that.isPlay) { audioCtx.pause() that.isPlay = false tools.showToast('您已暫停音樂播放~') } else { audioCtx.play() that.isPlay = true tools.showToast('背景音樂已開啟~') } }, getList () { const that = this const db = wx.cloud.database() const banner = db.collection('banner') banner.get().then(res => { that.list = res.data[0].bannerList }) }, getMusicUrl () { const that = this const db = wx.cloud.database() const music = db.collection('music') music.get().then(res => { let musicUrl = res.data[0].musicUrl audioCtx.src = musicUrl audioCtx.loop = true audioCtx.play() that.getList() }) } }, onShareAppMessage: function (res) { return { path: '/pages/index/main' } } } </script>

    以上代碼中使用到了雲開發相關功能,文章後面會介紹,請大家稍安勿躁

    相冊頁——就一個輪播圖,這裏就不過多介紹

    地圖頁——這裏着重講一下地圖標籤map

    map標籤 

    這裏講一下標記點markers:

    data () {
        return { // qqSdk: '',  markers: [{ iconPath: '../../static/images/nav.png', id: 0, latitude: 30.08059, longitude: 115.93027, width: 50, height: 50 }] } }
    <template>
        <div class="map">
            <image mode="aspectFit" class="head-img" src="../../static/images/t1.png"/>
            <map class="content" id="map" longitude="115.93027" latitude="30.08059" :markers="markers" scale="18" @tap="toNav">
            </map>
            <div class="call">
                <div class="left" @tap="linkHe">
                    <image src="../../static/images/he.png"/>
                    <span>呼叫新郎</span>
                </div>
                <div class="right" @tap="linkShe">
                    <image src="../../static/images/she.png"/>
                    <span>呼叫新娘</span>
                </div>
            </div>
            <image class="footer" src="../../static/images/grren-flower-line.png"/>
        </div>
    </template>

    祝福頁——也是雲開發相關內容,後面會介紹

    留言頁——也是雲開發相關內容,後面會介紹

    雲開發介紹

    project.config.json文件:

    "cloudfunctionRoot": "static/functions/"

    進行雲開發首先我們需要找到上面這個文件,在上面這個json文件中加上上面這行代碼

    cloudfunctionRoot 用於指定存放雲函數的目錄

    app.json文件:

    "window": {
        "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "WeChat", "navigationBarTextStyle": "black" }, "cloud": true

    增加字段 "cloud": true實現雲開發能力的兼容性

    開通雲開發

    在開發者工具工具欄左側,點擊 “雲開發” 按鈕即可開通雲開發

    雲開發控制台

    數據庫

     雲開發提供了一個 JSON 數據庫

     

    存儲

     

    雲開發提供了一塊存儲空間,提供了上傳文件到雲端、帶權限管理的雲端下載能力,開發者可以在小程序端和雲函數端通過 API 使用雲存儲功能。

     

    雲函數

     

    雲函數是一段運行在雲端的代碼,無需管理服務器,在開發工具內編寫、一鍵上傳部署即可運行後端代碼。

    使用雲開發

    雲能力初始化

    在小程序端開始使用雲能力前,需先調用 wx.cloud.init 方法完成雲能力初始化

    import Vue from 'vue'
    import App from './App' Vue.config.productionTip = false App.mpType = 'app' wx.cloud.init({ env: '雲開發環境ID' }) const app = new Vue(App) app.$mount()

    數據庫的使用

    在開始使用數據庫 API 進行增刪改查操作之前,需要先獲取數據庫的引用。以下調用獲取默認環境的數據庫的引用:

    const db = wx.cloud.database()

     要操作一個集合,需先獲取它的引用:

    const todos = db.collection('todos')

    操作數據庫的相關示例

    例:首頁獲取背景音樂資源

    getMusicUrl () {
          const that = this const db = wx.cloud.database() const music = db.collection('music') music.get().then(res => { let musicUrl = res.data[0].musicUrl audioCtx.src = musicUrl audioCtx.loop = true audioCtx.play() that.getList() }) }

     例:首頁獲取輪播圖數組

    getList () {
          const that = this const db = wx.cloud.database() const banner = db.collection('banner') banner.get().then(res => { that.list = res.data[0].bannerList }) }

    例:祝福頁,用戶送上祝福存儲用戶

    <script>
    import tools from 'common/js/h_tools' export default { name: 'Greet', data () { return { userList: [], openId: '', userInfo: '' } }, onShow () { const that = this that.getUserList() }, methods: { scroll (e) { console.log(e) }, sendGreet (e) { const that = this if (e.target.errMsg === 'getUserInfo:ok') { wx.getUserInfo({ success: function (res) { that.userInfo = res.userInfo that.getOpenId() } }) } }, addUser () { const that = this const db = wx.cloud.database() const user = db.collection('user') user.add({ data: { user: that.userInfo } }).then(res => { that.getUserList() }) }, getOpenId () { const that = this wx.cloud.callFunction({ name: 'user', data: {} }).then(res => { that.openId = res.result.openid that.getIsExist() }) }, getIsExist () { const that = this const db = wx.cloud.database() const user = db.collection('user') user.where({ _openid: that.openId }).get().then(res => { if (res.data.length === 0) { that.addUser() } else { tools.showToast('您已經送過祝福了~') } }) }, getUserList () { const that = this wx.cloud.callFunction({ name: 'userList', data: {} }).then(res => { that.userList = res.result.data.reverse() }) } } } </script>

    獲取送祝福的好友列表

    getUserList () {
          const that = this wx.cloud.callFunction({ name: 'userList', data: {} }).then(res => { that.userList = res.result.data.reverse() }) }

    這裏用到了雲函數,之所以用雲函數是因為小程序端在獲取集合數據時服務器一次默認並且最多返回 20 條記錄,雲函數端這個数字則是 100。

    雲函數的使用方法

    上面我們講過在project.config.json文件中配置雲函數存放位置

    下面是雲函數messageList的index.js文件:

    不成熟寫法:

    const cloud = require('wx-server-sdk')
    cloud.init()
    const db = cloud.database() const MAX_LIMIT = 100 exports.main = async (event, context) => { // 先取出集合記錄總數 const countResult = await db.collection('message').count() const total = countResult.total // 計算需分幾次取 const batchTimes = Math.ceil(total / 100) // 承載所有讀操作的 promise 的數組 const tasks = [] for (let i = 0; i < batchTimes; i++) { const promise = db.collection('message').skip(i * MAX_LIMIT).limit(MAX_LIMIT).get() tasks.push(promise) } // 等待所有 return (await Promise.all(tasks)).reduce((acc, cur) => ({ data: acc.data.concat(cur.data), errMsg: acc.errMsg })) }

    成熟寫法(分頁查詢):

    const cloud = require('wx-server-sdk')
    cloud.init()
    const db = cloud.database() exports.main = async (event, context) => { const wxContext = cloud.getWXContext() const dbName = 'message' const filter = event.filter ? event.filter : null const pageNum = event.pageNum ? event.pageNum : 1 const pageSize = event.pageSize ? event.pageSize : 10 const countResult = await db.collection(dbName).where(filter).count() const total = countResult.total const totalPage = Math.ceil(total / pageSize) let hasMore if (pageNum >= totalPage) { hasMore = false } else { hasMore = true } return db.collection(dbName).orderBy('time', 'desc').where(filter).skip((pageNum - 1) * pageSize).limit(pageSize).get().then(res => { res.hasMore = hasMore res.total = total res.openId = wxContext.OPENID return res }) }

    使用雲函數前,在開發者工具上,找到messageList目錄,右鍵如圖:

     點擊上傳並部署:雲端安裝依賴(不上傳node_modules)

    得到如圖的提示:

     安裝完點擊完成就能使用當前雲函數了,使用方法即:

    getUserList () {
          const that = this wx.cloud.callFunction({ name: 'userList', data: {} }).then(res => { that.userList = res.result.data.reverse() }) }

    數組之所以要倒序是因為希望新祝福的的用戶在最前面显示 

    用戶送上祝福的時候存儲用戶

    這裏我們用到了雲函數獲取用戶信息,

    當小程序端調用雲函數時,雲函數的傳入參數中會被注入小程序端用戶的 openid,開發者無需校驗 openid 的正確性,因為微信已經完成了這部分鑒權,開發者可以直接使用該 openid

    不成熟寫法:

    下面是雲函數user的index.js文件:

    // 雲函數入口文件
    const cloud = require('wx-server-sdk')
    
    cloud.init()
    
    // 雲函數入口函數
    exports.main = async (event, context) => { const wxContext = cloud.getWXContext() return { event, openid: wxContext.OPENID, appid: wxContext.APPID, unionid: wxContext.UNIONID } }

    主要是為了獲取當前操作用戶的openid,獲取當前用戶的openid方法:

    getOpenId () {
          const that = this wx.cloud.callFunction({ name: 'user', data: {} }).then(res => { that.openId = res.result.openid that.getIsExist() }) }

    接着判斷當前用戶是否已經存在於數據庫中,即getIsExist()方法:

    getIsExist () {
          const that = this const db = wx.cloud.database() const user = db.collection('user') user.where({ _openid: that.openId }).get().then(res => { if (res.data.length === 0) { that.addUser() } else { tools.showToast('您已經送過祝福了~') } }) }

    如果得到的數組長度為零則添加改用戶到數據庫,否則則提醒當前用戶已經送過祝福

    接下來介紹存儲用戶信息的方法,即addUser():

    addUser () {
          const that = this const db = wx.cloud.database() const user = db.collection('user') user.add({ data: { user: that.userInfo } }).then(res => { that.getUserList() }) }

    存入到數據庫的信息是這樣的:

    成熟寫法(使用雲函數一次搞定):

    // 雲函數入口文件
    const cloud = require('wx-server-sdk')
    cloud.init()
    const db = cloud.database() exports.main = async (event, context) => { const wxContext = cloud.getWXContext() const dbName = 'user' const filter = { _openid: wxContext.OPENID } const countResult = await db.collection(dbName).where(filter).count() const total = countResult.total if (total) { return { has: true } } else { return db.collection(dbName).add({ data: { user: event.user, _openid: wxContext.OPENID, time: db.serverDate() } }) } }
    toMessage (e) {
          const that = this
          if (e.target.errMsg === 'getUserInfo:ok') { wx.getUserInfo({ success: function (res) { that.userInfo = res.userInfo wx.navigateTo({ url: `/pages/writeMessage/main?avatar=${that.userInfo.avatarUrl}&name=${that.userInfo.nickName}`  }) that.addUser(that.userInfo) } }) } }, addUser (obj) { wx.cloud.callFunction({ name: 'addUser', data: { user: obj } }) }

    總結 

    大概的功能就是這麼多,希望可以幫助到大家,覺得寫得不錯的記得給作者點個贊,你們的支持是我不斷更新的最大動力!

    源碼地址

    後續優化

    • 留言審核
    • 一些動畫效果
    • 分頁處理
    • 雲函數優化
    • 回到頂部

    最後

    一開始不清楚隨筆和文章的區別,還是希望這篇隨筆能展示在博客園首頁(所以改成隨筆),讓更多的朋友看到

    希望對那些有想法又不會後台開發的朋友一些啟示作用,祝你早日做出只屬於自己的小程序。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

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

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

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

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

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

  • 湖北禁用廚餘垃圾餵豬 違規可罰5萬元

    摘錄自2020年1月7日星島日報報導

    中國非洲豬瘟疫情持續,湖北省政府立法,直接使用廚餘垃圾餵豬,將罰款最高5萬元人民幣,將於3月1日起實施。

    政府指,為加強廚餘垃圾管理,保障食品安全,促進廚餘垃圾資源化利用和無害化處理,所以實施《湖北省餐廚垃圾管理辦法》規定。根據規定,在廚餘垃圾投放、收集、運輸、處置過程中,不得實施下列行為:將廚餘垃圾與其他生活垃圾混合投放;將廚餘垃圾排入污水排水管道、雨水管道等市政公共設施以及河道、湖泊、水庫等公共水域;隨意傾倒、拋撒廚餘垃圾等。

    另外亦規定直接使用廚餘垃圾餵豬的,由縣級以上政府農業農村主管部門責令停止違法行為,可以處5000元以上2萬元以下罰款;情節嚴重的,處2萬元以上5萬元以下罰款。

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

    【其他文章推薦】

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

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

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

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

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

  • 傳蘋果電動車將於 2019 年問世

    蘋果公司生產汽車傳聞已久,美國《華爾街日報》21 日報導,蘋果正全力研發電動車,希望在 2019 年問世。   報導指出,蘋果電動車計劃的代號為「泰坦」(Titan),車款類型則像廂型休旅車。蘋果除了已聘請無人駕駛領域的專家外,也將泰坦的開發團隊由 600 人擴大 3 倍至 1,800 人,且可能會與傳統車廠合作,但蘋果官方發言人,拒絕對此消息作出評論。   外媒 AppleInsider 上周曾指出,蘋果已向電動車生產商特斯拉招兵買馬。 雖然有報導稱蘋果聘請無人駕駛領域的專家,但首款汽車仍需人操控駕駛。

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

    【其他文章推薦】

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

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

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

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

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