分類
發燒車訊

英國首例 寵物貓確診武漢肺炎

摘錄自2020年7月27日中央社報導

英國廣播公司(BBC)報導,英國出現第一例寵物貓確診2019冠狀病毒疾病(COVID-19,武漢肺炎),專家表示這是英國第一起動物確診病例。衛生官員強調,這起案例非常罕見,民眾不需緊張。

據信是遭到確診的飼主傳染,但這不代表病毒可能透過寵物傳播。目前飼主與貓都已經痊癒。英國首席獸醫官密道米斯(Christine Middlemiss)表示:「沒有證據顯示寵物會直接將病毒傳播給人類,我們將持續密切關注,若情況生變,將告知飼主最新守則。」

英格蘭公共衛生署主任陶艾爾(Yvonne Doyle)建議民眾定期清潔雙手,包括在接觸動物前後。若動物接觸患病的人,牠們的毛髮在短期內可能會攜帶病毒。

國際新聞
英國
武漢肺炎
寵物

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

分類
發燒車訊

柬埔寨首都擴張摧毀濕地 逾百萬人安全生計堪憂

摘錄自2020年7月27日中央社報導

人權團體今(27日)警告,柬埔寨首都金邊(Phnom Penh)的濕地遭公寓建設和工業活動摧毀,逾100萬人正面臨洪患加劇和失去生計的風險。社運人士在一份報告中表示,包括ING City衛星城在內的開發案,將使1500公頃的東本(Tompoun)濕地面積剩不到1/10,周邊超過1000戶家庭將被迫遷離。金邊市150萬人口當中,也會有數千戶在濕地耕種捕魚的家庭變得窮困。

地方民團組織在報告中寫道:「濕地支撐本地社群的生計,同時在金邊的汙水管理與防洪等方面扮演關鍵角色。」「摧毀濕地潛藏的毀滅性衝擊,可能影響數百萬名柬埔寨人。」斯德哥爾摩環境研究所(Stockholm Environment Institute)曼谷中心的高級研究員阿契爾(Diane Archer)表示,湖泊以及氾濫平原、紅樹林、沼澤等濕地,能夠調節水流、減少洪患、淨化水資源和補充地下水。

根據保育團體野禽與濕地保護基金會(Wildfowl and Wetlands Trust),柬埔寨雖支持國際濕地保護公約,但過去15年來,原有濕地已消失大半。

濕地
生物多樣性
國際新聞
柬埔寨
棲地保育

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

分類
發燒車訊

鴻海擴大綠色能源布局 打造越南最大屋頂太陽能系統

摘錄自2020年7月27日經濟日報報導

鴻海集團落實企業社會責任,並強化永續綠色能源布局,相關綠能布局延伸至海外,將在越南廠區屋頂開始安裝太陽能板,打造越南當地最大的屋頂太陽能發電系統。

鴻海集團越南當地規劃,關聯公司將逐步提高太陽能等永續能源採用比重。據說明,越南子公司依據新的購電協議,在越南工廠屋頂上安裝6MW(百萬瓦)太陽能系統發電。

鴻海集團和相關太陽能系統項目開發商簽訂合約,依據相關合作計畫,開發商將在鴻海集團河內附近工廠建設太陽能發電設施。

相關建設計畫到位後,太陽能設施將能滿足鴻海集團越南當地子公司New Wing Interconnect Technology (NWIT)製造業務所需25%能源。

NWIT主管指出,公司目標是達成100%使用潔淨能源,因此和相關開發商合作,目標建立越南最大屋頂太陽能發電系統。

國際新聞
越南
鴻海
太陽能板
太陽屋頂

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

分類
發燒車訊

新基建下,智慧交通發展新規劃:智慧隧道監控可視化系統

前言 隨着當代經濟的發展,交通環境日益緊張,加上山區地區的交通運輸的需求,隧道的交通建設開發方興未艾。
隧道交通的規劃越來越完備,而對於隧道內監控管理維護卻顯得有些不足。而
工業4.0的崛起,逐步進入了智能化的新時代,伴隨着
工業互聯網的新興力量,工控可視化系統應運而生,不僅能起到日常的監控管理維護,在發現事故或險情時能第一時間採取
應急預案;還能通過實時數據的採集反饋,遠程操控設備運行以及預測設備的優良性能,從而達到更立體更全面的工控系統的運行。
HT for Web
 不止自主研發了強大的基於 HTML5 的 2D、3D 渲染引擎,為可視化提供了豐富的展示效果。介於 2D 組態和 3D 組態上,Hightopo(以下簡稱 HT )的 HT for Web 產品上的有着豐富的組態化可供選擇,本文將介紹如何運用 HT 豐富的 2/3D 組態搭建出一個隧道監控可視化系統的解決方案 監控隧道內的車道堵塞情況、隧道內的車禍現場,在隧道中显示當前車禍位置並在隧道口給予提示等功能都是非常有必要的。這個隧道監控可視化系統的主要內容包括:照明、風機、車道指示燈、交通信號燈、情報板、消防、火災報警、車行橫洞、風向儀、微波車檢、隧道緊急逃生出口的控制以及事故模擬等等。  
界面簡介及效果預覽  

預覽鏈接:http://www.hightopo.com/demo/tunnel2/index.html

上圖中的各種設備都可以雙擊,此時 camera 的位置會從當前位置移動到雙擊的設備的正前方;隧道入口的展示牌會自動輪播,出現事故時會展示牌中的內容會由“限速80,請開車燈”變為“超車道兩車追尾,請減速慢行”;兩隧道中間的逃生通道上方的指示牌是可以點擊的,點擊切換為藍綠色激活狀態,兩旁的逃生通道門也會打開,再單擊指示牌變為灰色,門關閉;還有一個事故現場模擬,雙擊兩旁變壓器中其中一個,在隧道內會出現一個“事故現場圖標”,單擊此圖標,出現彈出框显示事故等等等等。

 
代碼實現
一、場景搭建 整個隧道都是基於 3D 場景上繪製的,先來看看怎麼搭建 3D 場景:

// 數據容器 dm = new ht.DataModel(); // 3d 場景 g3d = new ht.graph3d.Graph3dView(dm); // 將場景添加到 body 中 g3d.addToDOM();

上面代碼中的 addToDOM 函數,是一個將組件添加到 body 體中的函數的封裝,定義如下:

addToDOM = function(){ var self = this, // 獲取組件的底層 div view = self.getView(), style = view.style; // 將組件底層div添加進body中  document.body.appendChild(view); // ht 默認將所有的組件的position都設置為absolute絕對定位 style.left = '0'; style.right = '0'; style.top = '0'; style.bottom = '0'; // 窗口大小改變事件,調用刷新函數 window.addEventListener('resize', function () { self.iv(); }, false); }

 
二、JSON反序列化 整個場景是由名為 隧道1.json 的文件導出而成的,我只需要用代碼將 json 文件中的內容轉換為我需要的部分即可:

// xhrLoad 函數是一個異步加載文件的函數 ht.Default.xhrLoad('./scenes/隧道1.json', function(text) { // 將 json 文件中的文本轉為我們需要的 json 格式的內容 var json = ht.Default.parse(text); // 反序列化數據容器,解析用於生成對應的Data對象並添加到數據容器 這裏相當於把 json 文件中生成的 ht.Node 節點反序列化到數據容器中,這樣數據容器中就有這個節點了  dm.deserialize(json); });

由於 xhrLoad 函數是一個異步加載函數,所以如果 dm 數據容器反序列化未完成就直接調用了其中的節點,那麼會造成數據獲取不到的結果,所以一般來說我是將一些邏輯代碼寫在這個函數內部,或者給邏輯代碼設置 timeout 錯開時間差。

首先,由於數據都是存儲在 dm 數據容器中的(通過 dm.add(node) 添加的),所以我們要獲取數據除了可以通過 id、tag 等獨立的方式,還可以通過遍曆數據容器來獲取多個元素。由於這個場景比較複雜,模型的面也比較多,鑒於設備配置,我將能 Batch 批量的元素都進行了批量。

批量是 HT 實現下的一種特有的機制,批量能提高性能的原理在於,當圖元一個個獨立繪製模型時性能較差,而當一批圖元聚合成一個大模型進行一次性的繪製時, 則會極大提高 WebGL 刷新性能,執行代碼如下

dm.each(function(data) { // 對“電話”進行批量 if (data.s('front.image') === 'assets/sos電話.png'){ data.s('batch', 'sosBatch'); } // 逃生通道批量(透明度也會影響性能) else if (data.s('all.color') === 'rgba(222,222,222,0.18)') { data.s('batch', 'emergencyBatch'); } else if (data.s('shape3d') === 'models/隧道/攝像頭.json' || data.s('shape3d') === 'models/隧道/橫洞.json' || data.s('shape3d') === 'models/隧道/捲簾門.json') { // 個別攝像頭染色了 不做批量 if(!data.s('shape3d.blend')) // 基礎批量什麼也不做 data.s('batch', 'basicBatch'); } else if (data.s('shape3d') === 'models/大型變壓器/變壓器.json') { data.s('batch', 'tileBatch'); data.setToolTip('單擊漫遊,雙擊車禍地點出現圖標'); } else if (data.getDisplayName() === '地面') { // 設置隧道“地面”不可選中 data.s('3d.selectable', false); } else if (data.s('shape3d') === 'models/隧道/排風.json') { // 排風扇的模型比較複雜,所以做批量 data.s('batch', 'fanBatch'); } else if (data.getDisplayName() === 'arrow') { // 隧道兩旁的箭頭路標 if (data.getTag() === 'arrowLeft') data.s('shape3d.image', 'displays/abc.png'); else data.s('shape3d.image', 'displays/abc2.png'); data.s({ 'shape3d': 'billboard', // 緩存,設置了 cache 的代價是需要設置 invalidateShape3dCachedImage 'shape3d.image.cache': true, // 設置這個值,圖片上的鋸齒就不會太明顯了(若圖片類型為 json,則設置 shape3d.dynamic.transparent) 'shape3d.transparent': true }); g3d.invalidateShape3dCachedImage(data); } // 隧道入口處的情報板 else if (data.getTag() === 'board' || data.getTag() === 'board1') { // 業務屬性,用來控制文本的位置[x,y,width,height] data.a('textRect', [0, 2, 244, 46]); // 業務屬性,設置文本內容 data.a('limitText', '限速80,請開車燈'); var min = -245; var name = 'board' + data.getId(); window[name] = setInterval(function() { // 設置情報板中的文字向左滾動,並且當文字全部显示時重複閃爍三次  circleFunc(data, window[name], min); }, 100); } //給逃生通道上方的指示板 動態設置顏色 var infos = ['人行橫洞1', '人行橫洞2', '人行橫洞3', '人行橫洞4', '車行橫洞1', '車行橫洞2', '車行橫洞3']; infos.forEach(function(info) { if(data.getDisplayName() === info) { data.a('emergencyColor', 'rgb(138, 138, 138)'); } }); infos = ['車道指示器', '車道指示器1', '車道指示器2', '車道指示器3']; infos.forEach(function(info) { if (data.getDisplayName() === info) { // 考慮到性能問題 將六面體變換為 billboard 類型元素 createBillboard(data, 'assets/車道信號-過.png', 'assets/車道信號-過.png', info); } }); });

上面有一處設置了 tooltip 文字提示信息,在 3d 中,要显示這個文字提示信息,就需要設置 g3d.enableToolTip() 函數,默認 3d 組件是關閉這個功能的。  
三、邏輯代碼
情報板滾動條 我就直接按照上面代碼中提到的方法進行解釋,首先是 circleFunc 情報板文字循環移動的函數,在這個函數中我們用到了業務屬性 limitText 設置情報板中的文字屬性以及 textRect 設置情報板中文字的移動位置屬性:

// 設置情報板中的文字向左滾動,並且當文字全部显示時重複閃爍三次 function circleFunc(data, timer, min) { // 獲取當前業務屬性 limitText 的內容 var text = data.a('limitText'); // 設置業務屬性 textRect 文本框的坐標和大小 data.a('textRect', [data.a('textRect')[0]-5, 2, 244, 46]); if (parseInt(data.a('textRect')) <= parseInt(min)) { data.a('textRect', [255, 2, 244, 46]); } else if (data.a('textRect')[0] === 0) { clearInterval(timer); var index = 0; // 設置多個 timer 是因為能夠進入這個函數中的不止一個 data,如果在同一時間多個 data 設置同一個 timer,那肯定只會對最後一個節點進行動畫。後面還有很多這種陷阱,要注意 var testName = 'testTimer' + data.getId(); window[testName] = setInterval(function() { index++; // 如果情報板中文本內容為空 if(data.a('limitText') === '') { setTimeout(function() { // 設置為傳入的 text 值 data.a('limitText', text); }, 100); } else { setTimeout(function() { // 若情報板中的文本內容不為空,則設置為空 data.a('limitText', ''); }, 100); } // 重複三次 if(index === 11) { clearInterval(window[testName]); data.a('limitText', text); } }, 100); setTimeout(function() { timer = setInterval(function() { // 回調函數  circleFunc(data, timer, min); }, 100); }, 1500); } } 

由於 WebGL 對瀏覽器的要求不低,為了能盡量多的適應各大瀏覽器,我們將所有的“道路指示器” ht.Node 類型的六面體全部換成 billboard 類型的節點,性能能提升不少。

http://www.hightopo.com 設置 billboard 的方法很簡單,獲取當前的六面體節點,然後給這些節點設置:

node.s({
    'shape3d': 'billboard', 'shape3d.image': imageUrl, 'shape3d.image.cache': true }); // 還記得用 shape3d.image.cache 的代價么? g3d.invalidateShape3dCachedImage(node); 

當然,因為 billboard 不能雙面显示不同的圖片,只是一個“面”,所以我們還得在這個節點的位置創建另一個節點,在這個節點的“背面”显示圖片,並且跟這個節點的配置一模一樣,不過位置要稍稍偏移一點。  
Camera 緩慢偏移 其他動畫部分比較簡單,我就不在這裏多說了,這裡有一個雙擊節點能將視線從當前 camera 位置移動到雙擊節點正前方的位置的動畫我提一下。我封裝了兩個函數 setEye 和 setCenter,分別用來設置 camera 的位置和目標位置的:

// 設置“目標”位置 function setCenter(center, finish) { // 獲取當前“目標”位置,為一個數組,而 getCenter 數組會在視線移動的過程中不斷變化,所以我們先拷貝一份 var c = g3d.getCenter().slice(0), // 當前x軸位置和目標位置的差值 dx = center[0] - c[0], dy = center[1] - c[1], dz = center[2] - c[2]; // 啟動 500 毫秒的動畫過度  ht.Default.startAnim({ duration: 500, action: function(v, t) { // 將“目標”位置緩慢從當前位置移動到設置的位置處  g3d.setCenter([ c[0] + dx * v, c[1] + dy * v, c[2] + dz * v ]); } }); }; // 設置“眼睛”位置 function setEye(eye, finish) { // 獲取當前“眼睛”位置,為一個數組,而 getEye 數組會在視線移動的過程中不斷變化,所以我們先拷貝一份 var e = g3d.getEye().slice(0), dx = eye[0] - e[0], dy = eye[1] - e[1], dz = eye[2] - e[2]; // 啟動 500 毫秒的動畫過度  ht.Default.startAnim({ duration: 500, // 將 Camera 位置緩慢地從當前位置移動到設置的位置 action: function(v, t) { g3d.setEye([ e[0] + dx * v, e[1] + dy * v, e[2] + dz * v ]); } }); };

後期我們要設置的時候就直接調用這兩個函數,並設置參數為我們目標的位置即可。比如我這個場景中的各個模型,由於不同視角對應的各個模型的旋轉角度也不同,我只能找幾個比較有代表性的 0°,90°,180°以及360° 這四種比較典型的角度了。所以繪製 3D 場景的時候,我也盡量設置節點的旋轉角度為這四个中的一種(而且對於我們這個場景來說,基本上只在 y 軸上旋轉了):

// 獲取事件對象的三維坐標 var p3 = e.data.p3(), // 獲取事件對象的三維尺寸 s3 = e.data.s3(), // 獲取事件對象的三維旋轉值 r3 = e.data.r3(); // 設置“目標”位置為當前事件對象的三維坐標值 setCenter(p3); // 如果節點的 y 軸旋轉值 不為 0 if (r3[1] !== 0) { // 浮點負數得做轉換才能進行比值 if (parseFloat(r3[1].toFixed(5)) === parseFloat(-3.14159)) { // 設置camera 的目標位置 setEye([p3[0], p3[1]+s3[1], p3[2] * Math.abs(r3[1]*2.3/6)]);  } else if (parseFloat(r3[1].toFixed(4)) === parseFloat(-1.5708)) { setEye([p3[0] * Math.abs(r3[1]/1.8), p3[1]+s3[1], p3[2]]);  } else { setEye([p3[0] *r3[1], p3[1]+s3[1], p3[2]]); } } else { setEye([p3[0], p3[1]+s3[1]*2, p3[2]+1000]); }

 
事故模擬現場 最後來說說模擬的事故現場吧,這段還是比較接近實際項目的。操作流程如下:雙擊“變壓器”–>隧道中間某個部分會出現一個“事故現場”圖標–>單擊圖標,彈出對話框,显示當前事故信息–>點擊確定,則事故現場之前的燈都显示為紅色×,並且隧道入口的情報板上的文字显示為“超車道兩車追尾,請減速慢行”–>再雙擊一次“變壓器”,場景恢復事故之前的狀態。 在 HT 中,可通過 Graph3dView#addInteractorListener(簡寫為 mi)來監聽交互過程:

g3d.addInteractorListener(function(e) { if(e.kind === 'doubleClickData') { // 有“事故”圖標節點存在 if (e.data.getTag() === 'jam') return; // 如果雙擊對象是變壓器 if (e.data.s('shape3d') === 'models/大型變壓器/變壓器.json') { index++; // 通過唯一標識 tag 標籤獲取“事故”圖標節點對象 var jam = dm.getDataByTag('jam'); if(index === 1){ var jam = dm.getDataByTag('jam'); jam.s({ // 設置節點在 3d 上可見 '3d.visible': true, // 設置節點為 billboard 類型 'shape3d': 'billboard', // 設置 billboard 的显示圖片 'shape3d.image': 'assets/車禍.png', // 設置 billboard 圖片是否緩存 'shape3d.image.cache': true, // 是否始終面向鏡頭 'shape3d.autorotate': true, // 默認保持圖片原本大小,設置為數組模式則可以設置圖片显示在界面上的大小 'shape3d.fixSizeOnScreen': [30, 30], }); // cache 的代價是節點需要設置這個函數  g3d.invalidateShape3dCachedImage(jam); } else { jam.s({ // 第二次雙擊變壓器就將所有一切恢復“事故”之前的狀態 '3d.visible': false }); dm.each(function(data) { var p3 = data.p3(); if ((p3[2] < jam.p3()[2]) && data.getDisplayName() === '車道指示器1') { data.s('shape3d.image', 'assets/車道信號-過.png'); } if(data.getTag() === 'board1') { data.a('limitText', '限速80,請開車燈'); } }); index = 0; } } } });

既然“事故”節點圖標出現了,接着點擊圖標出現“事故信息彈出框”,監聽事件同樣是在 mi(addInteractorListener)中,但是這次監聽的是單擊事件,我們知道,監聽雙擊事件時會觸發一次單擊事件,為了避免這種情況,我在單擊事件裏面做了演示:

// 點擊圖元 else if (e.kind === 'clickData'){ timer = setTimeout(function() { clearTimeout(timer); // 如果是“事故”圖標節點 if (e.data.getTag() === 'jam') { // 創建一個對話框  createDialog(e.data); } }, 200); }

在上面的雙擊事件中我沒有 clearTimeout,怕順序問題給大家造成困擾,要記得加一下。 彈出框如下: 這個彈出框是由兩個 ht.widget.FormPane 表單構成的,左邊的表單隻有一行,行高為 140,右邊的表單是由 5 行構成的,點擊確定,則“事故”圖標節點之前的道路指示燈都換成紅色×的圖標:

// 彈出框右邊的表單 function createForm4(node, dialog) { // 表單組件 var form = new ht.widget.FormPane(); // 設置表單組件的寬 form.setWidth(200); // 設置表單組件的高 form.setHeight(200); // 獲取表單組件的底層 div var view = form.getView(); // 將表單組件添加到 body 中  document.body.appendChild(view); var infos = [ '編輯框內容為:2輛', '編輯框內容為:客車-客車', '編輯框內容為:無起火', '編輯框內容為:超車道' ]; infos.forEach(function(info) { // 向表單中添加行  form.addRow([ info // 第二個參數為行寬度,小於1的值為相對值 ], [0.1]); }); form.addRow([ { // 添加一行的“確認”按鈕  button: { label: '確認', // 按鈕點擊事件觸發 onClicked: function() { // 隱藏對話框  dialog.hide(); dm.each(function(data) { var p3 = data.p3(); // 改變“車道指示器”的显示圖片為紅色×,這裏我是根據“事故”圖標節點的坐標來判斷“車道显示器”是在前還是在後的 if ((p3[2] < node.p3()[2]) && data.getDisplayName() === '車道指示器1') { data.s('shape3d.image', 'assets/車道信號-禁止.png'); } // 將隧道口的情報板上的文字替換 if(data.getTag() === 'board1') { data.a('limitText', '超車道兩車追尾,請減速慢行'); } }); } } } ], [0.1]); return form; }

 
總結 伴隨着新基建的建設興起,是以新發展理念為引領,以技術創新為驅動,以信息網絡為基礎,面向高質量發展需要,提供数字轉型、智能升級、融合創新等服務的基礎設施體系的完備,國家正邁入新時代的建設,也迎來了新時代的挑戰與機遇。隧道交通的監控可以歸納為工控管理與智慧交通建設的產物,同樣具有極為重要的意義。在眾多行業上所積累的經驗,HT 已經實現了許多不同領域建設的案例,例如 路口監管可視化系統,有興趣的話也可以了解一下!   2019 我們也更新了數百個工業互聯網 2D/3D 可視化案例集,在這裏你能發現許多新奇的實例,也能發掘出不一樣的工業互聯網: https://mp.weixin.qq.com/s/ZbhB6LO2kBRPrRIfHlKGQA 同時,你也可以查看更多案例及效果: https://www.hightopo.com/demos/index.html 本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

分類
發燒車訊

前瞻布局 穩健經營 東風日產第800萬輛整車下線

建立經銷商能力診斷體系並擴大經銷商經營範圍,進行二手車、汽車保險、汽車金融、汽車租貸等業務的擴充發展,在2016年1-10月達到12。21%的置換率,為東風日產歷史新高,進一步提升了經銷商收益力、服務能力和渠道效率,從而實現更加便捷高效的服務。

2016年12月26日,東風日產第800萬整車下線儀式在花都二工廠舉行。100餘名媒體記者以及車主代表共同參与和見證這一盛事。

東風日產副總經理周先鵬在下線儀式上表示:“東風日產的經營理念始終伴隨着中國經濟發展以及汽車產業的升級而轉型,堅持用前瞻性的眼光探索行業發展的態勢,對未來的發展方向提前布局。13年來,東風日產穩健經營、用心發展,從容迎來第八百萬輛整車下線。”

伴隨着第800萬輛整車下線,東風日產提前完成2016年度銷售目標,截至12月25日,全年銷量達到110萬輛,比去年同期增長13%,再次穩健跨越百萬。

客戶至上 體系能力全面升級

品牌順應時代,不斷成長,是企業穩健經營的前提。在購車者年齡越來越年輕的新汽車消費時代,東風日產2016年繼續深化YOUNG NISSAN 戰略,通過一系列“創新、走心、用心”的營銷活動,全面彰顯品牌年輕化心態,極大提升了品牌知名度與好感度。無論是攜手NBA、歐冠等頂級賽事,還是邀約頂級明星易建聯代言新生代TIIDA,都讓消費者近距離感受體育運動的激情與活力;產品營銷方面,新樓蘭、新奇駿、全新軒逸、藍鳥等產品圍繞文化、越野、音樂等不同主題,通過創新的活動形式,不僅讓消費者體驗到各具特色的產品魅力,更展現出不同產品和目標消費者的情懷與個性。據悉,2016年東風日產品牌好感度相較於2015年提升3.8%,首次超越豐田,躋身合資品牌前三。

客戶服務是企業穩健經營的基礎。2016年,東風日產圍繞“客戶年”的主題,開展“擁抱客戶,用心服務”主題實踐活動,強化全員客戶意識;通過成立地區支持辦公室,以更扁平化的運作架構貼近客戶;同時,在全國77家店開展了一系列的呼叫制培訓方式,使受訓店服務投訴率降幅達到38%。此外,易誠認證車首推兩年四萬公里保修升級政策,此舉為行業首創,深度保障消費者利益。

渠道健康是企業穩健經營的保障。2016年,東風日產落實p20大城市戰略,優化專營店的數量及效率,經銷商整體收益得到提升;建立經銷商能力診斷體系並擴大經銷商經營範圍,進行二手車、汽車保險、汽車金融、汽車租貸等業務的擴充發展,在2016年1-10月達到12.21%的置換率,為東風日產歷史新高,進一步提升了經銷商收益力、服務能力和渠道效率,從而實現更加便捷高效的服務。

不僅如此,東風日產更在提升企業體系力方面,未雨綢繆,坐言起行。2016年,秉承“穩健經營”的理念,東風日產腳踏實地、強調客戶服務、渠道和品牌健康成長。價值鏈前端建設也初見成果,先進工程技術中心、啟辰造型中心及東風日產大學,全面投入使用,從產品、研發設計、製造、人才培養等多個緯度鍛造企業內功,提升綜合實力,為東風日產未來新中期事業提供有力支撐。

智能驅動未來 I³計劃全面展開

隨着社會及技術層面信息化、智能化的發展,以及國家“智能製造”戰略藍圖的提出,汽車企業面臨着新的的機遇及挑戰。汽車行業已進入了智能時代,順應消費者需求智能化發展的趨勢,東風日產聚焦智能時代,進入以智能技術為驅動的YOUNG NISSAN 3.0時代,發布了“I³計劃”。以全價值鏈智能升級為核心,從智能出行(Intelligent Mobility Technology)、智造品質(Intelligent Manufacture Quality)、智享體驗(Intelligent Customer Experience)三大維度布局未來。

在智能出行方面,以“零碰撞、零排放、零距離”作為終極目標,開啟汽車技術的智能化升級,東風日產將成為率先導入中國的量產電動車的首個合資品牌;在智造品質方面,構建國內首創“整建制”先進工程技術中心,以数字化開發平台、智能化精工製造和信息化品質管理,實現製造技術的智能化升級;在智享體驗等方面,依託國內首個合資汽車公司自建電商平台車巴巴、率先將VR技術應用於新車體驗的沉浸式產品数字體驗平台、車載智能信息服務的應用,進行顧客全觸點的智能化升級。

2017年是東風日產再次跨越百萬之後的重要一年,800萬輛整車下線,對東風日產來說是一個歷史性的里程碑,更是一個新的起點。東風日產將以“I³計劃”為基礎,助推品牌年輕化戰略再升級,進入以智能技術為驅動的YOUNG NISSAN 3.0時代。同時,東風日產還將以“客戶年2.0”作為2017年發展的整體指導方向,從消費者需求出發,持續提升品牌力和客戶滿意度,保證主力車型的銷量及新車上市,同時整合網絡安全,強化經銷商基礎,為客戶帶來更加精彩的智能化汽車生活。

周先鵬表示,“前瞻性的戰略思考,以及穩健高效的執行力,為東風日產更快速響應市場,決勝未來奠定了堅實基礎。在800萬份信賴之上,東風日產砥礪前行,以智能化的未來驅動力,助力東風日產引領行業趨勢,穩健前行。”本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

分類
發燒車訊

2016重磅轎車盤點 自主雄起/合資緊張?

無論如何,2016年都將要過去,而2016年我們看到了帝豪GL與艾瑞澤5這兩款代表自主品牌實力的力作,尤其是帝豪GL對於整個自主品牌都是意義重大,帝豪GL可以說開拓了一個新的細分市場,尺寸介於A級車與B級車之間,價格卻比緊湊型車型高不了多少,這樣的產品力表現值得讚歎。

這裏小編團隊特地舉行了一次盛大的年度討論,目的就是選出2016年最值得推薦的年度轎車/年度SUV以及2016年自主品牌的新技術。而經過了長時間的激烈討論之後我們終於確定了5款年度轎車,它們都具有強大的產品力,可以說對車市有不小的影響,雖然有些車型的銷量不那麼好看但是實力無需否認,那麼我們一起看看是什麼車型能夠成為年度推薦轎車吧。

無論如何,2016年都將要過去,而2016年我們看到了帝豪GL與艾瑞澤5這兩款代表自主品牌實力的力作,尤其是帝豪GL對於整個自主品牌都是意義重大,帝豪GL可以說開拓了一個新的細分市場,尺寸介於A級車與B級車之間,價格卻比緊湊型車型高不了多少,這樣的產品力表現值得讚歎。

而在合資車方面科沃茲的出現可以說是給合資三廂入門車型帶來了新鮮血液,不錯的產品力表現以及定位能夠給自主車帶來不小的衝擊,科沃茲上市開始就成為爆款車型也是實力的印證,混動雅閣的出現攪動了新能源市場,可以說是混動市場的一顆重磅炸彈,加之漂亮的外觀優秀的內飾,混動雅閣讓人難以拒絕。

最後就是沃爾沃S90了,這款車型依靠漂亮的設計吸引了不少人的目光,而最終價格也是讓人震驚,相信離大賣也不遠了,雖然2016年整个中國車市的重心全都放在了SUV方面,無論是開發的新車型數量還是現有SUV車型的銷量都是節節拔高,但是依然不少人選擇緊湊型轎車,緊湊型轎車的銷量佔比也是十分高,自主轎車在A到B級的產品力補充得不錯,但是B級以上還需要品牌力等更多的補充,2017年即將到來,轎車市場能否迎來更大的輝煌呢?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

分類
發燒車訊

奔馳最便宜的小轎車到來,價格可能比2.4雅閣還便宜?

奔馳Z級定位小型車,競爭對手鎖定奧迪A1以及MINI,從造型上來看奔馳Z級還是比較的有奔馳家族風格的,大尺寸的輪轂和小巧的車身形成鮮明對比,運動風格比較強烈。從前臉造型上看Z級的造型和奔馳GLC有着比較高的相似度,全LED大燈的造型也和奔馳高端車型幾乎一致,大燈尺寸和進氣格柵都十分大,加上GLC元素的使用使得前臉還是比較有氣勢的。

奔馳這個品牌在中國市場深耕多年早已深入人心,一說起豪車大家都會想到奔馳寶馬,但是奔馳給大多數人的印象一直是價格昂貴高高在上的,比如售價過百萬的奔馳S、全尺寸SUV奔馳GLS等,但奔馳不止有這些車。

↑↑↑目前能夠買到的最便宜的三廂轎車CLA指導價為26.60-37.80萬

↑↑↑目前能夠買到最便宜奔馳兩廂轎車奔馳A級指導價為指導價:23.40-36.00萬

你以為這就是奔馳最便宜車型的價格了嗎?當然不是,外媒繪製了一張奔馳Z級的假想圖,目前奔馳A級以及奔馳CLA都屬於緊湊型車型,而奔馳目前並沒有小型車,而Z級的出現即將填補奔馳在這一市場的空白。

奔馳Z級定位小型車,競爭對手鎖定奧迪A1以及MINI,從造型上來看奔馳Z級還是比較的有奔馳家族風格的,大尺寸的輪轂和小巧的車身形成鮮明對比,運動風格比較強烈。

從前臉造型上看Z級的造型和奔馳GLC有着比較高的相似度,全LED大燈的造型也和奔馳高端車型幾乎一致,大燈尺寸和進氣格柵都十分大,加上GLC元素的使用使得前臉還是比較有氣勢的。

來到尾部,層次豐富的尾部造型也頗有幾分GLC的味道,排氣管的造型十分有運動感,只是尺寸偏小,尾燈的造型也和奔馳現今的SUV車型設計比較相似,Z級在外觀上和奔馳SUV車系比較接近,因此小編預測未來Z級會衍生出SUV車型或者跨界版,名字就叫GLZ?到時候就是小號的GLC了。

從假想圖看來車頂高度在後排位置下降比較多,小編對於Z級的頭部空間表現表示擔憂。

Z級的出現拉低了奔馳車型的入門門檻,而和奧迪A1以及寶馬MINI對標的話,小編預計Z級的售價在18萬起,這樣的售價也算是對得起觀眾了,當然由於這類車型比較小眾,因此即使上市也會以進口身份銷售,因此希望售價過低還是不太可能。

競爭對手:

奔馳Z級上市后競爭對手主要是奧迪A1、寶馬MINI以及雪鐵龍DS3,相比之下A1有着奧迪的科技感以及龐大的受眾,DS3比較的怪異能夠贏得一些消費者的喜愛,MINI則是哪個經典造型,十分有個性,與它們相比奔馳Z級的道路還比較長。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

分類
發燒車訊

個性轎跑SUV不只有合資車型 自主品牌照樣玩兒得溜

77萬的頂配售價着實比較實惠,但個人覺得,可以等待它1。4T+6DCT的動力總成組合上市以後再做考慮。啟辰T90指導價格:10。98-15。48萬啟辰T90的關注度自今年早些時候曝光以來就一直不減,這台側面看上去很像本田歌詩圖的SUV尺寸也比以上兩款車型更大,定價也更高。

很多車企在SUV車型上玩起了跨界,高端的車型有寶馬的X4、X6,中端一些的也有今年大熱的馬自達CX-4、跨界造型的SUV有着轎跑般的外觀和符合SUV的離地間隙,在個性化和車輛通過實用性方面做出了權衡,滿足了很多追求車輛個性人群的需求。

然而,寶馬X4、X6之流對於普羅大眾來說畢竟還是太遙遠,CX-4作為合資中端跨界SUV,售價對於不少人來說還是高了,那麼就可以看看自主品牌車型,也有不少把個性與跨界玩兒得溜的代表車型。

吉利帝豪GS

指導價格:7.78-10.88萬

帝豪GS的名氣已經非常大啦,從上市以來就已經為自己圈了非常龐大的粉絲團,流線型的轎跑設計相當緊湊美觀,憑藉著吉利品牌近年來優秀的品質做工,帝豪GS從內到外的質感都做到了不輸於合資品牌一貫擁有的水準,十萬出頭的頂配指導價格,配置也是極其豐富,作為年輕人第一台車是非常值得考慮的選擇。

東風風神AX3

指導價格:6.97-8.77萬

AX3是風神旗下定位偏向年輕化的一款跨界型SUV,雖然車身線條勾勒方式趨於平緩紮實,沒有什麼太多的亮點可言,但整車給人的感覺還是相當提氣與精神,內飾層面也以簡潔實用的風格為主,作為一台家用小車來說,8.77萬的頂配售價着實比較實惠,但個人覺得,可以等待它1.4T+6DCT的動力總成組合上市以後再做考慮。

啟辰T90

指導價格:10.98-15.48萬

啟辰T90的關注度自今年早些時候曝光以來就一直不減,這台側面看上去很像本田歌詩圖的SUV尺寸也比以上兩款車型更大,定價也更高。外觀設計官方稱之為“風雕美學”,從視覺效果上看,溜背造型的車身舒展秀氣,而細節處的肌肉線條也體現出一台SUV該有的力量感。更大的車身尺寸也有着更大的車內空間,個人認為,啟辰T90更適合作為家庭的第二輛車購入。

總結:以上三款車型比較優秀的是在於各自品牌的質量控制方面可以說是當下自主品牌當中做的比較優秀的典型,而在終端售價表現上,十萬左右的價格也是更多人可以接受的範圍,如果是作為第一輛車,小編推薦的是帝豪GS,雖然從動力表現和机械性能層面或許還有提升空間,但是從外觀的顏值和內飾的質感上,GS可以說是非常不錯的選擇。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

分類
發燒車訊

個性外觀動力不凡的德系轎跑 車主們如何評價它?

40萬車主點評:相信大部分購買CC就是看中其外觀,雖然改款后沒以前那麼個性,卻多了幾分沉穩,外觀設計也見仁見智把。DSG雙離合變速箱平順性以及換擋邏輯做到相當出色,大眾可以說是造雙離合變速箱最早一個廠商,所以在技術性上大眾雙離合變速箱更成熟。

對於中級車市場來說,競爭仍然還是很激烈。雖然中級車非常普遍,但擁有一輛充滿個性運動且能兼顧到家用的,卻是少之又少,然後大眾CC就是其中一款造型非常獨特,有格調,那我們一起來看看已購買這款車的車主如何評價他們。

大眾CC

指導價:25.28-34.28萬

車主一:不忘初心

購買車型:大眾CC 2016款 1.8TSI 豪華型

裸車價格:25.28萬

車主點評:當時相中大眾CC無疑是被其外觀所吸引,轎跑外觀,無框車門,流暢車身線條,呈現出非常運動拉風的一面,個人也認為CC是史上最美的大眾車型。空間表現上,屬於中級車應有的水準。1.8T市內上下班足夠用,提速很輕快,高顏值動力強!是一款非常值得買的車型。

目前行駛里程:CC目前行駛3680公里,綜合油耗在11L/100km,由於走市區較多,且道路擁堵,這油耗表現我也挺滿意。

車主二:奮鬥ing

購買車型:大眾 2016款 2.0TSI 豪華型

裸車價格:26.40萬

車主點評:相信大部分購買CC就是看中其外觀,雖然改款后沒以前那麼個性,卻多了幾分沉穩,外觀設計也見仁見智把。DSG雙離合變速箱平順性以及換擋邏輯做到相當出色,大眾可以說是造雙離合變速箱最早一個廠商,所以在技術性上大眾雙離合變速箱更成熟。

目前行駛里程:目前CC跑了3800公里,綜合油耗在11.5L/100km,還是能接受!

車主三:透心涼

購買車型:大眾CC 2016款 2.0TSI 至尊型

裸車價格:28.08萬

車主點評:當初不買BBA就是因為之前很早就喜歡CC,也可以說是一種情懷。由於買的是2.0T頂配車型,配置非常豐富,還配備丹拿音響,也算是世界級音響,音質特別棒。其次就是在動力方面,2.0T動力輸出很強勁,高速上超車和加速都是輕鬆事情,轉向也很精準,在操控性上有一定的樂趣。

目前行駛里程:目前開了7200公里,綜合油耗在12L/100km,追求動力,油耗也必須高。

編者點評:

大眾CC可以說在同級別外觀造型最拉風,無框車門吸引不少消費者對其追捧。發動機與變速箱的搭配,動力輸出足夠強勁,急加速直觀感受非常不錯。具有一定操控樂趣和高顏值外觀,你還有什麼理由不買?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

分類
發燒車訊

小菜成長之路,警惕淪為 API 調用俠

小菜(化名)在某互聯網公司擔任運維工程師,負責公司後台業務的運維保障工作。由於自己編程經驗不多,平時有不少工作需要開發協助。

聽說 Python 很火,能快速開發一些運維腳本,小菜也加入 Python 大軍學起來。 Python 語言確實簡單,小菜很快就上手了,覺得自己應對運維開發工作已經綽綽有餘,便不再深入研究。

背景

這天老闆給小菜派了一個數據採集任務,要實時統計服務器 TCP 連接數。需求背景是這樣的:開發同事需要知道服務的連接數以及不同狀態連接的比例,以便判斷服務狀態。

因此,小菜需要開發一個腳本,定期採集並報告 TCP 連接數,提交數據格式定為 json :

{
  "LISTEN": 4,
  "ESTABLISHED": 100,
  "TIME_WAIT": 10
}

作為運維工程師,小菜當然知道怎麼查看系統 TCP 連接。
Linux 系統中有兩個命令可以辦到, netstat 和 ss :

$ netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:8388          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 192.168.56.3:22         192.168.56.1:54983      ESTABLISHED
tcp6       0      0 :::22                   :::*                    LISTEN
$ ss -nat
State                    Recv-Q                    Send-Q                                         Local Address:Port                                         Peer Address:Port
LISTEN                   0                         128                                                127.0.0.1:8388                                              0.0.0.0:*
LISTEN                   0                         128                                            127.0.0.53%lo:53                                                0.0.0.0:*
LISTEN                   0                         128                                                  0.0.0.0:22                                                0.0.0.0:*
ESTAB                    0                         0                                               192.168.56.3:22                                           192.168.56.1:54983
LISTEN                   0                         128                                                     [::]:22                                                   [::]:*

小菜還知道 ss 命令比 netstat 命令要快,但至於為什麼,小菜就不知道了。

小菜很快找到老闆,提出了自己的解決方案:寫一個 Python 程序,調用 ss 命令採集 TCP 連接信息,然後再逐條統計。

老闆告訴小菜,線上服務器很多都是最小化安裝,並不能保證每台機器上都有 ss 或者 netstat 命令。

老闆還告訴小菜,程序開發要學會 站在巨人的肩膀上 。動手寫代碼前,先調研一番,看是否有現成的解決方案。 切忌重複造輪子 ,浪費時間不說,可能代碼質量還差,效果也不好。

最後老闆給小菜指了條明路,讓他回去再看看 psutil 。 psutil 是一個 Python 第三方包,用於採集系統性能數據,包括: CPU 、內存、磁盤、網卡以及進程等等。臨走前,老闆還叮囑小菜,完成工作后花點時間研究下這個庫。

psutil 方案

小菜搜索 psutil 發現,原來有這麼順手的第三方庫,喜出望外!他立馬裝好 psutil ,準備開干:

$ pip install psutil

導入 psutil 后,一個函數調用就可以拿到系統所有連接,連接信息非常豐富:

>>> import psutil
>>> for conn in psutil.net_connections('tcp'):
...     print(conn)
...
sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='192.168.56.3', port=22), raddr=addr(ip='192.168.56.1', port=54983), status='ESTABLISHED', pid=None)
sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=8388), raddr=(), status='LISTEN', pid=None)
sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='0.0.0.0', port=22), raddr=(), status='LISTEN', pid=None)
sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.53', port=53), raddr=(), status='LISTEN', pid=None)
sconn(fd=-1, family=<AddressFamily.AF_INET6: 10>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::', port=22), raddr=(), status='LISTEN', pid=None)

小菜很滿意,感覺不用花多少時間就可搞定數據採集需求了,準時下班有望!噼里啪啦,很快小菜就寫下這段代碼:

import psutil
from collections import defaultdict

# 遍歷每個連接,按連接狀態累加
stats = defaultdict(int)
for conn in psutil.net_connections('tcp'):
    stats[conn.status] += 1

# 遍歷每種狀態,輸出連接數
for status, count in stats.items():
    print(status, count)

小菜接着在服務器上測試這段代碼,功能完全正常:

ESTABLISHED 1
LISTEN 4

小菜將數據採集腳本提交,並按既定節奏逐步發布到生產服務器上。開發同事很快就看到小菜採集的數據,都誇小菜能力不錯,需求完成得很及時。小菜也很高興,感覺 Python 沒白學。如果用其他語言開發,說不定現在還在加班加點呢!Life is short, use Python! 果然沒錯!

小菜愈發自信,早就把老闆的話拋到腦後了。 psutil 這個庫這麼好上手,有啥好深入研究的?

內存悲劇

突然有一天,其他同事緊急告訴小菜,他開發的採集腳本佔用很多內存, CPU 也跑到了 100% ,已經開始影響線上服務了。小菜還沉浸在成功的喜悅中,收到這個反饋如同晴天霹靂,有點举手無措。

業務同事告訴小菜,受影響的機器系統連接數非常大,質疑小菜是不是腳本存在性能問題。小菜覺得很背,腳本只是調用 psutil 並統計數據,怎麼就攤上性能故障?腳本影響線上服務,小菜壓力很大,但不知道如何是好,只能跑去找老闆尋求幫助。

老闆要小菜第一時間停止數據採集,降低影響。復盤故障時,老闆很敏銳地問小菜,是不是用容器保存所有連接了?小菜自己並沒有,但是 psutil 這麼做了:

>>> psutil.net_connections()
[sconn(fd=-1, family=<AddressFamily.AF_INET6: 10>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='::', port=22), raddr=(), status='LISTEN', pid=None), sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='0.0.0.0', port=22), raddr=(), status='LISTEN', pid=None), sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.53', port=53), raddr=(), status='LISTEN', pid=None), sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_DGRAM: 2>, laddr=addr(ip='10.0.2.15', port=68), raddr=(), status='NONE', pid=None), sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_DGRAM: 2>, laddr=addr(ip='127.0.0.1', port=8388), raddr=(), status='NONE', pid=None), sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='192.168.56.3', port=22), raddr=addr(ip='192.168.56.1', port=54983), status='ESTABLISHED', pid=None), sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_DGRAM: 2>, laddr=addr(ip='127.0.0.53', port=53), raddr=(), status='NONE', pid=None), sconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketKind.SOCK_STREAM: 1>, laddr=addr(ip='127.0.0.1', port=8388), raddr=(), status='LISTEN', pid=None)]

