摘要:如何避免內存泄露內存泄漏很常見,特別是前端去寫后端程序,閉包運用不當,循環引用等都會導致內存泄漏。有的時候很難避免一些可能產生內存泄漏的問題,可以利用每次調用都在一個沙箱環境下調用,用完回收調。
某一天用戶反饋打開的頁面白屏幕,怎么定位到產生錯誤的原因呢?日常某次發布怎么確定發布會沒有引入bug呢?此時捕獲到代碼運行的bug并上報是多么的重要。
既然捕獲錯誤并上報是日常開發中不可缺少的一環,那怎么捕獲到錯誤呢?萬能的**try...catch**
try{ throw new Error() } catch(e) { // handle error }
看上去錯誤捕獲是多么的簡單,然而下面的場景下就不能捕獲到了
try { setTimeout(() => { throw new Error("error") }) } catch (e) { // handle error }
你會發現上面的例子中的錯誤不能正常捕獲,看來錯誤捕獲并不是這樣簡單**try...catch**就能搞定,當然你也可以為異步函數包裹一層**try...catch**來處理。
瀏覽器中,**window.onerror**來捕獲你的錯誤
window.onerror = function (msg, url, row, col, error) { console.log("error"); console.log({ msg, url, row, col, error }) };
捕獲到錯誤后就可以將錯誤上報,上報方式很簡單,你可以通過創建簡單的**img**,通過**src**指定上報的地址,當然為了避免上報發送過多的請求,可以對上報進行合并,合并上報??梢远〞r將數據進行上報到服務端。
但但你去看錯誤上報的信息的時候,你會發現一些這樣的錯誤**Script error**
因為瀏覽器的同源策略,對于不同域名的錯誤,都拋出了**Script error**,怎么解決這個問題呢?特別是現在基本上js資源都會放在cdn上面。
解決方案
1:所有的資源都放在同一個域名下。但是這樣也會存在問題是不能利用cdn的優勢。
2:增加跨域資源支持,在cdn 上增加支持主域的跨域請求支持,在script 標簽加**crossorigin**屬性
在使用Promise過程中,如果你沒有catch,那么可以這樣來捕獲錯誤
window.addEventListener("unhandledrejection", function(err, promise) { // handle error here, for example log });如何在NodeJs中捕獲錯誤
NodeJs中的錯誤捕獲很重要,因為處理不當可能導致服務雪崩而不可用。當然了不僅僅知道如何捕獲錯誤,更應該知道如何避免某些錯誤。
當你寫一個函數的時候,你也許曾經思考過當函數執行的時候出現錯誤的時候,我是應該直接拋出throw,還是使用callback或者event emitter還是其它方式分發錯誤呢?
我是否應該檢查參數是否是正確的類型,是不是null
如果參數不符合的時候,你怎么辦呢?拋出錯誤還是通過callback等方式分發錯誤呢?
如果保存足夠的錯誤來復原錯誤現場呢?
如果去捕獲一些異常錯誤呢?try...catch還是domain
操作錯誤往往發生在運行時,并非由于代碼bug導致,可能是由于你的系統內存用完了或者是由于文件句柄用完了,也可能是沒有網絡了等等
編碼錯誤那就比較容易理解了,可能是undefined卻當作函數調用,或者返回了不正確的數據類型,或者內存泄露等等
你可以記錄一下錯誤,然后什么都不做
你也可以重試,比如因為鏈接數據庫失敗了,但是重試需要限制次數
你也可以將錯誤告訴前端,稍后再試
也許你也可以直接處理,比如某個路徑不存在,則創建該路徑
錯誤編碼是不好處理的,因為是由于編碼錯誤導致的。好的辦法其實重啟該進程,因為
你不確定某個編碼錯誤導致的錯誤會不會影響其它請求,比如建立數據庫鏈接錯誤由于編碼錯誤導致不能成功,那么其它錯誤將導致其它的請求也不可用
或許在錯誤拋出之前進行IO操作,導致IO句柄無法關閉,這將長期占有內存,可能導致最后內存耗盡整個服務不可用。
上面提到的兩點其實都沒有解決問題根本,應該在上線前做好測試,并在上線后做好監控,一旦發生類似的錯誤,就應該監控報警,關注并解決問題
在同步函數中,直接throw出錯誤
對于一些異步函數,可以將錯誤通過callback拋出
async/await可以直接使用try..catch捕獲錯誤
EventEmitter拋出error事件
一個NodeJs運用,僅僅從碼層面是很難保證穩定運行的,還要從運維層面去保障。
單進程的nodejs一旦掛了,整個服務也就不可用了,所以我萌需要多個進程來保障服務的可用,某個進程只負責處理其它進程的啟動,關閉,重啟。保障某個進程掛掉后能夠立即重啟。
可以參考TSW中多進程的設計。master負責對worker的管理,worker和master保持這心跳監測,一旦失去,就立即重啟之。
process.on("uncaughtException", function(err) { console.error("Error caught in uncaughtException event:", err); }); process.on("unhandleRejection", function(err) { // TODO })
上面捕獲nodejs中異常的時候,可以說是很暴力。但是此時捕獲到異常的時候,你已經失去了此時的上下文,這里的上下文可以說是某個請求。假如某個web服務發生了一些異常的時候,還是希望能夠返回一些兜底的內容,提升用戶使用體驗。比如服務端渲染或者同構,即使失敗了,也可以返回個靜態的html,走降級方案,但是此時的上下文已經丟失了。沒有辦法了。
function domainMiddleware(options) { return async function (ctx, next) { const request = ctx.request; const d = process.domain || domain.create(); d.request = request; let errHandler = (err) => { ctx.set("Content-Type", "text/html; charset=UTF-8"); ctx.body = options.staticHtml; }; d.on("error", errHandler); d.add(ctx.request); d.add(ctx.response); try { await next(); } catch(e) { errHandler(e) } }
上面是一個簡單的koa2的domain的中間件,利用domain監聽error事件,每個請求的Request, Response對象在發生錯誤的時候,均會觸發error?事件,當發生錯誤的時候,能夠在有上下文的基礎上,可以走降級方案。
內存泄漏很常見,特別是前端去寫后端程序,閉包運用不當,循環引用等都會導致內存泄漏。
不要阻塞Event Loop的執行,特別是大循環或者IO同步操作
for ( var i = 0; i < 10000000; i++ ) { var user = {}; user.name = "outmem"; user.pass = "123456"; user.email = "outmem[@outmem](/user/outmem).com"; }
上面的很長的循環會導致內存泄漏,因為它是一個同步執行的代碼,將在進程中執行,V8在循環結束的時候,是沒辦法回收循環產生的內存的,這會導致內存一直增長。還有可能原因是,這個很長的執行,阻塞了node進入下一個Event loop, 導致隊列中堆積了太多等待處理已經準備好的回調,進一步加劇內存的占用。那怎么解決呢?
可以利用setTimeout將操作放在下一個loop中執行,減少長循環,同步IO對進程的阻.阻塞下一個loop 的執行,也會導致應用的性能下降
模塊的私有變量和方法都會常駐在內存中
var leakArray = []; exports.leak = function () { leakArray.push("leak" + Math.random()); };
在node中require一個模塊的時候,最后都是形成一個單例,也就是只要調用該函數一下,函數內存就會增長,閉包不會被回收,第二是leak方法是一個私有方法,這個方法也會一直存在內存。加入每個請求都會調用一下這個方法,那么內存一會就炸了。
這樣的場景其實很常見
// main.js function Main() { this.greeting = "hello world"; } module.exports = Main;
var a = require("./main.js")(); var b = require("./main.js")(); a.greeting = "hello a"; console.log(a.greeting); // hello a console.log(b.greeting); // hello a
require得到是一個單例,在一個服務端中每一個請求執行的時候,操作的都是一個單例,這樣每一次執行產生的變量或者屬性都會一直掛在這個對象上,無法回收,占用大量內存。
其實上面可以按照下面的調用方式來調用,每次都產生一個實例,用完回收。
var a = new require("./main.js"); // TODO
有的時候很難避免一些可能產生內存泄漏的問題,可以利用vm每次調用都在一個沙箱環境下調用,用完回收調。
最后就是避免循環引用了,這樣也會導致無法回收
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/8070.html
摘要:淺談前端中的錯誤捕獲某一天用戶反饋打開的頁面白屏幕,怎么定位到產生錯誤的原因呢日常某次發布怎么確定發布會沒有引入呢此時捕獲到代碼運行的并上報是多么的重要。 淺談前端中的錯誤捕獲 某一天用戶反饋打開的頁面白屏幕,怎么定位到產生錯誤的原因呢?日常某次發布怎么確定發布會沒有引入bug呢?此時捕獲到代碼運行的bug并上報是多么的重要。 既然捕獲錯誤并上報是日常開發中不可缺少的一環,那怎么捕獲到...
摘要:如何避免內存泄露內存泄漏很常見,特別是前端去寫后端程序,閉包運用不當,循環引用等都會導致內存泄漏。有的時候很難避免一些可能產生內存泄漏的問題,可以利用每次調用都在一個沙箱環境下調用,用完回收調。 某一天用戶反饋打開的頁面白屏幕,怎么定位到產生錯誤的原因呢?日常某次發布怎么確定發布會沒有引入bug呢?此時捕獲到代碼運行的bug并上報是多么的重要。 既然捕獲錯誤并上報是日常開發中不可缺少的...
摘要:搭建一個應用,少不了一個主文件,不少人根據各自喜好來定義名字,像??偨Y一個完整的由個部分組成,大家只要把主文件當成白雪公主,把個組成部分當作七個小矮人就行了,哈哈,這個記法真天才。 前言 Node妹子的問世,著實讓我們前端攻城獅興奮了一把,尤其本屌聽說Javascript可以寫服務端后,興奮的像是看到了二次元蘿莉的胖子...(●?●)。呃哼...YY先到這里,原諒本屌是個二次元蘿莉控。...
摘要:如果有錯誤,則到的第二個回調函數中,對錯誤進行處理。假設第一個的第一個回調沒有返回一個對象,那么第二個的調用者還是原來的對象,只不過其的值變成了第一個中第一個回調函數的返回值。 ES6標準出爐之前,一個幽靈,回調的幽靈,游蕩在JavaScript世界。 正所謂: 世界本沒有回調,寫的人多了,也就有了})})})})})。 Promise的興起,是因為異步方法調用中,往往會出現回調函數一...
閱讀 3831·2021-10-12 10:12
閱讀 1461·2021-10-11 10:58
閱讀 2297·2021-10-09 10:01
閱讀 2608·2021-09-24 09:48
閱讀 2704·2021-09-09 11:38
閱讀 3531·2019-08-30 15:44
閱讀 1728·2019-08-30 14:22
閱讀 523·2019-08-29 12:42