春節從冬至開始_網頁設計公司

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

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

晉城麵塑 資料圖片

臘月里蒸麥芽饃 資料圖片

臘月里蒸麥芽饃 資料圖片

過年是從冬至開始的。從冬至到臘八,從臘八到小年,從小年到大年,人們沿襲着祖先傳下來的風俗,操持着,準備着,迎接着,春節的氣氛也在幸福的忙碌中漸漸達到頂點。年俗是文化,也是人情,生生不息,代代相傳。

夫冬至之節,陽氣始萌

“天時人事日相催,冬至陽生春又來。”這是杜甫的七律《小至》的兩句詩。杜工部是對的,春天是隨着冬至來到人間的。

如果認為春節代表春天的到來,那就錯了。其實我們所過的春節僅僅只是春天的一個節日。春節到來的時候,人間早已經屬於春天了。春節只是給了我們一個春天已經來到人間的強烈感覺。或者說,春節只是慶祝春天已經來到人間的一個隆重儀式。

而真正意義上的春天,是從冬至開始的。我們在這裏所說“真正意義”,是指中國古代哲學中的實在意義。我們的先哲們認為,宇宙的萬事萬物無不在陰陽交替、互動、對立、統一中生存和消長。整個世界,是由陰和陽組成的。陰與陽,渾藏於天地間,是中國古代哲學的文化內核。陰與陽也因此易位於冬至和夏至兩個節日之間。夏至之後,日漸短,夜漸長,陰漸盛,陽漸衰,所有事物,所有生命,都在漸趨低谷,即“萬物收斂”。冬至之後,晝漸長,夜漸短,“一天長一線,十天長一截”。然而,冬至過後,天氣會一天比一天冷。從冬至節那一天起,就開始了數九天。“一九二九,關門袖手”“三九四九,凍破碓臼”,天氣的確是越來越冷了。

“夫冬至之節,陽氣始萌。”雖然正值數九寒天,春潮卻在暗中涌動。陽氣在暗中上升,物候的腳步在不知不覺中邁向了春天。春如初胎,從冬至那天起,已經沉靜地安附於大自然的宮室里,正如《西遊記》中邵康節所說:“冬至子之半,天心無改移。一陽初動處,萬物未生時。”“五九六九,沿河看柳。”“七九河開,八九雁來。”“九九加一九,耕牛遍地走。”說明陽氣在暗中悄悄上升,春天在不聲不響走近人間。這一點也被羅貫中寫進了《三國演義》。程昱入告曹操:“今日東南風起,宜預提防。”操笑曰:“冬至一陽生,來複之時,安得無東南風?”把一場赤壁之戰放在冬至,顯得波瀾壯闊,意味無窮。

中國“四大名著”無不拿冬至說事。曹雪芹在《紅樓夢》更是把冬至說到特別:“這年正是十一月三十日冬至。到交節的那幾日,賈母、王夫人、鳳姐兒日日差人去看秦氏,回來的人都說:‘這幾日也沒見添病,也不見甚好。’王夫人向賈母說:‘這個癥候,遇着這樣大節不添病,就有好大的指望了。’”說明冬至是個不同尋常節令,不光宇宙間萬物在蘇醒、發生、成長,就連病疽疾癘也一樣不會放過其生命活躍起來的機會。

《水滸傳》似乎沒有必要寫到冬至,但施耐庵並沒有放過這一個重要節點:“聞知今上大張燈火,與民同樂,慶賞元宵。自冬至后,便造起燈,至今才完。”我們先不說作者在敘事中來上這麼一筆的用意是什麼,施耐庵給了我們一個信息:春節乃至元宵節都是從冬至開始的。

其實春節不只是從冬至開始的,春節在中國古代與冬至是並肩同行的。

《詩經·七月》:“七月流火,九月授衣。一之日觱發,二之日栗烈。無衣無褐,何以卒歲?三之日於耜,四之日舉趾。”“一之日”是周曆和豳歷的正月。而周曆和豳歷的正月正是夏曆的十一月,就是我們現在所過的冬至的月份。而今的冬至在十一月末,或十二月初,春節則在正月初一,之間相隔時間近一個月,這並不是春節與冬至分道揚鑣,而是我們選擇了夏曆,即農曆。所以,春節是從冬至開始的,有它的歷史淵源,也有它的文化根據。

摔老南瓜

今天,儘管春節姍姍來遲於冬至之後,但人們並沒有忘記春節是從冬至開始的。尤其是在我的家鄉晉東南,依然回蕩着歷史的流響餘韻,也總是忘不了想方設法給冬至塗上一點文化色彩,讓冬至節沉染在豐富的文化意蘊中。

冬至節的五更天,天還不明,就能聽到各家各戶“嗵嗵”的響聲傳出來。那是家家都在摔老南瓜。秋天收穫老南瓜的時候,家家都要挑好的大的收藏起來,等到冬至的五更天,把老南瓜從架上取下來,抱到炕頭上,高高舉起,重重地摔到地上,南瓜子濺得到處都是。驚醒的孩子們會問父母,冬至為什麼摔老南瓜?父母會告訴孩子們冬至摔老南瓜的故事:

傳說有人要在冬至那天夜裡刺殺孔子,藏匿孔子那一家人,暗中放走了孔子,在孔子睡覺的枕頭上放了個老南瓜,用被子蓋上,偽裝成孔子正在睡覺。刺客夜入寢室,舉刀便砍,只聽“咔嚓”一聲,半個老南瓜“嗵”的一聲滾落到地上,刺客以為大功造成。於是,冬至摔老南瓜就成了祀孔的一種特殊形式。

冬至節,村子里和小學校也要祀孔。冬至那天學校放假一天,說是放假,學生並不離開學校,只是不準翻書而已。意思是,翻書會迷了孔夫子的眼。學生要穿上新衣,跟着村幹部和老師攜酒脯祀孔。祀孔結束后,村幹部陪老師吃油疙麻雜燴菜,學生回家吃餃子。我們把餃子叫“凍耳朵”,意思是,吃了凍耳朵,耳朵不怕凍壞。

冬至摔成碎塊的老南瓜,可以煮成“紅小豆老南瓜”吃。色質特別好的,留給“五豆”和“臘八”。臘月初五是“五豆節”,小節。家家吃紅豆小米撈飯,即用紅豆和小米一起燜的乾飯。五豆是冬至到臘八的一個小小跳板。

臘八粥

臘月初八,家家吃“臘八粥”。

“粥”是外鄉人的叫法,我們不叫粥,實質上也不是粥。我們叫黏米飯,也叫黏飯,又叫甜飯,實實在在的“飯”,能夠用筷子夾起來的。黏米與小米顏色一樣,黃燦燦的,顆粒大小也相同,外地人用眼睛是分辨不出來的。黏米做成的“黏米飯”特別“黏”,特別香。

臘月初七晚上,母親就要煮紅豆。紅紅的蘭花炭火,煮豆的鍋在火上嘩啦嘩啦響,孩子們的夢裡也是黏米飯香。

半夜時分,豆子煮熟了,母親會將老南瓜、黏米、紅薯、紅棗、核桃、柿餅、柿圪蓮,花生、紅糖、白糠等依次入鍋。什麼東西什麼時間放,既不能早,也不能遲。比如花生,早了不脆,遲了不熟;比如柿餅柿圪蓮,時間不對,不但不甜,還會發澀。做黏米飯最好用砂鍋,攪黏米飯最好用木板。用鐵傢伙做出來的黏米飯顏色不鮮明,吃起來味道也不對。對着黏米飯鍋不能說話,不能咳嗽,如果把唾沫星兒噴到黏米飯里,黏米飯就會“涎”成米是米、水是水,不融和,也不黏稠。

早晨,軟米飯做好了,各家各戶要相互饋贈。送贈黏米飯的任務大都是孩子們的事。一碗碗紅紅的黏米飯,像一盞盞的小紅燈籠,像一捧捧的火焰,像一朵朵鮮艷的花,在飛雪中,在白雪皚皚的大街小巷裡,燃燒,跳躍,綻放,如竄梭一般地來來去去。每碗黏米飯都是滿滿的,偶爾會滴一點在雪地上,像掉在雪地上的一塊紅紅的火炭,像一枝火的花朵,讓雪天顯得溫暖,讓雪天顯得爛漫,點燃着鄉村裡永遠的鄉愁。

家家戶戶都做黏米飯,家家戶戶相互饋贈,那是一種風俗。你送我一碗,我送你一碗,“親戚箢還箢,鄰家碗還碗”。那是鄉里鄉親熱絡感情、鞏固關係的一種最簡單、最樸素的一種方式。千年不變,也不應該變。

學生都要給老師送軟米飯,老師收很多黏米飯,凍起來,天天拿鏊子焐着吃,放一點油,焐出一層薄薄的皮,又香又甜,天下風味。

也有送黏米飯給村子里的藥鋪商鋪的,藥鋪商鋪也會準備一些核桃、紅棗、花生、柿餅之類回贈。

為什麼要吃臘八粥?為什麼要吃黏米飯?歷來傳說不一。但最好緣由應該是紀念佛祖成道日,效法牧女獻乳糜於佛的故事。不過,這也是人們賦予臘八節的一種文化色調。我看,其蘊包含在《詩經》里。

《詩經》有言:“物其多矣,維其嘉矣。物其旨矣,維其偕矣。物其有矣,維其時矣。”冬至時節,乃至整個臘月天,各種糧食果蔬藏儲豐富,是乃“物其多矣”“物其旨矣”“物其有矣”;選擇節日,如冬至、五豆、臘八、小年,做成合節的諸如黏米飯、豆撈飯、火燒之類的飯食,乃是“維其嘉矣”“維其偕矣”“維其時矣”。也就說,臘八吃黏米飯才有臘八氣氛,才是臘八的味道。也如八月十五吃月餅,三月三吃春餅,端午節吃粽子,此即“物其有矣,維其時矣”。

冬至說儒,臘八說佛,小年以至大年,就應該說“道”了。佛、儒、道,三家文化,融化了我家鄉的臘月天。

過了臘八,接着就是小年。臘月二十三,祭灶的日子到了。

祭灶祭哪個?祭祝融。《周禮》說:“顓頊氏有子日黎,為祝融,祀以為灶神。”漢之後,灶王爺掌握了人們的禍福、壽夭,出現了“灶神晦日歸天,白人罪”的說法。人們小心奉祀灶君,傳說灶君每年臘月二十三都要到天帝那兒去陳說人間的善與惡,因此各家各戶要為灶王爺送行,打些火燒給灶爺做乾糧,弄些麩皮草料給灶爺秣灶馬,在火口上塗些糖餳給老灶爺糊嘴巴,好讓老灶爺到天帝那兒多說好話,把天帝所降的吉祥帶回人間。

“二十三祭罷灶,夾上包袱往回趵。”住在娘家的媳婦,祭過灶之後,就該回到婆婆家準備過年了。

“好了!好了!到跟前兒都好了!”

過了冬至節,男人們忙着殺豬,宰羊,割肉,買菜,辦年貨。女人碾米,磨面,扯布,買衣線,做新衣,做新鞋。過了臘月二十三,就要掃屋子,做豆腐,坐蒸鍋,蹲油鍋。

晉城不缺香炭,火邊圍一圈香炭,把火燒得旺旺的,女人們各顯身手,蒸棗山、棗供、棗花、豬、羊、小兔兒、小魚兒、小刺蝟,炸菱花、饊花。所蒸所煮,都是敬神的供品。棗山像個大青蛙,背上許多面花兒。每朵花上都有一個紅棗,看起來像一朵盛開的大紅花。雜獻只是一個大饅頭,上邊摞幾層面花兒,最上面安一個大紅棗。比雜獻小一點的叫棗花兒,只有一層花,花上邊只是一個酸棗。棗山和雜獻上的花兒,有盤花,有剪花。盤花像牡丹,剪花像菊花。把筷子頭破開,析出一朵“小梅花”。在小小的瓷碟子里化一點品紅,把“小梅花”在品紅里蘸蘸,在棗山和雜獻上點上朵朵小梅花。將高粱秸破成兩半,摳去瓤子,便成為一個彎彎的月牙。把月牙兒蘸一點品綠,在“小梅花”的四角點四片,好花有了綠恭弘=叶 恭弘扶持,所有的獻供便飛一片春色。每朵花都有兩層三層,看上去重重疊疊,有點“層巒聳翠”的意思。

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

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

給女兒女婿蒸兩個大口禮饃,個子比頭盔大。把揉好的面用細繩從頂向下勒一個十字,蒸熟的口禮饃如四瓣盛開的花朵,點綴上品紅品綠,顯得喜氣洋洋,一片祥和。

給自己家裡每個人蒸一個“人口禮饃”,其中一個裡邊放個“制錢”,其餘放上一個紅棗。初七人日,誰拿到放“制錢”的口禮饃,誰運氣就是最好的,福氣也是最大的,大事小事都會成為家裡的靠山。

煮一鍋玉茭豆兒,蒸些豆餡饃、菜餡饃。從初二到初五,每天早晨喝玉茭豆兒,吃餡饃。

為了一個年節,家家戶戶都會忙得團團轉,腳步總是匆匆的。人與人見了面總要關切地問一聲:“準備好了嗎?”總是笑着回答:“好了!好了!到跟前兒都好了!”

洗了澡,理了發。糊了窗戶,架起正火。貼上對聯,掃了院子,就到大年三十了。

“二十三日去,初一五更回”

三十的晚上,家家戶戶吃“面恭弘=叶 恭弘”。脫玉茭、大白菜、紅蘿蔔、豆腐、豆芽,一鍋煮熟。寬寬的麵條,不放蔥蒜,只放花椒,因為要獻祖先。先盛一碗獻祖宗。燃上一炷香,大門口放三個炮,接祖宗回家過年,祖先就會跟着香煙走進大門,走進屋門。祖先畢竟是泉鄉之物,沒有香煙引路,門神不會放祖先進入家門。

把祖先請到祖先桌上,給祖先獻上一碗面恭弘=叶 恭弘,俗稱“爺奶奶湯兒”,讓祖宗先喝口湯,歇歇氣,等五更天敬神時,再給祖宗上獻。

從臘月二十三到年三十,人間是沒有神的,諸神都到天帝那裡參加年會了。這一段時間,人間是無忌諱的,人們可以為所欲為,特別是結婚辦喜事,不用“擇好兒”,想哪天辦就哪天辦。

臘月三十,祖宗也回來了,家人與祖宗一起,早早就要睡覺了。睡覺時放一個“關門炮”,說明這一年已經過完了,關門了,再開門的時候就是春節了。

神回到人間的時間,是正月初一的五更天,是所謂的“二十三日去,初一五更回”。初一五更,起床先放一個開門炮,春節的大門從此就打開了。

