日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 單片機 > 架構(gòu)師社區(qū)
[導讀]作者:快應用服務器研發(fā)團隊-LinYupan一、業(yè)務背景許多面向用戶的互聯(lián)網(wǎng)業(yè)務都會在系統(tǒng)后端維護一份用戶數(shù)據(jù),快應用中心業(yè)務也同樣做了這件事??鞈弥行脑试S用戶對快應用進行收藏,并在服務端記錄了用戶的收藏列表,通過用戶賬號標識OpenID來關聯(lián)收藏的快應用包名。為了使用戶在快應...

作者:快應用服務器研發(fā)團隊-Lin Yupan

一、業(yè)務背景


許多面向用戶的互聯(lián)網(wǎng)業(yè)務都會在系統(tǒng)后端維護一份用戶數(shù)據(jù),快應用中心業(yè)務也同樣做了這件事??鞈弥行脑试S用戶對快應用進行收藏,并在服務端記錄了用戶的收藏列表,通過用戶賬號標識OpenID來關聯(lián)收藏的快應用包名。


為了使用戶在快應用中心的收藏列表能夠與快應用Menubar的收藏狀態(tài)打通,我們同時也記錄了用戶賬號標識OpenID與客戶端本地標識local_identifier的綁定關系。因為快應用Manubar由快應用引擎持有,獨立于快應用中心外,無法通過賬號體系獲取到用戶賬號標識,只能獲取到客戶端本地標識local_identifier,所以我們只能通過二者的映射關系來保持狀態(tài)同步。


靈活運用分布式鎖解決數(shù)據(jù)重復插入問題


在具體實現(xiàn)上,我們是在用戶啟動快應用中心的時候觸發(fā)一次同步操作,由客戶端將OpenID和客戶端本地標識提交到服務端進行綁定。服務端的綁定邏輯是:判斷OpenID是否已經(jīng)存在,如果不存在則插入數(shù)據(jù)庫,否則更新對應數(shù)據(jù)行的local_identifier字段(因為用戶可能先后在兩個不同的手機上登錄同一個vivo賬號)。在后續(xù)的業(yè)務流程中,我們就可以根據(jù)OpenID查詢對應的local_identifier,反之亦可。


靈活運用分布式鎖解決數(shù)據(jù)重復插入問題

靈活運用分布式鎖解決數(shù)據(jù)重復插入問題


但是代碼上線一段時間后,我們發(fā)現(xiàn)t_account數(shù)據(jù)表中居然存在許多重復的OpenID記錄。根據(jù)如上所述的綁定邏輯,這種情況理論上是不應該發(fā)生的。所幸這些重復數(shù)據(jù)并沒有對更新和查詢的場景造成影響,因為在查詢的SQL中我們加入了LIMIT 1的限制,因此針對一個OpenID的更新和查詢操作實際上都只作用于ID最小的那條記錄。


二、問題分析與定位


雖然冗余數(shù)據(jù)沒有對實際業(yè)務造成影響,但是這種明顯的數(shù)據(jù)問題也肯定是不能容忍的。因此我們開始著手排查問題。


首先想到的就是從數(shù)據(jù)本身入手。先通過對t_account表數(shù)據(jù)進行粗略觀察,發(fā)現(xiàn)大約有3%的OpenID會存在重復的情況。也就是說重復插入的情況是偶現(xiàn)的,大多數(shù)請求的處理都是按照預期被正確處理了。我們對代碼重新進行了走讀,確認了代碼在實現(xiàn)上確實不存在什么明顯的邏輯錯誤。


我們進一步對數(shù)據(jù)進行細致觀察。我們挑選了幾個出現(xiàn)重復情況的OpenID,將相關的數(shù)據(jù)記錄查詢出來,發(fā)現(xiàn)這些OpenID重復的次數(shù)也不盡相同,有的只重復一次,有的則更多。但是,這時候我們發(fā)現(xiàn)了一個更有價值的信息——這些相同OpenID的數(shù)據(jù)行的創(chuàng)建時間都是完全相同的,而且自增ID是連續(xù)的。


靈活運用分布式鎖解決數(shù)據(jù)重復插入問題