psutil 將採集到的所有 TCP 連接放在一個列表裡返回。如果服務器上有十萬個 TCP 連接,那麼列表裡將有十萬個連接對象。難怪採集腳本吃了那麼多內存!

老闆告訴小菜,可以用生成器加以解決。與列表不同,生成器逐個返回數據,因此不會佔用太多內存。Python2 中 range 和 xrange 函數的區別也是一樣的道理。

小菜從 pstuil  fork 了一個分支,並將 net_connections 函數改造成 生成器 :

def net_connections():
    while True:
        if done:
            break

        # 解析一個TCP連接
        conn = xxx

        yield conn

代碼上線后,採集腳本內存佔用量果然下降了! 生成器 將統計算法的空間複雜度由原來的 O(n) 優化為 O(1) 。經過這次教訓,小菜不敢再盲目自信了,他決定抽時間好好看看 psutil 的源碼。

源碼體會

深入學習源碼后,小菜發現原來 psutil 採集 TCP 連接數的秘笈是:從 /proc/net/tcp 以及 /proc/net/tcp6 讀取連接信息。

由此,他還進一步了解到 procfs ,這是一個偽文件系統,將內核空間信息以文件方式暴露到用戶空間。 /proc/net/tcp 文件則是提供內核 TCP 連接信息:

$ cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
   0: 0100007F:20C4 00000000:0000 0A 00000000:00000000 00:00000000 00000000 65534        0 18183 1 0000000000000000 100 0 0 10 0
   1: 3500007F:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000   101        0 16624 1 0000000000000000 100 0 0 10 0
   2: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 18967 1 0000000000000000 100 0 0 10 0
   3: 0338A8C0:0016 0138A8C0:D6C7 01 00000000:00000000 02:00023B11 00000000     0        0 22284 4 0000000000000000 20 13 23 10 20

小菜還注意到,連接信息看起來像個自定義類對象,但其實是一個 nametuple :

# psutil.net_connections()
sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
                             'status', 'pid'])

小菜一開始並不知道作者為啥要這麼做。後來,小菜開始研究 Python 源碼,學習了 Python 類機制后他恍然大悟。

Python 自定義類的每個實例對象均需要一個 dict 來保存對象屬性,這也就是對象的 屬性空間 。

如果用自定義類來實現,每個連接都需要創建一個字典,而字典又是 散列表 實現的。如果系統存在成千上萬的連接,開銷可想而知。

小菜將學到的知識總結起來:對於 數量大 而 屬性固定 的實體,沒有必要用自定義類來實現,用 nametuple 更合適,開銷更小。由此,小菜不經由衷佩服 psutil 的作者。