不論農家、官家與商家,不論窮家與富家,家家供奉灶爺,供奉天地爺、家堂老爺、財神爺,其他便不相同了。有的家庭供奉不知名姓的神仙,總稱仙家老爺。很多人家都供奉佛爺,讀書人供奉孔夫子,木匠供奉魯班,鐵匠供奉老君。

其實行走在我們桑梓間的不止佛、儒、道,還有天地、山神、龍王、馬王、牛王、青龍、白虎、紫姑、門神、樹神、花神、五道神、土地神,等等。當然,林林總總的神仙,也都出身於道家,平時不聞不問,過年時,不知道從哪裡蹣跚走來,在一元始旦,萬象更新,人世間充滿歡樂充滿希望的時候,同眾庶一起歡度春節,增加節日的氣氛,也讓人們的心靈有所依傍,靈魂有所棲止,精神有所寄託。

初一五更,點正火,接神,焚香。天地爺自是“滿斗焚香”,一炷炷的單香插得如滿天星斗。其他佛家道家的神仙都是三炷香,或者一炷整香。祖先不是神,所以不可與神一起受享獻饗。各種獻供只能等敬罷神,酌回來才能獻給祖先享用。

正火點着之後,青煙裊裊,火光衝天,火星直冒,紅光滿園,柏樹枝的香氣格外讓人心清氣爽。孩子們會圍着正火又跑又跳,又喊又笑,小臉一張張映得通紅,朝氣與喜氣沖溢在整個院子里。焚香的同時放鞭炮,孩子們手裡會提個小小的紅燈籠,到處搶鞭炮。大人們會告訴孩子,大年初一去抱住大椿樹,高聲喊:“椿樹娘,椿樹娘,你長高了做大梁,我長高了領衣裳。”希望椿樹帶領孩子與孩子一起長高。

初一五更,男人敬神,女人打火做飯。大年初一早晨多有吃拉麵的,意思全家互相拉着點,拉緊點,不要丟了一個,不要少了一個。女人拉麵,男人批蒜苗。白生生的面,拉得又長又細。頭一碗先送本院鄰居,鄰居也會送一碗過來,是鄰裡間年節時的一種交往。

吃過飯,父母要給孩派壓歲錢,還要教孩子怎麼拜年。同輩人見了面,也要互相說一聲:“拜年了!”“春節好!”“恭喜發財!”村子上的老會頭要飾鑼集中八音會,耍會,唱戲。

初五晚上要送祖先。端上半碗爺奶奶湯,燃上一炷香,隨着香煙裊裊,把祖宗送到大門口,放三個炮,年節基本就結束了。

元宵拱燈棚

說是年基本結束了,就是還沒有完全結束。還有個正月十五鬧元宵。

掛燈是我們家鄉一個古老的傳統,不僅家家門口掛燈,整個村子里也掛燈。我們村裡有好幾百對花燈,全部掛在後街上。後街從東到西二里長,燈棚用紅、黃、藍三色小布編成小方花格子作燈棚,把燈並排掛在燈棚下,橫看是一對一對的,順着街道看過去,像兩條火龍,從東頭一直蜿蜒到西頭。

吃晚飯時,鑼聲一響,人們都知道,要髮蠟了。二里長街,分段管理,負責髮蠟。髮蠟,就是點燃蠟燭的意思。蠟燭是用羊油掂的。熬上一大鍋羊油,把棉花做成的燭芯纏在蠟柱上,往大鍋里一沾一掂,反覆多次,便掂成一支蠟燭。白蠟是本色,紅蠟是添加了顏料。紅蠟白蠟都搖紅,把一條長街搖得輝煌壯麗。

蠟是年年要掂的,燈也是年年要掛的。即使最困難時期,也沒有停了掛燈。人們說:“討吃看煙火,肚飢眼歡樂。”可以不吃飯,卻不能不掛燈,好不容易又一年了。也許是想證明人這一年又活過來了。

燈棚下每隔十來步就有一爐正火。燒正火用的是香煤香炭,也叫蘭花香炭,也叫白煤,傳說是英國女王燒壁爐必用之物。燒正火不能用臭炭,否則會嗆得人打噴嚏,既出洋相,又不吉祥。

傍晚時分,把大塊的蘭花香炭添足,正火很快就燒起來,一堆火焰熊熊,一堆金光燦爛。天上月光,棚下燈光,街邊火光,加上社鼓笙簫,一派金碧輝煌。

後街的燈全是紗燈,繪有《三國演義》《封神榜》《水滸傳》人物情節。觀燈的人從燈棚下拱來拱去,拱燈棚大都是年輕人。拄着棍子的老人,引着孫兒老叟,傴僂着腰的老嫗,把孩子架在膀子上的父親,梳了圓頭的母親,大都圍在燈棚下,每盞燈下都會圍一個圈,仄着耳朵,聽老人們說些三國水滸的故事。

年輕人很少站在燈棚下看那些老故事。他們有自己的故事。“月上柳梢頭,人約黃昏后”,是他們故事的藍本。他們總是以“拱燈棚”,演繹他們的故事。

“拱燈棚”是小鎮一個古老的風俗。按風俗,不管男女老少,正月十五拱燈棚會吉祥如意,會免除許許多多的大災小難。

不管是男孩子還是女孩子,他們總是三個一群兩個一夥,貌似看燈,卻心不在焉。

女孩子們總手拉着手“拱燈棚”。從東頭拱到西頭,再從西頭拱到東頭。人前,不管內心多麼豐富,表面都是很文靜的,悄悄謐謐的,總是很害羞的樣子。如果不得不笑,也只是趕緊把嘴捂住,彎下腰,或者背過身,悄抿了嘴,齒頰之間發出一點吹涼風一樣的“噝噝”聲。當然,有時候也難免會笑出聲來,無非是忽然想起了,或者忽然看到了可笑的人和事,實在是忍不住了,就笑出來。一個人“疙啼”一聲,會傳染女孩子們無端“疙啼疙啼”笑成一堆。

有話說的時候,也只是悄聲細語,低聲喁喁。心裏卻熱烈。本來是三個兩個相跟着,牽着手,卻不知道什麼時候就少了一個。不知道其中哪個女孩什麼時候就拱出了燈棚,拱到了月光照不到,燈光也照不到的地方去了。

當然,過來人都知道,那燈火闌珊的角落有女孩子和男孩子的故事。他們知道,入春了,驚蟄了,野草也該句萌了。

藿谷洞的燈與後街不同,藿谷洞的燈不是為大眾看的,是專奉“四奶奶”即送子娘娘的,觀燈的人大都是母親或祖母,因此藿谷洞的燈上沒有故事,燈下也沒有故事,很雅靜,很純凈,很雅緻,雅緻到脫俗。燈的形式也很單純,一式宮燈,兩種燈罩。布罩和玻璃。布罩是綠裙紅褲,很鮮艷;玻璃罩繪有梅、蘭、竹、菊,石榴,荷花,魚兒,鳥兒,題些“玉燭豐年”“雨暘時若”“天地交泰”的文字。品位很高,有一點閨閣氣。倘若天空中有雪花飄下來,那才是真正的天上人間。後街的燈如果代表“俗”,那麼藿谷洞的燈就是一個“雅”。從後街到藿谷洞,就是一個雅俗共賞。

藿谷洞長不足百步,南瀕大箕河,北出後街。南北街口各有一爐正火,助“四奶奶”看烘火,供祖母或母親們熏手熏腳。母親們和奶奶們常常會搬個椅子或板床,穩穩噹噹坐在正火跟前,一邊烤火,一邊陪着“四奶奶”看燈。

俗話說三個女人一台戲,但那時候的女人卻沒戲,既不調笑,也不說穢語。所有的奶奶和母親們絕不開玩笑的。她們的心那時候是非常虔敬的,是懷着無限神聖的。她們心中的“四奶奶”就在燈下,護佑着他們的兒孫。不管誰家裡的孩子,都是四奶奶殿里來的,都是四奶奶的侍兒。四奶奶是鄉村女人們心中最可愛,最可敬,最可親,最嚴肅的一位女神。她們會拿五色紙給四奶奶糊些被子褥子、衣裳、鞋襪。四奶奶應該是三寸金蓮,因此那鞋也糊得小辣椒一樣,很俊,很俏。除了鋪、蓋、穿、戴,還要給四奶奶上“銀兩”,即錫箔之類。

供品是“桃”。面蒸的,洋紅洋綠點綴些花恭弘=叶 恭弘,給人的感覺是喜氣盈盈,這叫“壽桃”,是寶塔狀的。還有“油桃”,偏形的,卧式的。蒸熟之後,在鍋里放點油,燒熱,把“桃”背在油鍋里擦一擦,又香又好看。最簡單最聖潔的供品是“香米茶飯”。名字看上去太詩意了!把小米炒開花,放點水,熗一下,用酒盅一盅一盅扣在盤子里,像一個個小沙包。

祖母和母親們在用心觀燈。那綠褲紅裙之間所包含的,那玻璃罩所罩的,是仙緣,是仙機。她們在和“四奶奶”一起度過元宵,她們在恬靜中陪伴着她們心中的“四奶奶”。她們也知道“四奶奶”不是世界上最高和最有權威的神或仙,但“四奶奶”最喜歡孩子,是一位慈祥的奶奶。為了孩子,為了孫子,她們願意那麼陪着。那樣陪着,她們的心裏是安定的,是穩貼的。她們的內心永遠有那麼一團神聖。“漠虛靜以恬愉兮,澹無為而自得。聞赤松之清塵兮,願承風乎遺則。貴真人之休德兮,美往世之登仙。”這才是她們的真心,雖然她們並未讀過《楚辭》。

掛起燈的時候,能有雪花飄落,那是再美不過了。那叫“瑞雪兆豐年”,最富詩意。

到此,年就算完全結束了。“九九加一九,耕牛遍地走”,到一心一意種莊稼的時候了。

(作者:卓 然,系山西省晉城市作協名譽主席)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

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

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

吃糖瓜、祭灶神……小年如何過出儀式感?_網頁設計公司

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

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

“二十三日過小年,差不多就是過新年的‘綵排’。”著名作家老舍在《北京的春節》中,曾記錄過一系列有關春節的民俗,其中就提到了小年,字里行間透出了濃濃的年味兒。

不過,小年的日期並不一致,有的是在臘月二十三,有的是在臘月二十四……但一般而言,小年都被當做是春節的序曲,等那首“過年謠”唱起來,人們就開始按部就班做準備,直到迎來熱熱鬧鬧的中國年。

小年日期不相同

小年是一個傳統節日,也被稱為謝灶、祭灶節、灶王節等。由於各地風俗,“小年”有着不同的概念,日期也不盡相同。

在古代,過小年有“官三民四船五”的傳統,也就是說,官家的小年是臘月二十三,百姓家的是臘月二十四,而水上人家則是臘月二十五。還有的地方把“臘月廿四”和“除夕前一夜”都稱為小年。

關於小年,還有一首“過年謠”,忙碌中自帶喜慶:“二十三,捏糖瓜;二十四,掃屋子;二十五,凍豆腐;二十六,燉豬肉;二十七,殺肥雞;二十八,把面發;二十九,蒸饅頭;三十上炕包餃子。”

不同地區的過年謠,內容會略有差別。但總體來說,都是表達了一種辭舊迎新、迎祥納福的美好心愿。

過小年為啥要“祭灶”?

小年被視為過年的開端,也自然會受到格外重視,祭灶便是期間一項重要的民俗活動。

灶神是中國古代神話傳說中的司飲食之神。也有傳說稱,灶王爺原本是一介平民,叫做張生。他成家后終日花天酒地,終於淪落到以乞討為生的地步。有一天,他恰好乞討到前妻家,羞愧難當,一頭鑽到灶鍋底下燒死了。

玉帝知道后,認為張生還有羞恥心,又是在鍋底死去,就把他封為灶王,每年臘月二十三、二十四上天“彙報”一家人的善惡,大年三十再回去。於是,漢族民間就有了“小年”祭灶的習慣,祈求來年平安順遂。

宋代的范成大在《祭灶詩》中說:“古傳臘月二十四,灶君朝天欲言事……杓長杓短勿復雲,乞取利市歸來分。”這首詩很形象地說明了古代有關祭灶的一些風俗習慣。

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

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

灶王爺畫像一般貼在灶台附近,祭灶時要把關東糖用火融化,塗在灶王爺的嘴上,這樣他就不能在玉帝面前說壞話。還要把舊畫像揭下來,再和用稻草紮成的“馬”一起燒掉,象徵著送灶神上天,也叫“辭灶”。之後,新年後再把新畫像貼上,迎回灶神。

除了糖瓜,這時還會吃些啥?

提到小年的飲食,糖瓜自然是標誌性食品之一。除了用來祭灶外,過去也是人們口中的美食。

糖瓜一般用黃米和麥芽熬制而成。這種糖有兩種形狀,長條的叫“關東糖”,扁圓的叫“糖瓜”,吃起來滿口生香,別有風味。

舊日,农民們一般到了隆冬季節沒什麼農活時才能休息,那時從臘月二十三到正月十五甚至是整個正月都是“年”,過年自然要吃餃子,時間久了,吃餃子也成了小年的習俗。

晉東南地區,流行小年吃炒玉米的習俗,民諺有“二十三,不吃炒,大年初—一鍋倒”的說法。人們喜歡將炒玉米用麥芽糖粘結起來,冰凍成大塊,咬上一口,又酥又脆。

剪窗花、掃房子……過年準備工作有哪些?

過了小年,離着春節只剩下不到十天時間了。剪窗花、貼對聯、貼年畫……過年的準備工作會更加熱鬧。

例如,各家各戶都會認真打掃房子、清除灰塵,窗明几淨迎接新年。據說,小年也叫掃塵日,源於堯舜時期的“掃年”習俗,原本是古代人民驅除病疫的一種儀式。

在所有準備過年的活動中,剪貼窗花是最流行的。窗花的內容可以是喜鵲登梅、燕穿桃柳、蓮(連)年有魚(餘)、和合二仙等,也有各種戲劇故事。

過去,臘月二十三后,家家戶戶要蒸饅頭,特別要製做一個“大棗山”來供奉灶君,饅頭也可以有許多造型,往往是大家一展靈巧手藝的好機會。

俗話說,“有錢沒錢,剃頭過年”。在此期間,大人、小孩都要洗澡、理髮,一身清爽迎接新年,人們認為,這樣也有“辭舊迎新”的意味。

其實,小年並不只是一個簡單的日期,重要的是它作為春節的前奏,此後的一系列民俗活動都強調了“參與感”,也是在這個過程中,年味越來越濃了。(記者 上官雲)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

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

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