于是,我們猜測問題的產(chǎn)生應該是由于并發(fā)請求造成的!我們模擬了客戶端對接口的并發(fā)調(diào)用,確實出現(xiàn)了重復插入數(shù)據(jù)的現(xiàn)象,進一步證實了這個猜測的合理性。但是,明明客戶端的邏輯是每個用戶在啟動的時候進行一次同步,為什么會出現(xiàn)同一個OpenID并發(fā)請求呢?


事實上,代碼的實際運行并不如我們想象中的那么理想,計算機的運行過程中往往存在一些不穩(wěn)定的因素,比如網(wǎng)絡環(huán)境、服務器的負載情況。而這些不穩(wěn)定因素就可能導致客戶端發(fā)送請求失敗,這里的“失敗”可能并不意味著真正的失敗,而是可能整個請求時間過長,超過了客戶端設定的超時時間,從而被人為地判定為失敗,于是通過重試機制再次發(fā)送請求。那么最終就可能導致同樣的請求被提交了多次,而且這些請求也許在中間某個環(huán)節(jié)被阻塞了(比如當服務器的處理線程負載過大,來不及處理請求,請求進入了緩沖隊列),當阻塞緩解后這幾個請求就可能在很短的時間內(nèi)被并發(fā)處理了。


這其實是一個典型的并發(fā)沖突問題,可以把這個問題簡單抽象為:如何避免并發(fā)情況下寫入重復數(shù)據(jù)。事實上,有很多常見的業(yè)務場景都可能面臨這個問題,比如用戶注冊時不允許使用相同的用戶名。


一般來說,我們在處理這類問題時,最直觀的方式就是先進行一次查詢,當判斷數(shù)據(jù)庫中不存在當前數(shù)據(jù)時才允許插入。


靈活運用分布式鎖解決數(shù)據(jù)重復插入問題


顯然,這個流程從單個請求的角度來看是沒有問題的。但是當多個請求并發(fā)時,請求A和請求B都先發(fā)起一次查詢,并且都得到結(jié)果是不存在,于是兩者都又執(zhí)行了數(shù)據(jù)插入,最終導致并發(fā)沖突。


靈活運用分布式鎖解決數(shù)據(jù)重復插入問題


三、探索可行的方案


既然問題定位到了,接下來就要開始尋求解決方案了。面對這種情況,我們通常有兩種選擇,一種是讓數(shù)據(jù)庫來解決,另一種是由應用程序來解決。


3.1 數(shù)據(jù)庫層面處理——唯一索引


當使用MySQL數(shù)據(jù)庫及InnoDB存儲引擎時,我們可以利用唯一索引來保障同一個列的值具有唯一性。顯然,在t_account這張表中,我們最開始是沒有為open_id列創(chuàng)建唯一索引的。如果我們想要此時加上唯一索引的話,可以利用下列的ALTER TABLE語句。

ALTER TABLE t_account ADD UNIQUE uk_open_id( open_id );

一旦為open_id列加上唯一索引后,當上述并發(fā)情況發(fā)生時,請求A和請求B中必然有一者會優(yōu)先完成數(shù)據(jù)的插入操作,而另一者則會得到類似錯誤。因此,最終保證t_account表中只有一條openid=xxx的記錄存在。

Error Code: 1062. Duplicate entry 'xxx' for key 'uk_open_id'

3.2 應用程序?qū)用嫣幚怼?a href="/tags/分布式" target="_blank">分布式鎖


另一種解決的思路是我們不依賴底層的數(shù)據(jù)庫來為我們提供唯一性的保障,而是靠應用程序自身的代碼邏輯來避免并發(fā)沖突。應用層的保障其實是一種更具通用性的方案,畢竟我們不能假設所有系統(tǒng)使用的數(shù)據(jù)持久化組件都具備數(shù)據(jù)唯一性檢測的能力。


那具體怎么做呢?簡單來說,就是化并行為串行。之所以我們會遇到重復插入數(shù)據(jù)的問題,是因為“檢測數(shù)據(jù)是否已經(jīng)存在”和“插入數(shù)據(jù)”兩個動作被分割開來。由于這兩個步驟不具備原子性,才導致兩個不同的請求可以同時通過第一步的檢測。如果我們能夠把這兩個動作合并為一個原子操作,就可以避免數(shù)據(jù)沖突了。這時候我們就需要通過加鎖,來實現(xiàn)這個代碼塊的原子性。


靈活運用分布式鎖解決數(shù)據(jù)重復插入問題