CPU悲劇

後來小菜又收到業務反饋,採集腳本在高併發的服務器上, CPU 使用率很高,需要再優化一下。

小菜回憶 psutil 源碼,很快就找到了性能瓶頸處: psutil 將連接信息所有字段都解析了,而採集腳本只需要其中的 狀態 字段而已。

跟老闆商量后,小菜決定自行讀取 procfs 來實現採集腳本,只解析狀態字段,避免不必要的計算開銷。

procfs 方案

直接讀取 /proc/net/tcp ,可以得到完整的 TCP 連接信息:

>>> with open('/proc/net/tcp') as f:
...     for line in f:
...         print(line.rstrip())
...
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
   0: 0100007F:20C4 00000000:0000 0A 00000000:00000000 00:00000000 00000000 65534        0 18183 1 0000000000000000 100 0 0 10 0
   1: 3500007F:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000   101        0 16624 1 0000000000000000 100 0 0 10 0
   2: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 18967 1 0000000000000000 100 0 0 10 0
   3: 0338A8C0:0016 0138A8C0:D6C7 01 00000000:00000000 02:0007169E 00000000     0        0 22284 3 0000000000000000 20 20 33 10 20

其中, IP 、端口、狀態等字段都是以十六進制編碼的。例如, st 列表示狀態,狀態碼 0A 表示 LISTEN 。很快小菜就寫下這段代碼:

from collections import defaultdict

stat_names = {
    '0A': 'LISTEN',
    '01': 'ESTABLISHED',
    # ...
}

# 遍歷每個連接,按連接狀態累加
stats = defaultdict(int)

with open('/proc/net/tcp') as f:
    # 跳過表頭行
    f.readline()

    for line in f:
        st = line.strip().split()[3]
        stats[st] += 1

for st, count in stats.items():
    print(stat_names[st], count)

現在,小菜寫代碼比之前講究多了。在統計連接數時,他並不急於將狀態碼解析成名字,而是按原樣統計。等統計完成,他再一次性轉換,這樣狀態碼轉換開銷便降到最低: O(1)  而不是 O(n) 。

這次改進符合業務同事預期,但小菜決定好好做一遍性能測試,不打無準備之仗。他找業務同事要了一個連接數最大的 /proc/net/tcp 樣本,拉到本地測試。測試結果還算符合預期,採集腳本能夠扛住十萬連接採集壓力。

性能測試中,小菜發現了一個比較奇怪的問題。同樣的連接規模,把 /proc/net/tcp 拉到本地跑比直接在服務器上跑要快,而本地電腦性能肯定比不上服務器。

他百思不得其解,又去找老闆幫忙。老闆很快指出到其中的區別,將 /proc/net/tcp 拉到本地就成為普通 磁盤文件 ,而 procfs 是內核映射出來的 偽文件 ,並不是磁盤文件。

