IDEA自定義類註釋和方法註釋(自定義groovyScript方法實現多行參數註釋)

一、類註釋

1、打開設置面板:file -> setting -> Editor -> file and code Templates

選擇其中的inclues選項卡,並選擇File header,如圖。不要選擇Files選項卡再設置Class,這樣比較麻煩,而且這樣設置以後沒新建一個類都要自己寫一次Date。

2、在右邊編輯面板插入自己想要的註釋即可。其中${}是變量,需要在變量基本都在編輯款下面的Description,往下拉即可看到。
/*
* @Classname ${NAME}
*
* @Date ${DATE}
*
* @userName
*/
3、新建一個類,看是否自動加了註釋

 

二、方法註釋

1、打開設置面板:file -> setting -> Editor -> Live Templates

 

 2、新建一個Template Group…,命名隨意,假設為bokeyuan,然後選擇該組,點擊新建一個模板Live Template

 

3、名稱建議設為*,文本框輸入自己想要設置的註釋格式,右下角要選擇enter(原本是tab)。

 

 4、留意註釋格式,其中參數要直接寫變量$param$,開頭只有一個*號。寫好之後點擊上圖框中的edit variables

 

其中返回值return使用系統自帶的,下拉可以找到methodReturnType()

 

 

 5、自定義多行參數註釋

IDEA自帶的參數函數methodParameters()產出的註釋格式是這樣的:

/**
      * 
      * @param [a,b,c]
      * @return void
      * @throws 
      */

我們可能需要的是多行參數註釋:

/**
      * 
      * @param a
      * @param b
      * @param c
      * @return void
      * @throws 
      */

這個時候就要使用裏面的groovyScript()函數來自定義格式:

groovyScript("def result=''; def params=\"${_1}\".replaceAll('[\\\\[|\\\\]|\\\\s]', '').split(',').toList(); for(i = 0; i < params.size(); i++) {if(i == 0) result += '* @param ' + params[i] + ' ' + ((i < params.size() - 1) ? '\\n' : '');else result+='  * @param ' + params[i] + ' ' + ((i < params.size() - 1) ? '\\n' : '')}; return result", methodParameters())

直接複製在Expression裏面即可。

 

6、選擇語言,點擊Define勾選Java

 

 

有其他問題可以評論問我哦

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

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

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

萬級TPS億級流水-中台賬戶系統架構設計

萬級TPS億級流水-中台賬戶系統架構設計

標籤:高併發 萬級TPS 億級流水 賬戶系統

  • 背景
  • 業務模型
  • 應用層設計
  • 數據層設計
  • 日切對賬

背景

我們需要給所有前台業務提供統一的賬戶系統,用來支撐所有前台產品線的用戶資產管理,統一提供支持大併發萬級TPS、億級流水、數據強一致、風控安全、日切對賬、財務核算、審計等能力,在萬級TPS下保證絕對的數據準確性和數據溯源能力。

注:資金類系統只有合格和不合格,哪怕數據出現只有0.01分的差錯也是不合格的,局部數據不準也就意味着全局數據都不可信。

本文只分享系統的核心模型部分的設計,其他常規類的(如壓測驗收、系統保護策略-限流、降級、熔斷等)設計就不做多介紹,如果對其他方面有興趣歡迎進一步交流。

業務模型

基本賬戶管理: 根據交易的不同主體,可以分為個人賬戶機構賬戶
賬戶餘額在使用上沒有任何限制,很純粹的賬戶存儲、轉賬管理,可以滿足90%業務場景。

子賬戶功能: 一個用戶可以開通多個子賬戶,根據餘額屬性不同可以分為基本賬戶、過期賬戶,根據幣種不同可以分為人民幣賬戶、虛擬幣賬戶,根據業務形態不同可以自定義。
(不同賬戶的特定功能是通過賬戶上的賬戶屬性來區分實現。)

過期賬戶管理: 該賬戶中的餘額是會隨着進賬流水到期自動過期。
如:在某平台充值1000元送300元,其中300元是有過期時間的,但是1000元是沒有時間限制的。這裏的1000元存在你的基本賬戶中,300元存在你的過期賬戶中。

注:過期賬戶的每一筆入賬流水都會有一個到期時間。系統根據交易流水的到期時間,自動核銷用戶過期賬戶中的餘額,記為平台的確認收入。

賬戶組合使用:支持多賬戶組合使用,根據配置的優先扣減順序進行扣減餘額。比如:在 基本賬戶過期賬戶 (充值賬戶)中扣錢一般的順序是優先扣減過期賬戶的餘額。

應用層設計

根據上述業務模型,賬戶系統是一個典型的 數據密集型系統 ,業務層的邏輯不複雜。整個系統的設計關鍵點在於如何平衡大併發TPS和數據一致性。

熱點賬戶:前台直播類業務存在熱點賬戶問題,每到各種活動賽事的時候會存在 90%DAU 給少數幾個頭部主播打賞的場景。
DB就會有熱點行問題,由於 行鎖 關係併發一大肯定大量超時、RT突增DB活躍線程 增加等一系列問題,最終DB會被拖掛。

賬戶類系統有一個特點,原賬戶的扣減可以實時處理,目標賬戶可以異步處理,我們可以將轉賬動作拆解為兩個階段進行異步化。(可以參考銀行轉賬業務。)

比如:A給B轉賬100元,原賬戶A的100元餘額扣減可以同步處理,B賬戶的100增加可以異步處理。這樣哪怕10w人給主播打賞,這10w人的賬戶都是分散的,而主播的餘額增加則是異步處理的。

