高通正式推出 Snapdragon 870 5G 行動平台,小米、MOTO、一加等品牌將使用_網頁設計公司

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

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

行動裝置處理器知名大廠高通技術公司(Qualcomm)今日宣布推出 Qualcomm Snapdragon™ 870 5G 行動平台,是Snapdragon 865 Plus旗艦行動平台的升級產品,採用台積電的第二代7奈米鰭式場效電晶體製程技術所打造,搭載核心時脈速度高達3.2 GHz的增強版高通Kryo™ 585 CPU與 Adreno650 GPU。與 S888 將 5G 基頻晶片封裝進處理器不同,Snapdragon 870 一樣採用外掛 X55 基頻方式設計,可視為 Snapdragon 865 的升級版。

高通正式推出 Snapdragon 870 5G 行動平台

由於三星負責生產的 5 奈米 S888 處理器發生耗電與發熱等問題,加上台積電產能滿載暫時可能也無法接手高通 S888 訂單,高通目前可能有意藉 Snapdragon 870 延長 Snapdragon 865 壽命,也讓消費者如果對 Snapdragon 888 有疑慮的話還有其他產品可以選擇。目前確定搭 Qualcomm Snapdragon 870 處理器的品牌包括Motorola、iQOO、OnePlus、 OPPO和小米將會在 2021 第一季推出相關產品。

中國媒體實測高通 S888 處理器後,竟用「翻車」形容!功耗高也非常燙

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

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

POCO M3 正式在台發表:6000mAh 超大電量、4800 萬像素 AI 三鏡頭主相機、立體聲揚聲器,售價只要 3,999 元起_網頁設計公司

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

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

在 2018 年小米 POCO F1 在台灣市場推出後,以極致的性價比讓許多米粉相當期待,接下來 POCO 還會帶來哪些新機。隨著 2020 年 11 月 POCO 正式成為獨立品牌後,也終於在今(21)日稍早正式在台灣推出 POCO 品牌獨立後首款在台灣市場上市的智慧型手機「POCO M3」。

POCO M3 正式在台發表:6000mAh 超大電量、4800 萬像素 AI 三鏡頭主相機、立體聲揚聲器,售價只要 3,999 元起

台灣消費者許久未見到的 POCO 終於回來啦!今(21)日 POCO 終於推出品牌獨立後第一款產品「POCO M3」,狹帶著超越同價位的搭載 Qualcomm Snapdragon 662 行動平台、 6,000mAh 超大電量電池、 4800 萬像素 AI 三鏡頭主相機和立體聲揚聲器。外觀方面, POCO M3 在台灣推出「活力黃」、「冷酷藍」以及「動力黑」共三款配色。

 

開箱也幫大家準備好了,快來看看吧!(請點我)

螢幕方面, POCO M3 採用 6.53 吋 FHD+ (2340×1080)高解析度水滴全螢幕, 19.5:9 的螢幕比例與 90.34% 的高螢幕占比,提供更好的影音娛樂體驗。螢幕也通過德國萊因TÜV低藍光認證,即便長時間觀看手機也不易感到眼睛疲勞。螢幕採用康寧第三代大猩猩玻璃保護。

除了支援 AI 臉人臉解鎖,搭配結合側邊指紋辨識讓單手解鎖更加容易。

音效方面, POCO M3 內建立體聲雙喇叭和等效 0.76cc 的大音腔,擁有媲美旗艦機的高品質立體音效。

電量方面, POCO M3 配備 6,000mAh 高充電循環電池,擁有 24 天的超長待機時間;在聆聽音樂可使用連續約 8 天;觀看影片也能連續播放長達 17 小時,對於重度手機使用者更能滿足需求。 POCO M3 即便內建 6,000mAh 的超大電量電池,機身重量也控制在 198g 。
另外, POCO M3 支持最高18W快速充電,並標配 22.5W 快速充電器,提供高效的充電速度。此外, POCO M3 採用特製的高充電循環電池也更持久、耐用,高達 1,000 次電池循環充電次數,電池壽命在高溫環境相較其他手機提高 20% 。

相機部份, POCO M3 搭載 4800 萬像素 AI 三鏡頭主相機,包含 4800 萬像素 f/1.79 光圈主鏡頭、200 萬像素微距鏡頭以及 200 萬像素景深鏡頭的搭配,前置相機則為 800 萬像素自拍鏡頭。

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

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

銷售資訊

POCO M3 在台灣推出兩種規格選擇,售價分別為 4GB+64GB 版本建議售價 NT$3,999 、4GB+128GB 版本建議售價 NT$4,699 ,將於 1 月 22 日上午 10:00 起於 PChome24h 購物 POCO 品牌館獨家開賣。
首 2,000 名於 PChome24h 購物購買 POCO M3 並成功付款的消費者,將可獲贈「Redmi 行動電源 10000 標準版(價值 NT$345)」乙個(贈品數量有限,送完為止)。

POCO M3 直播專屬 NT$200 優惠碼:「YEAHPOCO」,可提前前往 PChome 24h 購物頁面完成登錄,使用名額有限、依完成結帳順序為準,用完為止(限量 500 名):點我前往

POCO Taiwan Facebook 粉絲專頁:點我前往
PChome2h 購物 POCO 品牌館:點我前往

開箱也幫大家準備好了,快來看看吧!(請點我)

POCO M3 開箱、評測|6000mAh 大電量超乎想像,極致性價比王者重返台灣市場

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

最新 iOS 框架整體梳理(一),Audio Unit 基礎,QuartzCore_網頁設計公司

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

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

前言

 

      這段話其實是我差不多寫完文章之後再回過頭來寫的,原本在寫文章之前想寫一下寫的初衷的,但當我寫完之後感覺初衷沒有收穫更真切一些。其實到這篇為止總結出來的也就三十多個,有些是比較新的框架,有些是我們開發者一直在使用接觸的框架,我感覺收穫還是很多。 很多東西你要不是一個一直在了解跟進WWDC內容的開發者可能時間一長你就不知道最近都出了些什麼新的框架,但這樣你走一遍之後你就會把許多遺漏掉的東西找回來,我的想法一直都沒有變,作為一個移動端的開發者,不僅要“向下深挖” ,“向上學習”也是最基本的條件,就算你不清楚每一個框架具體的使用細節, 至少你一定要知道框架可以用來干什麼!下面總結出來的框架有些也需要我自己去學習,果然欠了的遲早是要還的

    

Part – 1

 

      下面的框架我們按照我們的圖一個一個的說:

 

                                            

 

1、Accelerate: 一個大規模的數學計算和圖像計算的框架,它的作用和使用推薦下面兩篇文章

    Introduction to the Accelerate Framework in Swift

    官方文檔 Accelerate

2、Accounts: 它是iOS原生提供的一套賬戶管理框架,其支持Facebook,新浪微博,騰訊微博,Twitter和領英賬戶管理的功能。需要注意,在iOS 11 及以上系統中,將此功能已經刪除,因此Accounts.framework實際上已經沒有太大的意義,其只在iOS 11之前的系統上可用!所以這裏我們就不在仔細說它了,簡單的提一下就好。

3、AddressBook、AddressBookUI   通訊錄相關的框架,我們獲取聯繫人通訊錄先關的都是咋這兩個框架裏面。 

     AddressBook、AddressBookUI 使用

     AddressBook 官方文檔

     AddressBookUI 官方文檔

4、AdSupport

     AdSupport 從字面意思上理解是用來進行廣告支持,這個框架十分簡單,裏面只有一個類,類中只有一個方法和兩個屬性。

     AdSupport 的唯一用途是用來獲取設備唯一的一個廣告標識符。可以使用此標識符用來標記用戶是否來源於某個廣告推廣,設備重啟,重裝應用程序都不會使廣告標識符修改。

5、ARKit

     ARKit 這個我就不用多說了,它是做什麼的相信每個iOS開發者度清楚,它具體的使用以及學習大家可以上網去搜索,一大把資料的,也正是因為龐大,官網的說明反而很簡單的幾句話。

6、AssetsLibrary

    The Assets Library framework is deprecated as of iOS 9.0. Instead, use the PhotoKit framework, which in iOS 8.0 and later provides more features and better performance for working with a user’s photo library. 

    上面這句話就總結了這可框架了,具體的內容就不在介紹了,關注的點應該轉移到 PhotoKit 框架!