這款15萬級的國產SUV氣場竟然完爆路虎?_包裝設計

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

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

車身側面和尾部的設計風格則可以看出相當高的原創度,沒有採用溜背式的“極光”造型,而是用了較為方正的懸浮式車頂設計,這樣會使得車身側面顯得更加剛毅,整車的力量感與肌肉感也顯現出來。內飾層面同樣也是向成熟車型借鑒的意圖比較明顯,黑紅色的雙拼搭配是營造一台車內飾運動氛圍屢試不爽的設計風格,並且採用了一定量的鍍鉻裝飾進行點綴,中控造型十分前衛也比較符合當下設計潮流。

路虎,SUV車型中絕對的明星品牌,如果我告訴你十五萬不到就可以買到“路虎”,你還是會想到陸風X7?還是想到眾泰又準備複製哪一款成熟車型?不,今天的主角是一款可以比肩路虎的SUV——來自廣汽吉奧的GX6。

廣汽吉奧GX6

指導價格:10.98-14.68萬

我們可以看到吉奧GX6的外形讓人感到十分熟悉,很多路虎極光的設計元素在其身上,但是不得不說也融入了當下非常流行的運動化設計,讓GX6看上去十分年輕時尚。車身尺寸為4640*1815*1800,而且軸距達到了2745,而路虎攬勝極光的尺寸為4370*1900*1635,軸距為2660,尺寸上吉奧GX6不可謂不大。

車身側面和尾部的設計風格則可以看出相當高的原創度,

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

沒有採用溜背式的“極光”造型,而是用了較為方正的懸浮式車頂設計,這樣會使得車身側面顯得更加剛毅,整車的力量感與肌肉感也顯現出來。

內飾層面同樣也是向成熟車型借鑒的意圖比較明顯,黑紅色的雙拼搭配是營造一台車內飾運動氛圍屢試不爽的設計風格,並且採用了一定量的鍍鉻裝飾進行點綴,中控造型十分前衛也比較符合當下設計潮流。而且做工的細緻程度也是比較精細,不會有過於明顯的粗糙感。只是用料偏廉價,硬質塑料比較多。

動力總成方面廣汽吉奧GX6所使用的是來自三菱的4G69S4N汽油發動機,排量為2.4L,並且搭載的是手動變速箱,三菱的自然吸氣發動機可靠性毋庸置疑,但是全系沒有自動變速箱的配置着實有點對不起觀眾;而且這款發動機技術比較過時,以目前的汽車發展來說,吉奧GX6的油耗並不低。

編輯總結:採用借鑒的方式進行汽車的設計和生產是國內很多車企都在進行或者曾經做過的方式,好壞與否暫且不論,但這也說明這種外觀的設計是比較討好消費者的設計。

GX6的整車配置不低,像是目前自主品牌SUV車型普遍搭載的中控大屏、倒車影像、一鍵啟動、無鑰匙進入等等配置較為齊全,比較可惜的是這款車目前搭載的是全手動變速箱,據悉裝配了自動變速箱的新款吉奧GX6會在今年年底上市,屆時或許可以對於它的新車做一下期待。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

Microsoft 與 Parallels 合作,要將 Windows 10 帶到 M1 版 Mac 上_網頁設計

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

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

Apple 在上個月推出了以 ARM 為基礎的 M1 晶片,現在應該全球的開發者都在日以繼夜地努力將自家的應用程導入這個新平台上,對於雙系統用戶來說,還是最關心 Windows 10 到底什麼時候可以支援 M1 晶片。日前 Microsoft 與 Parallels 開展合作,盡量讓大家不用久等就能享用。

Microsoft 與 Parallels 合作,要將 Windows 10 帶到 M1 版 Mac 上

對雙系統用戶來說,Parallels 絕對不陌生,該公司已經開始開發自己的軟體,要將 Windows 10 帶到 Apple Silicon 的世界,在不久前該公司才剛發布了第一個測試預覽版本,讓用戶能夠盡早試用該產品。事實證明,Parallels 並不是唯一一個在這方面使力的人,雖然不清楚兩家公司的合作方式為何,但 Microsoft 旗下 OneDrive 副總裁 Omar Shahine 在 Twitter 上面讚揚了此一合作。

Dang this is amazing! Windows 10 ARM running on MacBook Air M1. Great performance and battery life! Thanks @ParallelsMac and @Microsoft for releasing the ARM build of Windows! pic.twitter.com/BR8vZhJV7V

— Omar Shahine (@OmarShahine) December 23, 2020

有趣的是,在不久前,Apple 的高層主管曾表示將 Windows 引進 Apple Silicon 是僅有 Microsoft 自己能夠做決定,在彼時,Apple 軟體工程高級副總裁 Craig Federighi 曾表示 Apple 擁有 Microsoft 能做到這點的核心技術,可以直接運行 ARM 版本的 Windows,但又能反過來支援 X86 的應用程式,但這必須由 Microsoft 做出決定,將該技術授權給用戶在 M1 版 Mac 設備上運行。

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

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

於此同時,Apple Silicon 世界正在持續壯大,不久前也有傳聞 Microsoft 也正在為新晶片的軟體最佳化投入大量資金,Office、Teams 與其他 Microsoft 應用程式正陸陸續續開放支援 M1 晶片,以求一切都能在新的 Apple 設備上盡可能地平穩運作。

◎資料來源:SoftPedia

您也許會喜歡:

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

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

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

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

小米推出小米便攜鼠標 2,內建雙模式可操作兩台電腦、4 段可調 DPI、一顆 5 號電池可用一年_貨運

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

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

小米便攜鼠標第一代推出至今也有超過二年的時間,依據其他產品的經驗,這款可說更新的相當慢,終於在稍早小米官方正式宣布二代「小米便攜鼠標 2」登場,除了延續上一代的雙模式特色外,還加入了 4 段可調 DPI 與更強悍的待機續航力,更重要是,價格還只需要 79 人民幣(約台幣 340 元),真的超值阿。

小米推出小米便攜鼠標 2

這次小米便攜鼠標 2 跟很多小米產品一樣,採用眾籌的方式開賣,眾籌價為 79 人民幣,預計 12/30 早上 10 開售,一直到 1/6 早上 10 點結束,隨後價格也會調整回零售的 99 人民幣(約台幣 425 元)。

就外型來看,小米便攜鼠標 2 跟上一代沒有太大差異,不過側面線條更佳時尚,也沒那麼厚,我覺得變得更好看,表面也採用細膩噴砂金屬處理,擁有耐磨耐腐蝕特性。另外雙模式(藍牙4.2 + USB 接收器)的按鍵也移到正面,使用上更容易:

搭載消耗功率低 Telink 藍牙晶片,並內建自動休眠技術,來降低待機功耗,實現更好的省電節能。根據官方測試數據,裝一顆 5 號電池,就能正常使用一整年的時間,用到後面你搞不好會以為它不需要裝電池。電量低於 10% 時,正面上方的 LED 紅燈也會亮起,來提醒使用者:

上一代 DPI 為固定的 1200,小米便攜鼠標 2 全新加入 4 段靈敏度調整,分別為 1200、1800、2400 與 4000,滿足使用者各種需求。DPI 功能鍵位於底部,切換時也會透過 LED 燈來提示你目前是什麼 DPI:

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

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

體積一樣相當輕巧,含電池僅 86 公克,攜帶上非常容易:

有白銀與黑灰兩種顏色。至於會不會進台灣,我是覺得有點難,畢竟第一代都沒有了。無論如何,有興趣入手的人還是可以透過集運之類方式,眾籌部分預計 1/13 起開始出貨:

多款小米筆電 Pro 現身 Geekbench,單核效能大幅提升

您也許會喜歡:

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

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

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

容器技術之Dockerfile(一)_網頁設計公司

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

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

  在前邊的隨筆中我們聊到了docker的基本命令,鏡像,網絡,存儲卷以及基於現有容器製做docker鏡像,相關隨筆可參考https://www.cnblogs.com/qiuhom-1874/category/1766327.html;今天我們來聊一聊docker的另一個製作鏡像的方式dockerfile;

  什麼是dockerfile?所謂dockerfile就是用來描述docker鏡像製作過程的一指令文件;該文件是一個純文本文件,docker Daemon 進程可以從該文件中讀取指令,從而自動生成鏡像;客戶端可以使用docker bulid命令來指定dockerfile 所在目錄,來生成以dockerfile里描述的鏡像構建過程的鏡像;

  首先我們來了解下dockerfile的格式,dockerfile是一個純文本文件,我們可以理解為構建鏡像的源碼;不同於其他編程語言,dockerfile里沒有if else 沒有循環,它裏面僅僅有註釋和構建鏡像的指令;對於dockerfile註釋就是以井號開頭的行為註釋,這個和shell和其他配置文件的語法一樣;除此之外dockerfile里就只有指令了;嚴格的講指令是不區分字符大小寫的,通常我們約定俗成指令都是純大寫;除此之外在dockerfile的第一非註釋行必須是FROM開頭來明確的說明我們在基於那個鏡像為基礎鏡像做鏡像;

  通常情況下dockerfile是放在一個目錄下,該目錄就是dockerfile的工作目錄,我們在製作鏡像所依賴的文件都必須放在該目錄下,而dockerfile的名稱也必須是Dockerfile(首字母大寫,名稱必須是Dockerfile),如果我們依賴的文件是一個目錄下的部分文件,我們也可在dockerfile所在目錄創建一個.dockerignore文件,把我們要忽略的文件名稱寫到裏面即可,該文件支持通配符;

  了解了上面的dockerfile的基本環境結構,接下來我們來說說dockerfile的指令的用法;

  1、FROM:用於指定所創建鏡像的基礎鏡像,如果本地不存在,默認會從dockerhub中下載;該指令是dockerfile里的第一個非註釋行的指令;語法格式 FROM <repository>[:<tag>] 或者FROM <repository>@<digest>  其中repository是指定的base 鏡像的名稱 <tag>是base 鏡像的標籤為可選項,不指定標籤默認表示使用指定鏡像的latest版本;

  2、MAINTAINER:用於指定維護者信息,dockerfile並不限制MAINTAINER指令可出現的位置,通常我們建議將該指令放在FROM之後;語法格式 MAINTAINER <authtor’s detail>, <author’s detail>可是任何文本信息,但約定俗成地使用作者名稱及郵件地址;

  示例:

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"


[root@node1 test]# 

  提示:通常我們都不用MAINTAINER來指定作者信息,該指令幾乎是廢棄狀態,建議使用LEBEL指令來指定;

  3、LABEL:該指令用於指定添加鏡像的元數據信息;語法格式為LABEL <key>=<value> <key>=<value> <key>=<value> …;如果添加的元數據信息值中有空格,我們需要使用引號和反斜線進行命令行解析;

  示例:

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."


[root@node1 test]# 

  提示:LABEL指令可以使用多次,用於添加更多的元數據信息;

  4、COPY:該指令用於從Docker主機複製文件至創建的鏡像文件中;語法格式 COPY <src> … <dest> 或 COPY [“<src>”,… “<dest>”];<src>表示要複製的源文件或目錄,支持使用通配符;<dest>表示目標路徑,即正在創建的image的文件系統路徑;建議為<dest>使用絕對路徑,否則,COPY指定則以WORKDIR為其起始路徑;注意:在路徑中有空白字符時,通常使用第二種格式;

  文件複製準則

  1、<src>必須是Dockerfile所在目錄中的文件,不能是其父目錄中的文件;

  2、如果<src>是目錄,則其內部文件或子目錄會被遞歸複製,但<src>目錄自身不會被複制;

  3、如果指定了多個<src>,或在<src>中使用了通配符,則<dest>必須是一個目錄,且必須以“/”結尾;

  4、如果<dest>事先不存在,它會被自動創建,則包括其父目錄路徑;

  示例:

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

COPY html /var/www/html/

COPY test.html /tmp/
[root@node1 test]# tree
.
├── Dockerfile
├── html
│   ├── test1.html
│   └── test2.html
└── test.html

1 directory, 4 files
[root@node1 test]# 

  測試:我們基於上面的Dockerfile構建鏡像,看看構建好的鏡像是否把對應文件都複製到對應目錄下了?

[root@node1 test]# ls
Dockerfile  html  test.html
[root@node1 test]# pwd
/root/test
[root@node1 test]# docker build . -t myimg:v0.1
Sending build context to Docker daemon  5.632kB
Step 1/6 : FROM busybox:latest
latest: Pulling from library/busybox
d9cbbca60e5f: Pull complete 
Digest: sha256:836945da1f3afe2cfff376d379852bbb82e0237cb2925d53a13f53d6e8a8c48c
Status: Downloaded newer image for busybox:latest
 ---> 78096d0a5478
Step 2/6 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Running in c311afd3d522
Removing intermediate container c311afd3d522
 ---> bf9b966a914b
Step 3/6 : LABEL version="1.0"
 ---> Running in 2ffb577afa08
Removing intermediate container 2ffb577afa08
 ---> 5dd2e4b3f594
Step 4/6 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Running in 489165bad668
Removing intermediate container 489165bad668
 ---> 994f06ff65f8
Step 5/6 : COPY html /var/www/html/
 ---> f2b46094d9a9
Step 6/6 : COPY test.html /tmp/
 ---> c78c7188f804
Successfully built c78c7188f804
Successfully tagged myimg:v0.1
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.1                c78c7188f804        8 seconds ago       1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# 

  提示:可以看到docker在讀dockerfile時會把一條指令啟動一容器來執行,然後每一條指令都會構建成一層鏡像;如果我們本地倉庫中沒有我們指定的基礎鏡像,它默認會從dockerhub倉庫中去把指定鏡像拖到本地;docker build命令是用來基於dockerfile來製作鏡像的命令,其中-t表示指定我們生成的鏡像的標籤信息;

  運行我們剛才製作好的鏡像,看看我們的指定的目錄和文件是否都複製到指定鏡像中的目錄中去了?

[root@node1 test]# docker run --name test --rm -it myimg:v0.1 /bin/sh
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # ls /var/www/html/
test1.html  test2.html
/ # ls /tmp/
test.html
/ # cat /var/www/html/test1.html 
this is test1 html
/ # cat /var/www/html/test2.html 
this is test2 html
/ # cat /tmp/test.html 
this is test dir html
/ # exit
[root@node1 test]# 

  提示:可以看到我們以剛才製作的鏡像運行成容器,容器對應目錄有我們指定複製的文件;
  5、ADD:該指令類似於COPY指令,ADD支持使用TAR文件和URL路徑;語法格式 ADD <src> … <dest> 或 ADD [“<src>”,… “<dest>”];如果<src>為URL且<dest>不以/結尾,則<src>指定的文件將被下載並直接被創建為<dest>;如果<dest>以/結尾,則URL指定的文件將被直接下載並保存為<dest>/<filename>;如果<src>是一個本地系統上的壓縮格式的tar文件,它將被展開為一個目錄,其行為類似於“tar -x”命令;然而,通過URL獲取到的tar文件將不會自動展開;如果<src>有多個,或其間接或直接使用了通配符,則<dest>必須是一個以/結尾的目錄路徑;如果<dest>不以/結尾,則其被視作一個普通文件,<src>的內容將被直接寫入到<dest>;

  示例:

[root@node1 test]# ls
Dockerfile  html  nginx-1.19.0.tar.gz  test.html
[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

COPY html /var/www/html/

COPY test.html /tmp/

ADD http://nginx.org/download/nginx-1.18.0.tar.gz /usr/src/

ADD nginx-1.19.0.tar.gz /tmp/
[root@node1 test]# 

  提示:以上Dokcerfile里指定把當前目錄下的nginx-1.19.0.tar.gz 添加到鏡像中的/tmp/下,並且會把nginx-1.19.0.tar.gz展開成一個目錄;而對於

ADD http://nginx.org/download/nginx-1.18.0.tar.gz /usr/src/這條指令它會把指定url的文件下載到/usr/src/,但是不會tar文件展開;

  測試:把上面dockerfile編譯成鏡像,看看對應目錄中的文件是否都展開了?

[root@node1 test]# docker build . -t myimg:v0.2
Sending build context to Docker daemon   1.05MB
Step 1/8 : FROM busybox:latest
 ---> 78096d0a5478
Step 2/8 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> bf9b966a914b
Step 3/8 : LABEL version="1.0"
 ---> Using cache
 ---> 5dd2e4b3f594
Step 4/8 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 994f06ff65f8
Step 5/8 : COPY html /var/www/html/
 ---> Using cache
 ---> f2b46094d9a9
Step 6/8 : COPY test.html /tmp/
 ---> Using cache
 ---> c78c7188f804
Step 7/8 : ADD http://nginx.org/download/nginx-1.18.0.tar.gz /usr/src/
Downloading   1.04MB/1.04MB
 ---> 23f47a028853
Step 8/8 : ADD nginx-1.19.0.tar.gz /tmp/
 ---> 6ba2693b3084
Successfully built 6ba2693b3084
Successfully tagged myimg:v0.2
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.2                6ba2693b3084        27 seconds ago      8.54MB
myimg               v0.1                c78c7188f804        25 minutes ago      1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker run --name test --rm -it myimg:v0.2 /bin/sh
/ # ls /usr/src/
nginx-1.18.0.tar.gz
/ # ls /tmp
nginx-1.19.0  test.html
/ # exit
[root@node1 test]# 

  提示:從上面的信息可以看到,在容器中的/usr/src/目錄下nginx-1.18.0.tar.gz並沒有展開,而在/tmp/下卻把niginx-1.19.0.tar.gz展開為nginx-1.19.0;從build的過程來看,除開後面的ADD過程,其他過程都很快,並且明確告訴我們使用了cache,則意味着在本地倉庫中有的鏡像層,默認是共享的;

  6、WORKDIR:該指令用於設定Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和ADD指令的工作目錄;一個Dockerfile中可以多次使用WORKDIR來指定當前WORKDIR指令到下一個WORKDIR指令之間的指令的工作目錄;語法格式WORKDIR <dirpath>;在Dockerfile文件中,WORKDIR指令可出現多次,其路徑也可以為相對路徑,不過,其是相對此前一個WORKDIR指令指定的路徑,另外,WORKDIR也可調用由ENV指定定義的變量;

  示例:

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

WORKDIR /var/www/

COPY html html/

COPY test.html /tmp/

WORKDIR /usr/

ADD http://nginx.org/download/nginx-1.18.0.tar.gz src/

ADD nginx-1.19.0.tar.gz /tmp/
[root@node1 test]# 

  提示:我們在dockerfile中指定了workdir的路徑后,其後的指令就可以以上一個workdir指定的路徑 為基準後面使用相對路徑;

  測試:製作鏡像運行容器,看看是否把對應文件都添加到容器的指定目錄里?

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

WORKDIR /var/www/

COPY html html/

COPY test.html /tmp/

WORKDIR /usr/

ADD http://nginx.org/download/nginx-1.18.0.tar.gz src/

ADD nginx-1.19.0.tar.gz /tmp/
[root@node1 test]# 
[root@node1 test]# docker build . -t myimg:v0.3
Sending build context to Docker daemon   1.05MB
Step 1/10 : FROM busybox:latest
 ---> 78096d0a5478
Step 2/10 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> bf9b966a914b
Step 3/10 : LABEL version="1.0"
 ---> Using cache
 ---> 5dd2e4b3f594
Step 4/10 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 994f06ff65f8
Step 5/10 : WORKDIR /var/www/
 ---> Running in 623002a4c418
Removing intermediate container 623002a4c418
 ---> 7e6898b36dd5
Step 6/10 : COPY html html/
 ---> 57ff5a48a104
Step 7/10 : COPY test.html /tmp/
 ---> f66a1bf4040a
Step 8/10 : WORKDIR /usr/
 ---> Running in b905ff1b4529
Removing intermediate container b905ff1b4529
 ---> 1fdceb9a6bfd
Step 9/10 : ADD http://nginx.org/download/nginx-1.18.0.tar.gz src/
Downloading   1.04MB/1.04MB
 ---> 92868df29f5b
Step 10/10 : ADD nginx-1.19.0.tar.gz /tmp/
 ---> 68161c3beb90
Successfully built 68161c3beb90
Successfully tagged myimg:v0.3
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.3                68161c3beb90        11 seconds ago      8.54MB
myimg               v0.2                6ba2693b3084        18 minutes ago      8.54MB
myimg               v0.1                c78c7188f804        43 minutes ago      1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker run --name test --rm -it myimg:v0.3 /bin/sh
/usr # pwd
/usr
/usr # ls 
sbin  src
/usr # ls src/
nginx-1.18.0.tar.gz
/usr # ls /tmp/
nginx-1.19.0  test.html
/usr # ls /var/www/
html
/usr # exit
[root@node1 test]#

  提示:可以看到我們製作的鏡像啟動為容器后,默認是我們指定的最後一個workdir的路徑;

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

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

  示例:使用環境變量指定workdi的路徑;

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ENV webhome="/var/www/" src_home="/usr/"


WORKDIR $webhome

COPY html html/

COPY test.html /tmp/

WORKDIR $src_home

ADD http://nginx.org/download/nginx-1.18.0.tar.gz src/

ADD nginx-1.19.0.tar.gz /tmp/
[root@node1 test]#

  提示:我們在原有的Dockerfile中添加了ENV webhome=”/var/www/” src_home=”/usr/” 表示添加兩個環境變量,而這兩個環境的變量的值分別是“/var/www/” 和“/usr” 這樣一來,在後面workdir引用該環境變量時 就會把對應的值給替換過去;dockerfile中變量引用同shell中的變量引用一樣都是使用$符合引用變量;

  測試:利用上面的Dockerfile編譯成鏡像,然後運行成容器,看看我們在dockerfile中定義的環境變量是否能夠被容器所引用?

[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.3                68161c3beb90        34 minutes ago      8.54MB
myimg               v0.2                6ba2693b3084        53 minutes ago      8.54MB
myimg               v0.1                c78c7188f804        About an hour ago   1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker build . -t myimg:v0.4
Sending build context to Docker daemon   1.05MB
Step 1/11 : FROM busybox:latest
 ---> 78096d0a5478
Step 2/11 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> bf9b966a914b
Step 3/11 : LABEL version="1.0"
 ---> Using cache
 ---> 5dd2e4b3f594
Step 4/11 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 994f06ff65f8
Step 5/11 : ENV webhome="/var/www/" src_home="/usr/"
 ---> Running in 82738c50a595
Removing intermediate container 82738c50a595
 ---> 9f089c14778f
Step 6/11 : WORKDIR $webhome
 ---> Running in 3e9d7d4276bf
Removing intermediate container 3e9d7d4276bf
 ---> 94e17268d7ea
Step 7/11 : COPY html html/
 ---> cf3be18998db
Step 8/11 : COPY test.html /tmp/
 ---> 3b81cb058412
Step 9/11 : WORKDIR $src_home
 ---> Running in 70478cb9d405
Removing intermediate container 70478cb9d405
 ---> e0d8ab9331f4
Step 10/11 : ADD http://nginx.org/download/nginx-1.18.0.tar.gz src/
Downloading   1.04MB/1.04MB
 ---> b4e546989783
Step 11/11 : ADD nginx-1.19.0.tar.gz /tmp/
 ---> df3d040b5766
Successfully built df3d040b5766
Successfully tagged myimg:v0.4
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.4                df3d040b5766        4 minutes ago       8.54MB
myimg               v0.3                68161c3beb90        41 minutes ago      8.54MB
myimg               v0.2                6ba2693b3084        59 minutes ago      8.54MB
myimg               v0.1                c78c7188f804        About an hour ago   1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker run --name test --rm -it myimg:v0.4 /bin/sh
/usr # pwd
/usr
/usr # ls /var/www/
html
/usr # ls /usr/src/
nginx-1.18.0.tar.gz
/usr # 

  提示:可以看到我們指定的ENV環境變量是能夠被WORKDIR說引用

  7、ENV:用於為鏡像定義所需的環境變量,並可被Dockerfile文件中位於其後的其它指令(如ENV、ADD、COPY等)所調用;調用格式為$variable_name或${variable_name};語法格式ENV <key> <value> 或 ENV <key>=<value> …;第一種格式中,<key>之後的所有內容均會被視作其<value>的組成部分,因此,一次只能設置一個變量;第二種格式可用一次設置多個變量,每個變量為一個”<key>=<value>”的鍵值對,如果<value>中包含空格,可以以反斜線(\)進行轉義,也可通過對<value>加引號進行標識;另外,反斜線也可用於續行;定義多個變量時,建議使用第二種方式,以便在同一層中完成所有功能;

  示例:

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ENV web_home="/var/www/html/"

COPY html ${web_home}


[root@node1 test]# 

  提示:上面Dockerfile設置了環境變量web_home=”/var/www/html” ,後面的COPY引用時直接使用${web_home}即可;COPY html ${web_home}表示把html目錄中的文件複製到web_home這個變量所指定的目錄中,即/var/www/html/;

  測試:編譯成鏡像,看看是否把對應html目錄下的文件複製到/var/www/html/目錄下了?

[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.4                df3d040b5766        17 minutes ago      8.54MB
myimg               v0.3                68161c3beb90        54 minutes ago      8.54MB
myimg               v0.2                6ba2693b3084        About an hour ago   8.54MB
myimg               v0.1                c78c7188f804        2 hours ago         1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker build . -t myimg:v0.5
Sending build context to Docker daemon   1.05MB
Step 1/6 : FROM busybox:latest
 ---> 78096d0a5478
Step 2/6 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> bf9b966a914b
Step 3/6 : LABEL version="1.0"
 ---> Using cache
 ---> 5dd2e4b3f594
Step 4/6 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 994f06ff65f8
Step 5/6 : ENV web_home="/var/www/html/"
 ---> Running in 4c136d790dd0
Removing intermediate container 4c136d790dd0
 ---> 90e193e4e810
Step 6/6 : COPY html ${web_home}
 ---> e21e9479b0a7
Successfully built e21e9479b0a7
Successfully tagged myimg:v0.5
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.5                e21e9479b0a7        4 seconds ago       1.22MB
myimg               v0.4                df3d040b5766        17 minutes ago      8.54MB
myimg               v0.3                68161c3beb90        54 minutes ago      8.54MB
myimg               v0.2                6ba2693b3084        About an hour ago   8.54MB
myimg               v0.1                c78c7188f804        2 hours ago         1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker run --name test --rm -it myimg:v0.5 /bin/sh
/ # ls /var/www/html/
test1.html  test2.html
/ # exit
[root@node1 test]# 

  提示:可以看到我們打包的鏡像運行成容器后,ENV指定的環境變量目錄里有html目錄下的所有文件;

  8、ARG:該指令用於編譯階段可以使用docker build –build-arg向dockerfile里ARG 指定的變量傳遞值;語法格式ARG <name>[=<default value>];該指令可以在dockerfile中使用多次,也可以給定默認變量一個默認的值,在用戶沒有向該變量傳遞值的情況;

  示例:

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home="/var/www/html/"

COPY html ${web_home}


[root@node1 test]# 

  提示:以上Dockerfile 指定了web_home變量的默認值是“/var/www/html/” 如果在我們build階段沒有使用–build-arg來向web_home傳遞值時,它默認就是“/var/www/html/”,如果我們使用了 –build-agr 指定web_home的值后,後面引用web_home變量的值就是我們用–build-arg傳遞給它的值;

  測試:使用–build-arg向web_home傳遞值

[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.5                e21e9479b0a7        14 minutes ago      1.22MB
myimg               v0.4                df3d040b5766        31 minutes ago      8.54MB
myimg               v0.3                68161c3beb90        About an hour ago   8.54MB
myimg               v0.2                6ba2693b3084        About an hour ago   8.54MB
myimg               v0.1                c78c7188f804        2 hours ago         1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker build . --build-arg web_home="/usr/share/www/html/" -t myimg:v0.6
Sending build context to Docker daemon   1.05MB
Step 1/6 : FROM busybox:latest
 ---> 78096d0a5478
Step 2/6 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> bf9b966a914b
Step 3/6 : LABEL version="1.0"
 ---> Using cache
 ---> 5dd2e4b3f594
Step 4/6 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 994f06ff65f8
Step 5/6 : ARG web_home="/var/www/html/"
 ---> Running in d8697abf9206
Removing intermediate container d8697abf9206
 ---> 6abb65dab341
Step 6/6 : COPY html ${web_home}
 ---> 385cba27c288
Successfully built 385cba27c288
Successfully tagged myimg:v0.6
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.6                385cba27c288        6 seconds ago       1.22MB
myimg               v0.5                e21e9479b0a7        15 minutes ago      1.22MB
myimg               v0.4                df3d040b5766        32 minutes ago      8.54MB
myimg               v0.3                68161c3beb90        About an hour ago   8.54MB
myimg               v0.2                6ba2693b3084        About an hour ago   8.54MB
myimg               v0.1                c78c7188f804        2 hours ago         1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker run --name test --rm -it myimg:v0.6 /bin/sh
/ # ls /usr/share/www/html/
test1.html  test2.html
/ # exit
[root@node1 test]# 

  提示:可以看到我們傳遞進去的路徑下有html目錄下的兩個網頁文件;這說明通過–build-arg 選項可以向Dockerfile里ARG指定指定的變量傳遞值的;

  示例:引用變量給定變量默認值

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}


[root@node1 test]# 

  提示:引用變量給定變量默認值的方式同shell中的用法一樣;${web_home:-“/data/htdoc/”} 表示如果web_home這個變量的值未設定或者為空時,就使用默認的值“/data/htdoc/”這個值,如果設置了,那麼設置的是什麼值就是什麼值;

  測試:不使用–build-arg向web_home傳遞值,看看web_home是否會拿到默認值?

[root@node1 test]# docker build . -t myimg:v0.7
Sending build context to Docker daemon   1.05MB
Step 1/6 : FROM busybox:latest
 ---> 78096d0a5478
Step 2/6 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> bf9b966a914b
Step 3/6 : LABEL version="1.0"
 ---> Using cache
 ---> 5dd2e4b3f594
Step 4/6 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 994f06ff65f8
Step 5/6 : ARG web_home
 ---> Running in 9d98f8e3a1f0
Removing intermediate container 9d98f8e3a1f0
 ---> f164cc3e24ad
Step 6/6 : COPY html ${web_home:-"/data/htdoc/"}
 ---> 4bedea2590b7
Successfully built 4bedea2590b7
Successfully tagged myimg:v0.7
[root@node1 test]# docker run --name test --rm -it myimg:v0.7 /bin/sh
/ # ls /
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var
/ # ls /data/
htdoc
/ # ls /data/htdoc/
test1.html  test2.html
/ # exit
[root@node1 test]# 

  提示:可以看到不想web_home傳遞值,默認就是使用我們給設定的默認值

  測試:向web_home傳遞值,看看是否還會使用默認值呢?

[root@node1 test]# docker build . --build-arg web_home=/web/html/ -t myimg:v0.9
Sending build context to Docker daemon   1.05MB
Step 1/6 : FROM busybox:latest
 ---> 78096d0a5478
Step 2/6 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> bf9b966a914b
Step 3/6 : LABEL version="1.0"
 ---> Using cache
 ---> 5dd2e4b3f594
Step 4/6 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 994f06ff65f8
Step 5/6 : ARG web_home
 ---> Using cache
 ---> f164cc3e24ad
Step 6/6 : COPY html ${web_home:-"/data/htdoc/"}
 ---> 4b6993911e6d
Successfully built 4b6993911e6d
Successfully tagged myimg:v0.9
[root@node1 test]# docker run --name test --rm -it myimg:v0.9 /bin/sh          
/ # ls /
bin   dev   etc   home  proc  root  sys   tmp   usr   var   web
/ # ls /web/
html
/ # ls /web/html/
test1.html  test2.html
/ # exit
[root@node1 test]# 

  提示:可以看到通過–build-arg傳遞了web_home的值為“/web/htm/”后,默認的“/data/htdoc/”的值就不會生效;

  9、VOLUME:用於在image中創建一個掛載點目錄,以掛載Docker host上的卷或其它容器上的卷;語法格式 VOLUME <mountpoint> 或 VOLUME [“<mountpoint>”];如果掛載點目錄路徑下此前的文件存在,docker run命令會在卷掛載完成后將此前的所有文件複製到新掛載的卷中;

  示例:

[root@node1 test]# cat Dockerfile
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}

VOLUME ${web_home:-"/data/htdoc/"}


[root@node1 test]# 

  提示:volume是指定鏡像里的掛載點,如果該掛載點裏面原來是有文件存在,在使用docker run 時,用-v指定把宿主機上的某個目錄掛載到該掛載點時,默認會把原來有的文件複製新掛載的卷中;這裏還需要特被說一下,這裏指定卷是在鏡像內部創建一個掛載點,運行成容器還需要我們手動的用-v去指定把那個目錄掛載到該掛載點,如果不指定默認就是docker-managed 類型的卷;

  測試:編譯成鏡像,然後運行成容器,看看我們指定的卷宗的文件會不會被覆蓋掉?是否會有文件存在?

[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v0.9                4b6993911e6d        21 minutes ago      1.22MB
myimg               v0.7                4bedea2590b7        23 minutes ago      1.22MB
myimg               v0.6                385cba27c288        30 minutes ago      1.22MB
myimg               v0.5                e21e9479b0a7        46 minutes ago      1.22MB
myimg               v0.4                df3d040b5766        About an hour ago   8.54MB
myimg               v0.3                68161c3beb90        2 hours ago         8.54MB
myimg               v0.2                6ba2693b3084        2 hours ago         8.54MB
myimg               v0.1                c78c7188f804        2 hours ago         1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker build . -t myimg:v1.0
Sending build context to Docker daemon   1.05MB
Step 1/7 : FROM busybox:latest
 ---> 78096d0a5478
Step 2/7 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> bf9b966a914b
Step 3/7 : LABEL version="1.0"
 ---> Using cache
 ---> 5dd2e4b3f594
Step 4/7 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 994f06ff65f8
Step 5/7 : ARG web_home
 ---> Using cache
 ---> f164cc3e24ad
Step 6/7 : COPY html ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> 4bedea2590b7
Step 7/7 : VOLUME ${web_home:-"/data/htdoc/"}
 ---> Running in 34cad9ca1f79
Removing intermediate container 34cad9ca1f79
 ---> 9554284e4bba
Successfully built 9554284e4bba
Successfully tagged myimg:v1.0
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v1.0                9554284e4bba        2 minutes ago       1.22MB
myimg               v0.9                4b6993911e6d        24 minutes ago      1.22MB
myimg               v0.7                4bedea2590b7        26 minutes ago      1.22MB
myimg               v0.6                385cba27c288        33 minutes ago      1.22MB
myimg               v0.5                e21e9479b0a7        49 minutes ago      1.22MB
myimg               v0.4                df3d040b5766        About an hour ago   8.54MB
myimg               v0.3                68161c3beb90        2 hours ago         8.54MB
myimg               v0.2                6ba2693b3084        2 hours ago         8.54MB
myimg               v0.1                c78c7188f804        2 hours ago         1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker run --name test --rm -it myimg:v1.0 /bin/sh
/ # ls /
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var
/ # ls /data/htdoc/
test1.html  test2.html
/ # [root@node1 test]# 
[root@node1 test]# docker container inspect test -f {{.Mounts}}
[{volume f3409b021267bd27d58c4b16fb1bc0dae77d4f92f7b5e6255f4ed26a6c27e459 /var/lib/docker/volumes/f3409b021267bd27d58c4b16fb1bc0dae77d4f92f7b5e6255f4ed26a6c27e459/_data /data/htdoc local  true }]
[root@node1 test]# ll /var/lib/docker/volumes/f3409b021267bd27d58c4b16fb1bc0dae77d4f92f7b5e6255f4ed26a6c27e459/_data
total 8
-rw-r--r-- 1 root root 19 May 31 01:51 test1.html
-rw-r--r-- 1 root root 19 May 31 01:51 test2.html
[root@node1 test]# 

  提示:可以看到運行成容器后,默認會把我們指定卷掛載成docker-managed類型的卷;同時我們也看到了對應掛載點上目錄會把原有文件複製到現volume中;

  測試:手動指定宿主機目錄掛載到掛載點,文件是否還會存在呢?

[root@node1 ~]# mkdir /work
[root@node1 ~]# docker run --name test1 --rm -it -v /work:/data/htdoc/ myimg:v1.0 /bin/sh 
/ # ls /data/htdoc/
/ # [root@node1 ~]# ls /work/
[root@node1 ~]# 

  提示:手動指定掛載關係,它就不會把原有目錄下的文件複製到現掛載目錄中了;也就是說,人為手動指定掛載關係,宿主機上的的目錄會覆蓋容器內掛載點下的文件;只有docker自身管理的掛載的卷才會把原目錄下的文件複製到現掛載目錄里;

  10、EXPOSE:用於為容器打開指定要監聽的端口以實現與外部通信(暴露端口);語法格式 EXPOSE <port>[/<protocol>] [<port>[/<protocol>] …]; <protocol>用於指定傳輸層協議,可為tcp或udp二者之一,默認為TCP協議;EXPOSE指令可一次指定多個端口;

  示例:

[root@node1 test]# cat Dockerfile 
FROM busybox:latest

MAINTAINER "qiuhom <qiuhom@linux-1874.com>"

LABEL version="1.0"

LABEL description="this is test file \ that label-values can span multiple lines."

ARG web_home

COPY html ${web_home:-"/data/htdoc/"}

VOLUME ${web_home:-"/data/htdoc/"}

EXPOSE 80/tcp 443/tcp


[root@node1 test]# 

  提示:以上dockerfile中暴露了tcp的80和443端口;在dockerfile中使用EXPOSE暴露端口,在運行成容器時,如果不使用-P來暴露端口,容器里的端口還是暴露不出來;這裏只是build階段明確說明要暴露80和443,而運行成容器我們需要使用-P來把build暴露的端口暴露出來;

  測試:運行時不使用-P暴露端口,看看是否能夠把80和443暴露出來呢?

[root@node1 test]# docker build . -t myimg:v1.1
Sending build context to Docker daemon   1.05MB
Step 1/8 : FROM busybox:latest
 ---> 78096d0a5478
Step 2/8 : MAINTAINER "qiuhom <qiuhom@linux-1874.com>"
 ---> Using cache
 ---> bf9b966a914b
Step 3/8 : LABEL version="1.0"
 ---> Using cache
 ---> 5dd2e4b3f594
Step 4/8 : LABEL description="this is test file \ that label-values can span multiple lines."
 ---> Using cache
 ---> 994f06ff65f8
Step 5/8 : ARG web_home
 ---> Using cache
 ---> f164cc3e24ad
Step 6/8 : COPY html ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> 4bedea2590b7
Step 7/8 : VOLUME ${web_home:-"/data/htdoc/"}
 ---> Using cache
 ---> 9554284e4bba
Step 8/8 : EXPOSE 80/tcp 443/tcp
 ---> Running in 8d7d5b4aab94
Removing intermediate container 8d7d5b4aab94
 ---> 79c118ea9eb3
Successfully built 79c118ea9eb3
Successfully tagged myimg:v1.1
[root@node1 test]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimg               v1.1                79c118ea9eb3        4 seconds ago       1.22MB
myimg               v1.0                9554284e4bba        24 minutes ago      1.22MB
myimg               v0.9                4b6993911e6d        46 minutes ago      1.22MB
myimg               v0.7                4bedea2590b7        48 minutes ago      1.22MB
myimg               v0.6                385cba27c288        55 minutes ago      1.22MB
myimg               v0.5                e21e9479b0a7        About an hour ago   1.22MB
myimg               v0.4                df3d040b5766        About an hour ago   8.54MB
myimg               v0.3                68161c3beb90        2 hours ago         8.54MB
myimg               v0.2                6ba2693b3084        2 hours ago         8.54MB
myimg               v0.1                c78c7188f804        3 hours ago         1.22MB
busybox             latest              78096d0a5478        2 weeks ago         1.22MB
[root@node1 test]# docker run --name test --rm -it myimg:v1.0 /bin/sh                        
/ # [root@node1 test]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
ca4cea9a6f58        myimg:v1.0          "/bin/sh"           15 seconds ago      Up 15 seconds                           test
[root@node1 test]# docker container port test
[root@node1 test]# 

  提示:可以看到運行時,我們不使用-P來暴露端口是把容器內部端口暴露不出來的;

  示例:使用-P來暴露dockerfile里定義的端口

[root@node1 test]# docker run --name test --rm -it -P myimg:v1.1 /bin/sh
/ # [root@node1 test]# docker container port test
443/tcp -> 0.0.0.0:32768
80/tcp -> 0.0.0.0:32769
[root@node1 test]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                                           NAMES
a78f6cebc0de        myimg:v1.1          "/bin/sh"           19 seconds ago      Up 19 seconds       0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp   test
[root@node1 test]# 

  提示:可以看到使用-P就可以在運行時把build階段定義的暴露端口全部給暴露出來;

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

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

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

一個工業級、跨平台、輕量級的 tcp 網絡服務框架:gevent_網頁設計公司

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

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

作為公司的公共產品,經常有這樣的需求:就是新建一個本地服務,產品線作為客戶端通過 tcp 接入本地服務,來獲取想要的業務能力。

與印象中動輒處理成千上萬連接的 tcp 網絡服務不同,這個本地服務是跑在客戶機器上的,Win32 上作為開機自啟動的 windows 服務運行;

Linux 上作為 daemon 在後台運行。總的說來就是用於接收幾個產品進程的連接,因此輕量化是其最重要的要求,在這個基礎上要能兼顧跨平台就可以了。

其實主要就是 windows,再兼顧一點兒 linux。

 

考察了幾個現有的開源網絡框架,從 ACE 、boost::asio 到 libevent,都有不盡於人意的地方:

a) ACE:太重,只是想要一個網絡框架,結果它扒拉扒拉一堆全提供了,不用還不行;

b) boost::asio:太複雜,牽扯到 boost 庫,並且引入了一堆 c++ 模板,需要高版本 c++ 編譯器支持;

c) libevent:這個看着不錯,當時確實用這個做底層封裝了一版,結果發版后發現一個比較致命的問題,導致在防火牆設置比較嚴格的機器上初始化失敗,這個後面我會詳細提到。

 