賬戶轉賬扣減A賬戶餘額,記錄A賬戶出賬流水,記錄B賬戶入賬流水,這三個動作可以在一個DBTransaction中處理,可以保證源賬戶進出帳一致性。目標賬戶B的入賬可以異步處理,為了保證萬無一失且滿足一定的實時性,需要兩步結合,程序里通過MQ走異步入賬,同時增加DB的兜底JOB定時掃描 入賬流水記錄未到賬的流水進行入賬。

我們通過異步化緩解熱點行處理,但是如果 收款方 強烈要求收款必須在一定的時間內完成,我們還是需要進一步處理,後面會講到。

過期賬戶: 通常過期賬戶用來管理贈送類賬戶,這類賬戶有一定的時效性,用戶在使用上也是優先扣減此類賬戶餘額。
這類使用需求其實覆蓋面不大,真正用戶賬戶餘額不使用等着被系統過期的很少,畢竟這是一個很傻的行為。

過期賬戶的兩種核銷情況:第一種是用戶使用過期賬戶時的核銷。第二種是某個過期流水到了過期時間,系統自動核銷記為平台的確認收入。

過期賬戶核銷邏輯:用戶充值1000元到基本賬戶,平台贈送300元到贈送賬戶。此時,基本賬戶記錄進賬流水+1000元,贈送賬戶記錄進賬流水+300元並且該筆流水的過期時間2020-12-29 23:59:59 (過期時間由前台業務方設置) 。

系統自動核銷:如果用戶不在此時間之前用完就會被系統自動划進平台的收入,贈送賬戶餘額扣減-300元。

用戶使用核銷:如果用戶在過期時間前陸續在使用贈送賬戶,比如使用100元,那麼我們需要核銷原本進賬的300元的那筆流水,減少-150元。
也就是說,該筆過期流水已經核銷掉150元,帶過期核銷150元,到期后只要核銷150元即可,而不是300元。

過期賬戶每次使用均產生待核銷負向流水,系統自動核銷前必須保證沒有任何負向流水記錄才可以去扣減贈送賬戶餘額。

考慮到極端情況下,剛好過期JOB在進行自動過期核銷,用戶又在此時使用過期賬戶,這點需要注意下。可以簡單通過加DB-X鎖解決,這個場景其實非常稀少。

數據層設計

在應用層設計的時候,我們通過異步化方式來繞開熱點問題。
同樣我們在設計數據層的時候也要考慮單次操作DB的性能,比如控制事務的大小,事務跨網絡的次數等問題。當然還包括金額存儲的精度問題,精度問題處理不好也會影響性能。

浮點數問題: 如果我們用浮點數近似值來存儲金額,那麼就一定會有偏差,隨着金額越大時間越長偏差就會越大。比較好的方式是通過整型來存儲,通過放大金額比例來達到不同的業務場景下對金額比率的要求。

正常的1.12元,存儲比率是1=100元,那麼表裡的存儲值就是112,不同的貨幣比例都可以自由縮放,永遠都可以保持最準確的精度。

分庫分表+讀寫分離: 根據業務特點和未來增量規劃,將DB分為16個邏輯庫,前期使用2個物理庫承載。16個邏輯庫,按照每次2倍擴容,最大擴容上限是16個物理庫。單實例的配置 8c 32g 2t 8000conn 9000iops

按照單次TPS-rt 1ms計算,TPS 1w 需求,每台承載5k TPS,單庫的活躍線程大概在8-10個(考慮網絡延遲)。
最後到達瓶頸的都是iops,因為只要rt足夠短,最終壓力都會在IO上。

分庫按照uid分為16個庫,賬戶表不分表默認16張。每張表按照 1kw*16=1.6 億個賬戶。

單表能存儲多少要綜合考慮,比如查詢類型,單次查詢的RT,冷熱數據佔比( innodb_buffer_pool 利用率)、是否充分發揮了索引,索引是否達到3星級別,索引片中沒有經常變更的字段等。

賬戶流水表按照日期分表365張,流水數據會隨着時間推移逐漸變成冷數據,定期歸檔冷數據。(這裏約定了,流水查詢只能按照uid+日期查詢。如果運營類的需求,要橫跨分片key獲取,走OLAP方案 clickhouse、hive等)

分庫分表採用阿里雲分佈式數據庫產品DRDS,1個主庫集群+2個讀庫集群(讀庫做了讀負載均衡,可以按需擴容)。

讀負載均衡器:https://github.com/Plen-wang/read-loadbalance

既然用了DRDS分佈式數據庫產品,那麼在查詢上需要充分考慮分片鍵的限制,如果存儲和查詢出現分片鍵衝突問題就需要我們手動計算分片路由,直接訪問物理節點。

訪問物理節點需要藉助DRDS專用SQL註釋子句來完成。

先通過 show node 查看物理DB ID、show topology from logic_table_name 查看物理表ID,然後在SQL帶上特定的註釋子句

SELECT /*+TDDL:scan('logic_table_name', real_table=("real_table_name"),node='real_db_node_id')*/ 
count(1) FROM logic_table_name ;

賬戶更新: 對賬戶更新都有一個前提就是賬戶已經開通,但是我們為了最大化賬戶系統在使用上的便利性,讓前台業務方不需要做初始化動作,由賬戶系統惰性初始化,比如發現賬戶不存在就自動初始化賬戶數據。

但是我們怎麼知道賬戶不存在,不可能每次都去查詢一次或者根據執行返回錯誤判斷。而且 update 語句是區分不了錯誤的 賬戶不存在 還是 餘額不足 或者其他原因。