7、AudioToolbox、AudioUnit

    AudioToolbox 是專門處理聲音的一個框架,AudioToolbox 這個庫是C的接口,偏向於底層,用於在線流媒體音樂的播放。AudioUnit 其實是最底層的,我們在使用的像 AVFoundation,Media Player ,AudioToolbox 等都是基於AudioUnit進行的封裝。

 

      具體的可以參考這篇文章:Audio Unit 基礎

8、AuthenticationServices: 一個讓用戶輕鬆登錄應用程序和服務的框架,我們在iOS13基礎上添加的蘋果登錄就是出自這個框架。 在官方文檔介紹它的功能點時候是這麼說的:

    1. 允許用戶從應用程序的登錄流中查找存儲的密碼。

    2. 在應用程序和web瀏覽器之間共享數據,使用OAuth等技術來利用應用程序中現有的基於web的登錄。

    3. 在企業應用程序中創建單點登錄(SSO)體驗。

    4. 簡單明了的註冊和登錄流程減少了用戶記住密碼

    官方文檔 AuthenticationServices

9、AVFoundation  AVKit 

     AVFoundation 這個框架我在以前做過一個專題專門有說過它,這裏也就不再多做介紹了。需要的可以翻一下我以前的博客。 AVKit框架基於AVFoundation框架,提供了一個用於播放視頻內容的高級界面,創建用於媒體播放的視圖級服務。主要包含兩個類:AVPictureInPictureController 、 AVPlayViewController  兩個類。

     關於AVKit 這裡有一個大概的介紹:  AVKit框架

      AVKit的官方文檔

10、BackgroundTasks         這個框架相信單從字面意思就能大致了解到它是作什麼的,這裏我們就不在具體的闡述它裏面的內容,可以看這兩文章對它有一個具體的了解  iOS 後台任務 BackgroundTask – 簡書

11、BusinessChat

      BusinessChat是iOS11.3后引入的新框架,這個框架配合iMessage應用將商家與用戶更加緊密的結合起來,並且為商家提供了另外一種非常方便的客服系統。關於它的資料我找到的還真的不是特別多,但官方對這一塊介紹的比較詳細。

      iOS開發之BusinessChat框架使用 這篇文章有介紹它的一個大致的使用 

      官方的介紹

12、CallKit

      它是一個很有意思的框架,它是蘋果 iOS 10 新發布的一個的框架。CallKit 框架能讓我們把 自己APP語音或視訊電話的UI 界面整合在 iPhone 原生的電話 App 中。下面是官方文檔對它的一個概述:

      CallKit允許您將您的呼叫服務與系統上其他與呼叫相關的應用程序集成在一起。CallKit提供調用接口,您可以使用VoIP服務處理後端通信。對於呼入和呼出的電話,CallKit显示與電話應用程序相同的界面,使您的應用程序具有更本機的外觀和感覺。CallKit會對系統級的行為做出適當的響應,比如不進行干擾。除了處理呼叫之外,您還可以提供一個呼叫目錄應用程序擴展,以提供來電显示信息和與您的服務相關的被阻止的號碼列表。

      但在大陸地區CallKit是受限制的,具體的信息可以上網了解。下面的這些文章內容能幫助我們了解這個框架:

      iOS10–CallKit的簡單應用

      iOS Call Kit for VOIP

      官方文檔

13、CarPlay

      CarPlay 是一個手機車機互聯繫統,可以把iPhone上的地圖、音樂、電話等功能映射到車載屏幕上使用。這句話概括了這個框架是用來干什麼的。

      iOS應用接入CarPlay初探

14、CFNetwork

      CFNetwork 這個框架還是有必要了解一下的,我們經常使用到的API的請求基本都是NSURL的,CFNetwork是一個比較底層的框架,C語言編寫的,NSURL也肯定就沒有CFNetwork那麼定製性更好了,官方文檔對它的描述是 訪問網絡服務並處理網絡配置中的更改。基於網絡協議的抽象來簡化任務,例如使用BSD套接字、管理HTTP和FTP服務器以及管理Bonjour服務。我的建議是要是對網絡處理這塊有想更好的一個了解的話有必要看安這個框架的使用以及它裏面具體的東西,畢竟它很接近 Socket 。

       CFNetwork的介紹和使用  

       官方文檔

15、ClassKit 

      這也是一個新的框架,在11.4中加入的,也很有趣,但關於它的資料我找到的也很少,但通過官方的介紹你也能了解到一些信息,官方介紹的也比較詳細。

      官方文檔

16、CloudKit

      這個框架我們首先能聯想到肯定是 iCloud了,的確這個框架也是專門用來給它服務的,每當我們看到一個新框架的時候我們腦袋裡想的肯定是這框架是用來干什麼的,具體我們該怎樣使用它。

      iOS CloudKit的使用  這篇文章也就了兩個問題,它是什麼,它是用來幹嘛的。

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

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

17、Combine

      Combine是Apple在2019年WWDC上推出的一個新框架。該框架提供了一個聲明性的Swift API,用於隨時間處理值。這些值可以表示多種異步事件

      Swift Combine

      Combine框架詳細解析

18、Contacts  ContactsUI

      這兩個框架我相信很多人還是比較熟悉了,以前的很多應用都喜歡獲取用戶的通訊錄,不過現在的APP我感覺在慢慢減少這方面的權限獲取,也可能和人們的生活方式有關吧,慢慢的很多人聯繫也就不再考通訊錄的手機號碼,這兩個框架我們也就不再細緻的介紹了。

19、CoreAudio  CoreAudioKit  CoreAudioTypes

      Core Audio 提供了数字音頻服務為iOS與OS X, 它提供了一系列框架去處理音頻。Core Audio 中也包含我們最常用的前面也有說過的 AudioToolbox和AudioUnit 框架。要具體的說它裏面的內容我們也能寫一本書了。想要大致的了解它和它的使用,下面的文章能做到。

      Core Audio音頻基礎概述

      官方文檔 Core Audio

      官方文檔 Core Audio Types

20、CoreBluetooth

      這個框架也是比較重要的一個框架,在我們的開發中也是經常使用到的一個框架 藍牙

      iOS中的藍牙 CoreBluetooth藍牙系列

      官方文檔

21、CoreData

      這個我就一句話帶過,他就蘋果提供的數據庫,CoreData我以前也有寫過關於它的文章,有需要的也可以往前面翻翻。

22、CoreFoundation

      說到 CoreFoundation 我們就不可避免的的說活 Foundation ,這個框架和Foundation有什麼區別和聯繫,他們之間使用的時候我們需要注意什麼,他們之間的橋接等等這些都是我們需要注意的東西。具體的我們就不在說了,下面的這文章能幫助到我們。這個框架我們還是有必要進行一個具體的了解的!

      提高性能之——Core Foundation

      官方文檔

23、CoreGraphices

      這個按照字面我們能把它接成“圖形核心”,其實它和我們常看到的 QuartzCore、Quartz2D等會很容易混淆,我以前在說Quartz2D的時候有提過關於他們之間的一些基本的區分以及關係,QuartzCore 這裏可以看,然後關於CoreGraphices具體的內容的確也是比較的龐大,需要我們花時間去弄清除。然後我們在這裏也沒法具體的再談了,還是下面的文章幫助我們理解。

      iOS圖像處理之Core Graphics和OpenGL ES小析

      iOS繪圖框架CoreGraphics分析

      CoreGraphic框架解析(一)—— 基本概覽 這篇後續還有具體的使用,這裏就不一一列表,可以通過它找到的。

24、CoreHaptics  [‘hæptiks]

      CoreHaptics 是 iOS13 中的新API,同時只有 iPhone 8 及之後的機型支持。CoreHaptics 提供了更加細膩,可控的震動表達方式,可以令APP產生一種全新的體驗。下面是一些簡單的文章和官方文檔。

      CoreHaptics

      官方文檔

25、CoreImage

      CoreImage 框架是iOS處理圖像的框架,主要用處可以給圖片添加濾鏡效果和圖像識別功能(人臉、條形碼等等)。

      CoreImage和GPUImage的結合使用  這篇文章是一個很好的使用介紹

      Core Image 官方文檔

26、CoreLocation   

      在移動互聯網時代,移動app能解決用戶的很多生活瑣事,比如導航:去任意陌生的地方 周邊:找餐館、找酒店、找銀行、找電影院 。在上述應用中,都用到了地圖和定位功能,在iOS開發中,要想加入這2大功能,必須基於2個框架進行開發 MapKit :用於地圖展示  CoreLocation :用於地理定位。所以CoreLocation和MapKit也是經常在一起使用的,也就是定位和地圖。

       關於CoreLocation定位服務的簡單使用         官方文檔

