01 . ELK Stack簡介原理及部署應用

簡介

ELK並不是一款軟件,是一整套解決方案,是由ElasticSearch,Logstash和Kibana三個開源工具組成:通常是配合使用,而且先後歸於Elastic.co公司名下,簡稱ELK協議棧.

日誌的收集和處理

在日常運維工作中,對於系統和業務日誌的處理尤為重要。日誌主要包括系統日誌,應用日誌,應用程序日誌和安全日誌。系統運維和開發人員可以通過日誌了解服務器軟硬件信息,檢查配置過程中的錯誤及錯誤發生的原因。經常分析日誌可以了解服務器的負荷,性能安全性,從而及時採取措施糾正錯誤。

通常,日誌被分散的存儲在不同的設備上,如果你管理上百台服務器,你還在使用依次登錄每台機器的傳統方法查閱日誌是很繁瑣且效率低下的。當務之急是使用集中化的日誌管理,例如: 開源的syslog,將所有服務器上的日誌收集匯總。集中化管理日誌后,日誌的統計和檢索又成為一件比較麻煩的事情.

一個完整的集中式日誌系統,是離不開以下幾個主要特點的

# 1. 收集 - 能夠收集多種來源的日誌數據
# 2. 傳輸 - 能夠穩定的把日誌數據傳輸到中央系統
# 3. 存儲 - 如何存儲日誌數據
# 4. 分析 - 可以支持UI分析
# 5. 警告 - 能夠提供錯誤報告
日誌分析工具

1 . grep,awk,wc,rsyslog,syslog-ng: 對於龐大的機器數量,要達到要求更高的查詢,排序和統計等使用這樣的方法難免有點力不從心.

2 . 商業化的splunk: Splunk作為企業級的分佈式機器數據的平台,擁有強大的分佈式配置,包括跨數據中心的集群配置,Splunk提供兩種集群,indexer集群和Search Head集群.具體情況請看Splunk文章.

3 . 開源的:

# 1 FaceBook公司的Scribe
# 2 Apache的Chukwa
# 3 Linkedin的Kafka
# 4 Cloudera的Fluentd
# 5 ELK
Elasticsearch

ElasticSearch是一個基於Lucene的開源分佈式搜索服務器.是一個實時的分佈式搜索和分析引擎,他可以用於全文搜索,結構化搜索以及分析,他是一個建立在全文搜索引擎Apache lucene基礎上的搜索引擎,使用Java語言編寫,並作為Apache許可條款下的開放源碼發布,是第二流行的企業搜索引擎。設計用於雲計算中,能夠達到實時搜索,穩定,可靠,快速,安裝使用方便,在elasticsearch中,所有節點的數據是均等的.

主要特點

# 1 實時分析
# 2 分佈式實時文件存儲,並將每一個字段都編入索引
# 3 文檔導向,所有的對象全部是文檔
# 4 高可用性,易擴展,支持集群(cluster),分片和複製(hards和Replicas
# 5 接口友好,支持JSON

Logstash

logstash是一個具有實時渠道能力的數據收集引擎,使用JRuby語言編寫,其做着是世界著名的運維工程師喬丹西塞,他是一個完全開源工具,可以對你的日誌進行收集,過濾

# 主要特點:
#        1> 幾乎可以訪問任何數據
#        2> 可以和多種外部應用結合
#        3> 支持彈性擴展

# 它主要由三個主要部分組成,見下圖.
#        1> Shipper - 發送日誌數據
#        2> Broker -  收集數據,缺省內置Redis
#        3> Indexer - 數據寫入

Kibana

Kibana是一款基於Apache開源協議,使用JavaScript語言編寫,為Elasticsearch提供分析和可視化的Web平台,他可以在Elasticsearch的索引中查找,交互數據,並生成各種維度的表圖.

Filebeat

ELK協議棧的新成員,一個輕量級開源日誌數據搜集器,基於Logstash-Forwarder源代碼開發,是對他的替代。是需要在採集日誌數據server上安裝filebeat,並指定日誌目錄或日誌文件后,Filebeat就能讀取數據,迅速發送到Logstash進行解析,亦或直接發送到Elasticsearch進行集中式存儲和分析

ELK協議棧體繫結構

最簡單架構

在這種架構中,只有一個Logstash,Elasticsearch和Kibana實例。Logstash通過輸入插件從多種數據源(比如日誌文件,標準輸入Stdin等)獲取數據,再經過過濾插件加工數據,然後經過Elasticsearch輸出插件輸出到Elasticsearch,通過Kibana展示.

Logstash作為日誌收集器

這種架構是對上面架構的擴展,把一個Logstash數據搜集節點擴展到多個,分佈於多台機器,將解析好的數據發送到Elasticsearch server進行存儲,最後在Kibana查閱,生成日誌報表等.

這種結構因為需要在各個服務器上部署Logstash,而它比較消耗CPU和內存資源,所以比較適合資源豐富的服務器,否則容易造成服務器性能下降,甚至可能導致無法正常工作.

Beats作為日誌搜集器

這種架構引入Beats作為日誌搜集器。目前Beats包括四種

# 1> Packetbeat (搜集網絡流量數據)
# 2> Topbeat (搜集系統,進程和文件系統級別的CPU和內存使用情況等數據)
# 3> Filebeat (搜集文件數據)
# 4> Winlogbeat (搜集Windows事件日誌數據)

Beats將搜集到的數據發送到Logstash,經Logstash解析,過濾后,將其發送到Elasticsearch存儲,並由Kibana呈現給用戶.

這種架構解決了Logstash在各服務器節點上佔用系統資源高的問題,相比Logstash,Beats所佔系統的CPU和內存幾乎可以省略不計,另外,Beats和Logstash之間保持SSL/TLS加密傳輸,客戶端和服務器雙向認證,保證了通信安全