那麼如何巧妙的解決這個問題,只要一次DB往返。

我們可以使用 Mysql INSERT INTO ... ON DUPLICATE KEY UPDATE ... 子句,但是該子句有一個限制就是不支持 where 子句。

-- cut_version 樂觀鎖、account_property 賬戶屬性
insert into tb_account(uid,balance,cut_version,account_property) values("%s",%d,%d,%d) ON DUPLICATE KEY UPDATE balance = balance + %d,cut_version = cut_version+1

其實不完全推薦使用這個方法,因為這個方法也有弊端就是將來 where 子句無法使用,還有一個辦法就是合併 賬戶查詢插入 為一條 sql 提交。

DB操作本身rt可能很短,但是如果跨網絡那麼事務的延遲會帶來DB的串行化增加,降低併發度,整體應用 rt就會增加。所以一個原則就是盡量不要跨網絡開事務,合併sql做一次事務提交,最短的事務周期,減少跨網絡的事務操作,如果我們將單次事務網絡交互減少2-3次,性能的提高可能會增加2-3倍,同樣由於網絡的不穩定抖動丟包對 999rt 線的影響也會減少2-3倍。

平衡好當前系統是業務密集型還是數據密集型
判斷當前系統是否有很強的業務層邏輯,是否要運用DDDRUP等強模型的工程方法。畢竟強模型高性能在落地的時候有些方面是衝突的,需要進一步藉助 CRQSGRASP等工程方法來解決。

單行熱點問題: 單行的TPS都是串行的,事務rt越短TPS就越高,按照1ms計算,差不多TPS就是1000。一般只有機構賬戶類型才會有這個需求。

我們可以將單行變成多行,增加行的并行度,加大賬戶操作的併發度。(這個方案要評估好寫入和查詢兩端需求)

id uid balance slot
1 10101010 1000 1
2 10101010 2000 2
3 10101010 3000 3
4 10101010 400 4
5 10101010 300 5
6 10101010 200 6
7 10101010 200 7
8 10101010 200 8
9 10101010 200 9
10 10101010 200 10
insert into tb_account (uid,balance,slot)
values(10101010, 1000, round(rand()*9)+1) 
on  duplicate key update balance=balance+values(balance)

這裏的 10slot*單個slot 1000TPS,理論上可以跑到1w,如果機構賬戶數據量很大,可以擴展slot個數。

賬戶的總餘額通過sum()匯總,如果業務場景中有餘額的頻繁sum()操作,可以通過增加餘額中間表,定期 insert into tb_account_total select sum(balance) total_balance from tb_account group by uid

通常機構賬戶的結算是有周期的(T+7、T+30等),而且基本是沒有併發,所以在賬戶餘額扣減方面就可以輕鬆處理。
有兩種實現方案:

第一種,賬戶餘額允許單個slot為負數,但是總的sum()是正數。通過子查詢來對餘額進行檢查。

insert into tb_account (uid, balance, slot)
select uid,-1000 as balance,round(rand() *9+ 1)
from(
    select uid, sum(balance) as ss
    from tb_account
    where uid= 10101010
    group by uid having ss>= 1000 for update) as tmp
on duplicate key update balance= balance+ values(balance)

第二種,如果條件允許可以藉助用戶自定義變量來在DB上完成餘額累計掃描,將可以扣減的slot的主鍵id返回給程序,但是只需要一次DB交互就可以獲取出可以扣減的賬戶solt,然後分別開始對slot賬戶進行扣減。

set @f:=0;
select * from tb_account where id in(select id from (select id, @f:=@f+balance from tb_account where @f<1000 order by id) as t);

第二種方案在默認的mysql數據庫上都是支持的,但是有些數據庫雲產品不支持,阿里雲rds是不支持的。

日切對賬

賬戶系統有一個基本的需求,就是每天餘額鏡像,簡單講就是餘額在每天的快照,用來做T+1對賬。
不管財務還是每季度的審計都會需要,最重要的是我們自己也需要對賬戶數據做摸底對賬。

由於每天產生上億的流水,這需要在大數據平台中完成。

日切對賬:昨天賬戶餘額前天賬戶餘額 = 昨天的流水前天的流水

比如,昨天的賬戶餘額是5000w,前台的賬戶餘額是4500w,差值就是500w。同樣道理,昨天的賬戶流水是5000w,前天的賬戶流水是4500w,那麼差值是500w,這就是沒問題的。

賬戶不僅有增加也有減少,可能昨天賬戶餘額比前天賬戶餘額差值是-500w,但是流水也要是-500w才行。

由於每天會產生億級的流水,用傳統的全量抽取不現實,這類數據抽取的速度都會有延遲,而且對賬最重要的是時間點必須非常精準,才能保證餘額和流水是對得上的。

要不然會出現HDFS的分區是2020-06-10號,但是該分區里有2020-06-11的數據,就是因為拉取的時候會延遲到第二天。這個問題也可以通過增加拉取sql的條件限制來解決這個問題,但是無法做到0點瞬間鏡像全部賬戶。

解決方案: 全量餘額+binlog增量更新
1.賬戶表,先做一次全量同步。
2.DB的所有變更通過binlog(默認row複製)進到數倉。(因為 binlog 是基於發生時間的,所以無所謂我們是不是在0點去計算鏡像)
3.T+1跑JOB的時候,獲取前一天的賬戶餘額,然後通過 binlog 來覆蓋前天與昨天的交集部分。