其它的就更不用說了,之前也粗略看過陳碩的 muddo,總的感覺吧,它是基於其它開源框架不足地方改進的一個庫,有相當可取的地方,但是這個改進的方向也主要是解決更大併發、更多連接,不是我的痛點,所以沒有繼續深入研究。

 

好了,與其在不同開源框架之間糾結,不如自己動手寫一個。

反正我的場景比較固定,不用像它們那樣面面俱,我給自己羅列了一些這個框架需要支持基本的功能:

1)同步寫、異步讀;

2)可同時監聽多路事件,基於 1)這裏只針對異步 READ 事件(包含連接進入、連接斷開),寫數據是同步的,因而不需要處理異步 WRITE 事件;

3)要有設置一次性和周期性定時器的能力 (業務決定的);

4)不需要處理信號 (windows 上也沒信號這一說,linux 自己搞搞 sigaction 就好啦);

……

 

雖然這個框架未來只會運行在用戶的單機上,但是我不希望它一出生就帶有性能缺陷,所以性能平平的 select 沒能進入我的法眼,我決定給它裝上最強大的心臟:

Windows 平台: iocp

Linux 平台:epoll

 

ok,從需求到底層技術路線,貌似都講清楚了,依照 libevent 我給它取名為 gevent,下面我們從代碼級別看下這個框架是怎麼簡化 tcp 服務搭建這類工作的。

首先看一下這個 tcp 服務框架的 sample:

svc_handler.h

 1 #include "EventBase.h"
 2 #include "EventHandler.h"
 3 
 4 class GMyEventBase : public GEventBase
 5 {
 6 public:
 7     GEventHandler* create_handler (); 
 8 }; 
 9 
10 
11 class svc_handler : public GJsonEventHandler
12 {
13 public:
14     virtual ~svc_handler () {}
15     virtual void on_read_msg (Json::Value const& val); 
16 };

epoll_svc.cpp

 1 #include <stdio.h>
 2 #include "svc_handler.h"
 3 #include <signal.h>
 4 
 5 GMyEventBase g_base; 
 6 GEventHandler* GMyEventBase::create_handler () 
 7 {
 8     return new svc_handler; 
 9 }
10 
11 void sig_int (int signo)
12 {
13     printf ("%d caught\n", signo); 
14     g_base.exit (1); 
15     printf ("exit ok\n"); 
16 }
17 
18 int main (int argc, char *argv[])
19 {
20     if (argc < 2)
21     {
22         printf ("usage: epoll_svc port\n"); 
23         return -1; 
24     }
25 
26     unsigned short port = atoi (argv[1]);
27 
28 #ifndef WIN32
29     struct sigaction act; 
30     act.sa_handler = sig_int; 
31     sigemptyset(&act.sa_mask);   
32     act.sa_flags = SA_RESTART; 
33     if (sigaction (SIGINT, &act, NULL) < 0)
34     {
35         printf ("install SIGINT failed, errno %d\n", errno); 
36         return -1; 
37     }
38     else
39         printf ("install SIGINT ok\n"); 
40 #endif
41 
42     // to test small message block
43     if (g_base.init (/*8, 10*/) < 0)
44         return -1; 
45 
46     printf ("init ok\n"); 
47     do
48     {
49         if (!g_base.listen (port))
50         {
51             g_base.exit (0); 
52             printf ("exit ok\n"); 
53             break; 
54         }
55 
56         printf ("listen ok\n"); 
57         g_base.run (); 
58         printf ("run  over\n"); 
59     } while (0); 
60 
61     g_base.fini (); 
62     printf ("fini ok\n"); 
63 
64     g_base.cleanup (); 
65     printf ("cleanup ok\n"); 
66     return 0; 
67 }

 

這個服務的核心是 GMyEventBase 類,它使用了框架中的 GEventBase 類,從後者派生而來,

只改寫了一個 create_handler 接口來提供我們的事件處理對象 svc_handler,它是從框架中的 GEventHandler 派生而來,

svc_handler 只改寫了一個 on_read_msg 來處理 Json 格式的消息輸入。

 

程序的運行就是分別調用 GMyEventBase(實際上是GEventBase)  的 init / listen / run / fini / cleaup 方法。

而與業務相關的代碼,都在 svc_handler 中處理:

svc_handler.cpp

 1 #include "svc_handler.h"
 2 
 3 void svc_handler::on_read_msg (Json::Value const& val)
 4 {
 5     int key = val["key"].asInt (); 
 6     std::string data = val["data"].asString (); 
 7     printf ("got %d:%s\n", key, data.c_str ()); 
 8 
 9     Json::Value root; 
10     Json::FastWriter writer; 
11     root["key"] = key + 1; 
12     root["data"] = data; 
13 
14     int ret = 0;
15     std::string resp = writer.write(root); 
16     resp = resp.substr (0, resp.length () - 1); // trim tailing \n
17     if ((ret = send (resp)) <= 0)
18         printf ("send response failed, errno %d\n", errno); 
19     else 
20         printf ("response %d\n", ret); 
21 }

 

它期待 Json 格式的數據,並且有兩個字段 key(int) 與 data (string),接收數據后將 key 增 1 后返回給客戶端。

再來看下客戶端 sample:

clt_handler.h

 1 #include "EventBaseAR.h"
 2 #include "EventHandler.h"
 3 
 4 class GMyEventBase : public GEventBaseWithAutoReconnect
 5 {
 6 public:
 7     GEventHandler* create_handler (); 
 8 }; 
 9 
10 
11 class clt_handler : public GJsonEventHandler
12 {
13 public:
14     virtual ~clt_handler () {}
15 #ifdef TEST_TIMER
16     virtual bool on_timeout (GEV_PER_TIMER_DATA *gptd); 
17 #endif
18     virtual void on_read_msg (Json::Value const& val); 
19 };

 

epoll_clt.cpp

  1 #include <stdio.h>
  2 #include "clt_handler.h"
  3 #include <signal.h>
  4 
  5 //#define TEST_READ
  6 //#define TEST_CONN
  7 //#define TEST_TIMER
  8 
  9 GMyEventBase g_base; 
 10 GEventHandler* GMyEventBase::create_handler () 
 11 {
 12     return new clt_handler; 
 13 }
 14 
 15 
 16 int sig_caught = 0; 
 17 void sig_int (int signo)
 18 {
 19     sig_caught = 1; 
 20     printf ("%d caught\n", signo); 
 21     g_base.exit (0); 
 22     printf ("exit ok\n"); 
 23 }
 24 
 25 void do_read (GEventHandler *eh, int total)
 26 {
 27     char buf[1024] = { 0 }; 
 28     int ret = 0, n = 0, key = 0, err = 0;
 29     char *ptr = nullptr; 
 30     while ((total == 0 ||  n++ < total) && fgets (buf, sizeof(buf), stdin) != NULL)
 31     {
 32         // skip \n
 33         buf[strlen(buf) - 1] = 0; 
 34         //n = sscanf (buf, "%d", &key); 
 35         key = strtol (buf, &ptr, 10); 
 36         if (ptr == nullptr)
 37         {
 38             printf ("format: int string\n"); 
 39             continue; 
 40         }
 41 
 42         Json::Value root; 
 43         Json::FastWriter writer; 
 44         root["key"] = key; 
 45         // skip space internal
 46         root["data"] = *ptr == ' ' ? ptr + 1 : ptr;  
 47 
 48         std::string req = writer.write (root); 
 49         req = req.substr (0, req.length () - 1); // trim tailing \n
 50         if ((ret = eh->send (req)) <= 0)
 51         {
 52             err = 1; 
 53             printf ("send %d failed, errno %d\n", req.length (), errno); 
 54             break; 
 55         }
 56         else 
 57             printf ("send %d\n", ret); 
 58     }
 59 
 60     if (total == 0)
 61         printf ("reach end\n"); 
 62 
 63     if (!err)
 64     {
 65         eh->disconnect (); 
 66         printf ("call disconnect to notify server\n"); 
 67     }
 68 
 69     // wait receiving thread 
 70     //sleep (3); 
 71     // if use press Ctrl+D, need to notify peer our break
 72 }
 73 
 74 #ifdef TEST_TIMER
 75 void test_timer (unsigned short port, int period_msec, int times)
 76 {
 77     int n = 0; 
 78     GEventHandler *eh = nullptr; 
 79 
 80     do
 81     {
 82         eh = g_base.connect (port); 
 83         if (eh == nullptr)
 84             break;
 85 
 86         printf ("connect ok\n"); 
 87         void* t = g_base.timeout (1000, period_msec, eh, NULL); 
 88         if (t == NULL)
 89         {
 90             printf ("timeout failed\n"); 
 91             break; 
 92         }
 93         else 
 94             printf ("set timer %p ok\n", t); 
 95 
 96         // to wait timer
 97         do
 98         {
 99             sleep (400); 
100             printf ("wake up from sleep\n"); 
101         } while (!sig_caught && n++ < times);
102 
103         g_base.cancel_timer (t); 
104     } while (0); 
105 }
106 #endif
107 
108 #ifdef TEST_CONN
109 void test_conn (unsigned short port, int per_read, int times)
110 {
111 #  ifdef WIN32
112     srand (GetCurrentProcessId()); 
113 #  else
114     srand (getpid ()); 
115 #  endif
116     int n = 0, elapse = 0; 
117     clt_handler *eh = nullptr; 
118 
119     do
120     {
121         eh = (clt_handler *)g_base.connect (port); 
122         if (eh == nullptr)
123             break;
124 
125         printf ("connect ok\n"); 
126 
127         do_read (eh, per_read); 
128 #  ifdef WIN32
129         elapse = rand() % 1000; 
130         Sleep(elapse); 
131         printf ("running  %d ms\n", elapse); 
132 #  else
133         elapse = rand () % 1000000; 
134         usleep (elapse); 
135         printf ("running  %.3f ms\n", elapse/1000.0); 
136 #  endif
137 
138     } while (!sig_caught && n++ < times);
139 }
140 #endif
141 
142 #ifdef TEST_READ
143 void test_read (unsigned short port, int total)
144 {
145     int n = 0; 
146     GEventHandler *eh = nullptr; 
147 
148     do
149     {
150         eh = g_base.connect (port); 
151         if (eh == nullptr)
152             break;
153 
154         printf ("connect ok\n"); 
155         do_read (eh, total); 
156     } while (0); 
157 }
158 #endif
159 
160 int main (int argc, char *argv[])
161 {
162     if (argc < 2)
163     {
164         printf ("usage: epoll_clt port\n"); 
165         return -1; 
166     }
167 
168     unsigned short port = atoi (argv[1]); 
169 
170 #ifndef WIN32
171     struct sigaction act; 
172     act.sa_handler = sig_int; 
173     sigemptyset(&act.sa_mask);   
174     // to ensure read be breaked by SIGINT
175     act.sa_flags = 0; //SA_RESTART;  
176     if (sigaction (SIGINT, &act, NULL) < 0)
177     {
178         printf ("install SIGINT failed, errno %d\n", errno); 
179         return -1; 
180     }
181 #endif
182 
183     if (g_base.init (2) < 0)
184         return -1; 
185 
186     printf ("init ok\n"); 
187 
188 #if defined(TEST_READ)
189     test_read (port, 0); // 0 means infinite loop until user break
190 #elif defined(TEST_CONN)
191     test_conn (port, 10, 100); 
192 #elif defined (TEST_TIMER)
193     test_timer (port, 10, 1000); 
194 #else
195 #  error please define TEST_XXX macro to do something!
196 #endif
197 
198     if (!sig_caught)
199     {
200         // Ctrl + D ?
201         g_base.exit (0); 
202         printf ("exit ok\n"); 
203     }
204     else 
205         printf ("has caught Ctrl+C\n"); 
206 
207     g_base.fini (); 
208     printf ("fini ok\n"); 
209 
210     g_base.cleanup (); 
211     printf ("cleanup ok\n"); 
212     return 0; 
213 }

 

客戶端同樣使用了 GEventBase 的派生類 GMyEventBase 來作為事件循環的核心,所不同的是(注意並非之前例子里的那個類,雖然同名),它提供了 clt_handler 來處理自己的業務代碼。

另外為了提供連接中斷後自動向服務重連的功能,這裏 GMyEventBase 派生自 GEventBase 類的子類 GEventBaseWithAutoReconnect (位於 EventBaseAR.h/cpp 中)。

程序的運行是分別調用 GEventBase 的 init / connect / fini / cleaup 方法以及 GEventHandler 的 send / disconnect 來測試讀寫與連接。

定義宏 TEST_READ 用來測試讀寫;定義宏 TEST_CONN 可以測試連接的通斷及讀寫;定義宏 TEST_TIMER 來測試周期性定時器及讀寫。它們是互斥的。

clt_handler 主要用來異步接收服務端的回送數據並打印:

clt_handler.cpp

 1 #include "clt_handler.h"
 2 
 3 #ifdef TEST_TIMER
 4 extern void do_read (clt_handler *, int); 
 5 bool clt_handler::on_timeout (GEV_PER_TIMER_DATA *gptd)
 6 {
 7     printf ("time out ! id %p, due %d, period %d\n", gptd, gptd->due_msec, gptd->period_msec); 
 8     do_read ((clt_handler *)gptd->user_arg, 1); 
 9     return true; 
10 }
11 #endif
12 
13 void clt_handler::on_read_msg (Json::Value const& val)
14 {
15     int key = val["key"].asInt (); 
16     std::string data = val["data"].asString (); 
17     printf ("got %d:%s\n", key, data.c_str ()); 
18 }

 

這個測試程序可以通過在控制台手工輸入數據來驅動,也可以通過測試數據文件來驅動,下面的 awk 腳本用來製造符合格式的測試數據:

epoll_gen.awk

 1 #! /bin/awk -f
 2 BEGIN {
 3         WORDNUM = 1000
 4         for (i = 1; i <= WORDNUM; i++) {
 5                 printf("%d %s\n", randint(WORDNUM), randword(20))
 6         }
 7 }
 8 
 9 # randint(n): return a random integer number which is >= 1 and <= n
10 function randint(n) {
11         return int(n *rand()) + 1
12 }
13 
14 # randlet(): return a random letter, which maybe upper, lower or number. 
15 function randlet() {
16         return substr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", randint(62), 1)
17 }
18 
19 # randword(LEN): return a rand word with a length of LEN
20 function randword(LEN) {
21         randw=""
22         for( j = 1; j <= LEN; j++) {
23                 randw=randw randlet()
24         }
25         return randw
26 }

 

生成的測試文件格式如下:

238 s0jKlYkEjwE4q3nNJugF
568 0cgNaSgDpP3VS45x3Wum
996 kRF6SgmIReFmrNBcCecj
398 QHQqCrB5fC61hao1BV2x
945 XZ6KLtA4jZTEnhcAugAM
619 WE95NU7FnsYar4wz279j
549 oVCTmD516yvmtuJB2NG3
840 NDAaL5vpzp8DQX0rLRiV
378 jONIm64AN6UVc7uTLIIR
251 EqSBOhc40pKXhCbCu8Ey

 

整個工程編譯的話就是一個 CMakeLists 文件,可以通過 cmake 生成對應的 Makefile 或 VS solution 來編譯代碼:

CMakeLists.txt

 1 cmake_minimum_required(VERSION 3.0)
 2 project(epoll_svc)
 3 include_directories(../core ../include)
 4 set(CMAKE_CXX_FLAGS "-std=c++11 -pthread -g -Wall ${CMAKE_CXX_FLAGS}")
 5 link_directories(${PROJECT_SOURCE_DIR}/../lib)
 6 set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../bin)
 7 
 8 add_executable (epoll_svc epoll_svc.cpp svc_handler.cpp ../core/EventBase.cpp ../core/EventHandler.cpp ../core/log.cpp)
 9 IF (WIN32)