因此這種架構適合對數據安全性要求較高,同時各服務器性能比較敏感的場景.

基於Filebeat架構的配置部署詳解

前面提到Filebeat已經完全替代了Logstash-Forwarder 成為新一代的日誌採集器,同時鑒於它輕量、安全等特點,越來越多人開始使用它。這個章節將詳細講解如何部署基於 Filebeat 的 ELK 集中式日誌解決方案,具體架構見下圖.

引入消息隊列機制的架構

Beats 還不支持輸出到消息隊列,所以在消息隊列前後兩端只能是 Logstash 實例。這種架構使用 Logstash 從各個數據源搜集數據,然後經消息隊列輸出插件輸出到消息隊列中。目前 Logstash 支持 Kafka、Redis、RabbitMQ 等常見消息隊列。然後 Logstash 通過消息隊列輸入插件從隊列中獲取數據,分析過濾后經輸出插件發送到 Elasticsearch,最後通過 Kibana 展示。見下圖

這種架構適合於日誌規模比較龐大的情況,但由於Logstash日誌解析節點和Elasticsearch的符合比較重,可將他們配置為集群模式,以分擔負荷,引入消息隊列,均衡了網絡傳輸,從而降低了網絡閉塞,尤其丟失數據的可能性,但依然存在Logstash佔用系統資源過多的問題.

部署

架構圖

999

List
Package:
# elasticsearch-7.2.0-linux-x86_64.tar.gz
# filebeat-7.2.0-x86_64.rpm
# jdk-8u121-linux-x64.rpm
# kibana-7.2.0-linux-x86_64.tar.gz
# logstash-7.2.0.tar.gz
IP hostname 軟件 配置要求 網絡 備註
192.168.144.131 ES/數據存儲 elasticsearch-7.2 內存最低2GB/硬盤40GB Nat,內網
192.168.144.128 Kibana/UI展示 kibana-7.2****logstash-7.2 內存最低2GB/硬盤40GB Nat,內網
192.168.144.135 Filebeat/數據採集 Filebeat-7.2/nginx 內存最低2GB/硬盤40GB Nat,內網
注意事項
# 1.一定要對時,時間校正,不然日誌出不來
# 2.啟動Elasticsearch必須切換成所創建的ELK用戶啟動,不然ES處於安全目的,會啟動報錯.
# 3.日誌從Filebeat到Logstash再到ES檢索到Kibana的讀取速度取決於機器配置
Elasticsearch安裝
# 1.初始化
setenforce 0
sed -i '/^SELINUX=/ s/enforcing/disabled/'  /etc/selinux/config
systemctl stop firewalld
systemctl disable firewalld
sed -i '/^GSSAPIAu/ s/yes/no/' /etc/ssh/sshd_config
sed -i '/^#UseDNS/ {s/^#//;s/yes/no/}' /etc/ssh/sshd_config
curl -o /etc/yum.repos.d/163.repo http://mirrors.163.com/.help/CentOS7-Base-163.repo &>/dev/null
curl  -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
    yum -y install ntpdate
    ntpdate -b  ntp1.aliyun.com

# 2.設置Hostname解析
hostnamectl set-hostname elk-1
        ## 修改/etc/hosts 增加如下內容
        192.168.144.131     elk-1

# 4.java安裝
# rpm -ivh jdk-8u121-linux-x64.rpm
# 如果使用rpm請到/etc/profile加上
# JAVA_HOME=/usr/java/jdk1.8.0_121
# export PATH=$PATH:$JAVA_HOME/bin
yum -y install java-1.8.0-openjdk.x86_64

# 5.創建用戶和組並準備相關目錄並授權
groupadd  elk
useradd  elk  -g  elk
mkdir  -pv  /data/elk/{data,logs}
chown  -R  elk:elk  /data/elk/

# 6.軟件包解壓、授權
# 上傳軟件包
            通過scp 或者FTP方式上傳到/opt下
# 解壓軟件包到/opt目錄
tar xvf elasticsearch-7.2.0-linux-x86_64.tar.gz -C  /opt/
# 授權
chown  -R  elk:elk  /opt/elasticsearch-7.2.0/  # 軟件包名

# 7.修改elk配置文件
[root@elk-1 ~]# vim /opt/elasticsearch-7.2.0/config/elasticsearch.yml
        # 集群名
        cluster.name:  elk
        # 節點名
        node.name: node-1
        # 存儲數據
        path.data:  /data/elk/data
        # 存放日誌
        path.logs:  /data/elk/logs
        # 鎖內存,盡量不使用交換內存
        bootstrap.memory_lock:  false
        # 網絡地址
        network.host: 0.0.0.0
        http.port: 9200
        # 發現集群hosts
        discovery.seed_hosts: ["elk-1"]
        # 設置集群master節點
        cluster.initial_master_nodes: ["node-1"]

# 8.修改/etc/security/limits.conf
        # *號不是註釋
        * soft nproc 65535
        * hard nproc 65535
        * soft nofile 65535
        * hard nofile 65535

ulimit -n 65535
ulimit -u 20480

# 9.修改/etc/sysctl.conf
        echo "vm.max_map_count=262144" >> /etc/sysctl.conf
        sysctl -p

# 10.啟動ES並檢查集群健康狀態
nohup runuser -l elk -c '/bin/bash /opt/elasticsearch-7.2.0/bin/elasticsearch' &
    # 這裏為了省事直接用的nohup 可以使用supervisord 對進程進行管理
[root@elk-1 ~]# curl -XGET 'elk-1:9200/_cluster/health?pretty'
{
  "cluster_name" : "elk",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}