由於數倉的 binlog 數據都是增量的,所以要想取到正確的全量數據需要用到一定的技巧。

select app_id,sub_type,sum(amount) records_amount from (
      select *,row_number()over(partition by id order by updated_at) as rn
      from hive_db_table
      where dt='${YESTERDAY}'
  ) t where t.rn=1
       group by t.sub_type,t.app_id

使用 hive 開窗函數 row_number()over() 對同樣的id進行分組,然後獲取最新的一條數據就是賬戶在T的最後的值。

作者:王清培(趣頭條 Tech Leader)

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

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

打入中國市場,Hyundai選陸電池廠供混合動力車

南韓汽車製造商現代汽車(Hyundai Motor)首次選擇與中國電池製造商合作,由總部位於福建的寧德時代(CATL)為現代插電式混合動力車Sonata 提供電池,預計將在2018 年上半年打入中國市場。日經報導分析,這項合作是出於中國反外商情結的壓力。

日經新聞報導,隨著中國加緊推進新能源汽車應對空氣污染,中國政府正在研擬一項電動車份額草案,在中國市場上,外國汽車製造商必須讓電動車的銷量達到總銷售量的8%,若外商達不到指標,很有可能會被迫購買中國車商的積分,等於對中國車商提供經濟資助。先前草案規定2019 年將比率提升到10%,2020 年到12%,都被汽車製造商拒絕。

此外,中國政府為了幫助國內電池業者,規定必須通過中國的安全標準才符合補貼資格,東京的汽車製造商GLM 認為,未來很可能只有使用中國製造的電池的電動車才能在中國市場販售。GLM 準備在2019 年在中國市場推出4 人座跑車G4,號稱電動車的法拉利。

中國現在是全球最大的電動車市場,每年賣出30 萬台,中國政府最新的5 年計畫目標到2020 年一年增加到500 萬台。中國市場是現代汽車和起亞汽車在全球的最大市場,2016 年兩車廠在中國銷量佔全球整體銷量比率,分別為23.5% 和21.5%。

但這幾個月由於中韓兩國因美國設置反導系統引發政治動盪,使得現代汽車銷售受牽連,2017 年第一季中國零售銷售下滑14%,現代汽車準備在今年夏天啟用在重慶的第5 座中國工廠,銷售下滑對現代而言不是好消息。

CATL 是中國動力鋰電池龍頭,目標2020 年成為僅次於Tesla 與松下合資公司的全球第二大電動車電池供應商,最大股東為蘋果電池供應商日本TDK 集團,投資人中也包括鴻海。

根據現代官方說法,選擇CATL 作為第一個中國電池供應商,是現代尋求供應商多元化的策略之一。報導認為,與CATL 合作除了強化與中國政府的關係之外,也是為了避免中國突如其來的反外商政策對現代造成的衝擊。

(合作媒體:。圖片出處:Hyundai)

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

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

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

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

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

※回頭車貨運收費標準

台達電動車充電座首度結合建案

看好電動車潛力,台中建商與台達電子合作,於新完工大樓採用台達電的電動車充電解決方案,設置兩座通用型快速充電站,充電30分鐘即可上路。

通用型充電站指Tesla之外的電動車皆適用,Tesla則要使用超級充電站(Supercharger),目前僅有台北花博公園有設置Tesla的超級充電站。

2017年1月,台達協助BMW台灣總代理汎德於台北市20處停車場建置40座電動車充電座;4月時又再次為汎德於台北101大樓建置直流和交流充電器。

(首圖來源:public domain CC0)

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

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

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

中國電動車補助改變,必翔出貨受阻、財報有困難

根據櫃買中心重大訊息公告,電動車及輔具廠商必翔實業,因子公司仍有許多待釐清事項,財報無法如期交出,因此18 日起將暫停交易。而且檢調也對必翔實業進行搜索,並且約談董事長蔣伍清明。在無法如期交出財報的消息傳出後,必翔16 日股價再度跌停板,已經連續10 個交易日跌停,股價由54.6 元下跌至每股19.2 元,跌幅超過64%。

據了解,必翔旗下子公司必翔電,近年在中國積極發展磷酸鋰鐵電池,而且在2014 年底獲得寧波雙鹿集團簽署新能源項目合作暨商務採購協議,當時成為能源類股當紅炸子雞。但是,由於中國官方的電動車補助政策改變,使得必翔電出貨受阻之外,還面臨收款不順的困境。

近期必翔電的輔導券商元大證,以及協辦券商德信證雙雙辭任雙雙辭任,還出現董事解職潮。未來,若無法順利找到輔導的券商,必翔電恐將面臨自興櫃下市的處境。

而根據必翔的公告指出,必翔之所以未能如期提交首季財報,主要因子公司揚明實業 (浙江) 在寧波所開立的二家銀行帳戶現金及相關投資理財商品合計人民幣1.7 億元,其管理及控制的合理性有待釐清。另外,另一子公司必翔電能方面共有包括營收、應收帳款、備抵呆帳以及存貨等合理性,也有進一步釐清的必要。該公司公告表示,已啟動跨境查核專案,委請專業人員組成查核小組跨境查核,將會盡速釐清問題,再補行公告。

而對於必翔無法如期交出財報,證交所表示將依證交所營業細則第50 條第1 項第1 款規定情事,公告自5 月18 日起,將停止有價證券之買賣。另外,檢調單位也於16 日至必翔公司進行搜索,並約談董事長蔣伍清明。

(合作媒體:。圖片出處:必翔)

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

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

厄瓜多原民擁抱太陽能獨木舟 盼對抗石油業入侵亞馬遜

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

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

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

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

