LOGO
首页 网站广场 站长动态 活跃度榜 审核查询 逛逛好站 留言交流 提交申请 关于本站

站长动态

站长动态所展示的是已加入好站网成员站长文章
共同步 2420 篇博文
(每2小时更新一次)
JN
入驻第1年
如何設定反向代理?在同一台主機同時跑好幾個服務(Nginx Proxy Manager / Reverse Proxy)
今天我要教你如何使用 docker & docker-compose 來設定好一個 reverse proxy server,而且還有一個漂漂亮亮的 GUI! 安裝 安裝 docker & docker-compose 我這邊 server 的 OS 是 Ubuntu 20.04,所以用 apt 來安裝。 1 2 3 sudo apt update sudo apt install docker.io sudo apt install docker-compose 把目前的 user 加到 docker user group 中(這樣才不用一直用 sudo 來下 docker 的指令)。 1 2 3 sudo groupadd docker sudo usermod -aG docker $USER newgrp docker 來跑個 Hello World 來確認一下,如果像下面畫面這樣的話就代表 docker 已經成功安裝。 1 docker run hello-world 安裝 Nginx Proxy Manager 建立一個資料夾並在底下建立 docker-compose.yml。 1 2 3 mkdir docker/nginx-proxy-manager -p cd docker/nginx-proxy-manager vim docker-compose.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 ## docker-compose.yml version: '3' services: app: image: 'jc21/nginx-proxy-manager:latest' restart: unless-stopped ports: - '80:80' - '81:81' - '443:443' volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt 接著就可以用 docker-compose 啓動並讓他在背景執行。 1 docker-compose up -d 初始設定 打開放火牆 如果你是租雲端主機,記得去把 port 80、81、443 都打開!(我這邊使用的是 Oracle Cloud) 80 和 443 是 HTTP 和 HTTPS;81 則是用來進入 Web GUI。 設定 Nginx Proxy Manager 確定防火牆沒問題的話就可以直接從瀏覽器到 <你的 ip>:81,進入 Nginx Proxy Manager 的管理頁面。 預設的帳號密碼是: Email: admin@example.com Password: changeme 第一次登入就會要求你修改帳號密碼。 點進 Hosts -> Proxy Hosts 就能看到目前的 Proxy 狀況,這邊還沒設定當然是什麼都沒有。 設定 DNS 這邊我們把 domain 和所有 subdomain 指到主機上。我的 domain 是在 NameCheap 買的,請到你購買 domain 的地方設定。 這時候我們就能透過 domain 訪問主機,而不是輸入 IP。接著再以剛剛改好的帳號密碼登入。 第一個 Proxy Host 接著我們來設定第一個 proxy 指向 Nginx Proxy Manager,也把 SSL 還有憑證處理好,這樣才能確保連線是 HTTPS 的。 成功登入了之後進到 Host -> Proxy Host,點 Add Proxy Host。 我這邊把 proxy.giveanornot.com 代理到 localhost:81(也就是現在的這個 proxy manager)。 在這邊選 Request a new SSL Certificate 的話,Nginx Proxy Manager 就會根據我們設定的 Domain 自動幫我們申請、更新憑證。怎麼樣,超方便的吧! 完成之後點 Save。如果設定都正確的話,直接到 proxy.<你的 domain>,就會看到 proxy manager 的登入畫面。這時候網址列有顯示鎖頭,代表說現在的連線是有經過 HTTPS 加密的。 初始設定到這邊完成,我們就可以開始使用了! 外部連結 接著我們來設定外部連結的 proxy,我以我自己在 Netlify 上的部落格爲例。 這邊記得要順便更新 blog 的預設網址,以及 Netlify 的 custom domain。 設定完成後我們就也可以透過 proxy 瀏覽部落格了。 首頁、重新導向 但是,在這個時候如果到 giveanornot.com,你會發現首頁還沒有被設定好。 我這邊想要把 bio.giveanornot.com 指到我放在 GitHub Pages 的 Link In Bio,並且把首頁 giveanornot.com 自動導向 bio.giveanornot.com。 graph LR U(User)--->|Request| A A[giveanornot.com] --->|Redirect| B[bio.giveanornot.com] B --->|Proxy| C[GitHub Pages]一樣我們新增一個 Proxy Host 並且取得憑證。 然後到 Hosts -> Redirect Hosts 新增,把 giveanornot.com 導到 bio.giveanornot.com。 也要記得在 GitHub Pages 這邊新增 custom domain。 此時到 giveanornot.com 的時候就會被自動導向 bio.giveanornot.com。 代理到主機上的其他 docker container 接這我們來設定 server 上其他服務的 proxy,我這邊以一個 httpd 來示範,你可以把 httpd 換成是你其他任何的 container。 把 httpd 加到 Nginx Proxy Manager 的網路裡面(我這邊用的是 portainer)。 然後新增一個 Proxy Host,這邊的 hostname 就輸入 container name(docker ps 可以查詢)。 這樣就可以透過代理存取 server 上的其他服務了! 至於非容器化的服務嘛……目前沒有找到好的解決方法:( References https://nginxproxymanager.com/guide/#quick-setup
JN
入驻第1年
從 100 天挑戰到失敗:原因、成效、價值
100 Days Of Code Challenge 在 這一篇文章 裡面有提到,我在 2021 的 11/08 開始一個爲期 100 天的挑戰。這個挑戰的目標是連續寫 100 天的 code,並且在這期間內成功找到一份滿意的工作。 結果是,這個挑戰只持續到 Day 48。 失敗的原因不僅僅是入伍時間一延再延打亂原本的計劃而已;更多的是我在遭遇變故後,心理一直處在一個不健康的狀態,而我沒有能力發現並有效的處理。在意識到自己失敗後,糟糕的心理狀態,加上當時其他的壓力來源,我無法有效的去面對、檢視、檢討這個挑戰。於是,逃避成了我當時的唯一選擇。不回頭看這個挑戰、不碰任何跟 coding 相關的東西、不去想求職和自我成長的事情——這些是我在那個狀態下,非常消極,但也有效能夠讓我過得比較舒服的方法。 沒有把大目標拆成具體的小目標也是失敗的原因之一。我能很明顯地感覺到:在 Day 27 取得 freeCodeCamp 的 backend certification 後,我失去了一大部分的動力。更準確地說,是少了完成證書裡每一個項目之後,所獲得的那種回饋感。而少了題目的指引,對於接下來要寫什麼也是越來越迷茫。在這種不知道寫什麼 & 回饋感驟降的狀態,挑戰會失敗似乎也是一件合情合理的事情。 失敗了,但獲得的不只是失敗 失敗不代表這次的挑戰一點價值都沒有。事實上,「失敗」只是用來描述沒有達成原本目標的的負面詞彙。也就是說:就算是只差 1% 就成功,只要沒有達到 100% ,都算是失敗。既然如此,我是不是可以反過來說:1% 的失敗,是 99% 的「成功」。 回頭看看我這 48 天做到的事情,最大的項目莫過於取得 freeCodeCamp 的 backend certification。如果我當初把目標設定在取得這個證書,那我這次的挑戰是一次大成功誒!而且,在取得證書的過程、以及拿到證書以後的到 day 48 的這段期間,我還解決了很多以前不曾解決過的問題。 當然我不是想要用把目標拉得很低這種方式來騙自己很成功。只不過,我不想因爲失敗掩埋了那些成果。畢竟,爲了得到那些我也是整整花了 48 天。因此,在這段期間所獲得的任何小結果,無論是失敗的或是成功的,我都應該把它們好好的整理、保存。因爲,那些是我接下來想做任何東西的重要參考資料來源。 花了 48 天換到的 48 天換取一個失敗的結果絕對不是浪費時間。如果把我用到、學到的技術和工具全部列出來,其實還是有點壯觀的。 完成的項目 freeCodeCamp Backend Certification Microservices Timestamp Microservice Request Header Parser Microservice URL Shortener Microservice File Metadata Microservice Web Apps Image search Voting App Book Trading Club Pinterest Clone Nightlife Coordination App Chart the Stock Market express-mongoose-oauth-boilerblate 漢堡! 失敗的項目 Cocktail Hublemon 學會使用的 Library Express.js Vue.js Chart.js Passport.js socket.io axios Bootstrap Sequelize Mongoosee vue-masonry-css 學會使用的軟體、技術 Git PostgreSQL MongoDB NGINX Redis Docker SSH Reverse Tunnel 學會使用的服務 GitHub Heroku AWS EC2 Yelp API Youtube API OAuth(GitHub, Google, Facebook) 整理、放下、解脫 從 Day 49 到現在的每一天,我一直都活在沒有完成挑戰的罪惡感中,因爲我不想承認我的失敗。只有當我下定決心,決定好好地回顧一下到底發生了什麼事情的時候,那膨脹已久的恐懼泡沫才會消除。在我能夠好好面對之後,才能把那些我需要的撿回來、好好地裝箱,我也才能安心地讓這件事情徹底地離開我的思緒。也因爲如此,我才能再開始開始下一個挑戰,不再原地踏步。
JN
入驻第1年
臺科大學長告訴你:四技推甄面試完全攻略
今天我要跟你分享我 2017 年(民 106)在臺科大資工系的推甄面試,並且成功順利錄取的經驗。 這篇會以我自身的經驗出發,和大家分享這次面試經驗的流程、自己的經驗、以及如果可以回到那個時候,我會希望我當時能知道的事情。 臺科大資工系的面試有什麼流程和項目? 我們先來看看臺科大資工系的官網怎麽說: 其中有三個重點: 面試地點在資工系辦進行(但其實是系辦旁的會議室) 面試共有兩站,各五分鐘 不能使用電腦以及投影機 而整體流程則是: 系辦簽名報到 等待 20 分鐘 面試第一站 等待 5 到 10 分鐘 面試第二站 面試結束 有大致的概念之後,接下來再來詳細看看各個階段,以及分別要注意什麼吧~ 六個階段的注意事項 當天報到之前 當天早上的準備工作也是非常重要!好好的規劃的話可以降低突發狀況的機率,也就比較不會緊張啦。我這邊分成 食、衣、行、帶,四個項目: 食 當天按照平常的飲食習慣就可以了,如果平常沒有習慣吃早餐的話不必特別吃,但就建議帶一點高熱量的小零食在身上以防萬一;如果你被安排在下午的話,午餐可以稍微不要吃那麼飽。不過,飲料和水分就適量少攝取一點,避免緊張一直想跑廁所,但如果天氣很熱的話也要小心不要中暑啦 >< 衣 當天的服裝我會建議以 半正式、整潔、樸素即可,我當時有看到一些同學穿正裝打領帶,我覺得是沒有必要甚至有點突兀的。其實資工系並沒有非常在意面試者的服裝,只要不要讓人感覺到太隨便或是太奇怪就可以了。 如果你不清楚所謂的半正式是什麼的話,我會建議以休閒襯衫、素色褲子爲主。鞋子不是很重要,皮鞋、運動鞋、帆布鞋其實都可以,只要不要太高調就好。 另外,頭髮會亂的,鬍渣之類的要記得整理,表示你對面試的重視,也是基本的禮貌。 行 如果你像我一樣不住在臺北或對臺北不熟悉的話,一定要事先規劃好當天如何前往資工系系辦! 我會建議坐臺鐵或是高鐵,因爲下車之後往月臺 高鐵 9 車、臺鐵 11 車的方向,有可以直達捷運的出口。 客運轉運站離捷運站有點距離也容易迷路,除非有自信可以找到路否則不建議! 出了臺鐵、高鐵 B3 出口之後對面就是捷運入口,直接逼悠遊卡或一卡通就可以進站,這時候錢不夠的同學請往左手邊去售票機儲值。 進站之後坐紅線(淡水信義線)往象山方向 => 中正紀念堂轉車 => 直接到走到對面等車 => 綠線(松山新店線)往新店方向 => 公館站下車 => 出 2 號出口。 上電扶梯出站後可以逼卡租 YouBike(要事先註冊綁卡,或者到櫃員機),然後騎進臺大在鹿明堂右轉;或是直走羅斯福路過圓環後左轉到臺科大門口還車(記得再逼卡)。如果不幸沒有車或是下大雨的話也可以走路,大約走 10 分鐘就到了~ 進校門後經過圖書館就可以左轉進 T4 大樓一樓穿堂,烏龜池對面就是電梯,上 5 樓就到資工系辦嘍! 我建議可以比規定的 20 分鐘前再提早 10~30 分鐘去報到,多的時間可以休息一下熟悉環境、逛一下校園、或是看一下小抄,讓自己不會那麼緊張 >< 帶 要帶的東西其實不多: 身份證件(報到用) 悠遊卡(坐車、YouBike) 小抄、考古題(在等的時候可以看) 就這樣!其他就是你平常出門會帶什麼就帶什麼就好了。 另外要提醒的是,除非你有非常特別的理由,不然不需要另外再帶資料給教授看。一來是教授其實面試你沒有什麼時間可以看,二來是發資料的過程也會消耗你不少寶貴的時間。 等待第一站的 20 分鐘 報到完之後,除非你剛好就是各個時段的前幾個,不然一定能遇到面試完出來的人——巴上去問他們問題。裡面有多少教授、誰是白臉誰是黑臉、教授都問了什麼問題……什麼都好。雖然不一定每個人都願意回答你,但儘可能地蒐集到更多資訊對你肯定是有利的。其餘時間可以看看自己帶的資料,在腦袋整理一下等等要說的東西,然後等待你的名字被叫到。 第一站 第一站在大會議室進行,主要是讓教授跟你聊聊天看看你這個人的 學經歷、基本能力、應答能力 ,具體的內容包括: 自我介紹 英文口試 數學口試 專題、經歷 提問 會議室的樣子是四張長桌圍起來,你坐一張,其他大約 68 位教授坐另外三張,整體大約是下面這張圖的 23 倍大。 自我介紹 這個階段要做的是用一到一分半左右的時間讓教授認識你,而重點只有一個:請只說和資工系有關的事情;家庭背景、成長經驗、你的其他興趣什麼的教授一點都不想知道。你該說的是: 你的經歷中有什麼和資工有關聯的地方 你的高中專題做了什麼,過程又是如何,有沒有遇到什麼困難? 你有過什麼其他和資工相關的活動 爲什麼選擇資工系(如果你不是資訊科畢業的話) 我來舉個例子: 各位教授好,我是 OOO,畢業於 OO 學校電子科。我在高中的時候最喜歡的科目是微處理機和單晶片實習,在單晶片實習的課程裡我學會了如何使用 C 語言去控制 8051,也是在這個時候我發現比起硬體、電路,我更喜歡電腦和寫程式,這也是我選擇資工系的原因。 我的專題也和 8051 和 C 語言相關,是用 8951 和兩個 8x8 LED 矩陣做的遊戲機,其中讓我花最多時間的是理解和學習如何使用計時器中斷。 最近在嘗試的事情是想要透過 Wi-Fi 和 ESP8266 控制房間的電燈。目前遇到的難題是:網路上的範例有提到 HTTP Server,但還不清楚那是什麼東西,目前還在慢慢的研究。 以上是我的自我介紹,謝謝各位教授。 當然你的經歷可能沒有這麼精彩,你可以在專題的部分用非常多的篇幅,大約控制在 200 字左右即可。這裡我想特別提醒大家:不用害怕自己做的成果很失敗就不敢說。事實上,不過你有多厲害多屌,在教授的眼中都是小兒科,教授想看到的是: 你遇到困難的時候,你是怎麽試圖解決問題、怎麽尋找有用的資訊,而你在這個過程又有什麼收穫。這是資工系非常需要的一種能力——自學。 英文口試 英文也是資工系重要的能力之一,幾乎所有的說明文件都是用英文寫的。 我當年面試的時候,桌上擺著一張紙,上面有一句英文,口試的內容就是把那句話翻譯成中文。我不太記得那句英文是什麼了,但我記得是有包含 database system 的一句被動句。其實不難,只要你能好好地用通順的中文說出這句話的意思就可以了。 萬一你真的遇到不認識的單字,請大方地說你不太確定某個字的意思,然後把剩下的部分翻譯完畢就好了。 數學口試 在這個階段,教授會問你一個數學問題,通常是排列組合或是機率的問題。因爲你手上不會有紙筆,所以不會是太複雜的問題,主要是想知道你的數學觀念對不對。我當時被問的問題是: 丟兩個骰子,同樣點數的機率是多少 這裡的評分不是只有答對或答錯兩種,和上面一樣,教授想知道你的策略是什麼。這時候你能做的最壞的表現是直接說你不會,或是沉默不語,然後說了一個錯誤的答案。 你不需要一聽到題目就知道如何解題,你需要做的是:你正在想什麼,就同時也說出來。 即便你一開始的策略錯了也沒關係,你可以說你覺得你的想法錯了,然後再開始一個新的策略。通常來說,如果教授聽到你的思路,而你卻一直卡關的話,教授通常都會好心推你一把的。 專題、經歷 到了這個階段,其實這裡就是讓教授和你聊聊天,讓教授有更充足的資訊能夠給你打一個分數。教授通常會針對你的專題還有經歷提問,而你就也按照實際的情況回答就好了,切忌說謊。你可以在自我介紹的時候埋一些短時間說不清楚的事情(線頭),吸引教授的好奇心。而這個階段也是最難準備,最能夠看出你的專題有沒有認真做、你到底有沒有料的階段。 提問 最後教授還可能會問一個問題: 你有什麼想問的問題嗎? 這時候其實就是教授沒梗了,但還是要禮貌地完成面試,如果前面聊得很多的話教授可能不會問這個問題,時間到了面試結束。 這個問題的答案就是:任何你想問的都可以問。當然我的意思不是說可以去問教授有幾個小孩、名下有幾棟房子;但只要任何和資工、學校、今天的面試沾的上一點點邊的都可以問。當然儘量避免網路上很簡單就能找到答案的問題,而這個問題通常也不太會大幅度影響你的分數(除非你很沒禮貌)。我這邊列幾個可以問的問題: 請問大一的課會有早八嗎? 請問剛剛那題數學的正確解答是什麼? 請問我在暑假這段期間可以先自己學什麼東西嗎? 請問大一的計算機程式設計具體課程內容是什麼 我對人工智慧很有興趣,請問教授可以給我一些指引或是建議嗎? 我在專題遇到的問題,請問教授有什麼比較好的解決方案嗎? 如果你想不太到問題的話,可以到 臺科大資工系課程地圖 看一下,你一定可以從這裡面找到你還不懂的東西。 如果沒有意外的話,在你回答出:「沒有問題了,謝謝教授」的時候,第一站在這裡就會結束了。結束的時候請記得禮貌和教授問候,如果有移動到會議室的物品的話也請記得歸位,然後就可以先到外面稍作休息。 第二站前的 5 到 10 分鐘 剛從大會議室走出來,你的心臟肯定還跳得很快。不管你剛剛的表現如何,那都已經是確定的事情,不是你能改變的。 如果讓剛剛的表現影響到你接下來的狀況的話就不好了。你要做的事情是:把自己的注意力放在準備第二站。 你可以看看自己帶的考古題,或是問問從第二站出來的同學他們被考了什麼問題。事實上,第二站能考的問題不多,有非常大的機率你會被問到一模一樣的問題。如果你多問一些同學的話,很有可能就被你問到要拿來考你的那個問題了。所以,儘量在這段短短時間獲得更多資訊,不僅可以提高你對問題的把握度,還能讓你把注意力從剛剛的表現轉移到接下來的第二站。 面試第二站 第二站的主軸是技術面試,也就是考你的專業知識如何。這邊分成兩個階段:自我介紹、白板題。 自我介紹 這邊的自我介紹目的是讓教授瞭解你的背景如何,這樣教授才能挑選適合你的題目。這邊教授不會和你聊你的專題如何,所以篇幅不必太長,只要簡短地敘述自己的背景、專長、經驗,大約在 30 秒到 1 分鐘即可,才不會白白浪費後面的答題時間。 白板題 這裡才是第二站的重頭戲! 這邊教授可能會根據你的長項給你一個題目,請你在白板上作答。 既然是資工系,這邊的題目就是寫程式,通常不會太複雜,只是測驗你有沒有基礎的程式設計能力而已,所以也不會限定你要用什麼語言、什麼方式。我這邊舉幾個題目的例子: 請在白板寫出一段輸出九九乘法表的程式。 請在白板寫出一段輸入爲 N ,輸出費氏數列前 N 項的程式。 請在白板寫出一段輸入爲 N ,輸出 N 的所有因數。 請在白板寫出一段輸入一個矩陣 M,輸出 M 的轉置矩陣的程式。 聽到問題之後,你對地方有一些地方可能還不太清楚,請教授解釋你不清楚的地方是完全沒問題的。請一定要在確認對題目沒有任何問題了,再開始著手解決問題。 與第一站一樣,你有可能需要時間思考,請你邊思考邊把你的想法說出來。 就算你最後無法寫出正確答案,你也還能讓教授知道你還是有程式設計的能力,只是卡在某個關卡而已。 寫完答案的時候,記得順著自己的程式檢查一下,也順便看看有沒有什麼沒加分號、括號的小問題,確定沒有問題之後就可以完成作答。如果有問題的話教授可能會在這個時候告訴你,請你修正你的答案。 這邊提供幾個可以找到題目練習的地方: Zero Judge Codewars LeetCode 如果作答完還有時間的話,教授可能會考你第二題。我當年就被考了一個「請在白板上畫出除五的同步計數器」,可能因爲我是電子科吧……所以如果你不是資訊科的話,還是建議複習一下數位邏輯會比較好。 面試結束 恭喜你撐過了這次的面試!這時候就可以放鬆了。如果你有其他學校要面試的話,就請儘量提早到下一個面試地點;如果沒有的話就可以留在學校休息一下,認識一下其他來面試的同學,他們很有機會會是你之後的同學。也可以到校園裡面走走,看一下教室長什麼樣子、吃看看學餐如何也是不錯的選擇! 重點整理 最後這邊再幫大家統整和補充一下幾個重點: 事先準備 規劃當天的行程、交通方式、衣服怎麽穿;準備好當天要帶的東西;瞭解一下學校或系上的課程和規定,這些都是絕對可以事先就確定並且準備好的。如果因爲這些事情沒做好的話一定會大大影響當天的狀態。 把握必拿的分數 自我介紹、專題探討都是一定會遇到的環節,如果沒有把握好這個部分的話就太可惜了。請一定要事先寫好稿、多順過幾次,也要想想教授可能會問的問題,被問到了要怎麼回答。說話的時候的速度如何、重點該擺在哪裡,這些也一定要事先想好和練習。 提高自己的勝率 資工系能考的題目雖然變化很多,但其實能考的概念不多,只要多做題目就能夠抓到其中的套路。當你把所有類型的題目都做過一次之後,你就會發現那些題目都有類似、可以參考的概念。隨著做過的題目越來越多,就能夠有越多的概念可以參考,解新的題目也就會越來越輕鬆。所以,請多做考古題、多做一些題目讓自己能夠有更高的機率可以會回答出正解。 台風 好的台風能讓教授能整個對談的過程更舒服,教授也能更專注在你說的事情上,也或多或少提高教授對你的認同度。如果你的經歷做得不錯,卻因爲沒辦法好好表達,而被另一個經歷普通但是能和教授侃侃而談的同學比下去,那不是非常的不甘心嗎?所以,不必對教授非常的畢恭畢敬也不要太吊兒郎當。自然、有自信、肯定、沒有冗言贅字,這樣的語氣無論是誰,都會覺得跟你對談是一件舒服的事情。 練習 “Repetition is the mother of skill” - Tony Robbins 如果你怕你表現得不好,那就多做幾次,直到你能夠輕鬆應付爲止;如果一道題你解不出來,那就多做幾道類似的題目,直到你能成功解答。你可以對著自己錄影,看看從別人的視角你的表現如何;也可以找幾個同學互相面試,看看彼此有什麼地方需要改進的。總之,重複和練習,這是兩件絕對不會背叛你的事情。 總結 以上是從一個資工系學生的角度,想要給大家的一些建議,也是我希望我當年就能知道的事情。 祝大家都能順利推甄上想要的學校和科系!
JN
入驻第1年
如何快速地把檔案傳到另外一台電腦上(不用經過雲端硬碟)
如何快速地把檔案傳到另外一台電腦? 隨身碟 這大概是最直覺的作法,不過你必須先找得到你的隨身碟。 如果你很順利地找到你的隨身碟,接下來還要把隨身碟插到電腦 -> 把檔案複製出來 -> 卸載隨身碟 -> 再把隨身碟插到另一台電腦 -> 終於拿到檔案,而且這還不考慮 USB 平均要插 3 次才會成功。 所以除非是要傳非常大的檔案,否則我覺得還是太麻煩了。而且手邊不見得會有 USB 3.0 以上的隨身碟和孔可以用,如果退回到 USB 2.0 的速度的話只有 480Mbps 以下(通常是有 200Mbps 就偷笑了)。 AirDrop 不得不說 AirDrop 是真的非常好用,不過限制也很明顯,那就是傳送和接收端兩邊必須都要是 Apple 的裝置才可以。 雖然也有像是 ShareDrop 這種類似 AirDrop 的服務,不過這類的服務大多是建立在區域網路或是 Wifi 直連上,也就是說,這類方式的另一個限制,就是傳送和接收端兩邊必須要在同一個地方或是在同一個網域內才行。 雲端硬碟 / 通訊軟體 / Email 這幾個服務我覺得比較類似就擺在一起討論了 基本上這些方式都需要登入,甚至要下載特定的軟體,有些也有檔案大小的上限,而且他們本來就不是被設計來傳輸檔案的,所以用起來都會有些彆扭的地方。另外這類服務大多數都會把檔案留在他們的伺服器上,如果你是要傳一些比較敏感的資料(比如說證件、文件翻拍),你有辦法安心的使用嗎? Send Anywhere 其實 Send Anywhere 是蠻好用的服務,你只要選擇好你要傳的檔案後,你就會得到一串數字(登入才能使用連結),然後在另一台電腦上一樣到 Send Anywhere 上輸入剛剛得到的那串數字之後,你就能在另外一台電腦接收到檔案。 不過 Send Anywhere 還是有缺點,就是還是必須要打開瀏覽器才能使用,有些進階功能還需要登入甚至收費。另外其實 Firefox 也曾經提供類似的服務,不過後來爲了防止被用來傳送有害的內容就結束服務了。或許 Send Anywhere 有一天也有可能步上 Firefox Send 的後塵,那麼到時候我們就必須再尋找下一個替代方案了。 SCP / FTP 假如你傳送或接收端的其中一邊是只能使用文字界面的伺服器的話,那你的選擇大概就只有這兩個了。 使用 SCP 的話你需要先在伺服器上設定好 SSH Server,這問題不大,麻煩的是每次要傳東西的時候你都必須要打很長的指令,而且其中還包括像是 IP 和路徑這種超級容易打錯的東西,所以我每次用 SCP 的時候都至少要試兩、三次才會成功。 1 scp -i ~/.ssh/private_key localfile remote_host:/path/remote_file 至於 FTP 的話,架 FTP Server 相比上面的方法都還要麻煩很多,除非是需要讓很多人一起存取檔案的情況,否則 FTP 完全不會是一個好的方法。 救世主 - Croc 廢話不多說,直接上圖 圖片裡面的左邊是我的本機,右邊是我的一台 VPS(伺服器)。我從我的本機傳送了一整個資料夾的內容到 VPS 上只需要各打一行指令就完成了!(雖然只有一個檔案) 發送端在確定發送之後會得到由一個數字和三個英文單字組成的通關密語,在接收端上輸入那段通關密語之後就能接收到檔案,就是這麼簡單! How it works? Croc 使用的是中繼的伺服器來傳輸檔案,也就是你需要一台伺服器來負責轉發傳輸的檔案,不過 Croc 預設就是使用官方提供的伺服器,所以你不用擔心需要自己架一台中繼伺服器啦!你可能會擔心官方的伺服器會不會有一天突然掛掉或是有資安的疑慮,其實架設 Croc 的中繼伺服器也很簡單,這部分 小惡魔 - AppleBOY 已經寫過一篇文章 說明得很清楚了,建議大家可以去他那邊參考。 優點 不需要自己架設任何伺服器也不需要 port 轉發 使用非常容易 使用 PAKE 點對點加密 支援跨平台(Windows, Linux, MacOS) 支援一次傳送多個檔案 意外中斷後可以接續傳輸 優先使用 IPv6 可以使用代理伺服器 Android 上有 第三方 App 限制 傳輸和接收端必須都能正常連接網路 傳輸速度受外網速度限制 不過這兩點都是可以透過自己架 Croc 的中繼伺服器就解決的,我自己是想不太到有其它的缺點了啦。 安裝 官方其實已經有現成的腳本了,你只需要一行指令就能安裝: 1 curl https://getcroc.schollz.com | bash 如果你是用的是 MacOS 的話也可以透過 Homebrew 來安裝: 1 brew install croc 而我現在用的是 Manjaro(基於 Arch Linux),Croc 已經收錄在 Pacman 裡了: 1 sudo pacman -S croc 如果是在 Android 的話我會推薦使用這個 第三方 App。 使用方法 使用方法非常簡單,首先你要在兩邊都安裝好 Croc,接著你只需要在要傳送檔案的那台電腦上輸入 croc ,然後後面接上你要傳送的檔案或者是資料夾,接著他會問你是不是要把這個檔案傳出去,這裡我們直接 Enter 就可以確認,然後你就會得到一個由一個數字和三個英文單字組成的通關密語,就像這樣: 1 2 3 4 5 6 7 $ croc testfile Did you mean to send 'testfile'? (Y/n) Sending 'testfile' (0 B) Code is: 1704-tiger-ticket-report On the other computer run croc 1704-tiger-ticket-report 然後到要接收檔案的那台電腦上輸入 croc,然後後面接上剛剛在另一台電腦得到的通關密語,接著他會問你是不是要接收這些檔案,一樣直接 Enter 確認,就能成功接收到檔案了: 1 2 3 4 5 $ croc 1704-tiger-ticket-report Accept 'testfile' (0 B)? (Y/n) Receiving (<-118.169.227.146:44682) 100% |████████████████████| ( 0/ 1B, ) 你可能會覺得那串通關密語還是太長太難打了,別擔心,這個通關密語是可以自己設定的,只要在傳送的時候加一些參數就可以設定,像是這樣: 1 2 3 4 5 6 $ croc send --code jnyiunn testfile Sending 'testfile' (0 B) Code is: giveanornot On the other computer run croc giveanornot 不過通關密語至少要六個字母或數字,太短的話會直接被拒絕: 1 2 $ croc send --code 12345 testfile 2021/09/25 14:08:48 code is too short 參考資料 以上就是 Croc 的安裝和使用的方法,其實 Croc 還有更多的進階用法,不過我從來都沒有使用過,有需要的話也可以到下面這幾個地方參考: https://github.com/schollz/croc https://schollz.com/blog/croc6/ https://blog.wu-boy.com/2021/02/share-files-between-two-computer-using-croc-tool/ 圖片來源 https://imgflip.com/memegenerator https://unsplash.com/photos/HONJP8DyiSM https://www.xiaoyao.tw/2018/09/LINE-alone-Group.html
JN
入驻第1年
解決部署 Discord.js Bot 到 Heroku 時報錯
錯誤現象 部署 Discord.js Bot 到 Heroku 時報錯: Web process failed to bind to $PORT within 60 seconds of launch 造成原因 當 Heroku 的 app 啓動時,Heroku 會幫你綁一個 port 給你,但你的 bot 並沒有在監聽任何一個 port,對應不上就出錯了。 解決方法 在 node 的專案根目錄新增一個檔案 Procfile 。 在 Procfile 加入下面這一行(如果你的 bot 啓動的指令不同的話也請在這裡一併修改)。 1 worker: npm start 把更動推上 Heroku 回到 Heroku,進到 App > Resources Tab 然後關閉 web 並且把 worker 打開。 這時你的 Bot 應該會重新啓動並且正常運作。
JN
入驻第1年
寫出好的推甄書面備審資料的 5 個重點
今天我要來跟你分享推甄的備審資料到底要怎麼寫,有哪些重點要注意,以及有哪些資源可以利用。 我會以推資工系爲目標出發,主要針對資電、電機類群爲目標,但我想這篇對推工科的學弟妹們都會有幫助。 自我介紹 小弟 2017 年(民 106)應屆畢業,參加統測爆運氣拿了 651 分,推甄錄取臺科大資訊工程系。後來才得知當年該系分發最低錄取分數只有 639.75 ,所以其實我直接登記分發就能上了,不過當年資電群的專業科目比較簡單(也反映在當年最低錄取分數),爲了求保險我還是花錢跑臺北面試一趟了。 以下就是我經歷大學四年後,回頭看看我當年寫的備審資料之後,很多我希望我能夠早點知道的事情。 正文開始 我會說大學的推甄其實很像是去公司面試職缺,都是拿履歷(備審資料)去跟你的主管(教授)換面試的門票。而看你履歷、和你面試的人就是你未來會修到他們的課的那些教授,既然如此,好好想想他們平常都喜歡看怎麼樣的東西就很重要了。 雖然你也是透過網路找到這篇文,但我覺得網絡上很多備審相關的分享看起來很厲害,但我覺得很熱門的那幾篇文章對推資工系來說或許有點不適用(有的甚至還是實體書面的攻略)。而且,你以爲你找到那些是稀世寶典,其實大家都找到一樣的這些資源,寫出來的東西都一樣,教授一次看了好幾十份一樣的東西其實早就疲勞了。 備審要準備哪些東西? 我直接到 第二階段報名系統【練習版】 查看到底需要上傳哪些東西。其實都還蠻貼心的,該寫哪些東西都已經列出來給你了,除非你的經歷很特殊,不然應該都不用煩惱要寫什麼。 其中大部分人的(一)、(二)項應該都不必自己再處理,所以我們接下來就聚焦討論(三)的各個項目。 專題製作學習成果或專業實習科目實習報告 這一項主要是放大家在高二、高三的時候做的專題,或是你一位大神,也可以在這裡放小論文、科展或甚至實習的經驗,總之就是放你在高中這三年做過的最大成果,也會是評分的重點項目。 我想在上專題課的時候,老師就應該會要求大家要寫專題計劃書、報告(結案)書,其實就算你原封不動交上去也不會有太大的問題,畢竟重點還是你的作品如何(笑)。不過,如果你自認作品不如人的話也可以試著修改一下內容,讓整體看起來更完整一些。我認爲論文的結構和內容很值得參考(畢竟教授也是常常在看論文)。 論文可以到 Google Scholar 搜尋,這是 Google 的學術文章搜尋引擎。 一般論文的結構大致如下: 摘要 前言 背景知識及相關研究 研究方法 實驗步驟 實驗結果 結論 未來展望 附錄 當然這是相對完整的論文的結構,大家可以自己斟酌刪減或是增加小項。下面解說一下各個章節要寫哪些東西。 摘要 把你這一整個報告書的內容濃縮,讓讀者可以在一開始對你的專題有一些基礎概念,大概知道瞭解一下 3 點: 爲什麼要做這個專題 怎麼做的 做得如何 如果有時間的話可以整理一下 code 推到 GitHub 上,雖然教授很高機率不會點進去看,但至少也能讓教授知道你會用 Git。 Git 對於資工系來說幾乎是必學的工具之一 前言 寫你爲什麼會想做這個專題,動機是什麼?想解決什麼問題? 背景知識及相關研究 如果你的專題有用到比較少見的硬體或技術,或是用了哪些函式庫,都可以在這裡提及。參考的研究結果、資料也都在這邊解釋。但如果是 Arduino 開發板或是 C 語言這種大家幾乎都有概念的東西則可以選擇省略。 研究方法 這邊是你計劃(但其實就是最終)得到的實作方法,在概念上你怎麽做到的,可以畫一些圖來輔助說明。 例如:這個系統的模組如何互相溝通 實驗步驟 這邊記錄你實作的詳細過程和參數,像是用了哪些資料、設定了哪些參數、電路圖長怎麼樣、甚至是電源用的是多少伏特都可以記錄在這裡。 實驗結果 很簡單,把跑出來的結果貼上來,看看跟你預期的結果有沒有相符,有沒有新的發現。 結論 跟摘要差不多,但著重在達成了什麼成就。 未來展望 這次專題有哪邊做得不滿意,或是這次專題做完發現可以再去研究的新題目。 附錄 所有引用的參考資料,或是太大的表格等等不適合直接放在內文的資料。 自傳及讀書計劃 自傳和讀書計劃其實是同一件事情,差別只是已經發生了還是未來才會發生。這邊的重點是絕對不要把無關的事情寫進來,無關的事情就是和你要申請的科系無關的事。 自傳 拜託,拜託不要再寫什麼「我來自OOO的家庭、從小生活XXXX、爸爸總跟我說¥%&#(&*……」。 教授根本不關心你家裡怎麼樣,你跟這個科系的關聯才是重點。 教授根本不關心你家裡怎麼樣,你跟這個科系的關聯才是重點。 教授根本不關心你家裡怎麼樣,你跟這個科系的關聯才是重點。 可以考慮從爲什麼你會選擇你高中的科系開始(如果你是讀高職),或是這個領域的啓蒙。寫寫你在學習這些東西的時候得到哪些成就、遇到什麼困難、如何解決它們。 或是,可以考慮在這邊的開頭列出一些自己的 Highlight,讓教授一眼就可以知道你的背景是怎麼樣。 其實也可以花一點篇幅寫寫專題的內容,寫寫報告書中沒有寫的–你做專題之後心理上得到的成長和感受。 這邊篇幅其實不設限,畢竟教授不會花非常多心思看這邊,我會覺得大約 7、800 字就很足夠了(聽起來很多但其實寫起來就會發現不難)。 讀書計劃 這邊比起自傳就重要多了,不過大家都不知道怎麽寫,常常都是寫什麼「增進實作能力」、「加強英文能力」。如果都是寫這些空泛的東西的話我會覺得有點可惜,其實這邊有很多東西可以寫的。 爲什麼會寫出空泛的東西?其實就是對未來會碰到的東西沒有什麼概念。 那要怎麽有概念?除了有學長姐可以問以外,這邊有三個資源推薦給大家: 臺科大資工系課程地圖 每個系上應該都有類似的資料,告訴你你的必修課程有哪些,要往某個領域發展的話該選哪些課。極度建議先去找找每個領域的入門課程稍微看一下,看看自己喜不喜歡這些東西,稍微看過的話應該就會對自己未來該學什麼東西有一些概念了。 假設你對人工智慧有興趣,下面這張截圖就幫你把該修的課都列出來了: freeCodeCamp.org Youtube Channel 這個組織有非常多免費、完整的初心者教學,你可以配合 1. 看看你對哪些課程比較有興趣 【密技】台灣軟體業業界現況詳解、軟體工程師指南 若你是就業導向的話,這篇巴哈的文也很值得稍微看一下,雖然對高中畢業的你來說有點遙遠,不過,看得更遠的話下一步會走得更踏實一點。 至於英文能力,資工系平常需要看大量的英文的資料,班上有好幾個同學本來看不太懂英文,最後都有能力直接看英文的說明文件,這方面不必太擔心。聽力的部分我推薦看看 Youtube 上的程式教學,除了專有名詞需要花時間收悉一下,其實不會有太艱深難懂的地方。而且就算聽不太明白,看圖說故事也能理解個 6、7 成,不至於會有很大的挫折感。只有口說平常會比較少用到,需要你另外規劃一下。 另外,其實不妨可以給自己設一個目標,有了目標也會比較清楚該如何做。雖然有點老掉牙,但如果放一個符合 SMART 原則 的目標在這裡的話會提升不少說服力。 競賽獲獎或證照證明 這邊相對簡單,大部分的人就是把該掃描的掃描一下貼一貼就好了,除非你有比較特別的獎項就要排版強調一下。 強烈建議:可以做一個表格或目錄在第一頁讓教授可以一眼就知道你得過什麼獎(不會有教授花時間看你一張一張掃描的圖檔的) 社團參與及學校幹部 這邊主要的目的是向教授展現你的問題解決能力、人際溝通能力、以及團隊合作能力,在這邊寫與申請科系無關的成就的話意義不大。 建議可以以事件爲主題來寫,可能是你如何帶領你的夥伴完成一個的目標;可能是夥伴們吵架你怎麽處理(這很常見);或是你的團隊在遭遇困難的時候是怎麽解決的。 其它學習成果 這邊放你曾經做過的小 project 或是一些小玩具,只要簡單列出用途和用到的技術就可以了,如果時間很充裕的話才考慮寫得像專題一樣。 重點整理 以我成爲資工人四年的資歷猜想資工系教授會喜歡看的備審資料大概是什麼樣子,整理出幾個原則: 包含大學的課程內容 用詞精確,並參考使用專業用詞代替 多使用條列的方式,並稍微填充讓資訊密度不至於太高 不要寫教授可能覺得無關的事 特別的美工非必要,簡單排版順眼即可 (可選)若有條件可以用英文寫 最後附上一張評分標準給大家參考 如果要說一個大原則,大概就是讓教授覺得你已經是個資工人,或至少讓他們覺得你對資工是有點 sense ,寫出來的東西和他們平常看的東西不會差太多。雖然很矛盾的是:你就是想要成爲一個資工人才想進這個系。 TL;DR 如果要用一句話總結的話: 用他們的語言,說他們的事情,拿他們的分數 資工 Bonus 如果大家覺得 Word 其實沒那麼好用的話,其實可以試試用最近在很流行的 MarkDown 來寫文章。用 MarkDown 寫文章的話,排版會變得很輕鬆,就像我這篇文章也是用 MarkDown 寫的,你只要要先花 10 分鐘就可以學會怎麽用 MarkDown 來排版了。 這邊有 NiceCode 的 影片教學 也有很多的工具可以直接把 MarkDown 轉成 PDF(而且還可以選不同格式),或是如果你偷懶不想要在電腦上裝軟體的話可以試試在 HackMD 編輯然後直接列印成 PDF 檔 XD。
JN
入驻第1年
消失的七個月
100 天的挑戰 去年的 11/08,我在舊的部落格發起「100 days of code 挑戰」,大致上是我要連續寫 100 天的 code,直到我找到工作爲止。在那之後,我每天花至少四個小時在寫 code 這件事情上。 倒下、沉淪 在挑戰開始的十天內,我遭遇了重大變故;二十天左右的時候確定自己不會如期入伍。 終於在約莫跨年前後、100 天接近過半的時候,生理出現警訊,隨之而來的是長達半個月的嚴重情緒低落。在這段時間我完全不碰寫 code 的事情,我嘗試用做我喜歡做的事情來消耗時間只是不過在大部分的日子,我喜歡的事情根本不足以消耗一整天這麼長的時間,剩下的時間如果沒有朋友陪我的話,要嘛在哭,不然就是在沉迷交友軟體。在這一切事情發生的同時,我還要一邊爲了搬離那時候的住處整理家當,非常的焦慮。 逃避 回到臺中後的生活,只能以廢一個字來描述,但那也是過得很舒服的一段日子。偶爾還是會有不好的事情打擾思緒。我也要特別感謝一位女性摯友的相挺,絕對是值得一提再提的一段經歷。 再次的生活改變 3/29 內政部開獎,幾個禮拜後收到徵集令,4/19 進入成功嶺成爲替代役男。在新訓期間徵選上原民會,之後又分發到臺南市原民會。相較於操到不行的消防役,還有被分發到山區或是離島,或是原本抽中的海軍陸戰隊(在屏東服役),我是幸運得不可思議。 然而,我依然沉迷在交友軟體,因爲有份執著是不能浪費住在臺南,可以在這裡到處玩玩的機會,只是我並沒有認識的朋友待在臺南。雖然幾乎每個週末都有如願在臺南、高雄走走,我的心理狀況比起在臺中的那段時間並沒有比較好,一個禮拜需要釋放一次情緒是我的生活。 新生活 就是上個月的事情,我擁有了穩定、高品質的陪伴,足夠我戒掉交友軟體,也更徹底地離開 Facebook、Instagram(雖然有其他原因)。我開始在 Mastodon 上記錄我的生活,也開始想要碰碰鍵盤打打 code,心理狀態也足夠好,讓我能夠規劃退伍後的日子,而不是純粹的感到焦慮而已。 新部落格 我原本是想把原本部落格的主題換掉,但意外發現 Wowchemy 這套還不錯的系統,就決定砍掉重練,也決定稍微調整一下我對部落格的使用方式。 我決定不再像以前一樣把部落格定位在傳授知識、技術,而是更專注在我的生活的收穫——也許是記錄我怎麽做出一個作品、怎麼解決一個問題、Debugging 的過程、也有可能是我生活純粹的體悟。這也是我會考慮在 Mastodon 上寫東西的理由,我希望部落格上的內容不是爲寫而寫,而是從生活的記錄中,去蕪存菁得到的,那些最有價值的經驗。 僅以這張圖紀念這七個月 GitHub 上綠綠的五十天和黑黑的七個月 希望一切如願都上正軌。
Debug
入驻第1年
使用 pprof 对 Go 程序进行分析优化
前言 在生产环境中,偶尔会发生 Go 程序 CPU 暴增的现象,排除某时段并发大的场景外,通过监控面板看不到程序是因为什么原因导致的,Go 语言原生就提供了工具 pprof,Google 对于 pprof 的解释就是一个用于可视化和分析数据的工具。 通过使用 Go pprof 可以对程序的 CPU性能、内存占用、Goroutine wait share resource、mutex lock 做剖面分析,我们可以使用该工具收集运行时的程序性能指标,从而分析出程序中是否由于代码编写不合理导致存在不合理的资源占用情况,从而对程序进行优化用来提升其性能。 功能 Go pprof 提供了以下五种不同维度观测其程序的功能: CPU Profiling:CPU 性能分析,按照指定时间采集监听其 Go 程序 CPU 的使用情况,可以确定 Go 程序在哪个程序段中占用 CPU 耗时长; Memory Profiling:内存性能分析,用来分析程序的内存堆栈区使用情况,用来检测是否存在内存泄漏; Block Profiling:Goroutine 等待共享资源阻塞分析; Mutex Profiling:互斥锁分析,用来报告共享资源使用互斥锁的竞争的情况; Goroutine Profiling:协程性能分析,用来报告对当前运行时的 Goroutine 操作及数量。 使用 Go pprof 工具的使用也是比较简单快捷的,可以使用runtime/pprof包生成一个 profile 文件,网上也有很多的教程,这里不再过多描述了,详细可以看下包提供的函数,上面介绍了使用方法。 目前我们主要使用的是net/http/pprof包,启动一个独立端口号 http 程序单独用来 Go 程序的分析,搭配着 graphviz 组件来可视化程序来分析数据,使用起来也是比较方便的: 第一步,将net/http/pprof包引用到程序中,建议直接放在程序入口处 main.go 文件 1 2 3 import ( _ "net/http/pprof" ) 第二步,若本身是一个 http 的程序,不需要此步骤,若不是 http web 程序或者不想将对应信息暴露在外网,可以单开一个 http web 程序用来专门监听服务: 1 2 3 4 5 6 7 func main() { // 程序逻辑代码 go func() { _ = http.ListenAndServe(":8848", nil) }() } 第三步,运行主程序,访问 pprof 界面: 1 2 3 4 5 6 7 8 9 10 11 http://127.0.0.1:8848/debug/pprof/ # 主界面 http://127.0.0.1:8848/debug/pprof/allocs # 所有过去内存分配的采样 http://127.0.0.1:8848/debug/pprof/block # 导致同步阻塞的堆栈跟踪 http://127.0.0.1:8848/debug/pprof/cmdline # 当前程序的命令行的完整调用路径 http://127.0.0.1:8848/debug/pprof/goroutine # 所有当前 Goroutine 的堆栈跟踪 http://127.0.0.1:8848/debug/pprof/heap # 活动对象的内存分配的采样 http://127.0.0.1:8848/debug/pprof/mutex # 争用互斥锁持有者的堆栈跟踪 http://127.0.0.1:8848/debug/pprof/profile # CPU 配置文件 http://127.0.0.1:8848/debug/pprof/threadcreate # 创建新 OS 线程的堆栈跟踪 http://127.0.0.1:8848/debug/pprof/trace # 当前程序执行的跟踪 后缀加上 ?debug=1 可以可视化查看对应描述,不加就可以下载成 profile 文件,使用 pprof 命令可视化查看对应数据。 第四步,使用 go tool pprof -http=:6001 profile 命令查看分析程序。 分析 上图是针对 CPU 使用做的采集可视化,箭头越粗、方块越大就代表着对应的操作消耗 CPU 大,可以看到占用 CPU 最多的操作就是 json 的序列化和反序列化操作。 同理对应的内存性能、Goroutine 阻塞的分析都可以看出对应的操作。 总结 使用 go pprof 工具可以分析解剖程序运行性能问题,可以快速定位生产环境中遇到的问题,并作出优化或者 fix bug,最后祝大家不会写出 bug code,程序稳定、头发永在。 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
Git 常用命令汇总
常规操作 git push origin test 推送本地分支到远程仓库 git rm -r --cached 文件/文件夹名字 取消文件被版本控制 git reflog 获取执行过的命令 git log --graph 查看分支合并图 git merge --no-ff -m '合并描述' 分支名 不使用Fast forward方式合并,采用这种方式合并可以看到合并记录 git check-ignore -v 文件名 查看忽略规则 git add -f 文件名 强制将文件提交 Git 创建项目仓库 git init 初始化 git remote add origin url 关联远程仓库 git pull git fetch 获取远程仓库中所有的分支到本地 忽略已加入到版本库中的文件 git update-index --assume-unchanged file 忽略单个文件 git rm -r --cached 文件/文件夹名字 (. 忽略全部文件) 取消忽略文件 git update-index --no-assume-unchanged file 拉取、上传免密码 git config --global credential.helper store 分支操作 git branch 创建分支 git branch -b 创建并切换到新建的分支上 git checkout 切换分支 git branch 查看分支列表 git branch -v 查看所有分支的最后一次操作 git branch -vv 查看当前分支 git branch -b 分支名 origin/分支名 创建远程分支到本地 git branch --merged 查看别的分支和当前分支合并过的分支 git branch --no-merged 查看未与当前分支合并的分支 git branch -d 分支名 删除本地分支 git branch -D 分支名 强行删除分支 git push origin --delete 分支名 删除远程仓库分支 git merge 分支名 合并分支到当前分支上 暂存操作 git stash 暂存当前修改 git stash apply 恢复最近的一次暂存 git stash pop 恢复暂存并删除暂存记录 git stash list 查看暂存列表 git stash drop 暂存名(例:stash@{0}) 移除某次暂存 git stash clear 清除暂存 回退操作 git reset --hard HEAD^ 回退到上一个版本 git reset --hard commitId 回退到某个版本 git checkout -- file撤销修改的文件(如果文件加入到了暂存区,则回退到暂存区的,如果文件加入到了版本库,则还原至加入版本库之后的状态) git reset HEAD file 撤回暂存区的文件修改到工作区 标签操作 git tag 标签名 添加标签(默认对当前版本) git tag 标签名 commitId 对某一提交记录打标签 git tag -a 标签名 -m '描述' 创建新标签并增加备注 git tag 列出所有标签列表 git show 标签名 查看标签信息 git tag -d 标签名 删除本地标签 git push origin 标签名 推送标签到远程仓库 git push origin --tags 推送所有标签到远程仓库 git push origin :refs/tags/标签名 从远程仓库中删除标签 远程仓库 git remote -v查看远程仓库地址 git remote show origin查看远程仓库详情信息 查看某个 commit 提交属于哪个分支 git branch -l --contains <commit_id> 本地分支 git branch -r --contains <commit_id> 远程分支 git branch --all --contains <commit_id> 所有分支 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
PWA 渐进式Web应用程序
简介 PWA是Progressive Web App的简称,是谷歌提出的新型Web技术,并由W3C及谷歌来推广这项技术,其主要目的是为了提升用户对网站原生使用体验,同时又能节省对网站的开启速度。 在我们国内,类似于PWA的技术可以简单地理解为微信主导的小程序,不过小程序的使用需要进行微信小程序前端重构开发,而渐进式Web应用程序开发只需要运用现代Web API以及传统渐进式式策略来构建网站的方式。 特点 由于这项技术是谷歌提出的,目前谷歌系的浏览器都支持PWA, 官方说有三个特点,分别是: 可靠 - 即使在不稳定的网络环境下,也能瞬间加载并展现 体验 - 快速响应,并且有平滑的动画响应用户的操作 粘性 - 像设备上的原生应用,具有沉浸式的用户体验,用户可以添加到桌面 而我感觉使用这个技术最大的特点就是将网站缓存下来,开启网站只需要0.3秒之内,使得网站加载速度异常地快,同时对用户非常友好,增添其用户交互性。 应用 近期对网站进行了PWA升级,只是用一个简单的插件就实现了对网站的APP转化,在开启我的网站的同时,在搜索栏会有加号提醒添加至桌面,如下图: 若是对我的网站感兴趣,可以点击按钮安装PWA,不用安装APP即可获取本站服务,若是手机用户可以在出现的将此页面发送至桌面来进行订阅。 功能 离线和缓存 Service Worker 这项技术主要是解决JS单线程问题,为了减少对浏览器网页开启峰值,页面加载问题。 浏览器中的 javaScript 都是运行在一个单一主线程上的,在同一时间内只能做一件事情。随着 Web 业务不断复杂,我们逐渐在 js 中加了很多耗资源、耗时间的复杂运算过程,这些过程导致的性能问题在 WebApp 的复杂化过程中更加凸显出来。 什么是 Service Worker Service Worker 有以下功能和特性: 一个独立的 worker 线程,独立于当前网页进程,有自己独立的 worker context。 一旦被 install,就永远存在,除非被手动 unregister 用到的时候可以直接唤醒,不用的时候自动睡眠 可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态) 离线内容开发者可控 能向客户端推送消息 不能直接操作 DOM 必须在 HTTPS 环境下才能工作 异步实现,内部大都是通过 Promise 实现 Web存储 选择正确的存储机制对于本地设备存储和基于云的服务器存储都非常重要。 良好的存储引擎可确保以可靠的方式保存信息,并减少带宽和提升响应能力。正确的存储缓存策略是实现离线移动网页体验的核心构建基块。 存储分类分为:数据模型、持久化、浏览器支持、事务处理、同步/异步。 强黏贴用户 将站点添加至屏幕 为了吸引并留住用户,不仅仅自己网站需要比较高质量的文章,良好的交互,还需要一些营销手段增强网站的曝光度,提升与用户的交互。 如果用户对你的网站比较感兴趣,最好的方式将站点添加到主屏幕,不但可以省去用户开启浏览器的操作,提高其转化率,这样就可以对用户强黏贴了。 消息通知 使用 service worker 的功能之一:通知 (notification),它允许服务器向用户提示一些信息,并根据用户不同的行为进行一些简单的处理。 用户自动登录 账户是网站必不可少的组成部分。账户体系的存在,可以让网站给用户提供分级服务,同时网站也能够通过收集用户行为实现精准推送。但账号的存在将使得用户不得不多出一步登录的步骤,要知道根据“漏斗模型”理论,从起点到终点,每个环节都会产生用户的流失,依次递减。因此想办法省去烦人的账号密码输入过程,不但能提高用户体验,也能够提高网站转化率。 记住网站传统密码形式; 凭证管理,第三方登陆验证。 网络安全 Web安全; 使用SSL 即 Https; 同源策略; 典型的安全漏洞; CSP(内容安全策略)。 详细内容请移步查看:https://lavas.baidu.com/pwa/web-security/introduction 参考文章 什么是PWA | PWA 文档| Lavas 简单介绍一下Progressive Web App(PWA) 渐进式Web 应用(PWA) | MDN 讲讲PWA - 前端学习- SegmentFault 思否 Progressive Web Apps 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
浅析悲观锁与乐观锁
在关系型数据库中,悲观锁与乐观锁是解决资源并发场景的解决方案,接下来将详细讲解一下这两个并发解决方案的实际使用及优缺点。 首先定义一下数据库,做一个最简单的库存表,如下设计: 1 2 3 4 5 6 CREATE TABLE `order_stock` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', `oid` int(50) NOT NULL COMMENT '商品ID', `quantity` int(20) NOT NULL COMMENT '库存', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; quantity代表着不同商品 oid 的库存,接下来 OCC 及 PCC 使用此数据库进行演示。 乐观锁 OCC 它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。 即“乐观锁”认为拿锁的用户多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。这样就可以避免使用数据库自身定义的行锁,可以避免死锁现象的产生。 1 UPDATE order_stock SET quantity = quantity - 1 WHERE oid = 1 AND quantity - 1 > 0; 乐观并发控制多数用于数据争用不大、冲突较少的环境中,这种环境中,偶尔回滚事务的成本会低于读取数据时锁定数据的成本,因此可以获得比其他并发控制方法更高的吞吐量。 悲观锁 PCC 它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。 这种设计采用了“一锁二查三更新”模式,就是采用数据库中自带 select ... for update 关键字进行对当前事务添加行级锁先将要操作的数据进行锁上,之后执行对应查询数据并执行更新操作。 1 2 3 4 BEGIN SELECT quantity FROM order_stock WHERE oid = 1 FOR UPDATE; UPDATE order_stock SET quantity = 2 WHERE oid = 1; COMMIT; MySQL还有个问题是select ... for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题。因此如果在MySQL中用悲观锁务必要确定走了索引,而不是全表扫描。 悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。 OCC 和 PCC 优缺点 OCC 优点及缺点 【优点】 乐观锁相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁; 可以快速响应事务,随着并发量增加,但会出现大量回滚出现; 效率高,但是要控制好锁的力度。 【缺点】 如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题; 随着并发量增加,但会出现大量回滚出现。 PCC 优点及缺点 【优点】 “先取锁再访问”的保守策略,为数据处理的安全提供了保证; 【缺点】 依赖数据库锁,效率低; 处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会; 降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据。 References 【MySQL】悲观锁&乐观锁 LearnKu 浅析乐观锁与悲观锁 维基百科 悲观并发控制 && 乐观并发控制 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
Gource 版本可视化工具 使用手册
Gource 是一款版本控制可视化的工具,使用这个工具可以将自己的 Git 提交的代码包括对 Mercurial,Bazaar 和 SVN 的内置日志生成可视化支持。Gource 还可以解析由多个第三方工具为 CVS 存储库生成的日志。 提交的代码按照时间轴的顺序动态显示出来,可以使你的工作过程以动画的形式显现,并且 Gource 这个工具可以显示出来不同用户对一个代码库进行同一时间内的修改操作。 官方网站:https://gource.io/ 常用命令 在这里我列举几个经常使用到的命令,PS:你需要先进入到对应项目目录中去,这个很重要,要不然会提示该目录下没有 log 记录。 1 2 3 4 5 6 7 gource # 使用Gource查看版本历史 gource -f -1280×720 # 设置分辨率大小 gource -s 0.5 # 每天以0.5秒的速度播放 gource -o 1.mp4 # 将版本动画导出到 1.mp4 文件中 gource -s 0.1 -o 2.mp4 # 每天以0.1秒的速度导出到 2.mp4 文件中 gource -f -b red # 将背景设置为红色 gource --title “Gource” # 为gource设置title 基本命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 ➜ ~ gource -help Gource v0.51 Usage: gource [options] [path] 用法: gource [选项] [路径] Options: -h, --help 帮助 -WIDTHxHEIGHT, --viewport 设定窗口大小 -f, --fullscreen 全屏显示 --screen SCREEN 画面编号 --multi-sampling 启用多重采样 --no-vsync 禁用垂直同步 --start-date 'YYYY-MM-DD hh:mm:ss +tz' 从日期和可选时间开始 --stop-date 'YYYY-MM-DD hh:mm:ss +tz' 停在某个日期和可选时间 -p, --start-position POSITION 从某个位置开始(0.0-1.0 or 'random') --stop-position POSITION 停在某个位置 -t, --stop-at-time SECONDS 在指定的秒数后停止 --stop-at-end 在日志结尾处停止 --dont-stop 在日志结束后继续运行 --loop 在日志末尾循环 -a, --auto-skip-seconds SECONDS 如果没有任何反应,则自动跳至下一个条目 持续几秒钟(default: 3) --disable-auto-skip 禁用自动跳过 -s, --seconds-per-day SECONDS 每天以秒为单位的速度(default: 10) --realtime 实时播放速度 --no-time-travel 如果提交时间是过去的时间请使用上一次提交的时间 -c, --time-scale SCALE 更改模拟时间范围(default: 1.0) -e, --elasticity FLOAT 节点弹性(default: 0.0) --key 显示文件扩展名 --user-image-dir DIRECTORY 包含要用作头像的图像的目录 --default-user-image IMAGE 默认用户图像文件 --colour-images 使用单色图像 -i, --file-idle-time SECONDS 时间文件保持空闲(default: 0) --max-files NUMBER 最大文件数或0(无限制) --max-file-lag SECONDS 提交的最大时间文件可能会出现 --log-command VCS 显示VCS日志命令(git,svn,hg,bzr,cvs2cl) --log-format VCS 指定日志格式(git,svn,hg,bzr,cvs2cl,custom) --load-config CONF_FILE 加载配置文件 --save-config CONF_FILE 使用当前选项保存配置文件 -o, --output-ppm-stream FILE 将PPM流输出到文件 ('-' for STDOUT) -r, --output-framerate FPS 输出帧率(25,30,60) PATH可以是受支持的版本控制目录,日志文件,Gource配置文件或用于读取STDIN的'-'。 如果省略,则gource将尝试从当前目录生成日志。 要查看完整的命令行选项,请使用 “-H” 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
Linux Tree 树状目录显示工具 使用手册
Tree 命令以树状形状列出目录的内容的一个工具,你时常在 Github 中常看到一些开源项目会将自己的项目目录展现出来,这篇文章的背景图就是展现的开源项目 Laravel 中 app 目录的树状图,接下来介绍一下基本使用语法。 基本语法 1 tree [-aACdDfFgilnNpqstux][-I <范本样式>][-P <范本样式>][目录...] 常用命令 1 2 3 4 5 6 7 8 9 10 11 tree --help 显示帮助信息 tree -d 只显示目录 tree -L n 只显示第n层目录 tree -l 遵循像目录这样的符号链接 tree -f 打印每个文件的完整路径前缀 tree -x 只保留在当前文件系统上 tree -L 级下降深层级目录 tree -R 达到最大等级时重新运行树 tree -P 模式只列出符合给定模式的文件 tree -I 模式不要列出与给定模式匹配的文件 tree -o 文件名输出到文件而不是标准输出 基本命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 [➜ ~ tree --help usage: tree [-acdfghilnpqrstuvxACDFJQNSUX] [-H baseHREF] [-T title ] [-L level [-R]] [-P pattern] [-I pattern] [-o filename] [--version] [--help] [--inodes] [--device] [--noreport] [--nolinks] [--dirsfirst] [--charset charset] [--filelimit[=]#] [--si] [--timefmt[=]<f>] [--sort[=]<name>] [--matchdirs] [--ignore-case] [--fromfile] [--] [<目录列表>] ------- 上市选项 ------- -a 列出所有文件。 -d 仅列出目录。 -l 跟随目录等符号链接。 -f 打印每个文件的完整路径前缀。 -x 仅保留在当前文件系统上。 -L 级别仅下降级别级别的目录。 -R 当达到最大目录级别时,重新运行树。 -P 模式仅列出与给定模式匹配的那些文件。 -I 模式不列出与给定模式匹配的文件。 --ignore-case 模式匹配时忽略大小写。 --matchdirs 在-P模式匹配中包括目录名称。 --noreport 在树列表的末尾关闭文件/目录计数。 --charset X 将charset X用于终端/ HTML和缩进线输出。 --filelimit# 不要使包含超过#个文件的dirs下降。 --timefmt <f>根据<f>格式打印和格式化时间。 -o filename 输出到文件而不是stdout。 ------- 文件选项 ------- -q 将不可打印的字符打印为'?'。 -N 按原样打印不可打印的字符。 -Q 引用双引号的文件名。 -p 打印每个文件的保护。 -u 显示文件所有者或UID号。 -g 显示文件组所有者或GID号。 -s 打印每个文件的大小(以字节为单位)。 -h 以更易于理解的方式打印尺寸。 --si 与-h类似,但以SI单位使用(1000的幂)。 -D 打印上次修改或(-c)状态更改的日期。 -F 附加'/','=','*','@','|'或按ls -F的'>'。 --inodes 打印每个文件的索引节点号。 --device 打印每个文件所属的设备ID号。 ------- 排序选项 ------- -v 按版本字母顺序对文件进行排序。 -t 按上次修改时间对文件排序。 -c 按上次状态更改时间对文件排序。 -U 不排序文件。 -r 颠倒排序顺序。 --dirsfirst 在文件之前列出目录(-U禁用)。 --sort X 选择排序:名称,版本,大小,mtime,ctime。 ------- 图形选项 ------- -i 不打印缩进线。 -A 打印ANSI线图形缩进线。 -S 使用CP437(控制台)图形缩进线打印。 -n 始终关闭着色(-C替代)。 -C 始终打开着色。 ------- XML / HTML / JSON选项 ------- -X 打印树的XML表示形式。 -J 打印树的JSON表示形式。 -H baseHREF打印出以baseHREF作为顶层目录的HTML格式。 -T 字符串用字符串替换默认的HTML标题和H1标头。 --nolinks 关闭HTML输出中的超链接。 ------- 输入选项 ------- --fromfile 从文件中读取路径(。= stdin) ------- 其他选项 ------- --version 打印版本并退出。 --help 打印用法和此帮助消息并退出。 -选项处理终止符。 展示效果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ➜ app tree . ├── Console │ └── Kernel.php ├── Exceptions │ └── Handler.php ├── Http │ ├── Controllers │ │ ├── Auth │ │ │ ├── ForgotPasswordController.php │ │ │ ├── LoginController.php │ │ │ ├── RegisterController.php │ │ │ ├── ResetPasswordController.php │ │ │ └── VerificationController.php │ │ ├── Controller.php │ │ └── IndexController.php │ ├── Kernel.php │ └── Middleware │ ├── Authenticate.php │ ├── CheckForMaintenanceMode.php │ ├── EncryptCookies.php │ ├── RedirectIfAuthenticated.php │ ├── TrimStrings.php │ ├── TrustProxies.php │ └── VerifyCsrfToken.php ├── Providers │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── BroadcastServiceProvider.php │ ├── EventServiceProvider.php │ └── RouteServiceProvider.php └── User.php 7 directories, 23 files 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
Restful API 设计指北
近期学习了 Go 语言,跟着七米在学习,学习过程中了解到了 API 的一个设计规范,也就是本文要讲的 Restful API 设计模式,现在互联网处在前后端分离的阶段,API 的书写及规范化是非常重要的,针对于 API 中 Restful API 中设计比较规范的是 Github API,可以直接访问他们的 https://api.github.com 直接查看 Github 针对与公共接口的链接及使用方法。 此篇文章也是针对于这几天学习 Restful API 做了一个笔记或小结,若有不足之处还望批评指正,谢谢。 使用 HTTPS 协议 这个协议使用本身与这个 API 设计标准没有什么直接联系,使用 HTTPS 协议主要目的是将用户客户端与 API 服务器连接过程中保证其数据的安全性。 注意:由于 API 接口使用 HTTPS 协议,不要让非 SSL 的链接访问重定向到 SSL 的链接。 API 地址和版本问题 为 API 使用专门子域名比较友好,例如使用如下链接使用: 1 https://api.debuginn.com 也可以将 API 放在主域名下,例如: 1 https://debuginn.com/api/ 当然,针对于 API 版本问题针对以上两种方法可以分别使用如下例子: 1 2 3 4 # 针对于 API 子域名方式 api.domain/v1/ https://api.debuginn.com/v1/ # 针对于 主域名目录方式 domain/api/v1/ https://debuginn.com/api/v1/ Schema 响应数据模式 现在前后端分离项目使用的数据响应模式大部分采用的是 JSON 格式数据,也有一些项目采用 XML 格式的数据。 针对于用户客户端请求,服务器响应尽量有 状态码 Status Code 及详细解释。 使用正确的 Method 使用正确的 Method 也就是使用正确的 HTTP 请求动词,即 HTTP 协议规定的常常使用的六种请求动词,并针对请求 SQL 语句辅助理解: 1 2 3 4 5 GET 请求 => SELECT 从服务端获取数据 POST 请求 => CREATE 从服务端创建数据 PUT 请求 => UPDATE 从服务端更新数据(将所有数据元素全部替换掉) PATCH 请求 => UPDATE 从服务端更新数据(将部分数据元素替换掉) DELETE请求 => DELETE 从服务端删除数据 还有两个不常使用的请求: 1 2 HEAD 获取资源的元数据。 OPTIONS 获取信息,关于资源的哪些属性是客户端可以改变的。 注意:更新和创建操作应该返回最新的资源,来通知用户资源的情况;删除资源一般不会返回内容。 过滤信息 针对用户端查询数据,需要服务端查询对应数据,包括了筛选、分页等操作。 筛选操作 1 2 3 4 5 api.domain/user/limit/10 指定返回记录的数量; api.domain/user/offset/10 指定返回记录的开始位置; api.domain/user/animal_type_id/1 指定筛选条件 api.domain/user/page/2/per_page/100 指定第几页,以及每页的记录数; api.domain/user/sortby/name/order/asc 指定返回结果按照哪个属性排序,以及排序顺序 分页操作 当返回某个资源的列表时,如果要返回的数目特别多,比如 github 的 /users,就需要使用分页分批次按照需要来返回特定数量的结果。 分页的实现会用到上面提到的 url query,通过两个参数来控制要返回的资源结果: per_page:每页返回多少资源,如果没提供会使用预设的默认值;这个数量也是有一个最大值,不然用户把它设置成一个非常大的值(比如99999999)也失去了设计的初衷。 page:要获取哪一页的资源,默认是第一页。 状态码 Status Code HTTP 应答中,需要带一个很重要的字段:status code。它说明了请求的大致情况,是否正常完成、需要进一步处理、出现了什么错误,对于客户端非常重要。状态码都是三位的整数,大概分成了几个区间: 2XX:请求正常处理并返回 3XX:重定向,请求的资源位置发生变化 4XX:客户端发送的请求有错误 5XX:服务器端错误 在 HTTP API 设计中,经常用到的状态码以及它们的意义如下表: 状态码 LABEL 解释 200 OK 请求成功接收并处理,一般响应中都会有 body 201 Created 请求已完成,并导致了一个或者多个资源被创建,最常用在 POST 创建资源的时候 202 Accepted 请求已经接收并开始处理,但是处理还没有完成。一般用在异步处理的情况,响应 body 中应该告诉客户端去哪里查看任务的状态 204 No Content 请求已经处理完成,但是没有信息要返回,经常用在 PUT 更新资源的时候(客户端提供资源的所有属性,因此不需要服务端返回)。如果有重要的 metadata,可以放到头部返回 301 Moved Permanently 请求的资源已经永久性地移动到另外一个地方,后续所有的请求都应该直接访问新地址。服务端会把新地址写在 Location 头部字段,方便客户端使用。允许客户端把 POST 请求修改为 GET。 304 Not Modified 请求的资源和之前的版本一样,没有发生改变。用来缓存资源,和条件性请求(conditional request)一起出现 307 Temporary Redirect 目标资源暂时性地移动到新的地址,客户端需要去新地址进行操作,但是不能修改请求的方法。 308 Permanent Redirect 和 301 类似,除了客户端不能修改原请求的方法 400 Bad Request 客户端发送的请求有错误(请求语法错误,body 数据格式有误,body 缺少必须的字段等),导致服务端无法处理 403 Forbidden 服务器端接收到并理解客户端的请求,但是客户端的权限不足。比如,普通用户想操作只有管理员才有权限的资源。 404 Not Found 客户端要访问的资源不存在,链接失效或者客户端伪造 URL 的时候会遇到这个情况 405 Method Not Allowed 服务端接收到了请求,而且要访问的资源也存在,但是不支持对应的方法。服务端必须返回Allow头部,告诉客户端哪些方法是允许的 415 Unsupported Media Type 服务端不支持客户端请求的资源格式,一般是因为客户端在Content-Type或者Content-Encoding中申明了希望的返回格式,但是服务端没有实现。比如,客户端希望收到xml返回,但是服务端支持Json 429 Too Many Requests 客户端在规定的时间里发送了太多请求,在进行限流的时候会用到 500 Internal Server Error 服务器内部错误,导致无法完成请求的内容 503 Service Unavailable 服务器因为负载过高或者维护,暂时无法提供服务。服务器端应该返回 Retry-After 头部,告诉客户端过一段时间再来重试 针对于状态码,请看此文章:HTTP常见状态码 错误处理 如果出错的话,在 response body 中通过 message 给出明确的信息。 比如客户端发送的请求有错误,一般会返回4XX Bad Request结果。这个结果很模糊,给出错误 message 的话,能更好地让客户端知道具体哪里有问题,进行快速修改。 如果请求的 JSON 数据无法解析,会返回Problems parsing JSON; 如果缺少必要的 filed,会返回422 Unprocessable Entity,除了 message 之外,还通过errors给出了哪些 field 缺少了,能够方便调用方快速排错。 基本的思路就是尽可能提供更准确的错误信息:比如数据不是正确的 json,缺少必要的字段,字段的值不符合规定…… 而不是直接说“请求错误”之类的信息。 验证及授权 一般来说,让任何人随意访问公开的 API 是不好的做法。验证和授权是两件事情: 验证(Authentication)是为了确定用户是其声明的身份,比如提供账户的密码。不然的话,任何人伪造成其他身份(比如其他用户或者管理员)是非常危险的 授权(Authorization)是为了保证用户有对请求资源特定操作的权限。比如用户的私人信息只能自己能访问,其他人无法看到;有些特殊的操作只能管理员可以操作,其他用户有只读的权限等等 如果没有通过验证(提供的用户名和密码不匹配,token 不正确等),需要返回401 Unauthorized状态码,并在 body 中说明具体的错误信息;而没有被授权访问的资源操作,需要返回403 Forbidden状态码,还有详细的错误信息。 注意:对某些用户未被授权访问的资源操作返回404 Not Found,目的是为了防止私有资源的泄露(比如黑客可以自动化试探用户的私有资源,返回 403 的话,就等于告诉黑客用户有这些私有的资源,无异于是给黑客提供了方向)。 Hypermedia API RESTful API 最好做到 Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。 比如访问 api.github.com,就可以看到 Github API 支持的资源操作。 易读的 API 接口文档 API 最终是给人使用的,不管是公司内部,还是公开的 API 都是一样。即使我们遵循了上面提到的所有规范,设计的 API 非常优雅,用户还是不知道怎么使用我们的 API。最后一步,但非常重要的一步是:为你的 API 编写优秀的文档。 注意:对每个请求以及返回的参数给出说明,最好给出一个详细而完整的示例。 参考资料 RESTful API 设计指南 - 阮一峰 跟着 Github 学习 Restful HTTP API 设计 REST API Tutorial Representational State Transfer (REST) - Roy Fielding 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
如何将豆瓣观影记录实时同步至博客中
事情的起因是这样的,前几日在看 idealclover 大佬的博客,不经意间看到了他的豆瓣观影记录,他博客中关于豆瓣观影记录是实时同步的,很好奇是如何实现的,经过查看,他是爬取的豆瓣观影界面来实现的,其实关于豆瓣观影记录,网上也有很多的教程,恰巧自己所学的 Go 语言也可以做简单的爬虫实现其效果,于是开始上手造轮子了,PS:了解到非法爬取网站信息是违法的,之前豆瓣 API 接口,关闭访问,在豆瓣上找了好久,终于在我的主页中找到了对于观影记录的官方提供 RSS 订阅,打开订阅,看到有自己所需要的字段,比较好获取,于是就开始了此项目。 分析 首先,需要获取豆瓣提供的 XML 文件,在我的主页右下角就可以看到 RSS 订阅链接: 找到了订阅地址,点击查看 XML 结构,可以看到豆瓣提供的结构还是挺理想的: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> <channel> <title>Meng小羽 的收藏</title> <link>https://www.douban.com/people/debuginn/</link> <description> <![CDATA[ Meng小羽 的收藏:想看、在看和看过的书和电影,想听、在听和听过的音乐 ]]> </description> <language>zh-cn</language> <copyright>© 2013, douban.com.</copyright> <pubDate>Sat, 30 May 2020 09:14:08 GMT</pubDate> <item> <title>看过黑衣人:全球追缉</title> <link>http://movie.douban.com/subject/19971676/</link> <description> <![CDATA[ <table><tr> <td width="80px"><a href="https://movie.douban.com/subject/19971676/" title="Men in Black International"> <img src="https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2558701068.webp" alt="Men in Black International"></a></td> <td> <p>推荐: 力荐</p> </td></tr></table> ]]> </description> <dc:creator>Meng小羽</dc:creator> <pubDate>Sat, 30 May 2020 09:14:08 GMT</pubDate> <guid isPermaLink="false">https://www.douban.com/people/debuginn/interests/2402808825</guid> </item> ...... <channel> 其实,我们提取的主要就是 item 标签下对应的电影信息内容: 1 2 3 4 5 6 7 8 9 10 <item> <title>看过黑衣人:全球追缉</title> <link>http://movie.douban.com/subject/19971676/</link> <description> <![CDATA[ <table><tr> <td width="80px"><a href="https://movie.douban.com/subject/19971676/" title="Men in Black International"> <img src="https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2558701068.webp" alt="Men in Black International"></a></td> <td> <p>推荐: 力荐</p> </td></tr></table> ]]> </description> <dc:creator>Meng小羽</dc:creator> <pubDate>Sat, 30 May 2020 09:14:08 GMT</pubDate> <guid isPermaLink="false">https://www.douban.com/people/debuginn/interests/2402808825</guid> </item> 设计 根据豆瓣官方提供的 XML 标签数据,可以利用 Go 语言中 encoding/xml 包来进行对数据的映射,可以设计成如下结构体: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // 豆瓣 xml 描述结构体 type Attributes struct { XMLName xml.Name `xml:"rss"` Version string `xml:"version,attr"` Channel Channel `xml:"channel"` } // XML 主题结构拆分 type Channel struct { Title string `xml:"title"` Link string `xml:"link"` Description string `xml:"description"` Language string `xml:"language"` Copyright string `xml:"copyright"` Pubdate string `xml:"pubDate"` MovieItem []MovieItem `xml:"item"` } // 豆瓣 电影列表结构体 type MovieItem struct { Title string `xml:"title"` Link string `xml:"link"` Description string `xml:"description"` Pubdate string `xml:"pubDate"` } 可以和 XML 文件对应字段进行匹配,可以从上面的结构体中我们可以看到,最终我们想获取到的数据就是结构体 MovieItem 的数据。 由于是从网上链接获取数据的,在这里首先我们需要将网上豆瓣提供的 XML 文件转换成 []byte 类型的数据: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // 获取 xml 文件数据 func getXMLData(url string) (data []byte, err error) { // 读取 xml 文件 client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) // 自定义Header req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)") resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() // 关闭文件 // 读取所有文件内容保存至 []byte data, err = ioutil.ReadAll(resp.Body) if err != nil { return nil, err } return } 上面这个函数实现的就是将 XML 文件保存至 Go 语言的数据结构的操作,现在可以将 XML 文件成功读取出来,接下来就是要进行 XML 字段与上面作出的结构体之间的映射,其实映射至结构体的过程是比较简单的,首先声明 Attributes{} 类型的结构体,之后通过 xml.Unmarshal 来实现映射拷贝,就可以得到对应的结构体类型的数据,由于我们想要的数据是结构体数据中的一部分,即 MovieItem,在得到结构体数据后就可以将想要的这一部分的数据选择抽取出来: 1 2 3 4 5 6 7 v := Attributes{} unMarshalErr := xml.Unmarshal(data, &v) if unMarshalErr != nil { fmt.Printf("xml unmarshal failed, err:%v\n", err) } movieItem := v.Channel.MovieItem Map 转换 在这里我们可以得到结构体中嵌套的结构体,在结构体中有一些字段我们是不想要的,需要进行处理,对于 description 这个字段中,官方提供的是一段 HTML 描述串,其中电影的描述文件是我们所需要的,对于 HTML 字符串的拆分,我们可以借助strings.Split 函数来实现截取,使用 \" 符号截取,虽然可以获取到我们想要的数据了,但是由于这个是嵌套的结构体,我们需要做一个匹配的 map 来进行存储处理好的数据,可以看代码中我的设计: 1 2 3 4 5 6 7 8 9 10 11 MoviesMap := make(map[int]interface{}) for i := 0; i < len(movieItem); i++ { movie := make(map[string]string) description := strings.Split(movieItem[i].Description, "\"") movie["Title"] = string([]rune(movieItem[i].Title)[2:]) movie["Link"] = movieItem[i].Link movie["Img"] = description[7] movie["Pubdate"] = movieItem[i].Pubdate MoviesMap[i] = movie } 外层 map 是采用 map[int]interface{} 类型,在 interface{} 中存储这内层 map map[string]string 类型。 针对于 Img 地址的获取,是现根据特定符号拆分,之后获取制定位置的数据获取的。 1 2 3 0 map[Img:https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2558701068.jpg Link:http://movie.douban.com/subject/19971676/ Pubdate:Sat, 30 May 2020 09:14:08 GMT Title:黑衣人:全球追缉] 1 map[Img:https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2263408369.jpg Link:http://movie.douban.com/subject/1294371/ Pubdate:Thu, 28 May 2020 10:06:23 GMT Title:摩登时代] ...... 最后就是将这个 map 做一下序列化处理,这样就可以返回给前台数据了。 1 data, _ = json.Marshal(MoviesMap) 服务 处理好数据,做了对应的处理,怎么将数据作为服务端提供给前台,在这里需要使用 Web 服务,Go 中可以使用原生 Web,不过我在这里使用的是之前学过的 Gin 框架,来提供服务的: 1 2 3 4 5 r := gin.Default() r.GET("/doubanmovies", func(context *gin.Context) { context.JSON(http.StatusOK, MoviesMap) }) _ = r.Run(":8080") 启动服务,可以得到对应的 json 数据,你若以为现在就可以实现了,那么你错了,远远没有那么简单…… 前台 由于我知晓我的博客采用的前台 UI 技术是 MDUI, 我利用自身的卡片 UI 迅速设计了一个模块,因为后期需要放在我的博客页面上,前端读取数据采用的是 VUE 和 axios 技术: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <div class="mdui-container-fluid" id="app"> <div class="mdui-row"> <div v-for="item in info"> <div class="mdui-col-xs-6 mdui-col-sm-4 mdui-col-md-3 mdui-col-lg-3 mdui-m-b-1 mdui-m-t-1"> <a :href="item.Link" target="_blank"> <div class="mdui-card mdui-hoverable"> <div class="mdui-card-media"> <img :src="item.Img" style="height: 260px;" /> <div class="mdui-card-media-covered"> <div class="mdui-card-primary"> <div class="mdui-card-primary-subtitle">{{ item.Title }}</div> </div> </div> </div> </div> </a> </div> </div> </div> </div> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <script src="./static/js/mdui.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js"></script> <script src="https://cdn.staticfile.org/axios/0.18.0/axios.min.js"></script> <script> new Vue({ el: '#app', data() { return { info: null } }, mounted() { axios .get('http://127.0.0.1:8080/doubanmovies') .then(response => (this.info = response.data)) .catch(function (error) { // 请求失败处理 console.log(error); }); } }) </script> 设计好了以后,访问页面,却加载不出来,emmmmmm CORS 看到了是 CORS 同源策略的原因,接下来就是要解决同源问题了,方法比较简单,就是将 Go 服务端加上 CORS 同源策略就可以了,方法如下: 1 2 3 4 5 6 7 r := gin.Default() r.Use(Cors()) r.GET("/doubanmovies", func(context *gin.Context) { context.JSON(http.StatusOK, MoviesMap) }) _ = r.Run(":8080") 在路由访问中添加 Cors() 函数: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 // 跨域 func Cors() gin.HandlerFunc { return func(c *gin.Context) { method := c.Request.Method //请求方法 origin := c.Request.Header.Get("Origin") //请求头部 var headerKeys []string // 声明请求头keys for k, _ := range c.Request.Header { headerKeys = append(headerKeys, k) } headerStr := strings.Join(headerKeys, ", ") if headerStr != "" { headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr) } else { headerStr = "access-control-allow-origin, access-control-allow-headers" } if origin != "" { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Origin", "*") // 这是允许访问所有域 c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求 // header的类型 c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma") // 允许跨域设置 可以返回其他子段 c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析 c.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒 c.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true c.Set("content-type", "application/json") // 设置返回格式是json } //放行所有OPTIONS方法 if method == "OPTIONS" { c.JSON(http.StatusOK, "Options Request!") } // 处理请求 c.Next() // 处理请求 } } 这样就可以看到结果了,如下图: 看到结果后,心中窃喜,感觉成功了,接下来就需要将 Go 服务部署到我的服务器中去了,部署步骤比较简单,就不过多解释了,最后访问服务器 IP 及对应单口可以呈现结果,最后将前台代码粘贴到新建的页面中,生成预览,emmmm,啥都没有,浏览器居然报 HTTPS 请求 HTTP 资源是不安全的,吐了一口血,解决吧,唉,经过查询资料,得出如下两个解决方案: Gin 框架服务本身使用 SSL 证书,实现 HTTPS 访问,不过需要配置域名; 使用 Nginx 服务做一下代理,将一个特定链接代理到本身服务中去。 作为学生党的我,没有太多的资金去申请过多的 SSL 证书(省着点用),于是我就在我的 debuginn.com 子域名下做了一个代理。 代理 Nginx 代理实现也是比较简单的,就是将前端访问某个接口代理至服务器中某个端口的服务中,表面上看是 Nginx 在做数据处理,实际上是 Nginx 只做了一个代理转发,由于我www.debuginn.com 子域名本身就是 https 的,所以设置好了代理之后,就可以使用固定的代理链接访问了,配置如下: 1 2 3 4 5 6 7 8 9 server{ ..... location /douban_movies { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host:80; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } 这样就可以实现 https 资源访问了:https://debuginn.com/doubanmovies 效果 解决了 HTTPS 访问 HTTP 资源的问题,就解决了所有问题,实现了效果。 具体效果如下:https://debuginn.com/doubanmovies 开源 针对于此小项目,我已经开源至 Github 中,若是你感兴趣或者有什么建议,可以联系我,我们一起改进,同时希望你可以给我一个 Star,万分感谢! https://github.com/debuginn/douban-movies 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
2021 年度总结
今年,时间依旧飞快流逝,转眼间,自己已经毕业了小2年了,渐渐的自己开始习惯了北漂的生活,一个人的北京,自由与孤独同在。也体会到了离家远的遗憾,远的连奶奶最后一面也没有见到,第一次感受到了亲人阴阳两隔的无奈与悲哀。疫情 😷 在全球范围内还在持续,国内偶尔也零星的出现,但愿 22 年疫情结束,全世界人们都可以回归到疫情之前,和爱人、亲人、朋友去想去的地方,去探索多彩的世界。 网站数据 2021年统计数据共享链接 今年,写了一些在工作中使用到的 Golang 的一些技巧及思考,以及一些规范。 让我意外的是大家对 Go 语言入门学习有着很大的兴趣,下面这个文章是今年访问最多的文章,访问量:2785 https://blog.debuginn.com/p/go-dev-design/ 技术导航 经常游荡于各个大厂的技术博客之中,于是做了一个集合导航,后续计划将大佬们也收集到此,大家有好的技术分享网站也可以评论区留言分享一下。 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
我们是如何用 Prometheus 对网关进行监控的
近期,我们对 APP 网关 Gateway 做了升级,由于项目创建时间过早(6年前的项目),那时候还没有好的包管理工具,使用的是最原始的 Go Path 来进行项目的依赖管理,历史包袱比较重,项目中很多的第三方引用都是直接将代码拷贝到项目目录下,升级与维护起来特别麻烦,升级之后就是现在官方主推的是 Go module 包管理方式。 解决了上面的这个痛点,网关程序就可以集成一些业界主流的基础工具,升级与维护起来就简单多了。 言归正传,本文主要是讲的我们是如何用 Prometheus 对网关进行监控的,之前我们的网关程序也是集成了我们公司开源打点监控工具 Open falcon,并且使用 Grafana 进行绘图并查看,但是为啥我们不再继续使用了?之后我们为啥拥抱了 Prometheus 生态?还有一些打点、报警、绘图的思考,还有一些我们在使用的过程中出现的问题以及解决方案,一一讲解一下。 抛弃 Open falcon 拥抱 Prometheus 在决定使用 Prometheus 之前,我们的 Gateway 使用的是 Open falcon,但是一直存在着一个对于我们而言的痛点,就是作为网关程序,历史维护的路由太多了,接口可用性及接口报错无法聚合报警,也就是我们的监控体系存在着盲区,这个对我们而言来说是最为致命的,那个接口出现了问题会直接导致用户的使用,并且我们使用的那些上游服务出现问题我们也无法及时感知。 使用 Prometheus 最主要的是我们可以通过 PromQL 语法进行正则匹配,实现对某个或多个接口的聚合计算并报警,这样就可以解决我们无法聚合报警的一个痛点。 打点、绘图、报警 打点 全面、量小 作为业务使用,怎么设计点位,既可以满足报警使用,对每个接口进行各项指标的监控,同时要保证点位数据是可穷举的(避免出现 OOM)和产生数据量比较小。简而言之,就是“监控要全面、打点数据量要小”,因为数据量大的话在 Prometheus 拉取指标的时间及周期就不得不设置的过大,这样的后果就是造成图的绘制缓慢甚至超时,同时报警也失去了实效性。 我们网关使用的是 http 协议,可以充分利用 Go 的 net/http 特性,使用中间件设计,对请求与返回进行打点,于是我们是这样设计的: 对任意一个请求做一个 qps 的打点记录(无任何的业务参与其中); 对单个路由请求进行打点(区分业务状态码); 对单个路由请求进行耗时打点(区分业务状态码)。 请求路由 按照业界通用的设计:/version/model/action 以上的场景,仅仅使用指标类型中的两种 Counter(计数器) 和 Histogram(直方图)就可以满足我们打点需求。 绘图 清晰、快速 构建一栋房子所需的材料都准备好了,准备建造, building…… 点位指标收集到了,接下来就是对点位进行各个维度的拼装,来呈现我们想要的图,这里解答一下为什么我们要把业务状态码打到指标中去,以及我们是如何使用的:我们的系统设计采用业务封装错误码,只要是传输调用链路没有问题,所有的场景都走业务状态码,类似的返回解决如下: 1 2 3 4 5 6 7 { "code": 0, "desc": "success", "data":{ "result": "ok" } } code 为 0,代表当前请求是正常的,返回数据会封装在 data 中; code 不为 0,代表着当前请求存在业务上可捕获或者自定义的错误。 作为网关程序,与下游微服务采用相同的接口设计,对我们现在的打点设计也是非常友好的。 同样的,有的服务使用的是 Restful API 思想,使用的是 http 标准状态码,那就是 200 代表着成功,非 200 代表着业务或者系统存在错误,当然 5XX 错误可以单独拿出来做可用性或者细化的报警。 之所以打点记录业务状态码,好处如下: 对业务状态码打点,可以对某个业务上的特定错误进行捕捉,看图及报警都是非常便捷的; 不影响对接口可用性进行计算,可以多维度聚合计算可用性(根据业务定义而言)。 当然,打点指标设置的粒度越小,对应的点位的存储大小以及聚合运算的代价也是成倍的提高的。 铺垫了好久,说一下我们是怎么进行绘图的,在打点的时候讲到使用 Counter、Histogram 进行打点,绘图的时候我们主要从以下三点进行可视化: 接口的 qps 看图呈现; 接口可用性(Pxx)看图呈现; 接口请求PXX 耗时统计 看图呈现。 接口 qps 看图绘图 qps 的点位数据怎么打?就是充分利用中间件的设计,在一个请求 prepare 阶段就将该路由记录并获取进行打点。 使用 PromQL 语句就可以实现对对应信息看图的绘制。 1 2 3 4 5 6 // 过去1分钟 每秒请求 qps // sum 求和函数 // rate 计算范围向量中时间序列的每秒平均增长率 // api_request_alert_counter 指标名称 // service_name 和 subject 都是 label kv参数 sum(rate(api_request_alert_counter{service_name="gateway", subject="total"}[1m])) by (subject) 接口可用性看图绘图 接口可用性就是验证当前接口在单位时间内的处理正确的请求数目比上总体的请求数目,在打点的时候也讲到,我们业务代码 0 代表着正确返回,非 0 的代表着存在问题,这样就可以很简单的算出来接口的可用性。 1 2 3 4 5 6 7 8 9 10 11 12 // 过去1分钟 每秒接口可用性 // sum 求和函数 // rate 计算范围向量中时间序列的每秒平均增长率 // api_request_cost_status_count 指标名称 // service_name 和 code 都是 label kv参数 (sum(rate(api_request_cost_status_count{service_name="gateway", code="0"}[1m])) by (handler) / ( sum(rate(api_request_cost_status_count{service_name="gateway", code="0"}[1m])) by (handler) + sum(rate(api_request_cost_status_count{service_name="gateway", code!="0"}[1m])) by (handler)) ) * 100.0 接口 Pxx 耗时统计看图绘图 接口耗时统计打点依赖 prometheus api 中的 histogram 实现,在呈现打点耗时的时候有时候局部的某个耗时过长并不能进行直接反应整体的,我们只需要关注 SLO (服务级别目标)目标下是否达标即可。 1 2 3 4 // 过去1分钟 95% 请求最大耗时统计 // histogram_quantile 1000* histogram_quantile(0.95, sum(rate(api_request_cost_status_bucket{service_name="gateway",handler=~"v1.app.+"}[1m])) by (handler, le)) histogram_quantile(φ float, b instant-vector) 从 bucket 类型的向量 b 中计算 φ (0 ≤ φ ≤ 1) 分位数(百分位数的一般形式)的样本的最大值。(有关 φ 分位数的详细说明以及直方图指标类型的使用,请参阅直方图和摘要)。向量 b 中的样本是每个 bucket 的采样点数量。每个样本的 labels 中必须要有 le 这个 label 来表示每个 bucket 的上边界,没有 le 标签的样本会被忽略。直方图指标类型自动提供带有 _bucket 后缀和相应标签的时间序列。 上面是官方对于 histogram_quantile 函数的解释,关注的是 设置 φ 分位数 对应的 bucket 桶,但是实际中有 分位数计算误差的问题。 Prometheus 官方 histogram 设置的默认 buckets 如下: 1 DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} 这里可以看到我们的接口指标分界时间,每一个请求的耗时都会根据具体的设置的 bucket 的范围落到不同的区间内,这里设置的桶的范围直接影响到计算值的准确度(上面所提到的 分位数计算误差问题)。 报警 及时、准确 使用 Prometheus 的 Alert Manager 就可以对服务进行报警,但是如何及时又准确的报警,已经如何合理设置报警,我们就要引入 SLO 的概念,在实际的业务场景中,我们会发现某个接口某个时间段的耗时是一组离散的点: 我们可以看到大部分的请求可以在 1s 之内就可以快速的返回,只有个别的请求可能由于网络的抖动、应用短暂升级或者其他因素导致过慢,若是我们直接设置接口最大请求耗时超过2s(持续一个时间段),那我们就面临着疯狂的告警轰炸,同时告警也就失去了针对某个接口的异常活动做出提示供开发人员处理的意义。 服务级别目标(Service-level objective,SLO)是指服务提供者向客户作出的服务保证的量化指标。服务级别目标与服务级别协议有所不同。服务级别协议是指服务提供者向客户保证会提供什么样的服务,服务级别目标则是服务的量化说明。 Service-level objective 服务级别目标 比方说我们发现上面的 90% 请求都在 1s 内返回,我们就可以只需要对 90% 请求耗时做监控分析其调用链路并告警。 举个栗子,比方说我们一个首页的接口 /v1/home/page 99% 的请求可以在 500ms 内返回,只有个别的请求超过 2s+ 的时间,大多数情况下我们就不会关心这 1%的请求,那我们就可以定制一个 持续 1分钟首页 99% 请求耗时大于 1s的报警,这样当我们收到报警的时候,我们就可以第一时间知道首页出现了问题,我们就可以根据报警及时处理。 **业务的报警是与接口的实现与调用链路的复杂度是紧密结合在一起的,根据不同的业务场景,配置合理的报警才满足我们及时准确的要求。**反之就是配置过高不灵敏、往往线上已经出现了好久报警就是没有,配置过低,分分钟触发报警,对业务开发人员增加了排查问题的时间成本。 遇到的问题 收集指标过大拉取超时 由于我们是 gateway BFF 层做得指标,本身的路由的基数就比较大,热点路由就有好几百个,再算上对路由的打点、耗时、错误码等等的打点,导致我们每台机器的指标数量都比较庞大,最终指标汇总的时候下游的 prometheus 节点拉取经常出现耗时问题。 前期解决方案比较粗暴,就是修改 prometheus job 的拉取频率及其超时时间,这样可以解决超时问题,但是带来的结果就是最后通过 grafana 看板进行看图包括报警收集上来的点位数据延迟大,并且随着我们指标的设置越来越多的话必然会出现超时问题。 目前的解决方案就是做分布式,采用 prometheus 联邦集群的方式来解决指标收集过大的问题,采用了分布式,就可以将机器分组收集汇总,之后就可以成倍速的缩小 prometheus 拉取的压力。 动态收集机器指标 因为我们机器都是部署在集群上并且会随着活动大促动态调整机器的数量,联邦集群中配置文件最重要的就是配置各个收集节点指标的 IP:Port ,我们不可能每次都去手动维护这个配置,成本比较高,那么我们就需要将配置动态写入,针对此问题,在 leader 的建议下,使用运维服务树拿到该节点下的机器的 Ip,使用脚本程序动态维护起来就非常方便了,默认 Prometheus 是 20s 读取一次配置。 请求的耗时看图与报警不准确 这个问题是在我们的业务中,请求耗时最常见的是在 2s 之内返回,但是通过 Prometheus histogram 对应 1-2s 的请求会落在 le 为 2.5 桶中,导致报警误报,我们看日志中的请求在 1.* s 的都算在 2.5 的桶上,而报警的配置是 大于 2s, emmm 1 DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} 之后根据我们的业务场景调整了一下,使用了自己的 CustomBuckets: 1 CustomBuckets = []float64{.01, .025, .05, .1, .25, .5, 1, 1.5, 2, 3, 4, 8} References Prometheus 官方文档 Prometheus 翻译文档 wiki SLO 服务级别目标 wiki 累积直方图 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
来小米,一起玩 !!!
永远相信美好的事情即将发生 欢迎大家联系我,让我为你内推吧,小米众多岗位等你来选,不清楚岗位信息的可以联系我(关注微信公众号「Debug客栈」直接发送消息即可),我会给你发对应的内推部门及岗位,也可以联系我查询内推情况,感觉 OK,你就来吧! 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
Git 命令 reset 和 revert 的区别
前言 在团队开发中,使用 Git 作为版本开发工具,可以便捷地协同多人管理并行开发,但是由于自己或者其他人代码提交污染了远程分支,就需要对远程代码进行恢复操作,Git 提供了 reset 和 revert 两种命令来进行恢复操作,这两种操作效果是截然不同的,不太清楚这个原理的同学需要了解一下,以免在实际的开发过程中翻车,导致线上远程仓库不可逆转的操作。 首先从英文释义来讲,reset 是重置的意思,revert 是恢复、还原的意思,作为 Coder ,第一感觉 reset 的效果比 revert 更猛一些,实际情况也的确如此,让我们一起探讨一下吧。 背景 Git 的每一次提交都是一次 commit,上图可以看到在时间线上有三次提交,此时 HEAD 指向 main 分支,main 分支又指向最新的 Commit3。 HEAD 是指向当前分支的最新提交的指针,可以在任意分支进行切换; main (master)分支,是一个 git 代码仓库的主分支也是默认分支; commit 每一次提交代码都会产生一个 commit id 来标识工作区的变更与改动。 实践出真理 为了直接明白的了解其原理,我这里在 github 上创建一个空白的仓库,按照上图创建三次提交: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 commit b0ef8f9125226af8f06ff1aba7c1f1fc83adea9b (HEAD -> master, origin/master) Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 16:36:39 2021 +0800 feat add 3.go commit 338bf3e30983d34074f37a18b3ff80ea9bca75f0 Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 16:36:09 2021 +0800 feat add 2.go commit 6b166ed34962da08d944e2b1d3f36d9015dd8f35 Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 16:35:16 2021 +0800 feat add 1.go Git Reset git reset 的作用是将 HEAD 指向指定的版本上去: 1 使用 git log 查看提交记录: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 commit b0ef8f9125226af8f06ff1aba7c1f1fc83adea9b (HEAD -> master, origin/master) Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 16:36:39 2021 +0800 feat add 3.go commit 338bf3e30983d34074f37a18b3ff80ea9bca75f0 Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 16:36:09 2021 +0800 feat add 2.go commit 6b166ed34962da08d944e2b1d3f36d9015dd8f35 Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 16:35:16 2021 +0800 feat add 1.go 这里可以看到我们提交了三次记录,我们现在想恢复到第一次 commit 提交的时候。 2 使用 git reset –hard 命令操作: 1 2 ➜ demo git:(master) git reset --hard 6b166ed34962da08d944e2b1d3f36d9015dd8f35 HEAD 现在位于 6b166ed feat add 1.go 再次查看 git log : 1 2 3 4 5 commit 6b166ed34962da08d944e2b1d3f36d9015dd8f35 (HEAD -> master) Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 16:35:16 2021 +0800 feat add 1.go 此时我们可以看到已经恢复到了第一次提交代码的时候,目前我们是使用 git reset --hard 的方式,其实这里存在着三种方式,TODO 下一篇 git 操作讲一下。 这时候我们只是讲本地的 HEAD 指向了 main 分支的 commit 1,但是远程并没有变更,此时需要强行推一下就可以了。 3 使用git push -f 强行推送到远程: 1 2 3 4 ➜ demo git:(master) git push -f 总共 0(差异 0),复用 0(差异 0),包复用 0 To github.com:debuginn/demo.git + b98f95e...6b166ed master -> master (forced update) 此时我们可以看到远程也没有了我们之前提交的三次记录而是只有第一次的提交记录。 在团队合作的共同操作一个仓库的时候, git reset 命令一定要慎重使用,在使用的时候一定要再三确认其他同学的代码是否会被重置操作而导致代码丢失,导致一些提交记录的丢失,这些都是不可逆的,一定要慎重。 Git revert git revert 是用来重做某一个 commit 提交的内容,在我们原始的提交之中,我们会发现分支上面有创建了一个新的 commit 提交,而此时我们对于想重做的某个 commit 提交的内容都不存在了: 1 使用git log查看提交记录: 1 2 3 4 Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 16:36:39 2021 +0800 feat add 3.go 2 使用git revert命令重做操作: 1 2 3 4 5 ➜ demo git:(master) git revert 338bf3e30983d34074f37a18b3ff80ea9bca75f0 删除 2.go [master ef822b7] Revert "feat add 2.go" 1 file changed, 9 deletions(-) delete mode 100644 2.go 再次查看 git log : 1 2 3 4 5 6 7 8 9 10 11 12 13 commit ef822b71c33a2dbbdaa350fddcfa14e8fc55e543 (HEAD -> master, origin/master) Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 17:12:00 2021 +0800 Revert "feat add 2.go" This reverts commit 338bf3e30983d34074f37a18b3ff80ea9bca75f0. commit b0ef8f9125226af8f06ff1aba7c1f1fc83adea9b Author: debuginn <debuginn@icloud.com> Date: Tue Sep 21 17:05:39 2021 +0800 feat add 3.go 可以看到当前已经重做了一下 commit 2 的提交,已经讲 2.go 删除掉了。 可以看到 github 上面有了四次提交记录。 总结 git reset和git revert都是属于重新恢复工作区以及远程提交的方式,但这两种操作有着截然不同的结果: git reset是将之前的提交记录全部抹去,将 HEAD 指向自己重置的提交记录,对应的提交记录都不复存在; git revert 操作是将选择的某一次提交记录 重做,若之后又有提交,提交记录还存在,只是将指定提交的代码给清除掉。 选择合适的方式回滚自己的代码在团队合作中很重要,但是要慎重操作,不要丢失代码哦。 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页
Debug
入驻第1年
使用数字人民币兑换建党100周年纪念币
今天使用数字人民币兑换了建党100周年纪念币,过程比较坎坷,不过最终还是兑换成功了。 预约纪念币成功后,今天中秋假期,正好去兑换纪念币,小雨转中雨 ☁️,作为多年没有使用纸质人民币的我实在是没有钱来兑换纪念币,之后搜索了一下附近可以兑换人民币的营业厅,都在千米之外,算了…… 突然想到前两天美团有一个数字人民币的活动,下载了数字人民币 APP,研究了一下,发现有工商银行支持数字人民币,之后搜寻了一下网点,发现北京地区都是支持数字人民币的了,之后我就去申请了工商银行电子钱包,往里面转了 200 元钱,之后去银行网点ing。 到了之后工作人员引领到专门兑换纪念币柜台,我问了一下是否可以使用数字人民币兑换,好家伙,社死瞬间,一下来了 6 个工作人员看我操作,柜台小姐姐说没有操作过数字人民币付款,之后那我当一下小白鼠 ? ? 操作出来数字人民币支付二维码页面,之后扫描发现不能使用 emmmmm,尴尬,看了提示,原来是让我下载工行的 APP,之后使用上面的数字人民币进行支付,一通下载注册之后,再次去柜台兑换,扫码 => 支付,等了 5s 左右,最终成功兑换了纪念币,现在想想,我应该是第一个使用数字人民币兑换纪念币的第一人了吧。 数字人民币未来由国家导向大力推广,会使人民的支付更加便捷,不过个人建议纸质币保留下来,照顾不会使用手机的老年群体,总之,技术的进步,未来看来我们都是为了一串数字而奋斗喽。 关注微信公众号,第一时间获取最新内容,让我们一起变得更强!Debug客栈:订阅本站· 文章归档· 我的项目· 友情链接· 我的使用· 飞湾计划· 摄影展集· 我的主页

© 2026 好站网HaoZhan.wang 1.5 版权所有

苏ICP备19065220号-4    萌ICP备20269980号    茶ICP备2026050346号
本站数据    2026年报    版本历史    关于本站