Kibana安裝
# 1.解壓Kibana安裝包
tar xvf kibana-7.2.0-linux-x86_64.tar.gz  -C /opt/

# 2.修改Kibana配置文件
server.port:  5601   # Port
server.host:  0.0.0.0   # 訪問限制
elasticsearch.hosts: ["http://192.168.144.131:9200"]        # ES主機IP:Port

# 3.啟動命令
[root@kibana ~]# nohup /opt/kibana-7.2.0-linux-x86_64/bin/kibana --allow-root &
[1] 14650
    # tailf nohup.out 即可實時查看日誌
Logstash安裝
[root@kibana ~]# rpm -ivh jdk-8u121-linux-x64.rpm
[root@kibana ~]# tar xvf logstash-7.2.0.tar.gz  -C /opt/
[root@kibana opt]# vim /opt/nginx.yml
# Sample Logstash configuration for creating a simple
# # Beats -> Logstash -> Elasticsearch pipeline.
#
input {
    beats {
        port => 5044
        }
}

filter {
    grok {
        match => ["message","%{DATA:log_date} %{WORD:method} %{TIME:log_localtime} %{HOSTNAME:host_name} %{WORD:workd}\[%{WORD:ls}\]\: %{DATA:log_date} %{TIME:log_localtime2} %{WORD:year_tmp}\: %{WORD:name_2}\: %{WORD:request_leixin} %{WORD:request_num}\, %{WORD}\: %{WORD:app_id}\, %{WORD}\: %{IP:ip}\, %{WORD}\: %{INT}\, %{WORD}\: %{USERNAME:device_id}"]
        }
}

output {
    elasticsearch {
       hosts => ["http://192.168.144.131:9200"]
       index => "app_log-%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
     }
}




# 啟動 (如果起不來或者報錯一般都是配置文件語法錯誤之類的)
    /opt/logstash-7.2.0/bin/logstash -f /opt/配置文件名.yml
    ## 後台運行
    nohup  /opt/logstash-7.2.0/bin/logstash -f /opt/配置文件名.yml  &
Filebeat安裝
# 安裝filebeat RPM包
[root@filebeat ~]# rpm -ivh filebeat-7.2.0-x86_64.rpm