27、CoreMedia

      它是屬於比較底層的一套音視頻C語言接口,提供對媒體文件操作的底層接口。它的具體的使用我們基礎到的比較多的是基於它的AVFoundation。

      官方文檔

28、CoreMIDI  這個我基本上是不想說了的,因為好像我們基本上都沒什麼使用,而且關於它的資料特別的少,MIDI是一套樂器数字接口,這個框架也是用來連接設備的 像MIDI 鍵盤,有興趣的自己再去了解吧。

29、CoreML

       CoreML 是一個機器學習框架,藉助 Core ML,您可以將已訓練好的機器學習模型,集成到自己的應用當中。

       Core ML介紹 (Apple機器學習框架)

       官方文檔

30、CoreMotion

      Core Motion 可以讓開發者從各個內置傳感器那裡獲取未經修改的傳感數據,並觀測或響應設備各種運動和角度變化。通過這些傳感器可以獲取加速度值,陀螺儀值等。

      iOS CoreMotion的使用

      官方文檔

31、CoreNFC

      NFC(近場通信)就是當兩台硬件設備相距4cm以內時可以實現互相通信 

      iOS11中使用CoreNFC

      官方文檔

32、CoreServices

      Core Services層為所有的應用程序提供基礎系統服務。可能應用程序並不直接使用這些服務,但它們是系統很多部分賴以建構的基礎。這麼去理解的時候就發現其實他是一個很少我們具體需要我們使用的框架,但真的是一個無處不在的框架。

      官方文檔

33、CoreSpotLight  [ˈspɑːtlaɪt]

      這也是一個很有趣的框架,它可以讓你 App 中的內容在 Spolite 中搜索到, 並且將相關的搜索結果展現給用戶, 並且允許用戶和搜索的結果進行交互. 當用戶選擇了其中一個搜索的結果后, 不但可以自動的打開你的應用程序, 同時還可以跳轉到指定的頁面來查看詳細的內容。

      如何使用 Core Spotlight

      官方文檔

 

 

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

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

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

一篇文章帶你吃透 Docker 原理_網頁設計公司

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

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

容器的實現原理

從本質上,容器其實就是一種沙盒技術。就好像把應用隔離在一個盒子內,使其運行。因為有了盒子邊界的存在,應用於應用之間不會相互干擾。並且像集裝箱一樣,拿來就走,隨處運行。其實這就是 PaaS 的理想狀態。

實現容器的核心,就是要生成限制應用運行時的邊界。我們知道,編譯后的可執行代碼加上數據,叫做程序。而把程序運行起來后,就變成了進程,也就是所謂的應用。如果能在應用啟動時,給其加上一個邊界,這樣不就能實現期待的沙盒嗎?

在 Linux 中,實現容器的邊界,主要有兩種技術 CgroupsNamespace. Cgroups 用於對運行的容器進行資源的限制,Namespace 則會將容器隔離起來,實現邊界。

這樣看來,容器只是一種被限制的了特殊進程而已。

容器的隔離:Namespace

在介紹 Namespace 前,先看一個實驗:

# 使用 python3.6.8 的官方鏡像,建立了一個運行 django 的環境
# 進入該容器后,使用 ps 命令,查看運行的進程
root@8729260f784a:/src# ps -A
  PID TTY          TIME CMD
    1 ?        00:01:22 gunicorn
   22 ?        00:01:20 gunicorn
   23 ?        00:01:24 gunicorn
   25 ?        00:01:30 gunicorn
   27 ?        00:01:16 gunicorn
   41 pts/0    00:00:00 bash
   55 pts/0    00:00:00 ps

可以看到,容器內 PID =1 的進程,是 gunicorn 啟動的 django 應用。熟悉 Linux 的同學都知道,PID =1 的進程是系統啟動時的第一個進程,也稱 init 進程。其他的進程,都是由它管理產生的。而此時,PID=1 確實是 django 進程。

接着,退出容器,在宿主機執行 ps 命令

# 環境為 Centos7
[root@localhost ~]# ps -ef | grep gunicorn
root      9623  8409  0 21:29 pts/0    00:00:00 grep --color=auto gunicorn
root     30828 30804  0 May28 ?        00:01:22 /usr/local/bin/python /usr/local/bin/gunicorn -c gunicorn_config.py ctg.wsgi
root     31171 30828  0 May28 ?        00:01:20 /usr/local/bin/python /usr/local/bin/gunicorn -c gunicorn_config.py ctg.wsgi
root     31172 30828  0 May28 ?        00:01:24 /usr/local/bin/python /usr/local/bin/gunicorn -c gunicorn_config.py ctg.wsgi
root     31174 30828  0 May28 ?        00:01:30 /usr/local/bin/python /usr/local/bin/gunicorn -c gunicorn_config.py ctg.wsgi
root     31176 30828  0 May28 ?        00:01:16 /usr/local/bin/python /usr/local/bin/gunicorn -c gunicorn_config.py ctg.wsgi

如果以宿主機的視角,發現 django 進程 PID 變成了 30828. 這也就不難證明,在容器中,確實做了一些處理。把明明是 30828 的進程,變成了容器內的第一號進程,同時在容器還看不到宿主機的其他進程。這也說明容器內的環境確實是被隔離了。

這種處理,其實就是 Linux 的 Namespace 機制。比如,上述將 PID 變成 1 的方法就是通過PID Namespace。在 Linux 中創建線程的方法是 clone, 在其中指定 CLONE_NEWPID 參數,這樣新創建的進程,就會看到一個全新的進程空間。而此時這個新的進程,也就變成了 PID=1 的進程。

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL); 

在 Linux 類似於 PID Namespace 的參數還有很多,比如:

容器的限制:Cgroups

通過 Namespace 技術,我們實現了容器和容器間,容器與宿主機之間的隔離。但這還不夠,想象這樣一種場景,宿主機上運行着兩個容器。雖然在容器間相互隔離,但以宿主機的視角來看的話,其實兩個容器就是兩個特殊的進程,而進程之間自然存在着競爭關係,自然就可以將系統的資源吃光。當然,我們不能允許這麼做的。

Cgroups 就是 Linux 內核中用來為進程設置資源的一個技術。

Linux Cgroups 全稱是 Linux Control Group,主要的作用就是限制進程組使用的資源上限,包括 CPU,內存,磁盤,網絡帶寬。

還可以對進程進行優先級設置,審計,掛起和恢復等操作。

在之前的版本中,可通過 libcgroup tools 來管理 cgroup, 在 RedHat7 后,已經改為通過 systemctl 來管理。

我們知道,systemd 在 Linux 中的功能就是管理系統的資源。而為了管理的方便,衍生出了一個叫 Unit 的概念,比如一個 unit 可以有比較寬泛的定義,比如可以表示抽象的服務,網絡的資源,設備,掛載的文件系統等。為了更好的區分,Linux 將 Unit 的類型主要分為 12 種。

類型 作用
.automount 用於自動掛載配置的掛載點
.swap 描述系統的交換區,反映了設備或文件的路徑
.target 在系統啟動或者改變狀態時,為其他 unit 提供同步點
.path 定義的文件路徑,用於激活。
.service 一個服務或者一個應用,具體定義在配置文件中。
.socket 一個網絡或者 IPC socket,FIFO buffer.
.device 描述一個需要被 systemd udevsysfs 文件系統管理的設備
.mount 定義的掛載點
.timer 定時器
.snapshot systemctl snapshot 命令自動創建的單元
.slice 用於關聯 Linux Control Group 節點,根據關聯的 slice 來限制進程。一個管理單元的組。Slice 並不包含任何進程,僅僅管理由 service 和 scope 組成的層級結構。
.scope systemd 從 bus 接口收到消息后自動創建。Scope 封裝了任意進程通過 fork() 函數開啟或停止的進程,並且在 systemd 運行時註冊。例如:用戶 sessions,容器和虛擬機。

Cgroup 中,主要使用的是 slice, scope and service 這三種類型。

如創建一個臨時 cgroup, 然後對其啟動的進程進行資源限制:

 # 創建一個叫 toptest 的服務,在名為 test 的 slice 中運行
[root@localhost ~]# systemd-run --unit=toptest --slice=test top -b
Running as unit toptest.service.

現在 toptest 的服務已經運行在後台了

# 通過 systemd-cgls 來查看 Cgroup 的信息
[root@localhost ~]#  systemd-cgls
├─1 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
├─test.slice
│ └─toptest.service
│   └─6490 /usr/bin/top -b