馴獸師遭棕熊攻擊 觀眾還以為是表演

摘錄自2019年10月26日三立新聞網俄羅斯報導

俄羅斯一知名馬戲團日前一名馴獸師與一隻300公斤中大棕熊進行表演,沒想到過程中大棕熊卻突然襲擊馴獸師,當下觀眾還以為是表驗內容,直到另一名馴獸師狠踹大棕熊,才引起觀眾一陣恐慌,紛紛往門口逃竄。事後馬戲團發出聲明,疑似是觀眾不斷拍照開閃光燈拍惹的禍,但所幸好馴獸師和大棕熊都沒有受到嚴重傷害。

綜合外媒報導,來自沃羅涅日州(Voronezh)的俄羅斯安史拉格馬戲團(Anshlag traveling circus)近來進行全國巡迴表演,不料日前23日卻發一起恐怖事件,馬戲團內一隻遭訓養重達660磅(近300公斤)的大棕熊,卻在表演中突然攻擊馴獸師。

影片中可見,一名馴獸師引導大棕熊推獨輪車之後,並給予大棕熊獎勵,但不知是大棕熊感覺獎勵不夠,還是怎麼了;大棕熊突然咬住馴獸師的手臂,之後將馴獸師撲倒在地,而現場觀眾看到該幕,還以為是表演內容,直到另一名訓練師上前踹了大棕熊3腳,才驚慌紛紛往門口逃竄。

報導指出,安史拉格巡迴表演馬戲團舞台上完全沒有設置柵欄,現場觀眾與舞台上的動物距離也只有數公尺,不過此事件沒有觀眾受到傷,而大棕熊事後也被電擊槍等制伏、甚至遭攻擊的馴獸師也沒有受到太重的傷。

對此,馬戲團經理明絲尼克(Lyudmila Misnik)表示,疑似是觀眾拍照時忘記關閉閃光燈,導致大棕熊受到驚嚇攻擊馴獸師,不過所幸訓練師和大棕熊均無大礙;並保證接下來的表演,包括狗、浣熊、大蟒蛇、猴子、鸚鵡,還有經典的空中飛人、魔術表演、鋼鐵人和小丑等將會如期舉行,但他也再次呼籲到場觀看表演民眾,拍照時務必關掉閃光燈,避免類似事件再次發生。

 

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

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

江申中國廠獲Tesla訂單,營收增新動能

裕隆集團汽車零組件廠江申雖因國內景氣不佳,大型商用車、巴士需求平緩,致營收缺乏動能,但在中國大陸轉投資廠接單風光下,公司去年繳出稅後淨利大增2成的好成績,EPS 7.21元則改寫歷史新高紀錄;而今年第1季,雖然營收、獲利大致持平,但江申總經理麥少保表示,福州福享、廣州NTN和襄陽NTN今年均接單暢旺,尤其,廣州NTN還正式出貨美國電動車大廠特斯拉(Tesla) Model 3的轉動軸訂單,襄陽NTN也開始交瀋陽華晨寶馬,而福享也接到寧德能源的電動車電池盒訂單,將成為挹注今年業外成長的動能。

江申為裕隆集團旗下汽車零組件廠商,是台灣最大的大貨車架、巴士車架、以及小貨車車架、木床的供應商,公司18日參加集團法說會,由總經理麥少保說明今年度業績展望,其中,廣州NTN接獲特斯拉訂單成為法說會中的焦點。

由於江申營收僅反映台灣廠業績,但台灣廠的獲利佔比卻不及1成,逾9成的獲利主要來自轉投資大陸廠的收益,包括廣州NTN、襄陽NTN、福州福享、廈門金龍江申等;其中,光廣州NTN的獲利佔比在第1季達75%,是主要獲利重心,因此,法人在觀察江申展望時,多聚焦大陸廠接單情形。

江申第1季營收2.62億元、年增1.55%,毛利率9.48%、年減0.86個百分點,營業淨利98萬元、年減76.27%,業外淨利則為1.29億元、年增0.78%,稅前盈餘1.3億元、年減1.52%,稅後淨利1.33億元、年增1.53%,EPS 1.82元,優於去年同期的1.79元。

從江申的財報觀察,江申台灣廠第1季幾乎損平,獲利遭匯兌侵蝕;而廣州NTN貢獻9790萬元,年增23.89%;襄陽NTN貢獻2103萬元,年增36.74%;福享貢獻1452萬元,年減47.03%;廈門金龍江申則虧損428萬元,較去年同期的淨利230萬由盈轉虧。

麥少保說明,第1季來台的陸客減少,導致大巴士需求下滑,但江申營收有持平表現,是因為大客戶國瑞新導入日本Hino的底盤公車,並接獲新店客戶、桃園、新竹客運等訂單,對江申來說形成營收支撐;今年營收將平穩。

至於大陸轉投資部份,福州福享今年首季獲利衰退,主因去年有馬瑞利的模具收入,拉高獲利基期,若扣除該一次性收入來看,福享的本業獲利實際是成長的。

麥少保進一步表示,福州福享受惠於東南汽車推出SUV車的策略符合市場需求,DX7、DX3熱賣,接單明顯成長;再加上該廠也接福建奔馳訂單,在該品牌推出VS20廂型車款後賣得不錯,也成為今年動能。

值得留意的是,福州福享新接獲香港投資的電動車鋰電池大廠寧德新能源的電池盒訂單,麥少保預估,該業務將成為福享未來營運很重要的一環。該客戶訂單今年第1季佔比是5%,全年預估會佔5~10%。