他讓小菜研究一下 Python 文件 IO 以及內核 IO 子系統在處理這兩種文件時有什麼區別,還讓小菜特別留意 IO 緩衝區大小。

IO緩衝

小菜打開一個普通的磁盤文件,發現 Python 選的默認緩衝區大小是 4K (讀緩存對象頭 152 字節):

>>> f = open('test.py')
>>> f.buffer.__sizeof__()
4248

但是如果打開的是 procfs 文件, Python 選的緩衝區卻只有 1K ,相差了 4 倍呢!

>>> f = open('/proc/net/tcp')
>>> f.buffer.__sizeof__()
1176

因此,理論上 Python 默認讀取 procfs 發生的上下文切換次數是普通磁盤文件的 4 倍,怪不得會慢。

雖然小菜還不知道這種現象背後的原因,但是他已經知道怎麼進行優化了。隨即他決定將緩衝區設置為 1M 以上,盡量避免 IO 上下文切換,以空間換時間:

with open('/proc/net/tcp', buffering=1*1024*1024) as f:
    # ...

經過這次優化,採集腳本在大部分服務器上運行良好,基本可以高枕無憂了。而小菜也意識到 編程語言 以及 操作系統 等底層基礎知識的重要性,他開始制定學習計劃補全計算機基礎知識。

netlink 方案

後來負載均衡團隊找到小菜,他們也想統計服務器上的連接信息。由於負載均衡服務器作為入口轉發流量,連接數規模特別大,達到幾十萬,將近百萬的規模。小菜決定好好進行性能測試,再視情況上線。

測試結果並不樂觀,採集腳本要跑幾十秒鐘才完成, CPU 跑到 100% 。小菜再次調高 IO 緩衝區,但效果不明顯。小菜又測試了 ss 命令,發現 ss 命令要快很多。由於之前嘗到了閱讀源碼的甜頭,小菜很想到 ss 源碼中尋找秘密。

由於項目時間較緊,老闆提醒小菜先用 strace 命令追蹤 ss 命令的系統調用,便可快速獲悉 ss 的實現方式。老闆演示了 strace 命令的用法,很快就找到了 ss 的秘密 —— Netlink :

$ strace ss -nat
...
socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_SOCK_DIAG) = 3
...

Netlink 套接字是 Linux 提供通訊機制,可用於內核與進程間、進程與進程間通訊。 Netlink 下的 sock_diag 子系統,提供了一種從內核獲取套接字信息的新方式。