# 通過 systemctl status 查看服務的狀態
[root@localhost ~]# systemctl status toptest
● toptest.service - /usr/bin/top -b
   Loaded: loaded (/run/systemd/system/toptest.service; static; vendor preset: disabled)
  Drop-In: /run/systemd/system/toptest.service.d
           └─50-Description.conf, 50-ExecStart.conf, 50-Slice.conf
   Active: active (running) since Tue 2020-06-02 14:01:01 CST; 3min 50s ago
 Main PID: 6490 (top)
   CGroup: /test.slice/toptest.service
           └─6490 /usr/bin/top -b

現在對運行的 toptest 服務進行資源的限制。

# 先看下,沒有被限制前的 Cgroup 的信息, 6490 為進程 PID
[root@localhost ~]# cat /proc/6490/cgroup
11:pids:/test.slice
10:blkio:/test.slice
9:hugetlb:/
8:cpuset:/
7:memory:/test.slice
6:devices:/test.slice
5:net_prio,net_cls:/
4:perf_event:/
3:freezer:/
2:cpuacct,cpu:/test.slice
1:name=systemd:/test.slice/toptest.service

# 對其使用的 CPU 和 內存進行限制
systemctl set-property toptest.service CPUShares=600 MemoryLimit=500M

# 再次查看 Cgroup 的信息,發現在 cpu 和 memory 追加了一些內容。
[root@localhost ~]# cat /proc/6490/cgroup
11:pids:/test.slice
10:blkio:/test.slice
9:hugetlb:/
8:cpuset:/
7:memory:/test.slice/toptest.service
6:devices:/test.slice
5:net_prio,net_cls:/
4:perf_event:/
3:freezer:/
2:cpuacct,cpu:/test.slice/toptest.service
1:name=systemd:/test.slice/toptest.service

這時可以在 /sys/fs/cgroup/memory/test.slice/sys/fs/cgroup/cpu/test.slice 目錄下,多出了一個叫 toptest.service 的目錄。

在其目錄下 cat toptest.service/cpu.shares 可以發現,裏面的 CPU 被限制了 600.

回到 Docker,其實 docker 和我們上面做的操作基本一致,具體需要限制哪些資源就是在 docker run 里指定:

$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash

關於 docker 具體的限制,可以在 sys/fs/cgroup/cpu/docekr/ 等文件夾來查看。

容器的文件系統:容器鏡像 – rootfs

現在我們知道,容器技術的核心就是通過 Namespace 限制了容器看到的視野,通過 Cgroup限制了容器可訪問的資源。 但關於 Mount Namespace 還有一些特殊的地方,需要着重關注下。

Mount Namespace 特殊之處在於,除了在修改時需要進程對文件系統掛載點的認證,還需要顯式聲明需要掛載那些目錄。在 Linux 系統中,有一個叫 chroot 的命令,可以改變進程的根目錄到指定的位置。而 Mount Namespace 正是基於 chroot 的基礎上發展出來的。

在容器內,應該看到完全獨立的文件系統,而且不會受到宿主機以及其他容器的影響。這個獨立的文件系統,就叫做容器鏡像。它還有一個更專業的名字叫 rootfs. rootfs 中包含了一個操作系統所需要的文件,配置和目錄,但並不包含系統內核。 因為在 Linux 中,文件和內核是分開存放的,操作系統只有在開啟啟動時才會加載指定的內核。這也就意味着,所有的容器都會共享宿主機上操作系統的內核。

在 PaaS 時代,由於雲端和本地的環境不同,應用打包的過程,一直是比較痛苦的過程。但有了 rootfs ,這個問題就被很好的解決了。因為在鏡像內,打包的不僅僅是應用,還有所需要的依賴,都被封裝在一起。這就解決了無論是在哪,應用都可以很好的運行的原因。

不光這樣,rootfs 還解決了可重用性的問題,想象這個場景,你通過 rootfs 打包了一個包含 java 環境的 centos 鏡像,別人需要在容器內跑一個 apache 的服務,那麼他是否需要從頭開始搭建 java 環境呢?docker 在解決這個問題時,引入了一個叫層的概念,每次針對 rootfs 的修改,都只保存增量的內容,而不是 fork 一個新鏡像。

層級的想法,同樣來自於 Linux,一個叫 union file system (聯合文件系統)。它最主要的功能就是將不同位置的目錄聯合掛載到同一個目錄下。對應在 Docker 裏面,不同的環境則使用了不同的聯合文件系統。比如 centos7 下最新的版本使用的是 overlay2,而 Ubuntu 16.04 和 Docker CE 18.05 使用的是 AuFS.

可以通過 docker info 來查詢使用的存儲驅動,我這裏的是 overlay2

[root@localhost ~]# docker info
Client:
 Debug Mode: false

Server:
 Containers: 4
  Running: 4
  Paused: 0
  Stopped: 0
 Images: 4
 Server Version: 19.03.8
 Storage Driver: overlay2

接着我們來了解下,Overlay2 的文件系統在 docker 中是如何使用的?

Overlay2

在 Linux 的主機上,OverlayFS 一般有兩個目錄,但在显示時具體會显示為一個目錄。這兩個目錄被稱為層,聯合在一起的過程稱為 union mount. 在其下層的目錄稱為 lowerdir, 上層的目錄稱為 upperdir. 兩者聯合后,暴露出來的視圖稱為 view. 聽起來有點抽象,先看下整體結構:

可以看到,lowerdir 其實對應的就是鏡像層,upperdir 對應的就是容器器。而 merged 對應的就是兩者聯合掛載之後的內容。而且我們發現,當鏡像層和容器層擁有相同的文件時,會以容器層的文件為準(最上層的文件為準)。通常來說,overlay2 支持最多 128 lower 層。

下面實際看下容器層和鏡像具體的體現,我這台 linux 主機上,運行着 4 個 container。

Docker 一般的存儲位置在 /var/lib/docker,先看下裏面的結構:

[root@localhost docker]# ls -l /var/lib/docker
total 16
drwx------.  2 root root   24 Mar  4 03:39 builder
drwx--x--x.  4 root root   92 Mar  4 03:39 buildkit
drwx------.  7 root root 4096 Jun  1 10:36 containers
drwx------.  3 root root   22 Mar  4 03:39 image
drwxr-x---.  3 root root   19 Mar  4 03:39 network
drwx------. 69 root root 8192 Jun  1 15:01 overlay2
drwx------.  4 root root   32 Mar  4 03:39 plugins
drwx------.  2 root root    6 Jun  1 15:00 runtimes
drwx------.  2 root root    6 Mar  4 03:39 swarm
drwx------.  2 root root    6 Jun  1 15:01 tmp
drwx------.  2 root root    6 Mar  4 03:39 trust
drwx------.  3 root root   45 May 18 10:28 volumes

需要着重關注的是 container, image, overlay2 這幾個文件夾。

  • container:這個不用多說,正在運行或創建的容器會在這個目錄下。
  • image:對應記錄的就是鏡像。
  • overlay2:記錄的是每個鏡像下包含的 lowerrdir.

之前提到,unionfs 的實現可能有多種,比如 overlay2,aufs,devicemapper 等。那麼自然在 image 文件夾下,就會存在多種驅動的文件夾,:

image/
└── overlay2
    ├── distribution
    ├── imagedb
    │   ├── content
    │   └── metadata
    ├── layerdb
    │   ├── mounts
    │   ├── sha256
    │   └── tmp
    └── repositories.json

這裏的 imagedblayerdb, 就是存儲元數據的地方。之前我們了解到,容器的文件系統構成就是通過 image 層 和 container 層聯合構成的,而每個 image 可能是由多個層構成。這就意味着,每個層可能會被多個 image 引用。那麼之間是如何關聯的呢?答案就在 imagedb 這個文件下。

這裏我以 mysql 鏡像為例:

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

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

# 查看 mysql 的鏡像 id
[root@localhost docker]# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ctg/mysql           5.7.29              84164b03fa2e        3 months ago        456MB

# 進入到 imagedb/content/sha256 目錄, 可以找到對應的鏡像 id
[root@localhost docker]# ls -l image/overlay2/imagedb/content/sha256/
...
-rw-------. 1 root root  6995 Apr 27 02:45 84164b03fa2ecb33e8b4c1f2636ec3286e90786819faa4d1c103ae147824196a