10 target_link_libraries(epoll_svc jsoncpp ws2_32)
11 ELSE ()
12 target_link_libraries(epoll_svc jsoncpp rt)
13 ENDIF ()
14 
15 add_executable (epoll_clt epoll_clt.cpp clt_handler.cpp ../core/EventBase.cpp ../core/EventBaseAR.cpp ../core/EventHandler.cpp ../core/log.cpp)
16 target_compile_definitions(epoll_clt PUBLIC -D TEST_READ)
17 IF (WIN32)
18 target_link_libraries(epoll_clt jsoncpp ws2_32)
19 ELSE ()
20 target_link_libraries(epoll_clt jsoncpp rt)
21 ENDIF ()
22 
23 add_executable (epoll_local epoll_local.cpp)
24 IF (WIN32)
25 target_link_libraries(epoll_local jsoncpp ws2_32)
26 ELSE ()
27 target_link_libraries(epoll_local jsoncpp rt)
28 ENDIF ()

 

 這個項目包含三個編譯目標,分別是 epoll_svc 、epoll_clt 與 epoll_local,其中前兩個可以跨平台編譯,后一個只能在 Linux 平台編譯,用來驗證 epoll 的一些特性。

編譯完成后,首先運行服務端:

>./epoll_svc 1025 

 然後運行客戶端:

>./epoll_clt 1025 < demo

測試多個客戶端同時連接,可以使用下面的腳本:

epoll_start.sh

1 #! /bin/bash
2 # /bin/sh -> /bin/dash, do not recognize our for loop
3 
4 for((i=0;i<10;i=i+1))
5 do
6     ./epoll_clt 1025 < demo &
7     echo "start $i"
8 done

 

可以同時啟動 10 個客戶端。

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

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

通過 Ctrl+C 退出服務端;通過 Ctrl+C 或 Ctrl+D 退出單個客戶端;

通過下面的腳本來停止多個客戶端與服務端:

epoll_stop.sh

1 #! /bin/sh
2 pkill -INT epoll_clt
3 sleep 1
4 pkill -INT epoll_svc

 

 

框架的用法介紹完之後,再簡單遊覽一下這個庫的各層級對外接口。

EventBase.h

  1 #pragma once
  2 
  3 
  4 #include "EventHandler.h" 
  5 #include <string>
  6 #include <map>
  7 #include <mutex> 
  8 #include <condition_variable>
  9 #include "thread_group.hpp"
 10 
 11 #define GEV_MAX_BUF_SIZE 65536
 12 
 13 class GEventBase : public IEventBase
 14 {
 15 public:
 16     GEventBase();
 17     ~GEventBase();
 18 
 19 #ifdef WIN32
 20     virtual HANDLE iocp () const; 
 21 #else
 22     virtual int epfd () const; 
 23 #endif
 24     virtual bool post_timer(GEV_PER_TIMER_DATA *gptd); 
 25     virtual GEventHandler* create_handler() = 0; 
 26 
 27     // thr_num : 
 28     //  =0 - no default thread pool, user provide thread and call run
 29     //  <0 - use max(|thr_num|, processer_num)
 30     //  >0 - use thr_num
 31     bool init(int thr_num = -8, int blksize = GEV_MAX_BUF_SIZE
 32 #ifndef WIN32
 33               , int timer_sig = SIGUSR1
 34 #endif
 35               ); 
 36 
 37     bool listen(unsigned short port, unsigned short backup = 10);
 38     GEventHandler* connect(unsigned short port, GEventHandler* exist_handler = NULL); 
 39     // PARAM
 40     // due_msec: first timeout milliseconds
 41     // period_msec: later periodically milliseconds
 42     // arg: user provied argument
 43     // exist_handler: reuse the timer handler
 44     //
 45     // RETURN
 46     //   NULL: failed
 47     void* timeout(int due_msec, int period_msec, void *arg, GEventHandler *exist_handler);
 48     bool cancel_timer(void* tid); 
 49     void fini();  
 50     void run(); 
 51     void exit(int extra_notify = 0); 
 52     void cleanup(); 
 53 
 54 protected:
 55 #ifdef WIN32
 56     bool do_accept(GEV_PER_IO_DATA *gpid); 
 57     bool do_recv(GEV_PER_HANDLE_DATA *gphd, GEV_PER_IO_DATA *gpid); 
 58     void do_error(GEV_PER_HANDLE_DATA *gphd); 
 59 
 60     int init_socket();
 61     bool issue_accept(); 
 62     bool issue_read(GEV_PER_HANDLE_DATA *gphd);
 63     bool post_completion(DWORD bytes, ULONG_PTR key, LPOVERLAPPED ol); 
 64 
 65 #else
 66     bool do_accept(int fd); 
 67     bool do_recv(conn_key_t key); 
 68     void do_error(conn_key_t key); 
 69 
 70     bool init_pipe(); 
 71     void close_pipe(); 
 72     bool post_notify (char ch, void* ptr = nullptr); 
 73     void promote_leader (std::unique_lock<std::mutex> &guard); 
 74 
 75     GEventHandler* find_by_key (conn_key_t key, bool erase); 
 76     GEventHandler* find_by_fd (int fd, conn_key_t &key, bool erase); 
 77 
 78 #  ifdef HAS_SIGTHR
 79     void sig_proc (); 
 80 #  endif
 81 #endif
 82 
 83     bool do_timeout(GEV_PER_TIMER_DATA *gptd); 
 84 
 85     virtual bool on_accept(GEV_PER_HANDLE_DATA *gphd);
 86     virtual bool on_read(GEventHandler *h, GEV_PER_IO_DATA *gpid); 
 87     virtual void on_error(GEventHandler *h);
 88     virtual bool on_timeout (GEV_PER_TIMER_DATA *gptd); 
 89     
 90 
 91 protected:
 92     volatile bool m_running = false;
 93     int m_thrnum = 0; 
 94     int m_blksize = GEV_MAX_BUF_SIZE; 
 95     std::thread_group m_grp; 
 96     SOCKET m_listener = INVALID_SOCKET;
 97 
 98     std::mutex m_mutex;  // protect m_map
 99     std::mutex m_tlock; // protect m_tmap
100     // timer_t may conflict when new timer created after old timer closed
101     //std::map <timer_t, GEventHandler *> m_tmap; 
102     std::map <GEV_PER_TIMER_DATA*, GEventHandler *> m_tmap; 
103 
104 #ifdef WIN32
105     LPFN_ACCEPTEX m_acceptex = nullptr; 
106     LPFN_GETACCEPTEXSOCKADDRS m_getacceptexsockaddrs = nullptr; 
107     HANDLE m_iocp = NULL; 
108     HANDLE m_timerque = NULL; 
109 
110     std::map<GEV_PER_HANDLE_DATA*, GEventHandler*> m_map; 
111 #else
112     int m_ep = -1; 
113     int m_pp[2]; 
114     int m_tsig = 0; // signal number for timer
115 
116     std::mutex m_lock;   // protect epoll
117     pthread_t m_leader = -1; 
118     std::map<conn_key_t, GEventHandler*> m_map; 
119 #  ifdef HAS_SIGTHR
120     // special thread only cares about signal
121     std::thread *m_sigthr = nullptr; 
122 #  endif
123 #endif
124 };

 

  • init,它在底層啟動 thr_num 個線程來跑 run 方法;每次 IO 的塊緩衝區大小由 blksize 指定;它內部還創建了對應的 iocp 或 epoll 對象,便於之后加入 socket 句柄進行處理。
  • exit,它通知線程池中的所有線程退出等待,windows 上是通過 PostQueuedCompletionStatus,Linux 上是通過在自建的一個 pipe 上寫數據以觸發 epoll 退出(這個 pipe 在 init 中創建並加入 epoll);
  • fini,它在所有工作線程退出后,關閉之前創建的對象,清理事件循環用到的資源;
  • cleanup,它清理之前建立的 fd-handler 映射,清理遺留的處理器並釋放資源;
  • run,它是線程池運行函數,windows 上是通過 GetQueuedCompletionStatus 在 iocp 上等待;在 linux 上是通過 epoll_wait 在 epoll 上等待事件。當有事件產生后,根據事件類型,分別調用 do_accept / on_accept、do_recv / on_read、do_error / on_error 回調來分派事件;
  • listen,創建偵聽 socket 並加入到 iocp 或 epoll 中;
  • connect,連接到遠程服務並將成功連接的 socket 加入到 iocp 或  epoll 中;
  • timeout,設置定時器事件,windows 上是通過 CreateTimerQueueTimer 實現定時器超時;linux 則是通過 timer_create 實現的,都是系統現成的東西,只不過在系統定時器到期后,給對應的 iocp 或 epoll 對象發送了一個通知而已,在 linux 上這個通知機制是上面提到過的 pipe 來實現的,因而有一定延遲,不能指定精度太小的定時器;
  • cancel_timer,取消之前設置的定時器。

 

然後看下 GEventHandler 提供的回調接口,應用可以從它派生並完成業務相關代碼:

EventHandler.h

  1 #pragma once
  2 #include "platform.h"
  3 
  4 #ifdef WIN32
  5 // must ensure <winsock2.h> precedes <widnows.h> included, to prevent winsock2.h conflict with winsock.h
  6 #  include <WinSock2.h>
  7 #  include <Windows.h>
  8 #  include <mswsock.h>  // for LPFN_ACCEPTEX & LPFN_GETACCEPTEXSOCKADDRS later in EventBase.h
  9 #else
 10 #  include <unistd.h> // for close
 11 #  include <sys/socket.h>
 12 #  include <sys/epoll.h>
 13 #  include <sys/time.h>
 14 #  include <netinet/in.h> // for struct sockaddr_in
 15 #  include <arpa/inet.h> // for inet_addr/inet_ntoa
 16 #  include <string.h> // for memset/memcpy
 17 #  include <signal.h>
 18 #endif
 19 
 20 #include <mutex>
 21 #include "jsoncpp/json.h"
 22 
 23 
 24 class GEventHandler; 
 25 struct GEV_PER_TIMER_DATA; 
 26 class IEventBase
 27 {
 28 public:
 29 #ifdef WIN32
 30     virtual HANDLE iocp () const = 0; 
 31 #else
 32     virtual int epfd () const = 0; 
 33 #endif
 34 
 35     virtual void* timeout(int due_msec, int period_msec, void *arg, GEventHandler *exist_handler) = 0; 
 36     virtual bool cancel_timer(void* tid) = 0; 
 37     virtual bool post_timer(GEV_PER_TIMER_DATA *gptd) = 0; 
 38 };
 39 
 40 
 41 #ifdef WIN32
 42 enum GEV_IOCP_OP
 43 {
 44     OP_TIMEOUT = 1, 
 45     OP_ACCEPT,
 46     OP_RECV,
 47 };
 48 #else 
 49 // the purpose of this key is to distinguish different connections with same fd !
 50 // (when connection break and re-established soon, fd may not change but port will change)
 51 struct conn_key_t
 52 {
 53     int fd; 
 54     unsigned short lport; 
 55     unsigned short rport; 
 56 
 57     conn_key_t (int f, unsigned short l, unsigned short r); 
 58     bool operator< (struct conn_key_t const& rhs) const; 
 59 }; 
 60 #endif
 61 
 62 
 63 struct GEV_PER_HANDLE_DATA
 64 {
 65     SOCKET so;
 66     SOCKADDR_IN laddr;
 67     SOCKADDR_IN raddr;
 68 
 69 #ifndef WIN32
 70     conn_key_t key () const; 
 71 #endif
 72 
 73     GEV_PER_HANDLE_DATA(SOCKET s, SOCKADDR_IN *l, SOCKADDR_IN *r); 
 74     virtual ~GEV_PER_HANDLE_DATA(); 
 75 };
 76 
 77 struct GEV_PER_IO_DATA
 78 {
 79     SOCKET so;
 80 #ifdef WIN32
 81     GEV_IOCP_OP op;
 82     OVERLAPPED ol;
 83     WSABUF wsa;         // wsa.len is buffer length
 84     DWORD bytes;        // after compeleted, bytes trasnfered
 85 #else
 86     char *buf; 
 87     int len; 
 88 #endif
 89 
 90     GEV_PER_IO_DATA(
 91 #ifdef WIN32
 92             GEV_IOCP_OP o, 
 93 #endif
 94             SOCKET s, int l); 
 95     virtual ~GEV_PER_IO_DATA(); 
 96 };
 97 
 98 struct GEV_PER_TIMER_DATA
 99 #ifdef WIN32
100        : public GEV_PER_IO_DATA
101 #endif
102 {
103     IEventBase *base; 
104     int due_msec; 
105     int period_msec; 
106     void *user_arg;
107     bool cancelled;
108 #ifdef WIN32
109     HANDLE timerque; 
110     HANDLE timer; 
111 #else
112     timer_t timer; 
113 #endif
114 
115     GEV_PER_TIMER_DATA(IEventBase *base, int due, int period, void *arg
116 #ifdef WIN32
117             , HANDLE tq);
118 #else
119             , timer_t tid); 
120 #endif
121 
122     virtual ~GEV_PER_TIMER_DATA(); 
123     void cancel (); 
124 };
125 
126 class GEventHandler
127 {
128 public:
129     GEventHandler();
130     virtual ~GEventHandler();
131 
132     GEV_PER_HANDLE_DATA* gphd(); 
133     GEV_PER_TIMER_DATA* gptd(); 
134     bool connected();
135     void disconnect(); 
136     void clear(); 
137     SOCKET fd(); 
138 
139     int send(char const* buf, int len);
140     int send(std::string const& str);
141     
142     virtual bool reuse();
143     virtual bool auto_reconnect();
144     virtual void arg(void *param) = 0;
145     virtual void reset(GEV_PER_HANDLE_DATA *gphd, GEV_PER_TIMER_DATA *gptd, IEventBase *base);
146     virtual bool on_read(GEV_PER_IO_DATA *gpid) = 0;
147     virtual void on_error(GEV_PER_HANDLE_DATA *gphd); 
148     // note when on_timeout called, handler's base may cleared by cancel_timer, use gptd->base instead if it is not null.
149     virtual bool on_timeout(GEV_PER_TIMER_DATA *gptd) = 0; 
150     virtual void cleanup(bool terminal);
151     void close(bool terminal);
152 
153 protected:
154     GEV_PER_HANDLE_DATA *m_gphd = nullptr; 
155     GEV_PER_TIMER_DATA *m_gptd = nullptr; 
156     IEventBase *m_base = nullptr;
157     // us so instead of m_gphd, 
158     // as the later one may destroyed during using..
159     SOCKET m_so;
160 };
161 
162 // a common handler to process json protocol.
163 class GJsonEventHandler : public GEventHandler
164 {
165 public:
166     //virtual void on_read();
167     virtual void arg(void *param);
168     virtual void reset(GEV_PER_HANDLE_DATA *gphd, GEV_PER_TIMER_DATA *gptd, IEventBase *base);
169     virtual bool on_read(GEV_PER_IO_DATA *gpid);
170     virtual void on_read_msg(Json::Value const& root) = 0;
171     virtual bool on_timeout(GEV_PER_TIMER_DATA *gptd);
172     virtual void cleanup(bool terminal);
173 
174 protected:
175     // protect m_stub to prevent multi-entry
176 #ifdef HAS_ET
177     std::mutex m_mutex; 
178 #endif
179 
180     std::string m_stub;
181 };

 