對于Java語言,大家最熟悉的鎖機制就是synchronized關鍵字了。

public synchronized void submit(String openId, String localIdentifier){ Account account = accountDao.find(openId); if (account == null) { // insert } else { // update }}

但是,事情可沒這么簡單。要知道,我們的程序可不是只部署在一臺服務器上,而是部署了多個節(jié)點。也就是說這里的并發(fā)不僅僅是線程間的并發(fā),而是進程間的并發(fā)。因此,我們無法通過java語言層面的鎖機制來解決這個同步問題,我們這里需要的應該是分布式鎖。


3.3 兩種解決方案的權(quán)衡


基于以上的分析,看上去兩種方案都是可行的,但最終我們選擇了分布式鎖的方案。為什么明明第一種方案只需要簡單地加個索引,我們卻不采用呢?


因為現(xiàn)有的線上數(shù)據(jù)已然在open_id列上存在重復數(shù)據(jù),如果此時直接去加唯一索引是無法成功的。為了加上唯一索引,我們必須首先將已有的重復數(shù)據(jù)先進行清理。但是問題又來了,線上的程序一直持續(xù)運行著,重復數(shù)據(jù)可能會源源不斷地產(chǎn)生。那我們能不能找一個用戶請求不活躍的時間段去進行清理,并在新的重復數(shù)據(jù)插入之前完成唯一索引的建立?答案當然是肯定的,只不過這種方案需要運維、DBA、開發(fā)多方協(xié)同處理,而且由于業(yè)務特性,最合適的處理時間段應該是凌晨這種夜深人靜的時候。即便是采取這么苛刻的修復措施,也不能百分之百完全保證數(shù)據(jù)清理完成到索引建立之間不會有新的重復數(shù)據(jù)插入。因此,基于唯一索引的修復方案乍看之下非常合適,但是具體操作起來還是略為麻煩。


事實上,建立唯一索引最合適的契機應該是在系統(tǒng)最初的設計階段,這樣就能有效避免重復數(shù)據(jù)的問題。然而木已成舟,在當前這個情景下,我們還是選擇了可操作性更強的分布式鎖方案。因為選擇這個方案的話,我們可以先上線加入了分布式鎖修復的新代碼,阻斷新的重復數(shù)據(jù)插入,然后再對原有的重復數(shù)據(jù)執(zhí)行清理操作,這樣一來只需要修改代碼并一次上線即可。當然,待問題徹底解決之后,我們可以重新再考慮為數(shù)據(jù)表加上唯一索引。


那么接下來,我們就來看看基于分布式鎖的方案如何實現(xiàn)。首先我們先來回顧一下分布式鎖的相關知識。


四、分布式鎖概述


4.1 分布式鎖需要具備哪些特性?


  • 在分布式系統(tǒng)環(huán)境下,同一時間只有一臺機器的一個線程可以獲取到鎖;

  • 高可用的獲取鎖與釋放鎖;

  • 高性能的獲取鎖與釋放鎖;

  • 具備可重入特性;

  • 具備鎖失效機制,防止死鎖;

  • 具備阻塞/非阻塞鎖特性。


4.2 分布式鎖有哪些實現(xiàn)方式?


分布式鎖實現(xiàn)主要有如下三種:

  • 基于數(shù)據(jù)庫實現(xiàn)分布式鎖;

  • 基于Zookeeper實現(xiàn)分布式鎖;

  • 基于Redis實現(xiàn)分布式鎖;


4.2.1 基于數(shù)據(jù)庫的實現(xiàn)方式


基于數(shù)據(jù)庫的實現(xiàn)方式就是直接創(chuàng)建一張鎖表,通過操作表數(shù)據(jù)來實現(xiàn)加鎖、解鎖。以MySQL數(shù)據(jù)庫為例,我們可以創(chuàng)建這樣一張表,并且對method_name進行加上唯一索引的約束:

CREATE TABLE `myLock` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `method_name` varchar(100) NOT NULL DEFAULT '' COMMENT '鎖定的方法名', `value` varchar(1024) NOT NULL DEFAULT '鎖信息', PRIMARY KEY (`id`), UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='鎖定中的方法';

然后,我們就可以通過插入數(shù)據(jù)和刪除數(shù)據(jù)的方式來實現(xiàn)加鎖和解鎖:

#加鎖insert into myLock(method_name, value) values ('m1', '1'); #解鎖delete from myLock where method_name ='m1';

基于數(shù)據(jù)庫實現(xiàn)的方式雖然簡單,但是存在一些明顯的問題:

  • 沒有鎖失效時間,如果解鎖失敗,就會導致鎖記錄永遠留在數(shù)據(jù)庫中,造成死鎖。

  • 該鎖不可重入,因為它不認識請求方是不是當前占用鎖的線程。

  • 當前數(shù)據(jù)庫是單點,一旦宕機,鎖機制就會完全崩壞。


4.2.2 基于Zookeeper的實現(xiàn)方式


ZooKeeper是一個為分布式應用提供一致性服務的開源組件,它內(nèi)部是一個分層的文件系統(tǒng)目錄樹結(jié)構(gòu),規(guī)定同一個目錄下的節(jié)點名稱都是唯一的。


ZooKeeper的節(jié)點(Znode)有4種類型:

  • 持久化節(jié)點(會話斷開后節(jié)點還存在)

  • 持久化順序節(jié)點

  • 臨時節(jié)點(會話斷開后節(jié)點就刪除了)

  • 臨時順序節(jié)點


當一個新的Znode被創(chuàng)建為一個順序節(jié)點時,ZooKeeper通過將10位的序列號附加到原始名稱來設置Znode的路徑。例如,如果將具有路徑/mynode的Znode創(chuàng)建為順序節(jié)點,則ZooKeeper會將路徑更改為/mynode0000000001,并將下一個序列號設置為0000000002,這個序列號由父節(jié)點維護。如果兩個順序節(jié)點是同時創(chuàng)建的,那么ZooKeeper不會對每個Znode使用相同的數(shù)字。


基于ZooKeeper的特性,可以按照如下方式來實現(xiàn)分布式鎖:


  • 創(chuàng)建一個目錄mylock;

  • 線程A想獲取鎖就在mylock目錄下創(chuàng)建臨時順序節(jié)點;

  • 獲取mylock目錄下所有的子節(jié)點,然后獲取比自己小的兄弟節(jié)點,如果不存在,則說明當前線程順序號最小,獲得鎖;

  • 線程B獲取所有節(jié)點,判斷自己不是最小節(jié)點,設置監(jiān)聽比自己次小的節(jié)點;

  • 線程A處理完,刪除自己的節(jié)點,線程B監(jiān)聽到變更事件,判斷自己是不是最小的節(jié)點,如果是則獲得鎖。


由于創(chuàng)建的是臨時節(jié)點,當持有鎖的線程意外宕機時,鎖依然可以得到釋放,因此可以避免死鎖的問題。另外,我們也可以通過節(jié)點排隊監(jiān)聽機制實現(xiàn)阻塞特性,也可以通過在Znode中攜帶線程標識來實現(xiàn)可重入鎖。同時,由于ZooKeeper集群的高可用特性,分布式鎖的可用性也能夠得到保障。不過,因為需要頻繁的創(chuàng)建和刪除節(jié)點,Zookeeper方式在性能上不如Redis方式。


4.2.3 基于Redis的實現(xiàn)方式


Redis是一個開源的鍵值對(Key-Value)存儲數(shù)據(jù)庫,其基于內(nèi)存實現(xiàn),性能非常高,常常被用作緩存。


基于Redis實現(xiàn)分布式鎖的核心原理是:嘗試對特定key進行set操作,如果設置成功(key之前不存在)了,則相當于獲取到鎖,同時對該key設置一個過期時間,避免線程在釋放鎖之前退出造成死鎖。線程執(zhí)行完同步任務后主動釋放鎖則通過delete命令來完成。


這里需要特別注意的一點是如何加鎖并設置過期時間。有的人會使用setnx expire這兩個命令來實現(xiàn),但這是有問題的。假設當前線程執(zhí)行setnx獲得了鎖,但是在執(zhí)行expire之前宕機了,就會造成鎖無法被釋放。當然,我們可以將兩個命令合并在一段lua腳本里,實現(xiàn)兩條命令的原子提交。


其實,我們簡單利用set命令可以直接在一條命令中實現(xiàn)setnx和設置過期時間,從而完成加鎖操作:

SET key value [EX seconds] [PX milliseconds] NX

解鎖操作只需要:

DEL key

五、基于Redis分布式鎖的解決方案


在本案例中,我們采用了基于Redis實現(xiàn)分布式鎖的方式。


5.1 分布式鎖的Java實現(xiàn)


由于項目采用了Jedis框架,而且線上Redis部署為集群模式,因此我們基于redis.clients.jedis.JedisCluster封裝了一個RedisLock類,提供加鎖與解鎖接口。

public?class?RedisLock?{ private static final String LOCK_SUCCESS = "OK"; private static final String LOCK_VALUE = "lock"; private static final int EXPIRE_SECONDS = 3; @Autowired protected JedisCluster jedisCluster; public boolean lock(String openId) { String redisKey = this.formatRedisKey(openId); String ok = jedisCluster.set(redisKey, LOCK_VALUE, "NX", "EX", EXPIRE_SECONDS); return LOCK_SUCCESS.equals(ok); } public void unlock(String openId) { String redisKey = this.formatRedisKey(openId); jedisCluster.del(redisKey); } private String formatRedisKey(String openId){ return "keyPrefix:" openId; }}

在具體實現(xiàn)上,我們設置了3秒鐘的過期時間,因為被加鎖的任務是簡單的數(shù)據(jù)庫查詢和插入,而且服務器與數(shù)據(jù)庫部署在同個機房,正常情況下3秒鐘已經(jīng)完全能夠足夠滿足代碼的執(zhí)行。


事實上,以上的實現(xiàn)是一個簡陋版本的Redis分布式鎖,我們在實現(xiàn)中并沒有考慮線程的可重入性,也沒有考慮鎖被其他進程誤釋放的問題,但是它在這個業(yè)務場景下已經(jīng)能夠滿足我們的需求了。假設推廣到更為通用的業(yè)務場景,我們可以考慮在value中加入當前進程的特定標識,并在上鎖和釋放鎖的階段做相對應的匹配檢測,就可以得到一個更為安全可靠的Redis分布式鎖的實現(xiàn)了。


當然,像Redission之類的框架也提供了相當完備的Redis分布式鎖的封裝實現(xiàn),在一些要求相對嚴苛的業(yè)務場景下,我建議直接使用這類框架。由于本文側(cè)重于介紹排查及解決問題的思路,因此沒有對Redisson分布式的具體實現(xiàn)原理做更多介紹,感興趣的小伙伴可以在網(wǎng)上找到非常豐富的資料。


5.2 改進后的代碼邏輯


現(xiàn)在,我們可以利用封裝好的RedisLock來改進原來的代碼了。

public?class?AccountService?{ @Autowired private RedisLock redisLock; public void submit(String openId, String localIdentifier) { if (!redisLock.lock(openId)) { // 如果相同openId并發(fā)情況下,線程沒有搶到鎖,則直接丟棄請求 return; } // 獲取到鎖,開始執(zhí)行用戶數(shù)據(jù)同步邏輯 try { Account account = accountDao.find(openId); if (account == null) { // insert } else { // update } } finally { // 釋放鎖 redisLock.unlock(openId); } }}

5.3 數(shù)據(jù)清理


最后再簡單說一下收尾工作。由于重復數(shù)據(jù)的數(shù)據(jù)量較大,不太可能手工去慢慢處理。于是我們編寫了一個定時任務類,每隔一分鐘執(zhí)行一次清理操作,每次清理1000個重復的OpenID,避免短時間內(nèi)大量查詢和刪除操作對數(shù)據(jù)庫性能造成影響。當確認重復數(shù)據(jù)已經(jīng)完全清理完畢后就停掉定時任務的調(diào)度,并在下一次版本迭代中將此代碼移除。


六、總結(jié)


在日常開發(fā)過程中難免會各種各樣的問題,我們要學會順藤摸瓜逐步分析,找到問題的根因;然后在自己的認知范圍內(nèi)盡量去尋找可行的解決方案,并且仔細權(quán)衡各種方案的利弊,才能最終高效地解決問題。

本站聲明: 本文章由作者或相關機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅(qū)動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅(qū)動性能的關鍵。

關鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質(zhì)量和效率直接關系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅(qū)動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關鍵字: LED 驅(qū)動電源 開關電源

LED驅(qū)動電源是把電源供應轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅(qū)動電源
關閉