# 接着看下裏面記錄的內容, 這裏截取有用的部分
cat  image/overlay2/imagedb/content/sha256/84164b03fa2ecb33e8b4c1f2636ec3286e90786819faa4d1c103ae147824196a
{
.........
  "os": "linux",
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da",
      "sha256:a9f6b7c7101b86ffaa53dc29638e577dabf5b24150577a59199d8554d7ce2921",
      "sha256:0c615b40cc37ed667e9cbaf33b726fe986d23e5b2588b7acbd9288c92b8716b6",
      "sha256:ad160f341db9317284bba805a3fe9112d868b272041933552df5ea14647ec54a",
      "sha256:1ea6ef84dc3af6506c26753e9e2cf7c0d6c1c743102b85ebd3ee5e357d7e9bc4",
      "sha256:6fce4d95d4af3777f3e3452e5d17612b7396a36bf0cb588ba2ae1b71d139bab9",
      "sha256:6de3946ea0137e75dcc43a3a081d10dda2fec0d065627a03800a99e4abe2ede4",
      "sha256:a35a4bacba4d5402b85ee6e898b95cc71462bc071078941cbe8c77a6ce2fca62",
      "sha256:1ff9500bdff4455fa89a808685622b64790c321da101d27c17b710f7be2e0e7e",
      "sha256:1cf663d0cb7a52a3a33a7c84ff5290b80966921ee8d3cb11592da332b4a9e016",
      "sha256:bcb387cbc5bcbc8b5c33fbfadbce4287522719db43d3e3a286da74492b7d6eca"
    ]
  }
}

可以看到 mysql 鏡像由 11 層組成,其中 f2cb 是最低層,bcb3 是最上層。

接着,我們看下 layerdb 的內容:

[root@localhost docker]# ls -l  image/overlay2/layerdb/
total 8
drwxr-xr-x.  6 root root 4096 May 13 13:38 mounts
drwxr-xr-x. 39 root root 4096 Apr 27 02:51 sha256
drwxr-xr-x.  2 root root    6 Apr 27 02:51 tmp

# 首先看下 sha256 目錄下的內容
[root@localhost docker]# ls -l  image/overlay2/layerdb/sha256/
total 0
....
drwx------. 2 root root 71 Apr 27 02:45 bbb9cccab59a16cb6da78f8879e9d07a19e3a8d49010ab9c98a2c348fa116c87
drwx------. 2 root root 71 Apr 27 02:45 f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da
....

可以發現,在這裏僅能找到最底層的層 ID,原因在於層之間的關聯是通過 chainID 的方式保存的,簡單來說就是通過 sha256 算法后能計算出一層的容器 id.

比如這裏,最底層 id 是 f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da , 上一層 id 是 a9f6b7c7101b86ffaa53dc29638e577dabf5b24150577a59199d8554d7ce2921, 那麼對應在 sha256 目錄下的下一層 id 的計算方法就是:

[root@localhost docker]# echo -n "sha256:f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da sha256:a9f6b7c7101b86ffaa53dc29638e577dabf5b24150577a59199d8554d7ce2921" | sha256sum
bbb9cccab59a16cb6da78f8879e9d07a19e3a8d49010ab9c98a2c348fa116c87  -

接着我們可以在 sha256 目錄下,找到 bbb9.. 這層的內容。

OK,現在我們已經把鏡像和層關聯起來,但之前說過,image 目錄下存的都是元數據。真實的 rootfs 其實在另一個地方 – /docker/overlay2 下。

# 通過查詢 cache-id,得到就是真實的 rootfs 層
[root@localhost docker]# cat  image/overlay2/layerdb/sha256/f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da/cache-id
2996b24990e75cbd304093139e665a45d96df8d7e49334527827dcff820dbf16[

進入到 /docker/overlay2 下查看:

[root@localhost docker]# ls -l overlay2/
total 4
...
drwx------. 3 root root   47 Apr 27 02:45 2996b24990e75cbd304093139e665a45d96df8d7e49334527827dcff820dbf16
...
drwx------. 2 root root 4096 May 13 13:38 l

這樣真實的 rootfs 層也被找到了。

這裏重新梳理下,我們先是在 mage/overlay2/imagedb/content/sha256/ ,根據 image id 查看該 image 具有所有的層ID,然後根據最底層ID和上層ID通過 sha256 計算得到,引用的上一層 ID, 依次類推,關聯所有的層。最後通過每一層的 cache-id,將元數據和真實的 rootfs 層數據對應起來了。

最後總結一下,rootfs 的構成。

每個 rootfs 由鏡像層(lowerdir)和 容器層(upperdir)構成,其中鏡像層只能只讀,而容器層則能讀寫。而且鏡像層可有最多128層構成。

其實,rootfs 構成還有另外一層,但由於在進行提交或編譯時,不會把這層加進去,所以就沒把這層算在rootfs裏面,但實際上存在的。

在之前我們查看 ls -l /var/lib/docker/overlay2/ 下鏡像層,會看到好幾個以 -init 結尾的目錄,而且數量恰好等於容器的數量。這層夾在鏡像層之上,容器層之下。是由 docker 內部單獨生成的一層,專門用於存放 etc/hosts、/etc/resolv.conf 等配置信息。存在的目的,是由於用戶在容器啟動時,需要配置一些特定的值,比如 hostname,dns 等,但這些配置僅對當前容器有效,放到其他環境下自然有別的配置,所以這層被單獨拿出來,在提交鏡像時,忽略這一層。

容器與虛擬機技術的對比

下面這張圖是 docker 官方中,截取下來的,基於上面我們學習的內容,重新分析下 docker 和 傳統 VM 的區別:

遷移性和性能:

  • 傳統 VM: 需要基於 Hypervisor 的硬件虛擬化技術,模擬出 CPU,內存等硬件。然後在其上搭建一套完整的操作系統,自然在性能上會有很大的損失。遷移自然更不用說,傳統的 ova 導出后就是一個完整的操作系統。
  • Docker:Docker 將 Hypervisor 的位置換成自己的 Docekr Engine. 然後運行的容器僅僅是一個特殊的進程,自然性能不會有太大的損失。並且可以應用和其所需要的系統文件打包成鏡像,無論在哪讀可以正常運行,而且相對於 ova 來說體積也小了更多。(需要內核支持)

一般來說,運行着 CentOS 的 KVM,啟動后,在不做優化的前提下,需要佔用 100~200 M 內存。在加上用戶對宿主機的調用,需要通過虛擬化軟件攔截和處理,又是一層性能損耗,特別是對計算資源,網絡和磁盤I/O等。

隔離性:

  • 傳統 VM:由於是虛擬化出一套完整的操作系統,所以隔離性非常好。比如微軟的 Azure 平台,就是在 Windows 服務器上,虛擬出大量的 Linux 虛擬機。

  • Docker:在隔離性上相差就很多了,因為本身上容器就是一種進程,而所有的進程都需要共享一個系統內核。

    • 這就意味着,在 Windows 上運行 Linux 容器,或者 Linux 宿主機運行高版本內核的容器就無法實現。

    • 在 Linux 內核中,有許多資源和對象不能 Namespace 化,如時間,比如通過 settimeofday(2) 系統調用 修改時間,整個宿主機的實際都會被修改。

    • 安全的問題,共享宿主機內核的事實,容器暴露出的攻擊面更大。

資源的限制:

  • 傳統 VM:非常便於管理,控制資源的使用,依賴於虛擬的操作系統。
  • Docker:由於 docker 內資源的限制通過 Cgroup 實現,而 Cgroup 有很多不完善的地方,比如
    • 對 /proc 的處理問題。進入容器后,執行 top 命令,看到的信息和宿主機是一樣的,而不是配置后的容器的數據。(可以通過 lxcfs 修正)。
    • 在運行 java 程序時,給容器內設置的內存為 4g,使用默認的 jvm 配置。而默認的 jvm 讀取的內存是宿主機(可能大於 4g),這樣就會出現 OOM 的情況。

解決的問題

  1. 容器是如何進行隔離的?

    在創建新進程時,通過 Namespace 技術,如 PID namespaces 等,實現隔離性。讓運行后的容器僅能看到本身的內容。

    比如,在容器運行時,會默認加上 PID, UTS, network, user, mount, IPC, cgroup 等 Namespace.

  2. 容器是如何進行資源限制的?

    通過 Linux Cgroup 技術,可為每個進程設定限制的 CPU,Memory 等資源,進而設置進程訪問資源的上限。

  3. 簡述下 docker 的文件系統?

    docker 的文件系統稱為 rootfs,它的實現的想法來自與 Linux unionFS 。將不同的目錄,掛載到一起,形成一個獨立的視圖。並且 docker 在此基礎上引入了層的概念,解決了可重用性的問題。

    在具體實現上,rootfs 的存儲區分根據 linux 內核和 docker 本身的版本,分為 overlay2 , overlay, aufs, devicemapper 等。rootfs(鏡像)其實就是多個層的疊加,當多層存在相同的文件時,上層的文件會將下層的文件覆蓋掉。

  4. 容器的啟動過程?

    1. 指定 Linux Namespace 配置
    2. 設置指定的 Cgroups 參數
    3. 切換進程的根目錄
  5. 容器內運行多個應用的問題?

    首先更正一個概念,我們都說容器是一個單進程的應用,其實這裏的單進程不是指在容器中只允許着一個進程,而是指只有一個進程時可控的。在容器內當然可以使用 ping,ssh 等進程,但這些進程時不受 docker 控制的。

    容器內的主進程,也就是 pid =1 的進程,一般是通過 DockerFile 中 ENTRYPOINT 或者 CMD 指定的。如果在一個容器內如果存在着多個服務(進程),就可能出現主進程正常運行,但是子進程退出掛掉的問題,而對於 docker 來說,僅僅控制主進程,無法對這種意外的情況作出處理,也就會出現,容器明明正常運行,但是服務已經掛掉的情況,這時編排系統就變得非常困難。而且多個服務,在也不容易進行排障和管理。

    所以如果真的想要在容器內運行多個服務,一般會通過帶有 systemd 或者 supervisord 這類工具進行管理,或者通過 --init 方法。其實這些方法的本質就是讓多個服務的進程擁有同一個父進程。

    但考慮到容器本身的設計,就是希望容器和服務能夠同生命周期。所以這樣做,有點背道而馳的意味。

    控制(回收和生命周期的管理)

參考

Cgroup 介紹

Overlay2介紹

docker 官網

在一個容器內搭建多個服務

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

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

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(二)_網頁設計公司

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

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

系列文章

  1. 基於 abp vNext 和 .NET Core 開發博客項目 – 使用 abp cli 搭建項目
  2. 基於 abp vNext 和 .NET Core 開發博客項目 – 給項目瘦身,讓它跑起來
  3. 基於 abp vNext 和 .NET Core 開發博客項目 – 完善與美化,Swagger登場
  4. 基於 abp vNext 和 .NET Core 開發博客項目 – 數據訪問和代碼優先
  5. 基於 abp vNext 和 .NET Core 開發博客項目 – 自定義倉儲之增刪改查
  6. 基於 abp vNext 和 .NET Core 開發博客項目 – 統一規範API,包裝返回模型
  7. 基於 abp vNext 和 .NET Core 開發博客項目 – 再說Swagger,分組、描述、小綠鎖
  8. 基於 abp vNext 和 .NET Core 開發博客項目 – 接入GitHub,用JWT保護你的API
  9. 基於 abp vNext 和 .NET Core 開發博客項目 – 異常處理和日誌記錄
  10. 基於 abp vNext 和 .NET Core 開發博客項目 – 使用Redis緩存數據
  11. 基於 abp vNext 和 .NET Core 開發博客項目 – 集成Hangfire實現定時任務處理
  12. 基於 abp vNext 和 .NET Core 開發博客項目 – 用AutoMapper搞定對象映射
  13. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(一)
  14. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(二)
  15. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(三)
  16. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(一)

上篇文章完成了兩個接口:文章列表頁、文章詳情頁,本篇繼續。

分類列表

分析:這裏多了一個統計文章數量的字段,可以直接新建一個模型QueryCategoryDto.cs繼承CategoryDto

//QueryCategoryDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{
    public class QueryCategoryDto : CategoryDto
    {
        /// <summary>
        /// 總數
        /// </summary>
        public int Count { get; set; }
    }
}