這裏主要有兩個類,GEventHandler 處理通用的基於流的數據;GJsonEventHandler 處理基於 json 格式的數據。

前者需要重寫 on_read 方法來處理塊數據;後者需要重寫 on_read_msg 方法來處理 json 數據。

目前 json 的解析是通過 jsoncpp 庫完成的,這個庫本身是跨平台的(本 git 庫僅提供 64 位 Linux 靜態鏈接庫及 VS2013 的 32 位 Release 版本 Windows 靜態庫)。

svc_handler 與 clt_handler  均從 GJsonEventHandler 派生。

如果有新的流格式需要處理 ,只需要從 GEventHandler 類派生新的處理類即可。

 

除了讀取連接上的數據,還有其它一些重要的回調接口,列明如下:

  • on_read,連接上有數據到達;
  • on_error,連接斷開;
  • on_tmeout,定時器事件;
  • ……

如果有新的事件需要處理 ,也可以在這裏擴展。

最後看下 GEventBaseWithAutoReconnect 提供的與自動重連相關的接口:

EventBaseAR.h

 1 #pragma once
 2 
 3 
 4 #include "EventBase.h"
 5 #include <thread>
 6 
 7 #define GEV_RECONNECT_TIMEOUT 2 // seconds
 8 #define GEV_MAX_RECONNECT_TIMEOUT 256 // seconds
 9 
10 class GEventBaseWithAutoReconnect : public GEventBase
11 {
12 public:
13     GEventBaseWithAutoReconnect(int reconn_min = GEV_RECONNECT_TIMEOUT, int reconn_max = GEV_MAX_RECONNECT_TIMEOUT);
14     ~GEventBaseWithAutoReconnect();
15 
16     bool do_connect(unsigned short port, void *arg);
17     GEventHandler* connector(); 
18 
19 protected:
20     virtual void on_error(GEventHandler *h);
21     virtual bool on_timeout(GEV_PER_TIMER_DATA *gptd);
22 
23     virtual void on_connect_break(); 
24     virtual bool on_connected(GEventHandler *app);
25 
26 protected:
27     void do_reconnect(void *arg);
28 
29 protected:
30     unsigned short m_port; 
31     GEventHandler* m_app;
32     GEventHandler* m_htimer; 
33     void* m_timer;
34     int m_reconn_min; 
35     int m_reconn_max; 
36     int m_reconn_curr;
37 };

 

其實比較簡單,只比 GEventBase 類多了一個  do_connect 方法,來擴展 connect 不能自動重連的問題。

底層的話,是通過定時器來實現指數後退重連算法的。

 

最後,如果你還是感到雲里霧裡的,可以參考一下下面的類結構圖:

 

 

黑色標註的是框架提供的類,紅色是服務端派生的類,藍色是客戶端派生的類,其實 GMyEventBase 的唯一作用就是將 svc_handler 與 clt_handler 分別引入各自的框架中,

所以用戶的關注點主要還是在派生自己的 GEventHandler 類,並在其中的回調接口中處理數據就可以了。

 

 

後記

這個框架已經應用到我司的公共產品中,併為數個 tcp 服務提供底層支撐,經過百萬級別用戶機器驗證,運行穩定性還是可以的,所以當得起“工業級”這三個字。

 

前面在說到開源庫的選型時還留了一個口子沒有交待,這裏一併說下。

其實最早的重構版本是使用 libevent 來實現的,但是發現它在 windows 上使用的是低效的 select,

而且為了增加、刪除句柄,它又使用了一種 self-pipe-trick 的技巧,簡單說來的就是下面的代碼序列:

listen (listen_fd, 1); 
……
connect (connect_fd, &addr, size); 
……
accept_fd = accept (listen_fd, &addr, &size); 

 

在缺乏 pipe 調用的 win32 環境製造了一個 socket 自連接,從而進行一些通知。

這一步是必要的,如果不能成功連接就會導致整個 libevent 初始化失敗,從而運行不起來。

不巧的是,在一些 windows 機器上(約佔用戶總量 10%),由於防火牆設置嚴格,上述 listen 與 connect 調用可以成功,

但是 accept 會失敗返回,從而導致整個服務退出 (防火牆會嚴格禁止不在白名單上偵聽的端口的連接)。

對於已知端口,可以通過在防火牆上設置白名單來避免,但是對於這種隨機 listen 的端口,真的是太難了,基本無解。

 

回頭考察了一下 asio,windows 上使用的是 iocp,自然沒有這個自連接;

ACE 有多種實現可供選擇,如果使用  ACE_Select_Reactor / ACE_TP_Reactor 是會有這個自連接,

但是你可以選擇其它實現,如基於 WaitForMultipleEvents 的 ACE_WFMO_Reactor(最大隻支持 62 個句柄,放棄),

或基於 iocp 的 ACE_Proactor (前攝式,與反應式在編程上稍有不同,更接近於 asio)就沒有這個自連接。

 

再說的深一點,其實公司最早的網絡庫使用的就是基於 boost 的 asio,大量的使用了 c++ 模板,

有時候產生了一些崩潰,但是根據 dump 完全無法定位崩潰點(各種冗長的模板展開名稱),

導致了一些頑固的已知 bug 一直找不到崩潰點而無法解決(雖然量不大),所以才有了要去重新選型網絡庫以及後來這一系列的東西。

 

本來一開始我是想用 ACE 的,因為我讀過這個庫的源碼,對裏面所有的東西都非常熟悉,

但是看看 ACE 小 5 MB 的 dll 尺寸,還是放棄了(產品本身安裝包也就這麼大吧),

對於一個公司底層的公共組件,被各種產品攜帶,需要嚴格控制“體重”

(後來聽說 ACE 按功能拆分了代碼模塊,你只需要選自己依賴的部分即可,不過我還沒有試過)。

 

使用這個庫代替之前的 boost::asio 后,我還有一個意外收穫,就是編譯出來的 dll 尺寸明顯小了很多,700 K -> 500 K 的樣子,看來所謂模板膨脹是真有其事……

 

最後奉上 gevent 的 github 鏈接,歡迎有相同需求的小夥伴前來“復刻” :

https://github.com/goodpaperman/gevent

 

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

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

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

繽智、XR-V都顫抖!這款1.5T小鋼炮SUV來到有多強?_包裝設計

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

XX-18。XX萬之間,屆時以下車型將會是其最大競爭對手:繽智(XRV)新車指導價:12。88-18。98萬2014年10月25日上市的廣汽本田繽智,已經上市銷售了兩年,目前還在以每個月1萬5千台銷量持續在買。雖然1。5T/1。8L這種組合在目前來看依然主流,但以推動1。

前言

馬自達靠家轎、MpV多款車型佔據市場的場面,但最近幾年已經還在大賣的車型已經變得越來越少了,隨着CX-4以及昂克賽拉的改款,以及以下這台即將來臨的CX-3,不僅會讓馬自達變得好轉,而且會讓目前沉悶的小型SUV市場更加活躍起來!

CX-3其實是以馬自達2基礎而來,可以說兩者幾乎是同平台產物,所以操控性乃至空間上CX-3都與馬自達2沒什麼區別,關鍵在於CX-3是一款緊湊型SUV,因此馬自達很有可能為其配備1.5T/2.0L兩套動力系統,兩者都為創馳藍天技術的發動機,1.5T應該是基於柴油發動機,最大105匹馬力,而2.0升自吸車型則有望配備156匹馬力。當然還有目前在阿特茲上基於發動機和底盤整合控制的GVC系統(G-Vectoring Control)。

外觀將繼續沿用目前家族化的前臉–魂動設計,除了必須有的魂動紅之外,還將新增有鋼鐵灰、雲母藍和躍雪白三種車身顏色。同時還會在高配車型上配備LED大燈以及215/50的18寸輪轂,

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

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

並將會加大塞入隔音棉以利於NVH隔音提升車身安靜度。(現款昂克賽拉以及阿特茲都比同級車型有更多道路信息)

其他配置方面,將會配備能自動剎停的主動防護系統。根據目前CX-5的16.98-24.58萬以及CX-4的14.08-21.58萬價格區間,CX-3很有可能會定位在12.XX-18.XX萬之間,屆時以下車型將會是其最大競爭對手:

繽智(XRV)

新車指導價:12.88-18.98萬

2014年10月25日上市的廣汽本田繽智,已經上市銷售了兩年,目前還在以每個月1萬5千台銷量持續在買。雖然1.5T/1.8L這種組合在目前來看依然主流,但以推動1.2噸-1.39噸的車身來說在後半段加速時確實有些力不從心,對於一向追求黑科技的本田來說,更新確實迫在眉睫。

北京現代ix25

新車指導價:11.98-18.68萬

ix25上市之初確實成為一道亮麗的小鮮肉,特別是都金秀賢的代言讓其一下子就其紅火起來,不過一樣是2014年10月10日上市的ix25目前月銷量僅有7700多以及大幅落後於XRV了更別說繽智。雖然2016款新增了兩台160匹馬力的1.6T車型,不過也難力挽狂瀾,最大原因是售價稍高的緣故吧。

今年6月21日上市的馬自達CX-4,未曾上市就受到了多方追捧,可惜4個月過去了,累計銷量僅2萬1千台,雖然同比馬自達CX-5月銷量上升了1000多,可是比起目前已經存在的緊湊SUV廣闊市場來說還是力量薄弱,CX-3其實已經在日本、台灣局部地區開始銷售了,但並非是全球車型,馬自達的自閉症啥時候才能治呢?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

Caviar 推出奢侈黄金版 AirPods Max 、PlayStation 5 ,前者要價 300 萬元起_網頁設計

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

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

月初我們才剛分享過 CAVIAR 這間來自俄羅斯的客製化品牌的 iPhone 12 Pro 鈦合金潛行款,隨著今年下半年兩款別具話題的產品 AirPods Max 與 PlayStation 5(PS5)開賣後, CAVIAR 這次也將腦筋動到了這兩款產品上。
一如既往, CAVIAR 選用了黃金和鱷魚皮作為客製化版本的材質,當然價格也不是一般消費者能負擔得起的程度,光是相對便宜的黃金版 AirPods Max 就要價約 300 萬元新台幣。

Caviar 推出奢侈黄金版 AirPods Max 、PlayStation 5 ,前者要價 300 萬元起

Caviar 近期宣布將於 2021 年推出度有純金的 AirPods Max ,售價高達 108,000 美元(約合新台幣 304.2 萬元)。據悉這款 AirPods Max 黃金版的耳罩部分將原本的鋁金屬材質改為 18K 金打造,頭帶部分則以鱷魚皮材質取代原本的網狀材質。

CAVIAR 將推出掰色和黑色兩種顏色的鱷魚皮搭配以黃金為主體的 AirPods Max 。雖然 CAVIAR 官方未公布黃金版本的 AirPods Max 機身重量有多重,不過回顧已經不算輕盈的標準版 AirPods Max  重量就有 384.8 公克,黃金版本的重量肯定沈重不少。

耳罩部分包括控制按鈕和旋鈕都以鍍金處理,更提升了整體的奢華感:

價格方面, AirPods Max 黃金版建議售價為 108,000 美元(約合新台幣 304.2 萬元),大約可等同 164 組標準版 AirPods Max 的價位。

與 AirPods Max 建議售價相當的 PlayStation 5 ,也同樣在 CAVIAR 這次黃金版客製化的名單中,黃金版 PS5 一樣採用 18K 黃金材質打造,總共使用了大約 20 公斤的黃金:

與以往 CAVIAR 客製化產品相同, PS5 黃金版主要在外殼上進行重新設計,不僅在機身換上兩塊 3D 打造的 18K 純金實心外殼:

CAVIAR 設計師表示這款 Golden Rock 的 PS5 靈感源自於金礦石的獨特幾何形狀和優美優美的岩石輪廓:

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

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

除此之外,在 Dual Sense 遊戲控制器除了黃金還有加入鱷魚皮等材質:

PS5 黃金版目前 CAVIAR 尚未公開價格,不過光是以黃金價格來估算就能推測他價格將非常驚人。

圖片/消息來源:CAVIAR

延伸閱讀:
iOS 應用小技巧:自動停止播放影片/音樂,睡前喜愛滑手機的人都必學!

Caviar 發表 iPhone 12 Pro 鈦合金「潛行款」,取消所有相機還加三倍價賣

您也許會喜歡:

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

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

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

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

340 萬台,據報 PS5 破 PlayStation 首月出貨最高紀錄_貨運

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

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

雖然距離前代 PlayStation 4 突破億台的銷量還有很長一段路要走,不過身為 2020 年 Sony 端出的最強次世代主機,PlayStation 5 還是以各種開箱攻佔了各大媒體與社群平台的熱門話題。現在據報也已經以首四週 340 萬台的好銷售達到破紀錄的銷售成績。繼續閱讀 340 萬台,據報 PS5 破 PlayStation 首月出貨最高紀錄報導內文。

340 萬台,據報 PS5 破 PlayStation 首月出貨最高紀錄

缺貨到讓人又愛又恨的 PlayStation 5,很多也好奇到底這次的銷量成績與同樣都受到極高歡迎的先前幾代有沒有更強的銷售力道?是說,儘管在極度缺貨的日本,銷量表現似乎有所抑制(但我們還是搶先開箱日本版的 PlayStation 5 了)不過 Sony 這台次世代遊戲主機的全球銷量部分,現在已經從 Digitimes 關於出貨量的報導裡,間接揭露了 PlayStation 5 全球首四週的銷量,已直破以往自家主機的紀錄,來到 340 萬台的好成績。

是說,這篇報導主要劃的重點是在於緊接著在 2021 年初,得益於產線的改進年產量預估可達 1,680 萬至 1,800 萬台;1 月起將預留供貨給等得焦急的亞洲消費者(太好惹)。但對於 Sony 來說,這銷量相較於以往大受歡迎的 PS4(2018 年 8 月開賣至 12 月初,達到 210 萬台)應該真的讓他們有點措手不及吧,必須馬上修正提供更高的產量對應。

必須說,看來在這全人類都因為疫情悶得發慌的年代裡, PlayStation 5 出現的時間點也算是剛好對應了許多人居家娛樂的需求。

引用來源

延伸閱讀:

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

PlayStation 5 第一手搶先開箱!絕對是現今最值得入手的遊戲主機

iPhone 12 Pro Max 深度實拍體驗:ProRAW 讓 iPhone 踏入專業

您也許會喜歡:

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

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

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

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式