最受關注的廣州NTN,麥少保則說,該廠的產能已達到40萬支/月的高峰,接單也不錯,主因東風日產的天籟、奇駿、SANTRA都賣得不錯,並持續有日產外銷訂單,雖然該廠有北京瑞韓現代訂單下滑影響,但由於外銷接單成長,因此該廠仍能維持成長。

他也首度證實,廣州NTN今年開始出貨Tesla Model 3傳動軸零件訂單,目前1個月訂單約為2萬個,並透露,只要是外銷訂單通常獲利都不錯。

而成長最大的襄陽NTN,則是因應廣州NTN產能不足而設,目前產能15萬支/月,年底目標18萬支/月。麥少保表示,4月起該廠正式交貨瀋陽華晨寶馬(BMW),同時也交廣州NTN交貨不足的天籟、奇駿車款,業績隨著擴產而有較大的動能。

不過,受到電動車騙補事件影響,廈門金龍今年首季繳出虧損成績單,麥少保也直言,大陸電動車很多人做,但今年普遍業績不好,今年該廠的業績將較去年差。

法人則預期,江申今年台灣廠營收將與過去2年相當,動能不強,但轉投資中國大陸在廣州NTN有訂單加入,襄陽NTN也因擴產、接單擴量,再加上福州福享有東南汽車DX7、DX3熱賣,可望使營運逐季增溫,下半年更優於上半年。

法人估,江申台灣營收將持穩,但大陸獲利成長下,全年獲利有機會較去年成長1成,EPS則有機會向8元扣關,再次創下史上最佳紀錄。

(本文內容由授權使用。圖片出處:Tesla)

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

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

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

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

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

※回頭車貨運收費標準

設計模式入門

最近想學設計模式,網上說 HeadFirst 設計模式書挺好的,我來此再鞏固一篇。

故事是這樣的:小明是一個剛畢業的小伙子,他來到了一個遊戲公司實習,項目經理分配了一個實習任務給小明:

設計一個遊戲角色,角色屬性包括(攻擊力,防禦力,敏捷度…等等),以及兩個召喚師技能(閃現和引燃)。

小明想這麼簡單的嗎,如是他用了一天的時間寫好了如下代碼

public class GameRole {
    
    private int atk; // 攻擊力
    private int def; // 防禦力
    private int dex; // 敏捷度
    
    public void flash_move() {
        System.out.println("指定方向瞬移一段距離");
    }
    
    public void ignite() {
        System.out.println("使其處於燃燒狀態 5 s");
    }
}

項目經理看到小明這麼快就完成了任務,表揚了一下小明,小明心裏樂開了花。然後項目經理又布置了一項任務給小明:
再設計兩個角色,他們的召喚師技能分別為(閃現,治療),(閃現,傳送)。

小明心想這難不倒我,然後他仍然只花了一天的時間就寫好了如下代碼:

public class GameRole {
    
    private int atk; // 攻擊力
    private int def; // 防禦力
    private int dex; // 敏捷度
    
    public void flash_move() {
        System.out.println("指定方向瞬移一段距離");
    }
    
    public void ignite() {
        System.out.println("使附近100碼內隊友恢復30%的血量");
    }
}

public class GameRole {
    
    private int atk; // 攻擊力
    private int def; // 防禦力
    private int dex; // 敏捷度
    
    public void flash_move() {
        System.out.println("指定方向瞬移一段距離");
    }
    
    public void ignite() {
        System.out.println("傳送至己方非英雄單位位置處");
    }
}

小明興高采烈的跑去給項目經理看了,項目經理看到小明過來了,心裏覺得這小伙子不錯麻,這麼快就做完了。

然後項目經理便看了他的代碼,這不看不要緊,一看便指着小明罵道:你真是一個糟糕的程序員!!!然後便讓小明改代碼去了。

小明此時還不太明白,我功能都實現了啊,沒啥毛病阿,然後小明不明所以的便問了辦公室的職員,職員告訴他,你代碼冗餘度太高了。

小明一看發現果然如此,然後便花了一天的時間改成如下代碼:

public abstract class GameRole {
    
    private int atk; // 攻擊力
    private int def; // 防禦力
    private int dex; // 敏捷度
  
  // 省略 Getter and Setter method
public void flash_move() { System.out.println("指定方向瞬移一段距離"); } public abstract void skill(); }
public class Role_One extends GameRole { @Override public void skill() { // TODO Auto-generated method stub System.out.println("使其處於燃燒狀態 5 s"); } }
public class Role_Two extends GameRole { @Override public void skill() { // TODO Auto-generated method stub System.out.println("使附近100碼內隊友恢復30%的血量"); } }
public class Role_Three extends GameRole { @Override public void skill() { // TODO Auto-generated method stub System.out.println("傳送至己方非英雄單位位置處"); } }

這次小明覺得冗餘度確實降低了,然後便給項目經理看,項目經理看后覺得確實還行,便又分配了一個任務:
(遊戲用戶希望能自由選擇召喚師技能就好了),所以任務是召喚師技能任意搭配。

小明心想:我寫的這種代碼似乎不用改耶,可以交差了,於是小明便偷懶了两天,然後便上報給項目經理了。

項目經理一看,這代碼沒有變化啊,便問小明,你代碼就這?小明回答是的,然後小明又被罵的狗血淋頭。

不明所以的小明又去問問辦公室的職員,你仔細想想,如果有100個(閃現,引燃),100個(閃現,治療),100個(傳送,治療)呢?

小明突然恍然大悟,那還是有好高的冗餘度啊,經過三天思考後,小明想出了最終答案:

public abstract class GameRole {
private int atk; // 攻擊力 private int def; // 防禦力 private int dex; // 敏捷度 private Skills skill_One; private Skills skill_Two;
  // 省略 Getter and Setter method
} public class Role_Demo extends GameRole { }
public interface Skills {
    public void skill();
}

public class Flush_Move implements Skills {
    @Override
    public void skill() {
        // TODO Auto-generated method stub
        System.out.println("指定方向瞬移一段距離");
    }
}

public class Ignite implements Skills {
    @Override
    public void skill() {
        // TODO Auto-generated method stub
        System.out.println("使其處於燃燒狀態 5 s");
    }
}

public class Treat implements Skills {