添加查詢分類列表接口和緩存接口。

//IBlogService.Category.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Blog
{
    public partial interface IBlogService
    {
        /// <summary>
        /// 查詢分類列表
        /// </summary>
        /// <returns></returns>
        Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync();
    }
}
//IBlogCacheService.Category.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Caching.Blog
{
    public partial interface IBlogCacheService
    {
        /// <summary>
        /// 查詢分類列表
        /// </summary>
        /// <param name="factory"></param>
        /// <returns></returns>
        Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync(Func<Task<ServiceResult<IEnumerable<QueryCategoryDto>>>> factory);
    }
}

分別實現這兩個接口。

//BlogCacheService.Category.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static Meowv.Blog.Domain.Shared.MeowvBlogConsts;

namespace Meowv.Blog.Application.Caching.Blog.Impl
{
    public partial class BlogCacheService
    {
        private const string KEY_QueryCategories = "Blog:Category:QueryCategories";

        /// <summary>
        /// 查詢分類列表
        /// </summary>
        /// <param name="factory"></param>
        /// <returns></returns>
        public async Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync(Func<Task<ServiceResult<IEnumerable<QueryCategoryDto>>>> factory)
        {
            return await Cache.GetOrAddAsync(KEY_QueryCategories, factory, CacheStrategy.ONE_DAY);
        }
    }
}
//BlogService.Category.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Blog.Impl
{
    public partial class BlogService
    {
        /// <summary>
        /// 查詢分類列表
        /// </summary>
        /// <returns></returns>
        public async Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync()
        {
            return await _blogCacheService.QueryCategoriesAsync(async () =>
            {
                var result = new ServiceResult<IEnumerable<QueryCategoryDto>>();

                var list = from category in await _categoryRepository.GetListAsync()
                           join posts in await _postRepository.GetListAsync()
                           on category.Id equals posts.CategoryId
                           group category by new
                           {
                               category.CategoryName,
                               category.DisplayName
                           } into g
                           select new QueryCategoryDto
                           {
                               CategoryName = g.Key.CategoryName,
                               DisplayName = g.Key.DisplayName,
                               Count = g.Count()
                           };

                result.IsSuccess(list);
                return result;
            });
        }
    }
}

緩存就不說了,查詢分類列表,聯合查詢文章和分類兩張表,關聯字段為CategoryId,然後分組,計算出對應的數量,在BlogController中添加API。

/// <summary>
/// 查詢分類列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("categories")]
public async Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync()
{
    return await _blogService.QueryCategoriesAsync();
}

標籤列表

分析:和分類列表差不多,新建模型QueryTagDto.cs繼承TagDto

//QueryTagDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{
    public class QueryTagDto : TagDto
    {
        /// <summary>
        /// 總數
        /// </summary>
        public int Count { get; set; }
    }
}

添加查詢標籤列表接口和緩存接口。

//IBlogCacheService.Tag.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Caching.Blog
{
    public partial interface IBlogCacheService
    {
        /// <summary>
        /// 查詢標籤列表
        /// </summary>
        /// <param name="factory"></param>
        /// <returns></returns>
        Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync(Func<Task<ServiceResult<IEnumerable<QueryTagDto>>>> factory);
    }
}
//IBlogService.Tag.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Blog
{
    public partial interface IBlogService
    {
        /// <summary>
        /// 查詢標籤列表
        /// </summary>
        /// <returns></returns>
        Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync();
    }
}

分別實現這兩個接口。

//BlogCacheService.Tag.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static Meowv.Blog.Domain.Shared.MeowvBlogConsts;

namespace Meowv.Blog.Application.Caching.Blog.Impl
{
    public partial class BlogCacheService
    {
        private const string KEY_QueryTags = "Blog:Tag:QueryTags";

        /// <summary>
        /// 查詢標籤列表
        /// </summary>
        /// <param name="factory"></param>
        /// <returns></returns>
        public async Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync(Func<Task<ServiceResult<IEnumerable<QueryTagDto>>>> factory)
        {
            return await Cache.GetOrAddAsync(KEY_QueryTags, factory, CacheStrategy.ONE_DAY);
        }
    }
}
//BlogService.Tag.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Meowv.Blog.Application.Blog.Impl
{
    public partial class BlogService
    {
        /// <summary>
        /// 查詢標籤列表
        /// </summary>
        /// <returns></returns>
        public async Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync()
        {
            return await _blogCacheService.QueryTagsAsync(async () =>
            {
                var result = new ServiceResult<IEnumerable<QueryTagDto>>();

                var list = from tags in await _tagRepository.GetListAsync()
                           join post_tags in await _postTagRepository.GetListAsync()
                           on tags.Id equals post_tags.TagId
                           group tags by new
                           {
                               tags.TagName,
                               tags.DisplayName
                           } into g
                           select new QueryTagDto
                           {
                               TagName = g.Key.TagName,
                               DisplayName = g.Key.DisplayName,
                               Count = g.Count()
                           };

                result.IsSuccess(list);
                return result;
            });
        }
    }
}