# 修改配置文件:
Filebeat到Logstash
(vim /etc/filebeat/filebeat.yml) shift + : 輸入set nu 显示行號
24:   enabled:  true                         # 更改為true以啟用輸入配置
28:   - /data/*.log                   # 替換為要抓取的日誌文件路徑,如果抓取nginx日誌,修改為/var/log/nginx/access.log,如果是日誌服務器,在下面再加上多行其他地方的日誌目錄即可
73:  reload.enabled:  true                  # 啟動Filebeat模塊
148: output.elasticsearch:                  # 加上註釋;
150:  hosts: ["localhost:9200"]              # 加上註釋;
158: output.logstash:                       # 去掉註釋;
160: hosts: ["192.168.144.128<logstash>:5044"]       # 去掉註釋,並修改localhost為logstash機器IP及對應端口號;


# 測試配置文件並啟動
filebeat  test  config  -e
systemctl  start filebeat && systemctl  enable filebeat

[root@filebeat ~]# cat /data/nginx.log        # 創建該目錄及文件,加入兩行日誌.
Sep  2 16:00:18 cc-prd-3-tk-13-pcs1 pcs[16118]: Mon Sep  2 16:00:18 2019: PCS: recv request, app_id: app_1w5B6O4R2o1k881k12177, ip: 223.104.145.136, os: 0, device_id: 02c3864050502d43dc514905133bcc9c
Sep  2 16:00:18 cc-prd-3-tk-13-pcs1 pcs[16118]: Mon Sep  2 16:00:18 2019: PCS: recv request, app_id: app_1w5B6O4R2o1k881k12177, ip: 223.104.145.136, os: 0, device_id: 02c3864050502d43dc514905133bcc9c

收集的日誌需要把有用的信息做切割,讓相應字段成為獨立的個一個字段,而不是一整條日誌是一個字段,那樣就沒法做分析,而做數據源切割很重要,否則日誌會過不來,或者日誌做不了分析,統計.

可以把源日誌拿到kibana上的grok上做切割,直到Structured Data能出現你想要的字段就可以放到logstash的配置文件中

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

能防颱、調節水患 和古墳並立的日本太陽能發電廠

文:宋瑞文(加州能源特約撰述)

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

【其他文章推薦】

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

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

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

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

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

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

綠色運輸衝一波 城市的規劃與挑戰

文:詹詒絜(台達電子文教基金會高級專員)

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

【其他文章推薦】

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

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

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

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

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

遵守自然法則的吃蟲文化 日本自古就有「追蜂」少年

文:宋瑞文

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

流產、死胎層出不窮 南蘇丹隱匿石油業環境報告 犧牲者至今未受保障

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

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

【其他文章推薦】

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

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

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

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

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

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

Quartz.Net系列(七):Trigger之SimpleScheduleBuilder詳解

所有方法圖

 

 SimpleScheduleBuilder方法

RepeatForever:指定觸發器將無限期重複。

WithRepeatCount:指定重複次數

var trigger = TriggerBuilder.Create().WithSimpleSchedule(s=>s.WithIntervalInSeconds(1).RepeatForever()).Build();

 

            var trigger = TriggerBuilder.Create().WithSimpleSchedule(s=>s.WithIntervalInSeconds(1)
                                                                         .WithRepeatCount(10)).Build();

 

注:底層實現是repeatCount+1,也就是總共執行repeatCount+1次

        /// <summary>
        /// Specify a the number of time the trigger will repeat - total number of
        /// firings will be this number + 1.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="repeatCount">the number of seconds at which the trigger should repeat.</param>
        /// <returns>the updated SimpleScheduleBuilder</returns>
        /// <seealso cref="ISimpleTrigger.RepeatCount" />
        /// <seealso cref="RepeatForever" />
        public SimpleScheduleBuilder WithRepeatCount(int repeatCount)
        {
            this.repeatCount = repeatCount;
            return this;
        }

 

 

WithInterval:以毫秒為單位指定重複間隔,由於是TimeSpan也可以指定時分秒

WithIntervalInHours:以小時為單位指定重複間隔

WithIntervalInMinutes:以分鐘單位指定重複間隔

WithIntervalInSeconds:以秒為單位指定重複間隔

            var trigger = TriggerBuilder.Create().WithSimpleSchedule(s=>s .WithIntervalInSeconds(1)
                                                                          .WithInterval(TimeSpan.FromDays(1))
                                                                          .WithIntervalInMinutes(1)
                                                                          .WithIntervalInHours(1)
                                                                          .WithRepeatCount(5))
                                                 .Build();

注:底層都是通過WithInterval實現的

        /// <summary>
        /// Specify a repeat interval in milliseconds.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="timeSpan">the time span at which the trigger should repeat.</param>
        /// <returns>the updated SimpleScheduleBuilder</returns>
        /// <seealso cref="ISimpleTrigger.RepeatInterval" />
        /// <seealso cref="WithRepeatCount(int)" />
        public SimpleScheduleBuilder WithInterval(TimeSpan timeSpan)
        {
            interval = timeSpan;
            return this;
        }

        /// <summary>
        /// Specify a repeat interval in seconds.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="seconds">the time span at which the trigger should repeat.</param>
        /// <returns>the updated SimpleScheduleBuilder</returns>
        /// <seealso cref="ISimpleTrigger.RepeatInterval" />
        /// <seealso cref="WithRepeatCount(int)" />
        public SimpleScheduleBuilder WithIntervalInSeconds(int seconds)
        {
            return WithInterval(TimeSpan.FromSeconds(seconds));
        }

靜態方法:

RepeatMinutelyForever

RepeatMinutelyForTotalCount

RepeatSecondlyForever

RepeatSecondlyForTotalCount

RepeatHourlyForever

RepeatHourlyForTotalCount

var trigger = TriggerBuilder.Create().WithSchedule(SimpleScheduleBuilder.RepeatSecondlyForTotalCount(2)).Build();

 

 /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with a 1 minute interval.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatMinutelyForever()
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromMinutes(1))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with an interval
        /// of the given number of minutes.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatMinutelyForever(int minutes)
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromMinutes(minutes))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with a 1 second interval.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatSecondlyForever()
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromSeconds(1))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with an interval
        /// of the given number of seconds.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatSecondlyForever(int seconds)
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromSeconds(seconds))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with a 1 hour interval.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatHourlyForever()
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromHours(1))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat forever with an interval
        /// of the given number of hours.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatHourlyForever(int hours)
        {
            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromHours(hours))
                .RepeatForever();

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with a 1 minute interval.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatMinutelyForTotalCount(int count)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromMinutes(1))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with an interval of the given number of minutes.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatMinutelyForTotalCount(int count, int minutes)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromMinutes(minutes))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with a 1 second interval.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatSecondlyForTotalCount(int count)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromSeconds(1))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with an interval of the given number of seconds.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatSecondlyForTotalCount(int count, int seconds)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromSeconds(seconds))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with a 1 hour interval.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatHourlyForTotalCount(int count)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromHours(1))
                .WithRepeatCount(count - 1);

            return sb;
        }

        /// <summary>
        /// Create a SimpleScheduleBuilder set to repeat the given number
        /// of times - 1  with an interval of the given number of hours.
        /// </summary>
        /// <remarks>
        /// <para>Note: Total count = 1 (at start time) + repeat count</para>
        /// </remarks>
        /// <returns>the new SimpleScheduleBuilder</returns>
        public static SimpleScheduleBuilder RepeatHourlyForTotalCount(int count, int hours)
        {
            if (count < 1)
            {
                throw new ArgumentException("Total count of firings must be at least one! Given count: " + count);
            }

            SimpleScheduleBuilder sb = Create()
                .WithInterval(TimeSpan.FromHours(hours))
                .WithRepeatCount(count - 1);

            return sb;
        }

 

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

【其他文章推薦】

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

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

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

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

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

WEB安全入門:如何防止 CSRF 攻擊?

現在,我們絕大多數人都會在網上購物買東西。但是很多人都不清楚的是,很多電商網站會存在安全漏洞。烏雲就通報過,國內很多家公司的網站都存在 CSRF 漏洞。如果某個網站存在這種安全漏洞的話,那麼我們在購物的過程中,就很可能會被網絡黑客盜刷信用卡。是不是瞬間有點「不寒而栗」 的感覺?

首先,我們需要弄清楚 CSRF 是什麼。它的全稱是 Cross-site request forgery ,翻譯成中文的意思就是「跨站請求偽造」,這是一種對網站的惡意利用。簡單而言,就是某惡意網站在我們不知情的情況下,以我們的身份在你登錄的某網站上胡作非為——發消息、買東西,甚至轉賬……

這種攻擊模式聽起來有點像跨站腳本(XSS),但 CSRF 與 XSS 非常不同,並且攻擊方式幾乎相左。XSS 利用站點內的信任用戶,而 CSRF 則通過偽裝來自受信任用戶的請求來利用受信任的網站。與 XSS 攻擊相比,CSRF 攻擊往往很少見,因此對其進行防範的資源也相當稀少。不過,這種「受信任」的攻擊模式更加難以防範,所以被認為比 XSS 更具危險性。

這個過程到底是怎樣的呢?讓我們看個簡單而鮮活的案例。

銀行網站 A,它以 GET 請求來完成銀行轉賬的操作,如:

http://www.mybank.com/Transfer.php?toBankId=11&money=1000

危險網站 B,它裏面有一段 HTML 的代碼如下:

<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

可能會發生什麼?你登錄了銀行網站 A,然後訪問危險網站 B 以後,突然發現你的銀行賬戶少了10000塊……

為什麼會這樣呢?原因是在訪問危險網站 B 之前,你已經登錄了銀行網站 A,而 B 中的以 GET 的方式請求第三方資源(這裏的第三方就是指銀行網站了,原本這是一個合法的請求,但這裏被不法分子利用了),所以你的瀏覽器會帶上你的銀行網站 A 的 Cookie 發出 GET 請求,去獲取資源

「http://www.mybank.com/Transfer.php?toBankId=11&money=1000」,

結果銀行網站服務器收到請求后,認為這是一個合理的轉賬操作,就立刻轉賬了……

其實,真實的銀行網站不會如此不加防範,但即使用 POST 替代 GET,也只是讓危險網站多花些力氣而已。危險網站 B 依然可以通過嵌入 Javascript 來嘗試盜取客戶資金,所以我們時不時會聽到客戶資金被盜的案件,其實這並不是很不稀奇。

相信,很多人了解到這兒,會出現一身冷汗,還讓不讓我們在「618」期間能夠愉快地享受網購的快感了?難道沒有什麼辦法防住它嘛?

當然是有的。

在業界目前防禦 CSRF 攻擊主要有三種策略:驗證 HTTP Referer 字段;在請求地址中添加 token 並驗證;在 HTTP 頭中自定義屬性並驗證。下面就分別對這三種策略進行詳細介紹。

驗證 HTTP Referer 字段

根據 HTTP 協議,在 HTTP 頭中有一個字段叫 Referer,它記錄了該 HTTP 請求的來源地址。在通常情況下,訪問一個安全受限頁面的請求來自於同一個網站,比如需要訪問http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,用戶必須先登陸 bank.example,然後通過點擊頁面上的按鈕來觸發轉賬事件。這時,該轉帳請求的 Referer 值就會是轉賬按鈕所在的頁面的 URL,通常是以 bank.example 域名開頭的地址。而如果黑客要對銀行網站實施 CSRF 攻擊,他只能在他自己的網站構造請求,當用戶通過黑客的網站發送請求到銀行時,該請求的 Referer 是指向黑客自己的網站。因此,要防禦 CSRF 攻擊,銀行網站只需要對於每一個轉賬請求驗證其 Referer 值,如果是以 bank.example 開頭的域名,則說明該請求是來自銀行網站自己的請求,是合法的。如果 Referer 是其他網站的話,則有可能是黑客的 CSRF 攻擊,拒絕該請求。

這種方法的顯而易見的好處就是簡單易行,網站的普通開發人員不需要操心 CSRF 的漏洞,只需要在最後給所有安全敏感的請求統一增加一個攔截器來檢查 Referer 的值就可以。特別是對於當前現有的系統,不需要改變當前系統的任何已有代碼和邏輯,沒有風險,非常便捷。

然而,這種方法並非萬無一失。Referer 的值是由瀏覽器提供的,雖然 HTTP 協議上有明確的要求,但是每個瀏覽器對於 Referer 的具體實現可能有差別,並不能保證瀏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴於第三方(即瀏覽器)來保障,從理論上來講,這樣並不安全。事實上,對於某些瀏覽器,比如 IE6 或 FF2,目前已經有一些方法可以篡改 Referer 值。如果 bank.example 網站支持 IE6 瀏覽器,黑客完全可以把用戶瀏覽器的 Referer 值設為以 bank.example 域名開頭的地址,這樣就可以通過驗證,從而進行 CSRF 攻擊。

即便是使用最新的瀏覽器,黑客無法篡改 Referer 值,這種方法仍然有問題。因為 Referer 值會記錄下用戶的訪問來源,有些用戶認為這樣會侵犯到他們自己的隱私權,特別是有些組織擔心 Referer 值會把組織內網中的某些信息泄露到外網中。因此,用戶自己可以設置瀏覽器使其在發送請求時不再提供 Referer。當他們正常訪問銀行網站時,網站會因為請求沒有 Referer 值而認為是 CSRF 攻擊,拒絕合法用戶的訪問。

在請求地址中添加 token 並驗證

CSRF 攻擊之所以能夠成功,是因為黑客可以完全偽造用戶的請求,該請求中所有的用戶驗證信息都是存在於 cookie 中,因此黑客可以在不知道這些驗證信息的情況下直接利用用戶自己的 cookie 來通過安全驗證。要抵禦 CSRF,關鍵在於在請求中放入黑客所不能偽造的信息,並且該信息不存在於 cookie 之中。可以在 HTTP 請求中以參數的形式加入一個隨機產生的 token,並在服務器端建立一個攔截器來驗證這個 token,如果請求中沒有 token 或者 token 內容不正確,則認為可能是 CSRF 攻擊而拒絕該請求。

這種方法要比檢查 Referer 要安全一些,token 可以在用戶登陸后產生並放於 session 之中,然後在每次請求時把 token 從 session 中拿出,與請求中的 token 進行比對,但這種方法的難點在於如何把 token 以參數的形式加入請求。對於 GET 請求,token 將附在請求地址之後,這樣 URL 就變成 http://url?csrftoken=tokenvalue。 而對於 POST 請求來說,要在 form 的最後加上 <input type=”hidden” name=”csrftoken” value=”tokenvalue”/>,這樣就把 token 以參數的形式加入請求了。但是,在一個網站中,可以接受請求的地方非常多,要對於每一個請求都加上 token 是很麻煩的,並且很容易漏掉,通常使用的方法就是在每次頁面加載時,使用 javascript 遍歷整個 dom 樹,對於 dom 中所有的 a 和 form 標籤后加入 token。這樣可以解決大部分的請求,但是對於在頁面加載之後動態生成的 html 代碼,這種方法就沒有作用,還需要程序員在編碼時手動添加 token。

該方法還有一個缺點是難以保證 token 本身的安全。特別是在一些論壇之類支持用戶自己發表內容的網站,黑客可以在上面發布自己個人網站的地址。由於系統也會在這個地址後面加上 token,黑客可以在自己的網站上得到這個 token,並馬上就可以發動 CSRF 攻擊。為了避免這一點,系統可以在添加 token 的時候增加一個判斷,如果這個鏈接是鏈到自己本站的,就在後面添加 token,如果是通向外網則不加。不過,即使這個 csrftoken 不以參數的形式附加在請求之中,黑客的網站也同樣可以通過 Referer 來得到這個 token 值以發動 CSRF 攻擊。這也是一些用戶喜歡手動關閉瀏覽器 Referer 功能的原因。

在 HTTP 頭中自定義屬性並驗證

這種方法也是使用 token 並進行驗證,和上一種方法不同的是,這裏並不是把 token 以參數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性里。通過 XMLHttpRequest 這個類,可以一次性給所有該類請求加上 csrftoken 這個 HTTP 頭屬性,並把 token 值放入其中。這樣解決了上種方法在請求中加入 token 的不便,同時,通過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用擔心 token 會透過 Referer 泄露到其他網站中去。

然而這種方法的局限性非常大。XMLHttpRequest 請求通常用於 Ajax 方法中對於頁面局部的異步刷新,並非所有的請求都適合用這個類來發起,而且通過該類請求得到的頁面不能被瀏覽器所記錄下,從而進行前進,後退,刷新,收藏等操作,給用戶帶來不便。另外,對於沒有進行 CSRF 防護的遺留系統來說,要採用這種方法來進行防護,要把所有請求都改為 XMLHttpRequest 請求,這樣幾乎是要重寫整個網站,這代價無疑是不能接受的。

Java 代碼示例

下文將以 Java 為例,對上述三種方法分別用代碼進行示例。無論使用何種方法,在服務器端的攔截器必不可少,它將負責檢查到來的請求是否符合要求,然後視結果而決定是否繼續請求或者丟棄。在 Java 中,攔截器是由 Filter 來實現的。我們可以編寫一個 Filter,並在 web.xml 中對其進行配置,使其對於訪問所有需要 CSRF 保護的資源的請求進行攔截。

在 filter 中對請求的 Referer 驗證代碼如下

 

清單 1. 在 Filter 中驗證 Referer

// 從 HTTP 頭中取得 Referer 值
String referer=request.getHeader("Referer"); 
// 判斷 Referer 是否以 bank.example 開頭
if((referer!=null) &&(referer.trim().startsWith(“bank.example”))){ 
   chain.doFilter(request, response); 
}else{ 
   request.getRequestDispatcher(“error.jsp”).forward(request,response); 
}

以上代碼先取得 Referer 值,然後進行判斷,當其非空並以 bank.example 開頭時,則繼續請求,否則的話可能是 CSRF 攻擊,轉到 error.jsp 頁面。

如果要進一步驗證請求中的 token 值,代碼如下

清單 2. 在 filter 中驗證請求中的 token

HttpServletRequest req = (HttpServletRequest)request; 
HttpSession s = req.getSession(); 
 
// 從 session 中得到 csrftoken 屬性
String sToken = (String)s.getAttribute(“csrftoken”); 
if(sToken == null){ 
 
   // 產生新的 token 放入 session 中
   sToken = generateToken(); 
   s.setAttribute(“csrftoken”,sToken); 
   chain.doFilter(request, response); 
} else{ 
 
   // 從 HTTP 頭中取得 csrftoken 
   String xhrToken = req.getHeader(“csrftoken”); 
 
   // 從請求參數中取得 csrftoken 
   String pToken = req.getParameter(“csrftoken”); 
   if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){ 
       chain.doFilter(request, response); 
   }else if(sToken != null && pToken != null && sToken.equals(pToken)){ 
       chain.doFilter(request, response); 
   }else{ 
       request.getRequestDispatcher(“error.jsp”).forward(request,response); 
   } 
}

首先判斷 session 中有沒有 csrftoken,如果沒有,則認為是第一次訪問,session 是新建立的,這時生成一個新的 token,放於 session 之中,並繼續執行請求。如果 session 中已經有 csrftoken,則說明用戶已經與服務器之間建立了一個活躍的 session,這時要看這個請求中有沒有同時附帶這個 token,由於請求可能來自於常規的訪問或是 XMLHttpRequest 異步訪問,我們分別嘗試從請求中獲取 csrftoken 參數以及從 HTTP 頭中獲取 csrftoken 自定義屬性並與 session 中的值進行比較,只要有一個地方帶有有效 token,就判定請求合法,可以繼續執行,否則就轉到錯誤頁面。生成 token 有很多種方法,任何的隨機算法都可以使用,Java 的 UUID 類也是一個不錯的選擇。

除了在服務器端利用 filter 來驗證 token 的值以外,我們還需要在客戶端給每個請求附加上這個 token,這是利用 js 來給 html 中的鏈接和表單請求地址附加 csrftoken 代碼,其中已定義 token 為全局變量,其值可以從 session 中得到。

清單 3. 在客戶端對於請求附加 token

function appendToken(){ 
   updateForms(); 
   updateTags(); 
} 
 
function updateForms() { 
   // 得到頁面中所有的 form 元素
   var forms = document.getElementsByTagName('form'); 
   for(i=0; i<forms.length; i++) { 
       var url = forms[i].action; 
 
       // 如果這個 form 的 action 值為空,則不附加 csrftoken 
       if(url == null || url == "" ) continue; 
 
       // 動態生成 input 元素,加入到 form 之後
       var e = document.createElement("input"); 
       e.name = "csrftoken"; 
       e.value = token; 
       e.type="hidden"; 
       forms[i].appendChild(e); 
   } 
} 
 
function updateTags() { 
   var all = document.getElementsByTagName('a'); 
   var len = all.length; 
 
   // 遍歷所有 a 元素
   for(var i=0; i<len; i++) { 
       var e = all[i]; 
       updateTag(e, 'href', token); 
   } 
} 
 
function updateTag(element, attr, token) { 
   var location = element.getAttribute(attr); 
   if(location != null && location != '' '' ) { 
       var fragmentIndex = location.indexOf('#'); 
       var fragment = null; 
       if(fragmentIndex != -1){ 
 
           //url 中含有隻相當頁的錨標記
           fragment = location.substring(fragmentIndex); 
           location = location.substring(0,fragmentIndex); 
       } 
 
       var index = location.indexOf('?'); 
 
       if(index != -1) { 
           //url 中已含有其他參數
           location = location + '&csrftoken=' + token; 
       } else { 
           //url 中沒有其他參數
           location = location + '?csrftoken=' + token; 
       } 
       if(fragment != null){ 
           location += fragment; 
       } 
 
       element.setAttribute(attr, location); 
   } 
}

在客戶端 html 中,主要是有兩個地方需要加上 token,一個是表單 form,另一個就是鏈接 a。這段代碼首先遍歷所有的 form,在 form 最後添加一隱藏字段,把 csrftoken 放入其中。然後,代碼遍歷所有的鏈接標記 a,在其 href 屬性中加入 csrftoken 參數。注意對於 a.href 來說,可能該屬性已經有參數,或者有錨標記。因此需要分情況討論,以不同的格式把 csrftoken 加入其中。

如果你的網站使用 XMLHttpRequest,那麼還需要在 HTTP 頭中自定義 csrftoken 屬性,利用 dojo.xhr 給 XMLHttpRequest 加上自定義屬性代碼如下:

清單 4. 在 HTTP 頭中自定義屬性

var plainXhr = dojo.xhr; 
 
// 重寫 dojo.xhr 方法
dojo.xhr = function(method,args,hasBody) { 
   // 確保 header 對象存在
   args.headers = args.header || {}; 
 
   tokenValue = '<%=request.getSession(false).getAttribute("csrftoken")%>'; 
   var token = dojo.getObject("tokenValue"); 
 
   // 把 csrftoken 屬性放到頭中
   args.headers["csrftoken"] = (token) ? token : "  "; 
   return plainXhr(method,args,hasBody); 
};

這裏改寫了 dojo.xhr 的方法,首先確保 dojo.xhr 中存在 HTTP 頭,然後在 args.headers 中添加 csrftoken 字段,並把 token 值從 session 里拿出放入字段中。

CSRF 防禦方法選擇之道

通過上文討論可知,目前業界應對 CSRF 攻擊有一些克制方法,但是每種方法都有利弊,沒有一種方法是完美的。如何選擇合適的方法非常重要。如果網站是一個現有系統,想要在最短時間內獲得一定程度的 CSRF 的保護,那麼驗證 Referer 的方法是最方便的,要想增加安全性的話,可以選擇不支持低版本瀏覽器,畢竟就目前來說,IE7+, FF3+ 這類高版本瀏覽器的 Referer 值還無法被篡改。

如果系統必須支持 IE6,並且仍然需要高安全性。那麼就要使用 token 來進行驗證,在大部分情況下,使用 XmlHttpRequest 並不合適,token 只能以參數的形式放於請求之中,若你的系統不支持用戶自己發布信息,那這種程度的防護已經足夠,否則的話,你仍然難以防範 token 被黑客竊取並發動攻擊。在這種情況下,你需要小心規劃你網站提供的各種服務,從中間找出那些允許用戶自己發布信息的部分,把它們與其他服務分開,使用不同的 token 進行保護,這樣可以有效抵禦黑客對於你關鍵服務的攻擊,把危害降到最低。畢竟,刪除別人一個帖子比直接從別人賬號中轉走大筆存款嚴重程度要輕的多。

如果是開發一個全新的系統,則抵禦 CSRF 的選擇要大得多。筆者建議對於重要的服務,可以盡量使用 XMLHttpRequest 來訪問,這樣增加 token 要容易很多。另外盡量避免在 js 代碼中使用複雜邏輯來構造常規的同步請求來訪問需要 CSRF 保護的資源,比如 window.location 和 document.createElement(“a”) 之類,這樣也可以減少在附加 token 時產生的不必要的麻煩。

最後,要記住 CSRF 不是黑客唯一的攻擊手段,無論你 CSRF 防範有多麼嚴密,如果你系統有其他安全漏洞,比如跨站域腳本攻擊 XSS,那麼黑客就可以繞過你的安全防護,展開包括 CSRF 在內的各種攻擊,你的防線將如同虛設。我們只有充分重視 CSRF,根據系統的實際情況選擇最合適的策略,這樣才能把 CSRF 的危害降到最低。

 

點擊關注,第一時間了解華為雲新鮮技術~

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

地球發燒了研究:2080年前熱死人數激增20倍

摘錄自2018年8月1日蘋果日報台北報導

天氣讓人熱得受不了,而這種情況若不加以控制,地球上熱死人的數目將增加20倍。最新研究指出,在各國政府完全不做任何事的最糟情況,部分國家在2080年前,因為高溫導致的死亡數字將增加2000%。

這項刊登在「公共科學圖書館醫學期刊」(PLOS Medicine)的報告指出,歐洲、部分亞洲與北美洲地區的氣溫逐年升高,熱浪造成數以千計的死亡案例。該報告首席研究員、澳洲蒙納士大學(Monash University)副教授郭玉明(Yuming Guo,音譯)說:「未來的熱浪會發生得更頻繁、更強烈,維持時間也更長」、「如果我們找不到減緩氣候變遷的方法,並幫助人民適應熱浪氣候,和熱浪有關的死亡案例將會急遽增加。」

報告中指出,最糟的情況下,哥倫比亞在2031至2080年間被高溫熱死的人數,會比1971年至2010年增加2000%,即增加20倍;菲律賓在2031到2080因熱浪而額外死亡的人數,是1971到2020的12倍;澳洲、美國是5倍,英國是4倍。

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

【其他文章推薦】

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

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

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

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

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

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

石油公司看好電動車,預測2030年石油需求逐下滑

隨著相關技術逐漸成熟,如今已經不再只有相關業者看好電動車市場,就連石油大廠道達爾(Total)近日都承認,在這樣的發展下,石油的需求可能即將見頂,未來電動車的商機將不容小覷。

彭博社報導,道達爾的能源經濟學家Joel Couse 表示,隨著電動車市場持續擴大,估計至2030 年時,電動車將佔有新車市場15~30%,石油需求預計將在屆時達到頂峰,之後就不會再出現增長,甚至可能逐漸下滑。

這樣的話從石油主要生產商的口中說出有些令人感到意外,就連彭博新能源財經(BNEF)的主管Colin McKerracher 都表示,道達爾的分析是目前為止最看好電動車產業的預測,甚至超過彭博的預估值。

其實不只是道達爾,荷蘭皇家殼牌(Shell)也已經開始削減石油需求的長期預測。為了因應未來科技的走向,殼牌已經建立一個全新業務部門,針對多種綠能可能造成的經濟效益進行分析。

彭博新能源財經指出,在電動車的製程中,電池是成本中最昂貴的部分,幾乎可以佔到總成本的一半,高昂的成本價格也是造成許多車商在發展電動車時,會選擇高價車款為主打的原因。

然而隨著科技持續發展,電池價格開始逐年下降、效能持續提升,電動車已經開始能與燃油車在價格及性能上做競爭。儘管在全球汽車銷量中,電動車目前僅佔有約1% 的市場,但許多車商已經注意到這個趨勢,開始投入數十億美元進入電動車市場耕耘。

除了開始進入電動車市場的捷豹(Jaguar)、富豪(Volvo)之外,賓士(Mercedes-Benz)、通用(General)及許多車廠也將在2020 年時推出數十種電動車款,豐田(Toyota)更計劃在2050 年時,淘汰所有燃油車款,汽車市場「雪崩式」改變的時刻即將來到。

彭博新能源財經負責人Michael Liebreich 認為,至2020 年時,將會有120 種以上不同型號的電動車在市面上供消費者選擇,「到了那個時候,燃油車會變成很過時的東西。」 

(合作媒體:。圖片出處:publidc domain CC0)

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

【其他文章推薦】

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

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

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

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

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

打入中國市場,Hyundai選陸電池廠供混合動力車

南韓汽車製造商現代汽車(Hyundai Motor)首次選擇與中國電池製造商合作,由總部位於福建的寧德時代(CATL)為現代插電式混合動力車Sonata 提供電池,預計將在2018 年上半年打入中國市場。日經報導分析,這項合作是出於中國反外商情結的壓力。

日經新聞報導,隨著中國加緊推進新能源汽車應對空氣污染,中國政府正在研擬一項電動車份額草案,在中國市場上,外國汽車製造商必須讓電動車的銷量達到總銷售量的8%,若外商達不到指標,很有可能會被迫購買中國車商的積分,等於對中國車商提供經濟資助。先前草案規定2019 年將比率提升到10%,2020 年到12%,都被汽車製造商拒絕。

此外,中國政府為了幫助國內電池業者,規定必須通過中國的安全標準才符合補貼資格,東京的汽車製造商GLM 認為,未來很可能只有使用中國製造的電池的電動車才能在中國市場販售。GLM 準備在2019 年在中國市場推出4 人座跑車G4,號稱電動車的法拉利。

中國現在是全球最大的電動車市場,每年賣出30 萬台,中國政府最新的5 年計畫目標到2020 年一年增加到500 萬台。中國市場是現代汽車和起亞汽車在全球的最大市場,2016 年兩車廠在中國銷量佔全球整體銷量比率,分別為23.5% 和21.5%。

但這幾個月由於中韓兩國因美國設置反導系統引發政治動盪,使得現代汽車銷售受牽連,2017 年第一季中國零售銷售下滑14%,現代汽車準備在今年夏天啟用在重慶的第5 座中國工廠,銷售下滑對現代而言不是好消息。

CATL 是中國動力鋰電池龍頭,目標2020 年成為僅次於Tesla 與松下合資公司的全球第二大電動車電池供應商,最大股東為蘋果電池供應商日本TDK 集團,投資人中也包括鴻海。

根據現代官方說法,選擇CATL 作為第一個中國電池供應商,是現代尋求供應商多元化的策略之一。報導認為,與CATL 合作除了強化與中國政府的關係之外,也是為了避免中國突如其來的反外商政策對現代造成的衝擊。

(合作媒體:。圖片出處:Hyundai)

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準