環境資訊中心綜合外電;黃鈺婷 翻譯;林大利 審校;稿源:Mongabay
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※教你寫出一流的銷售文案?
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※回頭車貨運收費標準
※別再煩惱如何寫文案,掌握八大原則!
※超省錢租車方案
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※推薦台中搬家公司優質服務,可到府估價
摘錄自2020年5月19日自由時報報導
南韓LG化學再傳工安意外,位在忠清南道瑞山市的催化劑工廠,在當地時間今(19)日下午2時20分(台灣時間下午1時20分)左右爆炸失火,造成1死2傷。
《韓聯社》報導,該化工廠的催化劑疑似在過高的壓力下爆炸而引發大火,火勢已在下午3時30分(台灣時間下午2時30分)被撲滅,據消防部門表示,沒有有害化學物質外洩。目前該設施已關閉,警方和消防部門將在清理現場後調查確切事故原因。
本月7日,LG化學在印度投資的一家化工廠發生重大事故,廠內兩座5000公噸級苯乙烯儲存槽因不明原因發生嚴重外洩,造成12人死亡,1000多人住院。
建築
公害污染
生活環境
污染治理
國際新聞
南韓
化學工廠
工安事故
化工廠爆炸
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※回頭車貨運收費標準
※推薦評價好的iphone維修中心
※超省錢租車方案
※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!
※推薦台中搬家公司優質服務,可到府估價
摘錄自2020年5月19日自由時報報導
美國國防部已對國會提出一項提案,擴大對稀土的投資上限,以停止對中國的依賴程度,這些稀土可以用來製造飛彈和彈藥、極音速武器,及相關電子產品。如果美國可以重新生產稀土,中國打「稀土牌」的威脅程度將大幅降低。
根據《國防新聞》報導,美國國防部希望提高《國防生產法》的支出上限,在開採稀土上提升至最高17.5億美元(約新台幣523億),在微電子晶片上增至3.5億美元(約新台幣104億),當涉及到極音速武器時,將會沒有上限。據悉,此提案已於本月初提出,已納入國會正在起草的年度國防政策法案。
美國防部副部長洛德(Ellen Lord)去年8月曾表示,國防部正與澳洲進行談判,要求其為美軍提供稀土。澳洲Lynas公司擁有稀土礦,同時在馬來西亞也有精煉廠,可能是此計劃的核心。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※回頭車貨運收費標準
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※推薦評價好的iphone維修中心
※教你寫出一流的銷售文案?
※台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!
※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!
運動版全身大幅採用超高張力鋼板,並在車身關鍵部位進行了強化,安全性進一步提升。在主動安全性方面,T600運動版更將安全防護展現得淋漓盡致,安全配一應俱全。ESC車身穩定系統、HAC上坡輔助系統、前後倒車雷達及360°可視倒車影像等安全配置,與6方位安全氣囊、盲點信息系統、紅外夜視系統、TpMS智能胎壓監測、可選裝的HUD抬頭显示系統等尖端科技配備聯合上演重重壁壘,出色安全,呼之欲出,滿滿自信應對挑戰,盡享出行便利。
12月6日,中國汽車場地越野錦標賽(COC)廈門站比賽圓滿結束,也是最後一場分站賽,在各組別激烈的決賽搶分大戰中,眾泰T600越野車隊在汽油廠商組中奪得頭籌,車手鹿丙龍奪取該組冠軍,並與隊友徐瑩一起為車隊捧回了廠商杯冠軍殊榮,從而擴大了在年度積分方面的領先優勢,眾泰已經在今年分站比賽中已獲得六連冠,高歌猛進,一步步接近年度總冠軍。
作為本年度分站賽的最後一站,各個車隊之間的競爭日趨白熱化,尤其是之前比賽積分接近的車隊及隊員,比賽前已經是“劍拔弩張”,力爭本站取得更好排名和積分。眾泰車隊隊長楊逵如是向記者說道:“相對來說,我們在汽油改裝組的優勢要大一些,汽油廠商組和奇瑞車隊比較接近,由於總決賽採用雙倍積分的賽制,能否最終獲得全年總冠軍,廈門站比賽顯得尤為重要”。
【場地航拍圖】
【車隊大營】
【眾泰T600戰車】
汽油廠商組
本次比賽最大的變化就是之前因嚴重違規被禁賽的長安CS75車隊,重新回到了比賽。針對COC廈門站比賽形勢的變化,眾泰T600越野車隊對參賽陣容也進行了微調,喬旭與刁志剛攜手出擊汽油改裝組,鹿丙龍回歸汽油廠商組,和徐瑩搭檔。
在6圈的第一輪預賽中,車手們都拿出渾身解數,以求跑出好成績,從而得到決賽中最好的發車位置。眾泰車隊的鹿丙龍和徐瑩不負眾望,以小組第三、第四的成績闖進決賽,一起進入決賽的還有長安CS75車隊的文凡和孟斌。
“我們自身和車輛都調整到了最好狀態,對下午進行的決賽充滿信心”,眾泰T600越野車隊的車手鹿丙龍在決賽前向記者如是說。決賽中,鹿丙龍的表現堪稱“完美”,以絕對優勢力壓長安CS75車隊的孟斌和文凡獲得本組冠軍,其隊友徐瑩獲得本組季軍,獲得本組亞軍的是來自長安CS75車隊的孟斌,同時,鹿丙龍和徐瑩為眾泰T600越野車隊爭得了汽油廠商組的車隊團體冠軍獎盃。
汽油改裝組
眾泰T600越野車隊的喬旭在第一輪預賽中並不順利,他在第三圈的時候賽車出現失誤,賽車在幾處急彎都發生失控打橫,這極大地影響了喬旭的成績,儘管第二輪成績出色,但仍與決賽失之交臂,其隊友刁志剛以小組第三的成績征戰第二天進行的決賽。
6日下午的決賽中,車手刁志剛一人獨自面對其他三位車手的多面夾擊,面對發車位置不力的劣勢,刁志剛仍然奮起直追,最後以微小差距獲得了本小組的季軍,獲得本組冠亞軍的是來自另外兩支車隊的趙向前和童振榮。
作為“主流價值SUV”的眾泰T600,同眾泰車隊一樣,已然成為乘用車銷售市場上的佼佼者,早已進入月銷量“萬台俱樂部”,2016年1-10月份更是實現了94371台的銷量,以月均近萬台的銷量位居自主品牌中型SUV銷量榜首。
而且2016年眾泰汽車推出了更為年輕時尚的眾泰T600運動版,作為在眾泰T600優勢平台上推出的車型,眾泰T600運動版同樣以其年輕時尚又不乏沉穩的外觀、越級的配置在整個市場中還是有着普遍好評,銷量也是芝麻開花節節高。
眾泰T600運動版全系標配10寸中控彩色大屏,內容豐富。而Tye-net智控系統的優勢融入,實現手機操遠程控愛車,娛樂隨行,舒心便利。
此外,眾泰T600運動版還配備了一鍵啟動/無鑰匙進入、紅外夜視系統、腳步感應式電動尾門等尖端科技配備,讓駕乘人員充分享受科技智能帶來的便捷體驗。電動全景天窗、电子駐車系統、前排座椅分級加熱、雙區獨立自動恆溫空調、手機無線充電、方向盤/座椅/后視鏡三項聯動記憶功能、全液晶儀錶盤、定速巡航等帶來更加細緻入微的貼心關懷,讓出行一路無虞。
安全配置方面,眾泰T600運動版同級領先的安全性讓駕乘者無需前瞻後顧,無憂外出。運動版全身大幅採用超高張力鋼板,並在車身關鍵部位進行了強化,安全性進一步提升。在主動安全性方面,T600運動版更將安全防護展現得淋漓盡致,安全配一應俱全。ESC車身穩定系統、HAC上坡輔助系統、前後倒車雷達及360°可視倒車影像等安全配置,與6方位安全氣囊、盲點信息系統、紅外夜視系統、TpMS智能胎壓監測、可選裝的HUD抬頭显示系統等尖端科技配備聯合上演重重壁壘,出色安全,呼之欲出,滿滿自信應對挑戰,盡享出行便利。
而眾泰T600運動版不只是在外觀上吸引目光,在內飾的色彩搭配上,更是可圈可點,整個車內空間看起來既神秘又科技時尚。
眾泰T600運動版擁有的2807mm的傲人軸距,有效保證了車輛的駕乘空間。車內豐富的儲物空間為日常儲物提供了便利,而且後排座椅放倒後進一步拓展了後備箱空間,可以盡情享受眾泰T600運動版帶來的寬適空間。
動力方面,T600運動版提供1.5T及2.0T兩種發動機車型,1.5T渦輪增壓發動機與5速手動變速器搭配出黃金動力組合,最大功率達119KW,最大扭矩達215N·m。更加值得期待的是其2.0T車型,搭配使用旋鈕換擋式6速雙離合或5速手動變速器,最大功率140KW,最大扭矩250N·m,百公里加速只需9.26秒,充分提高了燃油的利用率,更加的節能環保,同時降低了用車成本。眾泰T600運動版,就是這樣讓你既有“面子”,又有“裡子”。
還有值得一說的是,眾泰T600在2015年J.D.power亞太公司發布的中國新車質量研究(IQS)報告,眾泰T600在中型SUV中pp100(每百車問題數)為100,優於中型SUV平均水平(pp100:106),全國綜合排名第13位,位列中型SUV中國品牌第二名。
2016年度COC總決賽將於12月中旬在廣西柳州打響,總決賽將實行雙倍積分制,各組別總決賽冠軍將收穫50分,這也讓之前積分落後並不太多的車手擁有了翻身逆轉的機會,那眾泰T600能否攜勢而來,獲得全年比賽的總冠軍,讓我們拭目以待!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多該如何選擇?
※智慧手機時代的來臨,RWD網頁設計為架站首選
※評比南投搬家公司費用收費行情懶人包大公開
※回頭車貨運收費標準
※網頁設計最專業,超強功能平台可客製化
※別再煩惱如何寫文案,掌握八大原則!
什麼是async/await?
await和async是.NET Framework4.5框架、C#5.0語法裏面出現的技術,目的是用於簡化異步編程模型。
async和await的關係?
async和await是成對出現的。
async出現在方法的聲明裡,用於批註一個異步方法。光有async是沒有意義的。
await出現在方法內部,Task前面。只能在使用async關鍵字批註的方法中使用await關鍵字。
private async Task DoSomething()
{
await Task.Delay(TimeSpan.FromSeconds(10));
}
async/await會創建新的線程嗎?
不會。async/await關鍵字本身是不會創建新的線程的,但是被await的方法內部一般會創建新的線程。
asp.net mvc/webapi action中使用async/await會提高請求的響應速度嗎?
不會。
我們都知道web應用不同於winform、wpf等客戶端應用,客戶端應用為了保證UI渲染的一致性往往都是採用單線程模式,這個UI線程稱為主線程,如果在主線程做耗時操作就會導致程序界面假死,所以客戶端開發中使用多線程異步編程非常必要。
可web應用本身就是多線程模式,服務器會為每個請求分配工作線程。
既然async/await不能創建新線程,又不能使提高請求的響應速度,那.NET Web應用中為什麼要使用async/await異步編程呢?
在 web 服務器上,.NET Framework 維護用於處理 ASP.NET 請求的線程池。 當請求到達時,將調度池中的線程以處理該請求。 如果以同步方式處理請求,則處理請求的線程將在處理請求時處於繁忙狀態,並且該線程無法處理其他請求。
在啟動時看到大量併發請求的 web 應用中,或具有突發負載(其中併發增長突然增加)時,使 web 服務調用異步會提高應用程序的響應能力。 異步請求與同步請求所需的處理時間相同。 如果請求發出需要兩秒鐘時間才能完成的 web 服務調用,則該請求將需要兩秒鐘,無論是同步執行還是異步執行。 但是,在異步調用期間,線程在等待第一個請求完成時不會被阻止響應其他請求。 因此,當有多個併發請求調用長時間運行的操作時,異步請求會阻止請求隊列和線程池的增長。
下面用代碼來實際測試一下:
public ActionResult Index()
{
DateTime startTime = DateTime.Now;//進入DoSomething方法前的時間
var startThreadId = Thread.CurrentThread.ManagedThreadId;//進入DoSomething方法前的線程ID
DoSomething();//耗時操作
DateTime endTime = DateTime.Now;//完成DoSomething方法的時間
var endThreadId = Thread.CurrentThread.ManagedThreadId;//完成DoSomething方法后的線程ID
return Content($"startTime:{ startTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } startThreadId:{ startThreadId }<br/>endTime:{ endTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } endThreadId:{ endThreadId }<br/><br/>");
}
/// <summary>
/// 耗時操作
/// </summary>
/// <returns></returns>
private void DoSomething()
{
Thread.Sleep(10000);
}
使用瀏覽器開3個標籤頁進行測試(因為瀏覽器對同一域名下的連接數有限制,一般是6個左右,所以就弄3個吧):
可以看到耗時都是10秒,開始和結束的線程ID一致。
public async Task<ActionResult> Index()
{
DateTime startTime = DateTime.Now;//進入DoSomething方法前的時間
var startThreadId = Thread.CurrentThread.ManagedThreadId;//進入DoSomething方法前的線程ID
await DoSomething();//耗時操作
DateTime endTime = DateTime.Now;//完成DoSomething方法的時間
var endThreadId = Thread.CurrentThread.ManagedThreadId;//完成DoSomething方法后的線程ID
return Content($"startTime:{ startTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } startThreadId:{ startThreadId }<br/>endTime:{ endTime.ToString("yyyy-MM-dd HH:mm:ss:fff") } endThreadId:{ endThreadId }<br/><br/>");
}
/// <summary>
/// 耗時操作
/// </summary>
/// <returns></returns>
private async Task DoSomething()
{
await Task.Run(() => Thread.Sleep(10000));
}
結果:
可以看到3次請求中,雖然耗時都是10秒,但是出現了開始和結束的線程ID不一致的情況,ID為22的這個線程工作了多次,這意味着使用異步方式在同一時間可以處理更多的請求!(這句話不太對,3個同步的併發請求必然會分配3個工作線程,但是使用異步的話,同一個線程可以被多個請求重複利用。因為線程池的線程數量是有上限的,所以在相同數量的線程下,使用異步方式能處理更多的請求。)
IIS默認隊列長度:
await關鍵字不會阻塞線程直到任務完成。 它將方法的其餘部分註冊為任務的回調,並立即返回。 當await的任務最終完成時,它將調用該回調,並因此在其中斷時繼續執行方法。
簡單來說:就是使用同步方法時,線程會被耗時操作一直佔有,直到耗時操作完成。而使用異步方法,程序走到await關鍵字時會立即return,釋放線程,餘下的代碼會放進一個回調中(Task.GetAwaiter()的UnsafeOnCompleted(Action)回調),耗時操作完成時才會回調執行,所以async/await是語法糖,其本質是一個狀態機。
那是不是所有的action都要用async/await呢?
不是。一般的磁盤IO或者網絡請求等耗時操作才考慮使用異步,不要為了異步而異步,異步也是需要消耗性能的,使用不合理會適得其反。
async/await異步編程不能提升響應速度,但是可以提升響應能力(吞吐量)。異步和同步各有優劣,要合理選擇,不要為了異步而異步。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多該如何選擇?
※智慧手機時代的來臨,RWD網頁設計為架站首選
※評比南投搬家公司費用收費行情懶人包大公開
※回頭車貨運收費標準
※網頁設計最專業,超強功能平台可客製化
※別再煩惱如何寫文案,掌握八大原則!
這個階段會給cute-dl添加循環層,使之能夠支持RNN–循環神經網絡. 具體目標包括:
RNN模型用來捕捉序列數據的特徵. 給定一個長度為T的輸入系列\(X=(x_1, x_2, .., X_T)\), RNN層輸出一個長度為T的序列\(H=(h_1, h_2, …, H_T)\), 對於任意時間步t, 可以表示為:
\[H_t = δ(X_tW_x + H_{t-1}W_h + b), \quad t = 2, 3, .., T \]
函數δ是sigmoid函數:
\[δ = \frac{1}{1 + e^{-x}} \]
\(H_t\)包含了前面第1到t-1步的所有信息。 和CNN層類似, CNN層在空間上共享參數, RNN層在時間步上共享參數\(W_x, W_h, b\).
RNN層中隱藏層的數量為T-2, 如果T較大(超過10), 反向傳播是很容易出現梯度爆炸. GRU和LSTM就是為了解決這個問題而誕生, 這兩種模型,可以讓RNN能夠支持長度超過1000的輸入序列。
GRU使用了不同功能的門控單元, 分別捕捉序列上不同時間跨度的的依賴關係。每個門控單元都會都有獨立的參數, 這些參數在時間步上共享。
GRU的門控單元有:
\(R_t = δ(X_tW^r_x + H_{t-1}W^r_h + b^r)\), 重置門用於捕捉短期依賴關係.
\(U_t = δ(X_tW^u_x + H_{t-1}W^u_h + b^u)\), 更新門用於捕捉長期依賴關係
\(\bar{H}_t = tanh(X_t\bar{W}_x + (R_t * H_{t-1})\bar{W}_h + \bar{b})\)
除此之外, 還有一個輸出單元:
\(H_t = U_t * H_{t-1} + (1-U_t)*\bar{H}_t\)
LSTM的設計思路和GRU類似, 同樣使用了多個門控單元:
\(I_t = δ(X_tW^i_x + H_{t-1}W^i_h + b^i)\), 輸入門,過濾記憶門的輸出.
\(F_t = δ(X_tW^f_x + H_{t-1}W^f_h + b^f)\), 遺忘門, 過濾前面時間步的記憶.
\(O_t = δ(X_tW^o_x + H_{t-1}W^o_h + b^o)\), 輸出門, 過濾當前時間步的記憶.
\(M_t = tanh(X_tW^m_x + H_{t-1}W^m_h + b^m)\), 記憶門.
它還有自己獨有的記憶單元和輸出單元:
\(\bar{M}_t = F_t * \bar{M}_{t-1} + I_t * M_t\)
\(H_t = O_t * tanh(\bar{M}_t)\)
設計要求:
文件: cutedl/rnn_layers.py, 類名: RNN
這個類是RNN層基類, 它主要功能是控制向前傳播和向後傳播的主流程.
初始化參數:
'''
out_units 輸出單元數
in_units 輸入單元數
stateful 保留當前批次的最後一個時間步的狀態作為下一個批次的輸入狀態, 默認False不保留
RNN 的輸入形狀是(m, t, in_units)
m: batch_size
t: 輸入系列的長度
in_units: 輸入單元數頁是輸入向量的維數
輸出形狀是(m, t, out_units)
'''
def __init__(self, out_units, in_units=None, stateful=False, activation='linear'):
向前傳播
def forward(self, in_batch, training):
m, T, n = in_batch.shape
out_units = self.__out_units
#所有時間步的輸出
hstatus = np.zeros((m, T, out_units))
#上一步的輸出
pre_hs = self.__pre_hs
if pre_hs is None:
pre_hs = np.zeros((m, out_units))
#隱藏層循環過程, 沿時間步執行
for t in range(T):
hstatus[:, t, :] = self.hiden_forward(in_batch[:,t,:], pre_hs, training)
pre_hs = hstatus[:, t, :]
self.__pre_hs = pre_hs
#pdb.set_trace()
if not self.stateful:
self.__pre_hs = None
return hstatus
反向傳播
def backward(self, gradient):
m, T, n = gradient.shape
in_units = self.__in_units
grad_x = np.zeros((m, T, in_units))
#pdb.set_trace()
#從最後一個梯度開始反向執行.
for t in range(T-1, -1, -1):
grad_x[:,t,:], grad_hs = self.hiden_backward(gradient[:,t,:])
#pdb.set_trace()
if t - 1 >= 0:
gradient[:,t-1,:] = gradient[:,t-1,:] + grad_hs
#pdb.set_trace()
return grad_x
\[sigmoid = \frac{1}{1+e^{-x}} \]
\[\frac{d}{dx}sigmoid = sigmoid(1-sigmoid) \]
\[tanh = \frac{e^x – e^{-x}}{e^x + e^{-x}} \]
\[\frac{d}{dx}tanh = 1 – tanh^2 \]
文件: cutedl/rnn_layers.py, 類名: GateUint
門控單元是RNN層基礎的參數單元. 和Dense層類似,它是Layer的子類,負責學習和使用參數。但在學習和使用參數的方式上有很大的不同:
下面我們會主要看一下GateUnit特別之處的代碼.
在__ init__方法中定義參數和棧:
#3個參數
self.__W = None #當前時間步in_batch權重參數
self.__Wh = None #上一步輸出的權重參數
self.__b = None #偏置量參數
#輸入棧
self.__hs = [] #上一步輸出
self.__in_batchs = [] #當前時間步的in_batch
正向傳播:
def forward(self, in_batch, hs, training):
W = self.__W.value
b = self.__b.value
Wh = self.__Wh.value
out = in_batch @ W + hs @ Wh + b
if training:
#向前傳播訓練時把上一個時間步的輸出和當前時間步的in_batch壓棧
self.__hs.append(hs)
self.__in_batchs.append(in_batch)
#確保反向傳播開始時參數的梯度為空
self.__W.gradient = None
self.__Wh.gradient = None
self.__b.gradient = None
return self.activation(out)
反向傳播:
def backward(self, gradient):
grad = self.activation.grad(gradient)
W = self.__W.value
Wh = self.__Wh.value
pre_hs = self.__hs.pop()
in_batch = self.__in_batchs.pop()
grad_in_batch = grad @ W.T
grad_W = in_batch.T @ grad
grad_hs = grad @ Wh.T
grad_Wh = pre_hs.T @ grad
grad_b = grad.sum(axis=0)
#反向傳播計算
if self.__W.gradient is None:
#當前批次第一次
self.__W.gradient = grad_W
else:
#累積當前批次的所有梯度
self.__W.gradient = self.__W.gradient + grad_W
if self.__Wh.gradient is None:
self.__Wh.gradient = grad_Wh
else:
self.__Wh.gradient = self.__Wh.gradient + grad_Wh
if self.__b.gradient is None:
self.__b.gradient = grad_b
else:
self.__b.gradient = self.__b.gradient + grad_b
return grad_in_batch, grad_hs
文件: cutedl/rnn_layers.py, 類名: GRU
隱藏單初始化:
def set_parent(self, parent):
super().set_parent(parent)
out_units = self.out_units
in_units = self.in_units
#pdb.set_trace()
#重置門
self.__g_reset = GateUnit(out_units, in_units)
#更新門
self.__g_update = GateUnit(out_units, in_units)
#候選輸出門
self.__g_cddout = GateUnit(out_units, in_units, activation='tanh')
self.__g_reset.set_parent(self)
self.__g_update.set_parent(self)
self.__g_cddout.set_parent(self)
#重置門乘法單元
self.__u_gr = MultiplyUnit()
#輸出單元
self.__u_out = GRUOutUnit()
向前傳播:
def hiden_forward(self, in_batch, pre_hs, training):
gr = self.__g_reset.forward(in_batch, pre_hs, training)
gu = self.__g_update.forward(in_batch, pre_hs, training)
ugr = self.__u_gr.forward(gr, pre_hs, training)
cddo = self.__g_cddout.forward(in_batch, ugr, training)
hs = self.__u_out.forward(gu, pre_hs, cddo, training)
return hs
反向傳播:
def hiden_backward(self, gradient):
grad_gu, grad_pre_hs, grad_cddo = self.__u_out.backward(gradient)
#pdb.set_trace()
grad_in_batch, grad_ugr = self.__g_cddout.backward(grad_cddo)
#計算梯度的過程中需要累積上一層輸出的梯度
grad_gr, g_pre_hs = self.__u_gr.backward(grad_ugr)
grad_pre_hs = grad_pre_hs + g_pre_hs
g_in_batch, g_pre_hs = self.__g_update.backward(grad_gu)
grad_in_batch = grad_in_batch + g_in_batch
grad_pre_hs = grad_pre_hs + g_pre_hs
g_in_batch, g_pre_hs = self.__g_reset.backward(grad_gr)
grad_in_batch = grad_in_batch + g_in_batch
grad_pre_hs = grad_pre_hs + g_pre_hs
#pdb.set_trace()
return grad_in_batch, grad_pre_hs
文件: cutedl/rnn_layers.py, 類名: LSTM
隱藏單元初始化:
def set_parent(self, layer):
super().set_parent(layer)
in_units = self.in_units
out_units = self.out_units
#輸入門
self.__g_in = GateUnit(out_units, in_units)
#遺忘門
self.__g_forget = GateUnit(out_units, in_units)
#輸出門
self.__g_out = GateUnit(out_units, in_units)
#記憶門
self.__g_memory = GateUnit(out_units, in_units, activation='tanh')
self.__g_in.set_parent(self)
self.__g_forget.set_parent(self)
self.__g_out.set_parent(self)
self.__g_memory.set_parent(self)
#記憶單元
self.__memory_unit =LSTMMemoryUnit()
#輸出單元
self.__out_unit = LSTMOutUnit()
向前傳播:
def hiden_forward(self, in_batch, hs, training):
g_in = self.__g_in.forward(in_batch, hs, training)
#pdb.set_trace()
g_forget = self.__g_forget.forward(in_batch, hs, training)
g_out = self.__g_out.forward(in_batch, hs, training)
g_memory = self.__g_memory.forward(in_batch, hs, training)
memory = self.__memory_unit.forward(g_forget, g_in, g_memory, training)
cur_hs = self.__out_unit.forward(g_out, memory, training)
return cur_hs
反向傳播:
def hiden_backward(self, gradient):
#pdb.set_trace()
grad_out, grad_memory = self.__out_unit.backward(gradient)
grad_forget, grad_in, grad_gm = self.__memory_unit.backward(grad_memory)
grad_in_batch, grad_hs = self.__g_memory.backward(grad_gm)
tmp1, tmp2 = self.__g_out.backward(grad_out)
grad_in_batch += tmp1
grad_hs += tmp2
tmp1, tmp2 = self.__g_forget.backward(grad_forget)
grad_in_batch += tmp1
grad_hs += tmp2
tmp1, tmp2 = self.__g_in.backward(grad_in)
grad_in_batch += tmp1
grad_hs += tmp2
return grad_in_batch, grad_hs
接下來, 驗證示例將會構建一個簡單的RNN模型, 使用該模型擬合一個正餘弦疊加函數:
#採樣函數
def sample_function(x):
y = 3*np.sin(2 * x * np.pi) + np.cos(x * np.pi) + np.random.uniform(-0.05,0.05,len(x))
return y
訓練數據集和測試數據集在這個函數的不同定義域區間內樣. 訓練數據集的採樣區間為[1, 200.01), 測試數據集的採樣區間為[200.02, 240.002). 模型任務是預測這個函數值的序列.
示例代碼在examples/rnn/fit_function.py文件中.
def fit_gru():
model = Model([
rnn.GRU(32, 1),
nn.Filter(),
nn.Dense(32),
nn.Dense(1, activation='linear')
])
model.assemble()
fit('gru', model)
訓練報告:
def fit_lstm():
model = Model([
rnn.LSTM(32, 1),
nn.Filter(),
nn.Dense(2),
nn.Dense(1, activation='linear')
])
model.assemble()
fit('lstm', model)
訓練報告:
這個階段,框架新增了RNN的兩個最常見的實現:GRU和LSTM, 相應地增加了它需要的激活函數. cute-dl已經具備了構建最基礎RNN模型的能力。通過驗證發現, GRU模型和LSTM模型在簡單任務上都表現出了很好的性能。會添加嵌入層,使框架能夠構建文本分類任務的模型,然後在imdb-review(電影評價)數據集上進行驗證.
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
※回頭車貨運收費標準
首先來介紹下什麼是優雅地停止,簡而言之,就是對應用進程發送停止指令之後,能保證正在執行的業務操作不受影響,可以繼續完成已有請求的處理,但是停止接受新請求。
在 Spring Boot 2.3 中增加了新特性優雅停止,目前 Spring Boot 內置的四個嵌入式 Web 服務器(Jetty、Reactor Netty、Tomcat 和 Undertow)以及反應式和基於 Servlet 的 Web 應用程序都支持優雅停止。
下面,我們先用新版本嘗試下:
首先創建一個 Spring Boot 的 Web 項目,版本選擇 2.3.0.RELEASE,Spring Boot 2.3.0.RELEASE 版本內置的 Tomcat 為 9.0.35。
然後需要在 application.yml 中添加一些配置來啟用優雅停止的功能:
# 開啟優雅停止 Web 容器,默認為 IMMEDIATE:立即停止
server:
shutdown: graceful
# 最大等待時間
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
其中,平滑關閉內置的 Web 容器(以 Tomcat 為例)的入口代碼在 org.springframework.boot.web.embedded.tomcat 的 GracefulShutdown 里,大概邏輯就是先停止外部的所有新請求,然後再處理關閉前收到的請求,有興趣的可以自己去看下。
內嵌的 Tomcat 容器平滑關閉的配置已經完成了,那麼如何優雅關閉 Spring 容器了,就需要 Actuator 來實現 Spring 容器的關閉了。
然後加入 actuator 依賴,依賴如下所示:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
然後接着再添加一些配置來暴露 actuator 的 shutdown 接口:
# 暴露 shutdown 接口
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: shutdown
其中通過 Actuator 關閉 Spring 容器的入口代碼在 org.springframework.boot.actuate.context 包下 ShutdownEndpoint 類中,主要的就是執行 doClose() 方法關閉並銷毀 applicationContext,有興趣的可以自己去看下。
配置搞定后,然後在 controller 包下創建一個 WorkController 類,並有一個 work 方法,用來模擬複雜業務耗時處理流程,具體代碼如下:
@RestController
public class WorkController {
@GetMapping("/work")
public String work() throws InterruptedException {
// 模擬複雜業務耗時處理流程
Thread.sleep(10 * 1000L);
return "success";
}
}
然後,我們啟動項目,先用 Postman 請求 http://localhost:8080/work 處理業務:
然後在這個時候,調用 http://localhost:8080/actuator/shutdown 就可以執行優雅地停止,返回結果如下:
{
"message": "Shutting down, bye..."
}
如果在這個時候,發起新的請求 http://localhost:8080/work,會沒有反應:
再回頭看第一個請求,返回了結果:success。
其中有幾條服務日誌如下:
2020-05-20 23:05:15.163 INFO 102724 --- [ Thread-253] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2020-05-20 23:05:15.287 INFO 102724 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete
2020-05-20 23:05:15.295 INFO 102724 --- [ Thread-253] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
從日誌中也可以看出來,當調用 shutdown 接口的時候,會先等待請求處理完畢后再優雅地停止。
到此為止,Spring Boot 2.3 的優雅關閉就講解完了,是不是很簡單呢?如果是在之前不支持優雅關閉的版本如何去做呢?
在這裏介紹 GitHub 上 issue 里 Spring Boot 開發者提供的一種方案:
選取的 Spring Boot 版本為 2.2.6.RELEASE,首先要實現 TomcatConnectorCustomizer 接口,該接口是自定義 Connector 的回調接口:
@FunctionalInterface
public interface TomcatConnectorCustomizer {
void customize(Connector connector);
}
除了定製 Connector 的行為,還要實現 ApplicationListener<ContextClosedEvent> 接口,因為要監聽 Spring 容器的關閉事件,即當前的 ApplicationContext 執行 close() 方法,這樣我們就可以在請求處理完畢後進行 Tomcat 線程池的關閉,具體的實現代碼如下:
@Bean
public GracefulShutdown gracefulShutdown() {
return new GracefulShutdown();
}
private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
private volatile Connector connector;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
this.connector.pause();
Executor executor = this.connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
try {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
log.warn("Tomcat thread pool did not shut down gracefully within 30 seconds. Proceeding with forceful shutdown");
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
有了定製的 Connector 回調,還需要在啟動過程中添加到內嵌的 Tomcat 容器中,然後等待監聽到關閉指令時執行,addConnectorCustomizers 方法可以把定製的 Connector 行為添加到內嵌的 Tomcat 中,具體代碼如下:
@Bean
public ConfigurableServletWebServerFactory tomcatCustomizer() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.addConnectorCustomizers(gracefulShutdown());
return factory;
}
到此為止,內置的 Tomcat 容器平滑關閉的操作就完成了,Spring 容器優雅停止上面已經說過了,再次就不再贅述了。
通過測試,同樣可以達到上面那樣優雅停止的效果。
本文主要講解了 Spring Boot 2.3 版本和舊版本的優雅停止,避免強制停止導致正在處理的業務邏輯會被中斷,進而導致產生業務異常的情形。
另外使用 Actuator 的同時要注意安全問題,比如可以通過引入 security 依賴,打開安全限制並進行身份驗證,設置單獨的 Actuator 管理端口並配置只對內網開放等。
本文的完整代碼在 https://github.com/wupeixuan/SpringBoot-Learn 的 graceful-shutdown 目錄下。
最好的關係就是互相成就,大家的在看、轉發、留言三連就是我創作的最大動力。
參考
https://github.com/spring-projects/spring-boot/issues/4657
https://github.com/wupeixuan/SpringBoot-Learn
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※Google地圖已可更新顯示潭子電動車充電站設置地點!!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※別再煩惱如何寫文案,掌握八大原則!
※網頁設計最專業,超強功能平台可客製化
※回頭車貨運收費標準
最近在整理優化.net代碼時,發現幾個很不友好的處理現象:登錄判斷、權限認證、日誌記錄、異常處理等通用操作,在項目中的action中到處都是。在代碼優化上,這一點是很重要着力點。這時.net中的過濾器、攔截器(Filter)就派上用場了。現在根據這幾天的實際工作,對其做了一個簡單的梳理,分享出來,以供大家參考交流,如有寫的不妥之處,多多指出,多多交流。
.net中的Filter中主要包括以下4大類:Authorize(授權),ActionFilter(自定義),HandleError(錯誤處理)。
|
過濾器 |
類名 |
實現接口 |
描述 |
|
授權 |
AuthorizeAttribute |
IAuthorizationFilter |
此類型(或過濾器)用於限制進入控制器或控制器的某個行為方法,比如:登錄、權限、訪問控制等等 |
|
異常 |
HandleErrorAttribute |
IExceptionFilter |
用於指定一個行為,這個被指定的行為處理某個行為方法或某個控制器裏面拋出的異常,比如:全局異常統一處理。 |
|
自定義 |
ActionFilterAttribute |
IActionFilter和IResultFilter |
用於進入行為之前或之後的處理或返回結果的之前或之後的處理,比如:用戶請求日誌詳情日誌記錄 |
認證授權主要是對所有action的訪問第一入口認證,對用戶的訪問做第一道監管過濾攔截閘口。
實現方式:需要自定義一個類,繼承AuthorizeAttribute並重寫OnAuthorization,在OnAuthorization中能夠獲取到用戶請求的所有Request信息,其實我們做的所有認證攔截操作,其所有數據支撐都是來自Request中。
具體驗證流程設計:
IP白名單:這個主要針對的是API做IP限制,只有指定IP才可訪問,非指定IP直接返回
請求頻率控制:這個主要是控制用戶的訪問頻率,主要是針對API做,超出請求頻率直接返回。
登錄認證:登錄認證一般我們採用的是通過在請求的header中傳遞token的方式來進行驗證,這樣即使用與一般的MVC登錄認證,也使用與API接口的Auth認證,並且也不依賴於用戶前端js設置等。
授權認證:授權認證就簡單了,主要是驗證該用戶是否具有該權限,如果不具有,直接做下相應的返回處理。
MVC和API異同:
命名空間:MVC:System.Web.Http.Filters;API:System.Web.Mvc
注入方式:在注入方式上,主要包括:全局->控制器Controller->行為Action
全局註冊:針對所有系統的所有Aciton都使用
Controller:只針對該Controller下的Action起作用
Action:只針對該Action起作用
其中全局註冊,針對MVC和API還有一些差異:
MVC在 FilterConfig.cs中注入
filters.Add(new XYHMVCAuthorizeAttribute());
API 在 WebApiConfig.cs 中注入
config.Filters.Add(new XYHAPIAuthorizeAttribute());
注意事項:在實際使用中,針對認證授權,我們一般都是添加全局認證,但是,有的action又不需要做認證,比如本來的登錄Action等等,那麼該如何排除呢?其實也很簡單,我們只需要在自定定義一個Attribute集成Attribute,或者系統的AllowAnonymousAttribute,在不需要驗證的action中只需要註冊上對於的Attribute,並在驗證前做一個過濾即可,比如:
// 有 AllowAnonymous 屬性的接口直接開綠燈
if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
{
return;
}
API AuthFilterAttribute實例代碼
/// <summary> /// 授權認證過濾器 /// </summary> public class XYHAPIAuthFilterAttribute : AuthorizationFilterAttribute { /// <summary> /// 認證授權驗證 /// </summary> /// <param name="actionContext">請求上下文</param> public override void OnAuthorization(HttpActionContext actionContext) { // 有 AllowAnonymous 屬性的接口直接開綠燈 if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any()) { return; } // 在請求前做一層攔截,主要驗證token的有效性和驗簽 HttpRequest httpRequest = HttpContext.Current.Request; // 獲取apikey var apikey = httpRequest.QueryString["apikey"]; // 首先做IP白名單校驗 MBaseResult<string> result = new AuthCheckService().CheckIpWhitelist(FilterAttributeHelp.GetIPAddress(actionContext.Request), apikey); // 檢驗時間戳 string timestamp = httpRequest.QueryString["Timestamp"]; if (result.Code == MResultCodeEnum.successCode) { // 檢驗時間戳 result = new AuthCheckService().CheckTimestamp(timestamp); } if (result.Code == MResultCodeEnum.successCode) { // 做請求頻率驗證 string acitonName = actionContext.ActionDescriptor.ActionName; string controllerName = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName; result = new AuthCheckService().CheckRequestFrequency(apikey, $"api/{controllerName.ToLower()}/{acitonName.ToLower()}"); } if (result.Code == MResultCodeEnum.successCode) { // 簽名校驗 // 獲取全部的請求參數 Dictionary<string, string> queryParameters = httpRequest.GetAllQueryParameters(); result = new AuthCheckService().SignCheck(queryParameters, apikey); if (result.Code == MResultCodeEnum.successCode) { // 如果有NoChekokenFilterAttribute 標籤 那麼直接不做token認證 if (actionContext.ActionDescriptor.GetCustomAttributes<XYHAPINoChekokenFilterAttribute>().Any()) { return; } // 校驗token的有效性 // 獲取一個 token string token = httpRequest.Headers.GetValues("Token") == null ? string.Empty : httpRequest.Headers.GetValues("Token")[0]; result = new AuthCheckService().CheckToken(token, apikey, httpRequest.FilePath); } } // 輸出 if (result.Code != MResultCodeEnum.successCode) { // 一定要實例化一個response,是否最終還是會執行action中的代碼 actionContext.Response = new HttpResponseMessage(HttpStatusCode.OK); //需要自己指定輸出內容和類型 HttpContext.Current.Response.ContentType = "text/html;charset=utf-8"; HttpContext.Current.Response.Write(JsonConvert.SerializeObject(result)); HttpContext.Current.Response.End(); // 此處結束響應,就不會走路由系統 } } }
MVC AuthFilterAttribute實例代碼
/// <summary> /// MVC自定義授權 /// 認證授權有兩個重寫方法 /// 具體的認證邏輯實現:AuthorizeCore 這個裡面寫具體的認證邏輯,認證成功返回true,反之返回false /// 認證失敗處理邏輯:HandleUnauthorizedRequest 前一步返回 false時,就會執行到該方法中 /// 但是,我平時在應用過程中,一般都是在AuthorizeCore根據不同的認證結果,直接做認證后的邏輯處理 /// </summary> public class XYHMVCAuthorizeAttribute : AuthorizeAttribute { /// <summary> /// 認證邏輯 /// </summary> /// <param name="filterContext">過濾器上下文</param> public override void OnAuthorization(AuthorizationContext filterContext) { // 此處主要寫認證授權的相關驗證邏輯 // 該部分的驗證一般包括兩個部分 // 登錄權限校驗 // --我們的一般處理方式是,通過header中傳遞一個token來進行邏輯驗證 // --當然不同的系統在設計上也不盡相同,有的也會採用session等方式來驗證 // --所以最終還是根據其項目本身的實際情況來進行對應的邏輯操作 // 具體的頁面權限校驗 // --該部分的驗證是具體的到頁面權限驗證 // --我看有得小夥伴沒有做到這一個程度,直接將這一步放在前端js來驗證,這樣不是很安全,但是可以攔住小白用戶 // --當然有的系統根本就沒有做權限控制,那就更不需要這一個邏輯了。 // --所以最終還是根據其項目本身的實際情況來進行對應的邏輯操作 // 現在用一個粗暴的方式來簡單模擬實現過,用系統當前時間段秒廚藝3,取餘數 // 當餘數為0:認證授權通過 // 1:代表為登錄,調整至登錄頁面 // 2:代表無訪問權限,調整至無權限提示頁面 // 當然,在這也還可以做一些IP白名單,IP黑名單驗證 請求頻率驗證等等 // 說到這而,還有一點需要注意,如果我們選擇的是全局註冊該過濾器,那麼如果有的頁面根本不需要權限認證,比如登錄頁面,那麼我們可以給不需要權限的認證的控制器或者action添加一個特殊的註解 AllowAnonymous ,來排除 // 獲取Request的幾個關鍵信息 HttpRequest httpRequest = HttpContext.Current.Request; string acitonName = filterContext.ActionDescriptor.ActionName; string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; // 注意:如果認證不通過,需要設置filterContext.Result的值,否則還是會執行action中的邏輯 filterContext.Result = null; int thisSecond = System.DateTime.Now.Second; switch (thisSecond % 3) { case 0: // 認證授權通過 break; case 1: // 代表為登錄,調整至登錄頁面 // 只有設置了Result才會終結操作 filterContext.Result = new RedirectResult("/html/Login.html"); break; case 2: // 代表無訪問權限,調整至無權限提示頁面 filterContext.Result = new RedirectResult("/html/NoAuth.html"); break; } } }
自定義過濾器,主要是監控action請求前後,處理結果返回前後的事件。其中API只有請求前後的兩個方法。
|
重新方法 |
方法功能描述 |
使用於 |
|
OnActionExecuting |
一個請求在進入到aciton邏輯前執行 |
MVC、API |
|
OnActionExecuted |
一個請求aciton邏輯執行后執行 |
MVC、API |
|
OnResultExecuting |
對應的view視圖渲染前執行 |
MVC |
|
OnResultExecuted |
對應的view視圖渲染后執行 |
MVC |
在這幾個方法中,我們一般主要用來記錄交互日誌,記錄每一個步驟的耗時情況,以便後續系統優化使用。具體的使用,根據自身的業務場景使用。
其中MVC和API的異同點,和上面說的認證授權的異同類似,不在詳細說明。
下面的一個實例代碼:
API定義過濾器實例DEMO代碼
/// <summary> /// Action過濾器 /// </summary> public class XYHAPICustomActionFilterAttribute : ActionFilterAttribute { /// <summary> /// Action執行開始 /// </summary> /// <param name="actionContext"></param> public override void OnActionExecuting(HttpActionContext actionContext) { } /// <summary> /// action執行以後 /// </summary> /// <param name="actionContext"></param> public override void OnActionExecuted(HttpActionExecutedContext actionContext) { try { // 構建一個日誌數據模型 MApiRequestLogs apiRequestLogsM = new MApiRequestLogs(); // API名稱 apiRequestLogsM.API = actionContext.Request.RequestUri.AbsolutePath; // apiKey apiRequestLogsM.API_KEY = HttpContext.Current.Request.QueryString["ApiKey"]; // IP地址 apiRequestLogsM.IP = FilterAttributeHelp.GetIPAddress(actionContext.Request); // 獲取token string token = HttpContext.Current.Request.Headers.GetValues("Token") == null ? string.Empty : HttpContext.Current.Request.Headers.GetValues("Token")[0]; apiRequestLogsM.TOKEN = token; // URL apiRequestLogsM.URL = actionContext.Request.RequestUri.AbsoluteUri; // 返回信息 var objectContent = actionContext.Response.Content as ObjectContent; var returnValue = objectContent.Value; apiRequestLogsM.RESPONSE_INFOR = returnValue.ToString(); // 由於數據庫中最大隻能存儲4000字符串,所以對返回值做一個截取 if (!string.IsNullOrEmpty(apiRequestLogsM.RESPONSE_INFOR) && apiRequestLogsM.RESPONSE_INFOR.Length > 4000) { apiRequestLogsM.RESPONSE_INFOR = apiRequestLogsM.RESPONSE_INFOR.Substring(0, 2000); } // 請求參數 apiRequestLogsM.REQUEST_INFOR = actionContext.Request.RequestUri.Query; // 定義一個異步委託 ,異步記錄日誌 // Func<MApiRequestLogs, string> action = AddApiRequestLogs;//聲明一個委託 // IAsyncResult ret = action.BeginInvoke(apiRequestLogsM, null, null); } catch (Exception ex) { } } }
異常處理對於我們來說很常用,很好的利用異常處理,可以很好的避免全篇的try/catch。異常處理箱單很簡單,值需要自定義集成:ExceptionFilterAttribute,並自定義實現:OnException方法即可。
在OnException我們可以根據自身需要,做一些相應的邏輯處理,比如記錄異常日誌,便於後續問題分析跟進。
OnException還有一個很重要的處理,那就是對異常結果的統一包裝,返回一個很友好的結果給用戶,避免把一些不必要的信息返回給用戶。比如:針對MVC,那麼跟進不同異常,統一調整至友好的提示頁面等等;針對API,那麼我們可以一個統一的返回幾個封裝,便於用戶統一處理結果。
MVC 的異常處理實例代碼:
/// <summary> /// MVC自定義異常處理機制 /// 說道異常處理,其實我們腦海中的第一反應,也該是try/cache操作 /// 但是在實際開發中,很有可能地址錯誤根本就進入不到try中,又或者沒有被try處理到異常 /// 該類就發揮了作用,能夠很好的未經捕獲的異常,並做相應的邏輯處理 /// 自定義異常機制,主要集成HandleErrorAttribute 重寫其OnException方法 /// </summary> public class XYHMVCHandleError : HandleErrorAttribute { /// <summary> /// 處理異常 /// </summary> /// <param name="filterContext">異常上下文</param> public override void OnException(ExceptionContext filterContext) { // 我們在平時的項目中,異常處理一般有兩個作用 // 1:記錄異常的詳細日誌,便於事後分析日誌 // 2:對異常的統一友好處理,比如根據異常類型重定向到友好提示頁面 // 在這裏面既能獲取到未經處理的異常信息,也能獲取到請求信息 // 在此可以根據實際項目需要做相應的邏輯處理 // 下面簡單的列舉了幾個關鍵信息獲取方式 // 控制器名稱 注意,這樣獲取出來的是一個文件的全路徑 string contropath = filterContext.Controller.ToString(); // 訪問目錄的相對路徑 string filePath = filterContext.HttpContext.Request.FilePath; // url完整地址 string url = (filterContext.HttpContext.Request.Url.AbsoluteUri).ExUrlDeCode(); // 請求方式 post get string httpMethod = filterContext.HttpContext.Request.HttpMethod; // 請求IP地址 string ip = filterContext.HttpContext.Request.GetIPAddress(); // 獲取全部的請求參數 HttpRequest httpRequest = HttpContext.Current.Request; Dictionary<string, string> queryParameters = httpRequest.GetAllQueryParameters(); // 獲取異常對象 Exception ex = filterContext.Exception; // 異常描述信息 string exMessage = ex.Message; // 異常堆棧信息 string stackTrace = ex.StackTrace; // 根據實際情況記錄日誌(文本日誌、數據庫日誌,建議具體步驟採用異步方式來完成) filterContext.ExceptionHandled = true; // 模擬根據不同的做對應的邏輯處理 int statusCode = filterContext.HttpContext.Response.StatusCode; if (statusCode>=400 && statusCode<500) { filterContext.Result = new RedirectResult("/html/404.html"); } else { filterContext.Result = new RedirectResult("/html/500.html"); } } }
API 的異常處理實例代碼:
/// <summary> /// API自定義異常處理機制 /// 說道異常處理,其實我們腦海中的第一反應,也該是try/cache操作 /// 但是在實際開發中,很有可能地址錯誤根本就進入不到try中,又或者沒有被try處理到異常 /// 該類就發揮了作用,能夠很好的未經捕獲的異常,並做相應的邏輯處理 /// 自定義異常機制,主要集成ExceptionFilterAttribute 重寫其OnException方法 /// </summary> public class XYHAPIHandleError : ExceptionFilterAttribute { /// <summary> /// 處理異常 /// </summary> /// <param name="actionExecutedContext">異常上下文</param> public override void OnException(HttpActionExecutedContext actionExecutedContext) { // 我們在平時的項目中,異常處理一般有兩個作用 // 1:記錄異常的詳細日誌,便於事後分析日誌 // 2:對異常的統一友好處理,比如根據異常類型重定向到友好提示頁面 // 在這裏面既能獲取到未經處理的異常信息,也能獲取到請求信息 // 在此可以根據實際項目需要做相應的邏輯處理 // 下面簡單的列舉了幾個關鍵信息獲取方式 // action名稱 string actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName; // 控制器名稱 string controllerName =actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName; // url完整地址 string url = (actionExecutedContext.Request.RequestUri.AbsoluteUri).ExUrlDeCode(); // 請求方式 post get string httpMethod = actionExecutedContext.Request.Method.Method; // 請求IP地址 string ip = actionExecutedContext.Request.GetIPAddress(); // 獲取全部的請求參數 HttpRequest httpRequest = HttpContext.Current.Request; Dictionary<string, string> queryParameters = httpRequest.GetAllQueryParameters(); // 獲取異常對象 Exception ex = actionExecutedContext.Exception; // 異常描述信息 string exMessage = ex.Message; // 異常堆棧信息 string stackTrace = ex.StackTrace; // 根據實際情況記錄日誌(文本日誌、數據庫日誌,建議具體步驟採用異步方式來完成) // 自己的記錄日誌落地邏輯略 ...... // 構建統一的內部異常處理機制,相當於對異常做一層統一包裝暴露 MBaseResult<string> result = new MBaseResult<string>() { Code = MResultCodeEnum.systemErrorCode, Message = MResultCodeEnum.systemError }; actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.OK); //需要自己指定輸出內容和類型 HttpContext.Current.Response.ContentType = "text/html;charset=utf-8"; HttpContext.Current.Response.Write(JsonConvert.SerializeObject(result)); HttpContext.Current.Response.End(); // 此處結束響應,就不會走路由系統 } }
.net過濾器,我個人的一句話理解就是:對action的各個階段進行統一的監控處理等操作。.net過濾器中,其中每一個種過濾器的執行先後順序為:Authorize(授權)–>ActionFilter(自定義)–>HandleError(錯誤處理)
好了,就先聊到這而,如果什麼地方說的不對之處,多多指點和多多包涵。我自己寫了一個練習DEMO,裏面會有每一種情況的處理說明。有興趣的可以取下載下來看一看,謝謝。
DEMO在GitHub地址為:https://github.com/xuyuanhong0902/XYH.FilterTest.git
END
為了更高的交流,歡迎大家關注我的公眾號,掃描下面二維碼即可關注,謝謝:
認證授權
時間戳
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※想知道最厲害的網頁設計公司"嚨底家"!
※別再煩惱如何寫文案,掌握八大原則!
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※回頭車貨運收費標準
※台中搬家公司費用怎麼算?