查詢標籤列表需要聯合查詢tags和post_tags,根據TagId進行關聯,然後分組從而獲取標籤下文章的總數,在BlogController中添加API。

/// <summary>
/// 查詢標籤列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("tags")]
public async Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync()
{
    return await _blogService.QueryTagsAsync();
}

分類名稱&文章列表

分析:此頁面下包含兩個接口,查詢分類的名稱和當前分類下的文章列表,和文章列表不同的是,它不帶分頁。分類包含兩個字段,分類名稱和展示名稱,我們要把真正的名稱查詢出來展示在頁面上。

分類名稱

不需要給他添加返回模型,直接返回一個string類型即可,同時給一個查詢參數name,添加獲取分類名稱接口和緩存接口。

//IBlogService.Category.cs
/// <summary>
/// 獲取分類名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<ServiceResult<string>> GetCategoryAsync(string name);
//IBlogCacheService.Category.cs
/// <summary>
/// 獲取分類名稱
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<string>> GetCategoryAsync(string name, Func<Task<ServiceResult<string>>> factory);

實現這兩個接口。

//BlogCacheService.Category.cs
...
    public partial class BlogCacheService
    {
        private const string KEY_GetCategory = "Blog:Category:GetCategory-{0}";

        /// <summary>
        /// 獲取分類名稱
        /// </summary>
        /// <param name="name"></param>
        /// <param name="factory"></param>
        /// <returns></returns>
        public async Task<ServiceResult<string>> GetCategoryAsync(string name, Func<Task<ServiceResult<string>>> factory)
        {
            return await Cache.GetOrAddAsync(KEY_GetCategory.FormatWith(name), factory, CacheStrategy.ONE_DAY);
        }
    }
...
//BlogService.Category.cs
/// <summary>
/// 獲取分類名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public async Task<ServiceResult<string>> GetCategoryAsync(string name)
{
    return await _blogCacheService.GetCategoryAsync(name, async () =>
    {
        var result = new ServiceResult<string>();

        var category = await _categoryRepository.FindAsync(x => x.DisplayName.Equals(name));
        if (null == category)
        {
            result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("分類", name));
            return result;
        }

        result.IsSuccess(category.CategoryName);
        return result;
    });
}

FormatWith()是擴展方法,ResponseText.WHAT_NOT_EXIST是之前說過的常量,直接查詢是否存在當前name的分類,如果不存在給出錯誤提示,存在的話,則只返回分類名稱,在BlogController中添加API。

/// <summary>
/// 獲取分類名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet]
[Route("category")]
public async Task<ServiceResult<string>> GetCategoryAsync(([Required] string name)
{
    return await _blogService.GetCategoryAsync(name);
}

[Required]Attribute 指定參數name必填。

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

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

文章列表

通過分類名稱查詢文章列表和分頁查詢文章列表返回模型是一樣的,只是不用分頁,所以直接返回一個列表就可以了,添加通過分類名稱查詢文章列表和緩存的接口。

//IBlogService.Post.cs
/// <summary>
/// 通過分類名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync(string name);
//IBlogCacheService.Post.cs
/// <summary>
/// 通過分類名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync(string name, Func<Task<ServiceResult<IEnumerable<QueryPostDto>>>> factory);

分別實現這兩個接口。

//BlogCacheService.Post.cs
...
    public partial class BlogCacheService
    {
        private const string KEY_QueryPostsByCategory = "Blog:Post:QueryPostsByCategory-{0}";

        /// <summary>
        /// 通過分類名稱查詢文章列表
        /// </summary>
        /// <param name="name"></param>
        /// <param name="factory"></param>
        /// <returns></returns>
        public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync(string name, Func<Task<ServiceResult<IEnumerable<QueryPostDto>>>> factory)
        {
            return await Cache.GetOrAddAsync(KEY_QueryPostsByCategory.FormatWith(name), factory, CacheStrategy.ONE_DAY);
        }
    }
...
//BlogService.Post.cs
/// <summary>
/// 通過分類名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync(string name)
{
    return await _blogCacheService.QueryPostsByCategoryAsync(name, async () =>
    {
        var result = new ServiceResult<IEnumerable<QueryPostDto>>();

        var list = (from posts in await _postRepository.GetListAsync()
                    join categories in await _categoryRepository.GetListAsync()
                    on posts.CategoryId equals categories.Id
                    where categories.DisplayName.Equals(name)
                    orderby posts.CreationTime descending
                    select new PostBriefDto
                    {
                        Title = posts.Title,
                        Url = posts.Url,
                        Year = posts.CreationTime.Year,
                        CreationTime = posts.CreationTime.TryToDateTime()
                    })
                   .GroupBy(x => x.Year)
                   .Select(x => new QueryPostDto
                   {
                       Year = x.Key,
                       Posts = x.ToList()
                   });

        result.IsSuccess(list);
        return result;
    });
}

這個邏輯和分頁查詢文章列表是差不多的,聯合查詢文章表和分類表,關聯字段為CategoryId,指定查詢條件categories.DisplayName==name,以CreationTime倒序排序,年份分組,篩選出所需字段返回,在BlogController中添加API。

/// <summary>
/// 通過分類名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet]
[Route("posts/category")]
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync([Required] string name)
{
    return await _blogService.QueryPostsByCategoryAsync(name);
}

標籤名稱&文章列表

分析:此頁面和分類頁一樣,包含兩個接口,查詢標籤的名稱和當前標籤下的文章列表。

標籤名稱

添加獲取標籤名稱接口和緩存接口,GetTagAsync()

//IBlogService.Tag.cs
/// <summary>
/// 獲取標籤名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<ServiceResult<string>> GetTagAsync(string name);
//IBlogCacheService.Tag.cs
/// <summary>
/// 獲取標籤名稱
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<string>> GetTagAsync(string name, Func<Task<ServiceResult<string>>> factory);

實現這兩個接口。

//BlogCacheService.Tag.cs
...
    public partial class BlogCacheService
    {
        private const string KEY_GetTag = "Blog:Tag:GetTag-{0}";

        /// <summary>
        /// 獲取標籤名稱
        /// </summary>
        /// <param name="name"></param>
        /// <param name="factory"></param>
        /// <returns></returns>
        public async Task<ServiceResult<string>> GetTagAsync(string name, Func<Task<ServiceResult<string>>> factory)
        {
            return await Cache.GetOrAddAsync(KEY_GetTag.FormatWith(name), factory, CacheStrategy.ONE_DAY);
        }
    }
...
//BlogService.Tag.cs
/// <summary>
/// 獲取標籤名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public async Task<ServiceResult<string>> GetTagAsync(string name)
{
    return await _blogCacheService.GetTagAsync(name, async () =>
    {
        var result = new ServiceResult<string>();

        var tag = await _tagRepository.FindAsync(x => x.DisplayName.Equals(name));
        if (null == tag)
        {
            result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("標籤", name));
            return result;
        }

        result.IsSuccess(tag.TagName);
        return result;
    });
}

FormatWith()是擴展方法,ResponseText.WHAT_NOT_EXIST是之前說過的常量,直接查詢是否存在當前name的分類,如果不存在給出錯誤提示,存在的話,則只返回分類名稱,在BlogController中添加API。

/// <summary>
/// 獲取標籤名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet]
[Route("tag")]
public async Task<ServiceResult<string>> GetTagAsync(string name)
{
    return await _blogService.GetTagAsync(name);
}

[Required]Attribute 指定參數name必填。

文章列表

和上面一模一樣的,添加通過標籤名稱查詢文章列表接口和緩存接口。

//IBlogService.Post.cs
/// <summary>
/// 通過標籤名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name);
//IBlogCacheService.Post.cs
/// <summary>
/// 通過標籤名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name, Func<Task<ServiceResult<IEnumerable<QueryPostDto>>>> factory);

分別實現這兩個接口。

//BlogCacheService.Post.cs
...
    public partial class BlogCacheService
    {
        private const string KEY_QueryPostsByTag = "Blog:Post:QueryPostsByTag-{0}";

        /// <summary>
        /// 通過標籤名稱查詢文章列表
        /// </summary>
        /// <param name="name"></param>
        /// <param name="factory"></param>
        /// <returns></returns>
        public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name, Func<Task<ServiceResult<IEnumerable<QueryPostDto>>>> factory)
        {
            return await Cache.GetOrAddAsync(KEY_QueryPostsByTag.FormatWith(name), factory, CacheStrategy.ONE_DAY);
        }
    }