procfs 不同,sock_diag 採用網絡通訊的方式,內核作為服務端接收客戶端進程查詢請求,並以二進制數據包響應查詢結果,效率更高。

這就是 ss 比 netstat 更快的原因, ss 採用 Netlink 機制,而 netstat 採用 procfs 機制。

很不幸 Python 並沒有提供 Netlink API ,一般人可能又要干著急了。好在小菜先前有意識地研究了部分 Python 源碼,對 Python 的運行機制有所了解。

他知道可以用 C 寫一個 Python 擴展模塊,在 C 語言中調用原生系統調用。

編寫 Python C 擴展模塊可不簡單,對編程功底要求很高,必須全面掌握 Python 運行機制,特別是對象內存管理。

一朝不慎可能導致程序異常退出、內存泄露等棘手問題。好在小菜已經不是當年的小菜了,他經受住了考驗。

小菜的擴展模塊上線后,效果非常好,頂住了百萬級連接的採集壓力。

一個看似簡單得不能再簡單的數據採集需求,背後涉及的知識可真不少,沒有一定的水平還真搞不定。好在小菜成長很快,他最終還是徹底地解決了性能問題,找回了久違的信心。

內核模塊方案

雖然性能問題已經徹底解決,小菜還是沒有將其淡忘。

他時常想:如果可以將統計邏輯放在內核空間做,就不用在內核和進程之間傳遞大量連接信息了,效率應該是最高的!受限於當時的知識水平,小菜還沒有能力實現這個設想。

後來小菜在研究 Linux 內核時,發現可以用內核模塊來擴展內核的功能,結合 procfs 的工作原理,他找到了技術方案!他順着 /proc/net/tcp 在內核中的實現源碼,依樣畫葫蘆寫了這個內核模塊:

#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <net/tcp.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Xiaocai");
MODULE_DESCRIPTION("TCP state statistics");
MODULE_VERSION("1.0");

// 狀態名列表
static char *state_names[] = {
    NULL,
    "ESTABLISHED",
    "SYN_SENT",
    "SYN_RECV",
    "FIN_WAIT1",
    "FIN_WAIT2",
    "TIME_WAIT",
    "CLOSE",
    "CLOSE_WAIT",
    "LAST_ACK",
    "LISTEN",
    "CLOSING",
    NULL
};


static void stat_sock_list(struct hlist_nulls_head *head, spinlock_t *lock,
    unsigned int state_counters[])
{
    // 套接字節點指針(用於遍歷)
    struct sock *sk;
    struct hlist_nulls_node *node;

    // 鏈表為空直接返回
    if (hlist_nulls_empty(head)) {
        return;
    }

    // 自旋鎖鎖定
    spin_lock_bh(lock);

    // 遍歷套接字鏈表
    sk = sk_nulls_head(head);
    sk_nulls_for_each_from(sk, node) {
        if (sk->sk_state < TCP_MAX_STATES) {
            // 自增狀態計數器
            state_counters[sk->sk_state]++;
        }
    }

    // 自旋鎖解鎖
    spin_unlock_bh(lock);
}


static int tcpstat_seq_show(struct seq_file *seq, void *v)
{
    // 狀態計數器
    unsigned int state_counters[TCP_MAX_STATES] = { 0 };
    unsigned int state;

    // TCP套接字哈希槽序號
    unsigned int bucket;

    // 先遍歷Listen狀態
    for (bucket = 0; bucket < INET_LHTABLE_SIZE; bucket++) {
        struct inet_listen_hashbucket *ilb;

        // 哈希槽
        ilb = &tcp_hashinfo.listening_hash[bucket];

        // 遍歷鏈表並統計
        stat_sock_list(&ilb->head, &ilb->lock, state_counters);
    }

    // 遍歷其他狀態
    for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) {
        struct inet_ehash_bucket *ilb;
        spinlock_t *lock;

        // 哈希槽鏈表
        ilb = &tcp_hashinfo.ehash[bucket];
        // 保護鎖
        lock = inet_ehash_lockp(&tcp_hashinfo, bucket);

        // 遍歷鏈表並統計
        stat_sock_list(&ilb->chain, lock, state_counters);
    }

    // 遍歷狀態輸出統計值
    for (state = TCP_ESTABLISHED; state < TCP_MAX_STATES; state++) {
        seq_printf(seq, "%-12s: %d\n", state_names[state], state_counters[state]);
    }

    return 0;
}


static int tcpstat_seq_open(struct inode *inode, struct file *file)
{
    return single_open(file, tcpstat_seq_show, NULL);
}


static const struct file_operations tcpstat_file_ops = {
    .owner   = THIS_MODULE,
    .open    = tcpstat_seq_open,
    .read    = seq_read,
    .llseek  = seq_lseek,
    .release = single_release
};


static __init int tcpstat_init(void)
{
    proc_create("tcpstat", 0, NULL, &tcpstat_file_ops);
    return 0;
}


static __exit void tcpstat_exit(void)
{
    remove_proc_entry("tcpstat", NULL);
}

module_init(tcpstat_init);
module_exit(tcpstat_exit);

內核模塊編譯好並加載到內核后, procfs 文件系統提供了一個新文件 /proc/tcpstat ,內容為統計結果:

$ cat /proc/tcpstat
ESTABLISHED : 5
SYN_SENT    : 0
SYN_RECV    : 0
FIN_WAIT1   : 0
FIN_WAIT2   : 0
TIME_WAIT   : 1
CLOSE       : 0
CLOSE_WAIT  : 0
LAST_ACK    : 0
LISTEN      : 14
CLOSING     : 0

當用戶程序讀取這個文件時,內核虛擬文件系統( VFS )調用小菜在內核模塊中寫的處理函數:遍歷內核 TCP 套接字完成統計並格式化統計結果。內核模塊、 VFS 以及套接字等知識超出專欄範圍,不再贅述。

小菜在服務器上試驗這個內核模塊,真的快得飛起!

經驗總結

小菜開始總結這次腳本開發工作中的經驗教訓,他列出了以下關鍵節點:

  1. 依靠 psutil 採集,沒有關注 psutil 實現導致性能問題;
  2. 用生成器代替列表返回連接信息,解決內存瓶頸;
  3. 直接讀取 procfs 文件系統,部分解決 CPU 性能瓶頸;
  4. 通過調節 IO 緩衝區大小,進一步降低 CPU 開銷;
  5. Netlink 代替 procfs ,徹底解決性能問題;
  6. 實驗內核模塊思路,終極解決方案快得飛起;

這些問題節點,一個比一個深入,沒有一定功底是搞不定的。小菜從剛開始跌跌撞撞,到後來獨當一面,快速成長的關鍵在於善於在問題中總結經驗教訓:

  • 程序開發完一定要做性能測試,看能夠扛住多大的壓力;
  • 使用任何工具,需要準確理解其背後的原理,避免誤用;
  • 對編程語言以及操作系統源碼要保持好奇心;
  • 計算機基礎知識很重要,需要及時補全才能達到新高度;
  • 學會問題發散,舉一反三;

更多章節

洞悉 Python 虛擬機運行機制,探索高效程序設計之道!

到底如何才能提升我的 Python 開發水平,向更高一級的崗位邁進? 如果你有這些問題或者疑惑,請訂閱我們的專欄,閱讀更多章節:

  • 內建對象
  • 虛擬機
  • 函數機制
  • 類機制
  • 生成器與協程
  • 內存管理機制

附錄

更多 Python 技術文章請訪問:小菜學Python,轉至 原文 可獲得最佳閱讀體驗。

訂閱更新,獲取更多學習資料,請關注 小菜學編程 :

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案