    @Override
    public void skill() {
        // TODO Auto-generated method stub
        System.out.println("使附近100碼內隊友恢復30%的血量");
    }
}
public class Main {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Role_Demo role = new Role_Demo();
        role.setSkill_One(new Flush_Move());
        role.setSkill_Two(new Ignite());
        role.getSkill_One().skill();
        role.getSkill_Two().skill();
    }
}

小明寫完了,這次小明怕又被罵,便去問問職員小花了,小花說寫的不錯嗎,於是小明膽戰心驚的去交差了。

項目經理看到小明過來,看着小明的囧樣,內心是想笑的,然後看了看代碼發現這回沒啥問題了,便放心的交給它最後一個任務:

給每個英雄添加四個不相同的技能:

小明經過幾次寫代碼的經歷后,一天便寫出來了:

public abstract class GameRole {
    private int atk; // 攻擊力
    private int def; // 防禦力
    private int dex; // 敏捷度
    private Skills skill_One;
    private Skills skill_Two;
    
    // 省略 Getter and Setter method
    
    public abstract void Skill_One();
    public abstract void Skill_Two();
    public abstract void Skill_Three();
    public abstract void Skill_Four();
}

public class Role_Demo extends GameRole {

    @Override
    public void Skill_One() {
        // TODO Auto-generated method stub
    }

    @Override
    public void Skill_Two() {
        // TODO Auto-generated method stub    
    }

    @Override
    public void Skill_Three() {
        // TODO Auto-generated method stub    
    }

    @Override
    public void Skill_Four() {
        // TODO Auto-generated method stub    
    }
}

經過這幾次任務,小明感覺寫的代碼更漂亮,更優雅了,腰也不酸了,背也不疼了。小明上交任務后,項目經理也露出了滿意的笑容!

最後,小明成功通過了實習,然而項目經理給他分配了下一項任務……

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

使用SpringCloud Stream結合rabbitMQ實現消息消費失敗重發機制

前言:實際項目中經常遇到消息消費失敗了,要進行消息的重發。比如支付消息消費失敗后,要分不同時間段進行N次的消息重發提醒。

本文模擬場景

  1. 當金額少於100時,消息消費成功
  2. 當金額大於100,小於200時,會進行3次重發,第一次1秒;第二次2秒;第三次3秒。
  3. 當金額大於200時,消息消費失敗,會進行5次重發,第一次1秒;第二次2秒;第三次3秒;第四次4秒;第五次5秒。重試五次后,消息自動進入死信隊列,在死信隊列存活60秒后消失。

代碼實例

特別注意代碼與配置文件中的註釋,各個使用說明都已經詳細寫在配置文件中

pom包引入

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.cloudstream</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR5</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- ①關鍵配置:引入stream-rabbit 依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <!-- ②關鍵配置:由於stream是基於spring-cloud的,所以這裏要引入 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>

</project>

配置application.yml文件

注意各個配置的縮進格式,別搞錯了

server:
  port: 8081
spring:
  application:
    name: stream-demo
  #rabbitmq連接配置
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456
  cloud:
    stream:
      bindings:
        #消息生產者,與DelayDemoTopic接口中的DELAY_DEMO_PRODUCER變量值一致
        delay-demo-producer:
          #①定義交換機名
          destination: demo-delay-queue
        #消息消費者,與DelayDemoTopic接口中的DELAY_DEMO_CONSUMER變量值一致
        delay-demo-consumer:
          #定義交換機名,與①一致,就可以使發送和消費都指向一個隊列
          destination: demo-delay-queue
          #分組,這個配置可以開啟消息持久化、可以解決在集群環境下重複消費的問題。
          #比如A、B兩台服務器集群,如果沒有這個配置,則A、B都能收到同樣的消息,如果有該配置則只有其中一台會收到消息
          group: delay-consumer-group
          consumer:
            #最大重試次數,默認為3。不使用默認的,這裏定義為1,由我們程序控制發送時間和次數
            maxAttempts: 1
      rabbit:
        bindings:
          #消息生產者,與DelayDemoTopic接口中的DELAY_DEMO_PRODUCER變量值一致
          delay-demo-producer:
            producer:
              #②申明為延遲隊列
              delayedExchange: true
          #消息消費者,與DelayDemoTopic接口中的DELAY_DEMO_CONSUMER變量值一致
          delay-demo-consumer:
            consumer:
              #申明為延遲隊列,與②的配置的成對出現的
              delayedExchange: true
              #開啟死信隊列
              autoBindDlq: true
              #死信隊列中消息的存活時間
              dlqTtl: 60000

定義隊列通道

  1. 定義通道
/**
 * 定義延遲消息通道
 */
public interface DelayDemoTopic {
    /**
     * 生產者,與yml文件配置對應
     */
    String DELAY_DEMO_PRODUCER = "delay-demo-producer";
    /**
     * 消費者,與yml文件配置對應
     */
    String DELAY_DEMO_CONSUMER = "delay-demo-consumer";