...
//BlogService.Post.cs
/// <summary>
/// 通過標籤名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name)
{
    return await _blogCacheService.QueryPostsByTagAsync(name, async () =>
    {
        var result = new ServiceResult<IEnumerable<QueryPostDto>>();

        var list = (from post_tags in await _postTagRepository.GetListAsync()
                    join tags in await _tagRepository.GetListAsync()
                    on post_tags.TagId equals tags.Id
                    join posts in await _postRepository.GetListAsync()
                    on post_tags.PostId equals posts.Id
                    where tags.DisplayName.Equals(name)
                    orderby posts.CreationTime descending
                    select new PostBriefDto
                    {
                        Title = posts.Title,
                        Url = posts.Url,
                        Year = posts.CreationTime.Year,
                        CreationTime = posts.CreationTime.TryToDateTime()
                    })
                    .GroupBy(x => x.Year)
                    .Select(x => new QueryPostDto
                    {
                        Year = x.Key,
                        Posts = x.ToList()
                    });

        result.IsSuccess(list);
        return result;
    });
}

這個查詢有點特殊,聯合查詢了3張表,先查post_tags和tags,關聯字段TagId,再根據PostId查詢posts,指定查詢條件tags.DisplayName==name,以CreationTime倒序排序,年份分組,篩選出所需字段返回,在BlogController中添加API。

/// <summary>
/// 通過標籤名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet]
[Route("posts/tag")]
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name)
{
    return await _blogService.QueryPostsByTagAsync(name);
}

至此,基本上完成了博客前端所需的所有查詢接口,就還剩下友鏈的查詢,大家可以自己完成,後面如果需要什麼新的接口再回頭來寫就好了。

開源地址:https://github.com/Meowv/Blog/tree/blog_tutorial

搭配下方課程學習更佳 ↓ ↓ ↓

http://gk.link/a/10iQ7

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

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

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

十幾萬預算 如何用最少的錢買到最好的車_網頁設計公司

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

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

這些都是給大家的參考標準,不一定都要滿足以上條件,畢竟人無完人,車也一樣嘛。下面就介紹幾台實用可靠的家用車,希望供大家一個好的選擇。10萬級別家用車:轎車:卡羅拉、思域、帝豪SUV:繽智、博越、瑞虎720萬級別家用車:轎車:雅閣、君越、博瑞SUV:昂科威、奇駿、途觀。

買車之前得選車,

選車之前得看指標,

指標喜歡但並不一定能下得了手,

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

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

這不都是錢在作怪嘛!

今天就不拋開錢來說,

說點實際一點的東西,

就是如何用最少的錢買到最好的車,

十幾二十萬的家用車我們該如何選?

這些都是給大家的參考標準,

不一定都要滿足以上條件,

畢竟人無完人,車也一樣嘛!

下面就介紹幾台實用可靠的家用車,

希望供大家一個好的選擇。

10萬級別家用車:

轎車:卡羅拉、思域、帝豪

SUV:繽智、博越、瑞虎7

20萬級別家用車:

轎車:雅閣、君越、博瑞

SUV:昂科威、奇駿、途觀本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

為什麼要找教授買車?看完這裏給您答案_網頁設計公司

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

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

粉絲“小珊”是一名廣州在讀的女研究生,老家在廣東陽江,這次購買的車是皇冠2。0T,車是幫她爸爸買的,找到我們幫忙是因為爸爸在老家當地一直談不到滿意的價格,恰巧小珊一直有關注玩車並且知道可以幫忙購車,就抱着試一試的心態找到我們,結果是給到了小珊爸爸一個很滿意的價格,並且從訂車到提車只用了一周的時間。

又是新一天的開始,迅速進入工作狀態,回復用戶諮詢訂單;接聽用戶諮詢電話;幫用戶找車談價格;邀約用戶到店看車;幫助用戶成功買車;協助用戶驗車提車。

以上是每天在重複的工作,這既有規律又重複的事情在很多人看來是枯燥而乏味的,但是卻每天都樂在其中,因為每當看到用戶充滿笑容的提車照片時所有的辛苦都可以拋之腦後,而為了讓大家也能感受到和車主的那份喜悅,我們特意邀請了三位粉絲車主錄了一段視頻專訪來給大家展示。

粉絲“小珊”是一名廣州在讀的女研究生,老家在廣東陽江,這次購買的車是皇冠2.0T,車是幫她爸爸買的,找到我們幫忙是因為爸爸在老家當地一直談不到滿意的價格,恰巧小珊一直有關注玩車並且知道可以幫忙購車,

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

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

就抱着試一試的心態找到我們,結果是給到了小珊爸爸一個很滿意的價格,並且從訂車到提車只用了一周的時間。

粉絲“阿全”是一名土生土養的廣州90后,目標車型是鋒范,作為一名“老粉”,阿全一開始就找到了,當時剛好是9月初,各大經銷商為了十一黃金周沖銷量9月份的車價都開始收緊了,為了滿足阿全近期提車的要求,找到了合作較好的廣本經銷商,在市場都收緊價格的情況下為阿全爭取到了接近收價前的優惠,並且在原本沒現車的情況下也和經銷商協調回來了一台現車。

粉絲“阿曾”是一名廣州當地醫院的內科醫生,平時工作十分忙,根本就沒有時間去看車談價,所以從一開始就找到了幫忙找車談價,目標車型是雅閣,卻在糾結2.0L還是2.4L版本,然後分析過阿曾平時大部分時間是在市區用車,因為工作忙跑高速的機會是少之又少,所以給推薦了2.0L版本的雅閣,市區用車省油之餘保養費用也較2.4L版本要經濟,最後阿曾接納了的建議,也在取得滿意的價格后一周左右的時間就提車了

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

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

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

疫情期網購夯 新加坡民眾愛烘焙與珍奶材料_網頁設計公司

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

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

摘錄自2020年5月25日中央社報導

為遏止疫情擴散,星國政府自4月7日起實施「阻斷措施」,許多民眾改成在家上班,專賣手搖飲料等店家也都暫時關閉。突如其來的疫情改變了許多民眾的生活習慣。「海峽時報」(The Straits Times)報導,啞鈴、珍奶材料可能是許多人之前不會想購買的商品,但現在都成為熱搜品。

生活必需品、食物、室內運動器材都是現在新加坡熱門網購商品。專家分析,這股網購潮流在防疫措施放寬後仍會持續,很多人也預期,電商在「新常態」生活中將扮演更重要的角色。疫情興起民眾對烹飪的興趣,促使麵粉成為網路搜尋熱門關鍵字。

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

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

另一方面,疫情期間長時間待在家也激起民眾對「安慰食品」的興趣,珍奶就是其中一項。外送平台Grab在4月收到的珍奶訂單較前一個月大增60%。

除了仰賴外送服務,不少民眾也對珍奶DIY很感興趣,越來越多人上網搜尋食譜,製作珍奶所需要的粉圓材料即是蝦皮的熱門商品之一。隨著在家時間變長,烘焙成為民眾新興的消遣活動,許多烘焙材料店都出現排隊人潮。

生活環境
國際新聞
新加坡
武漢肺炎
疫情
網購食品
疫情下的食衣住行

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

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

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

本田為應付歐洲碳排放規定 與特斯拉、FCA結盟_網頁設計公司

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

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

摘錄自2020年11月3日鉅亨網報導

日本本田汽車與美國特斯拉,為因應歐洲在 2020 年到 2021 年間導入的全新乘用車排放規定,決定結盟以讓碳排放額度的買賣變得可行。如果無法達成碳排放目標,將被課處高額罰金。

歐盟規定在2020至2021年期間,於當地市場推出的新款乘用車,有義務將行駛每公里的平均碳排放量,階段性的調降到95公克以下。在2020年有95%的新車成為規範對象,同年給予電動車(EV)等的優惠待遇也較2021年要來的更多。

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

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

如果無法達成碳排放規定,汽車製造商每賣出一輛車,當局就會對每公克的碳排放來課處95歐元罰金。

歐盟的碳排放規定,允許汽車製造廠共享額度。由於專門生產純電電動車的特斯拉在歐洲市場銷售情況良好,FCA也提前本田一步與特斯拉結盟,以大幅降低平均的碳排放量。有消息指出,FCA 每年支付6億歐元給特斯拉。

氣候變遷
國際新聞
日本
本田
電動車

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

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

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

歐洲農業政策里程碑 3900億歐元補助 只給氣候友善農民_網頁設計公司

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

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

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

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

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

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。