    /**
     * 定義消息消費者,在@StreamListener監聽消息的時候用到
     * @return
     */
    @Input(DELAY_DEMO_CONSUMER)
    SubscribableChannel delayDemoConsumer();

    /**
     * 定義消息發送者,在發送消息的時候用到
     * @return
     */
    @Output(DELAY_DEMO_PRODUCER)
    MessageChannel delayDemoProducer();
}
  1. 綁定通道
/**
 * 配置消息的binding
 *
 */
@EnableBinding(value = {DelayDemoTopic.class})
@Component
public class MessageConfig {

}

消息發送模擬

/**
 * 發送消息
 */
@RestController
public class SendMessageController {
    @Autowired
    DelayDemoTopic delayDemoTopic;

    @GetMapping("send")
    public Boolean sendMessage(BigDecimal money) throws JsonProcessingException {

        Message<BigDecimal> message = MessageBuilder.withPayload(money)
                //設置消息的延遲時間,首次發送,不設置延遲時間,直接發送
                .setHeader(DelayConstant.X_DELAY_HEADER,0)
                //設置消息已經重試的次數,首次發送,設置為0
                .setHeader(DelayConstant.X_RETRIES_HEADER,0)
                .build();
        return delayDemoTopic.delayDemoProducer().send(message);
    }
}

消息監聽處理

@Component
@Slf4j
public class DelayDemoTopicListener {
    @Autowired
    DelayDemoTopic delayDemoTopic;

    /**
     * 監聽延遲消息通道中的消息
     * @param message
     */
    @StreamListener(value = DelayDemoTopic.DELAY_DEMO_CONSUMER)
    public void listener(Message<BigDecimal> message) {
        //獲取重試次數
        int retries = (int)message.getHeaders().get(DelayConstant.X_RETRIES_HEADER);
        //獲取消息內容
        BigDecimal money = message.getPayload();
        try {
            String now = DateUtils.formatDate(new Date(),"yyyy-MM-dd HH:mm:ss");
            //模擬:如果金額大於200,則消息無法消費成功;金額如果大於100,則重試3次;如果金額小於100,直接消費成功
            if (money.compareTo(new BigDecimal(200)) == 1){
                throw new RuntimeException(now+":金額超出200,無法交易。");
            }else if (money.compareTo(new BigDecimal(100)) == 1 && retries <= 3) {
                if (retries == 0) {
                    throw new RuntimeException(now+":金額超出100,消費失敗,將進入重試。");
                }else {
                    throw new RuntimeException(now+":金額超出100,當前第" + retries + "次重試。");
                }
            }else {
                log.info("消息消費成功!");
            }
        }catch (Exception e) {
            log.error(e.getMessage());
            if (retries < DelayConstant.X_RETRIES_TOTAL){
                //將消息重新塞入隊列
                MessageBuilder<BigDecimal> messageBuilder = MessageBuilder.fromMessage(message)
                        //設置消息的延遲時間
                        .setHeader(DelayConstant.X_DELAY_HEADER,DelayConstant.ruleMap.get(retries + 1))
                        //設置消息已經重試的次數
                        .setHeader(DelayConstant.X_RETRIES_HEADER,retries + 1);
                Message<BigDecimal> reMessage = messageBuilder.build();
                //將消息重新發送到延遲隊列中
                delayDemoTopic.delayDemoProducer().send(reMessage);
            }else {
                //超過重試次數,做相關處理(比如保存數據庫等操作),如果拋出異常,則會自動進入死信隊列
                throw new RuntimeException("超過最大重試次數:" + DelayConstant.X_RETRIES_TOTAL);
            }
        }
    }
}

規則定義

目前寫在一個常量類里,實際項目中,通常會配置在配置文件中

public class DelayConstant {
    /**
     * 定義當前重試次數
     */
    public static final String X_RETRIES_HEADER = "x-retries";
    /**
     * 定義延遲消息,固定值,該配置放到消息的header中,會開啟延遲隊列
     */
    public static final String X_DELAY_HEADER = "x-delay";

    /**
     * 定義最多重試次數
     */
    public static final Integer X_RETRIES_TOTAL = 5;

    /**
     * 定義重試規則,毫秒為單位
     */
    public static final Map<Integer,Integer> ruleMap = new HashMap(){{
        put(1,1000);
        put(2,2000);
        put(3,3000);
        put(4,4000);
        put(5,5000);
    }};
}

測試

經過以上配置和實現就可完成模擬的重發場景。

  • 瀏覽器中輸入http://127.0.0.1:8081/send?money=10,可以看到控制台中輸出:
消息消費成功!
  • 瀏覽器中輸入http://127.0.0.1:8081/send?money=110,可以看到控制台中輸出:
2020-06-20 10:59:42:金額超出100,消費失敗,將進入重試。
2020-06-20 10:59:43:金額超出100,當前第1次重試。
2020-06-20 10:59:45:金額超出100,當前第2次重試。
2020-06-20 10:59:48:金額超出100,當前第3次重試。
消息消費成功!

  • 瀏覽器中輸入http://127.0.0.1:8081/send?money=110,可以看到控制台中輸出:

注意事項

由於本文用到了延遲隊列,需要在rabbitMQ中安裝延遲插件,具體安裝方式,可以查看:延遲隊列安裝參考

源碼獲取

以上示例都可以通過我的GitHub獲取完整的代碼.

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

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

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