疫情讓自然資源復甦 泰國家公園擬每年關3個月

摘錄自2020年5月11日中央社報導

為了避免武漢肺炎疫情擴大,泰國從3月25日關閉所有的國家公園,例如知名的考艾國家公園(Khao Yai National Park)是開園58年以來第一次閉園,而且數條穿過國家公園的公路也因此封閉。

泰國公視(Thai PBS)報導,泰國自然資源和環境部(Ministry of Natural Resources and Environment)部長烏拉沃(Varawut Silpa-archa)日前表示,過去兩個月國家公園因為疫情閉園,反讓許多野生動物再生。

烏拉沃表示,受到這樣狀況的啟發,自然資源和環境部未來準備讓全泰國157個國家公園每年閉園三個月,他要求國家公園局(Department of National Parks, Wildlife and Plant Conservation)在一切恢復正常後,研擬適合的閉園時間表。

泰媒經理人日報(Manager Daily)報導,國家公園局局長譚亞(Thanya Netithamkul)表示,國家公園局已經和相關單位討論過,會要求各個國家公園準備相關計畫。

生態保育
生物多樣性
國際新聞
泰國
國家公園
疫情

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

【其他文章推薦】

※回頭車貨運收費標準

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

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

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

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

分佈式鎖沒那麼難,手把手教你實現 Redis 分佈鎖!|保姆級教程

書接上文

上篇文章「MySQL 可重複讀,差點就讓我背上了一個 P0 事故!」發布之後,收到很多小夥伴們的留言,從中又學習到很多,總結一下。

上篇文章可能舉得例子有點不恰當,導致有些小夥伴沒看懂為什麼餘額會變負。

這次我們舉得實際一點,還是上篇文章 account 表,假設 id=1,balance=1000,不過這次我們扣款 1000,兩個事務的時序圖如下:

這次使用兩個命令窗口真實執行一把:

注意事務 2,③處查詢到 id=1,balance=1000,但是實際上由於此時事務 1 已經提交,最新結果如②處所示 id=1,balance=900

本來 Java 代碼層會做一層餘額判斷:

if (balance - amount < 0) {
  throw new XXException("餘額不足,扣減失敗");
}

但是此時由於 ③ 處使用快照讀,讀到是箇舊值,未讀到最新值,導致這層校驗失效,從而代碼繼續往下運行,執行了數據更新。

更新語句又採用如下寫法:

UPDATE account set balance=balance-1000 WHERE id =1;

這條更新語句又必須是在這條記錄的最新值的基礎做更新,更新語句執行結束,這條記錄就變成了 id=1,balance=-1000

之前有朋友疑惑 t12 更新之後,再次進行快照讀,結果會是多少。

上圖執行結果 ④ 可以看到結果為 id=1,balance=-1000,可以看到已經查詢最新的結果記錄。

這行數據最新版本由於是事務 2 自己更新的,自身事務更新永遠對自己可見

另外這次問題上本質上因為 Java 層與數據庫層數據不一致導致,有的朋友留言提出,可以在更新餘額時加一層判斷:

UPDATE account set balance=balance-1000 WHERE id =1 and balance>0;

然後更新完成,Java 層判斷更新有效行數是否大於 0。這種做法確實能規避這個問題。

最後這位朋友留言總結的挺好,粘貼一下:

先贊后看,微信搜索「程序通事」,關注就完事了

手擼分佈式鎖

現在切回正文,這篇文章本來是準備寫下 Mysql 查詢左匹配的問題,但是還沒研究出來。那就先寫下最近在鼓搗一個東西,使用 Redis 實現可重入分佈鎖。

看到這裏,有的朋友可能會提出來使用 redisson 不香嗎,為什麼還要自己實現?

哎,redisson 真的很香,但是現有項目中沒辦法使用,只好自己手擼一個可重入的分佈式鎖了。

雖然用不了 redisson,但是我可以研究其源碼,最後實現的可重入分佈鎖參考了 redisson 實現方式。

分佈式鎖

分佈式鎖特性就要在於排他性,同一時間內多個調用方加鎖競爭,只能有一個調用方加鎖成功。

Redis 由於內部單線程的執行,內部按照請求先後順序執行,沒有併發衝突,所以只會有一個調用方才會成功獲取鎖。

而且 Redis 基於內存操作,加解鎖速度性能高,另外我們還可以使用集群部署增強 Redis 可用性。

加鎖

使用 Redis 實現一個簡單的分佈式鎖,非常簡單,可以直接使用 SETNX 命令。

SETNX 是『SET if Not eXists』,如果不存在,才會設置,使用方法如下:

不過直接使用 SETNX 有一個缺陷,我們沒辦法對其設置過期時間,如果加鎖客戶端宕機了,這就導致這把鎖獲取不了了。

有的同學可能會提出,執行 SETNX 之後,再執行 EXPIRE 命令,主動設置過期時間,偽碼如下:

var result = setnx lock "client"
if(result==1){
    // 有效期 30 s
    expire lock 30
}

不過這樣還是存在缺陷,加鎖代碼並不能原子執行,如果調用加鎖語句,還沒來得及設置過期時間,應用就宕機了,還是會存在鎖過期不了的問題。

不過這個問題在 Redis 2.6.12 版本 就可以被完美解決。這個版本增強了 SET 命令,可以通過帶上 NX,EX 命令原子執行加鎖操作,解決上述問題。參數含義如下:

  • EX second :設置鍵的過期時間,單位為秒
  • NX 當鍵不存在時,進行設置操作,等同與 SETNX 操作

使用 SET 命令實現分佈式鎖只需要一行代碼:

SET lock_name anystring NX EX lock_time

解鎖

解鎖相比加鎖過程,就顯得非常簡單,只要調用 DEL 命令刪除鎖即可:

DEL lock_name

不過這種方式卻存在一個缺陷,可能會發生錯解鎖問題。

假設應用 1 加鎖成功,鎖超時時間為 30s。由於應用 1 業務邏輯執行時間過長,30 s 之後,鎖過期自動釋放。

這時應用 2 接着加鎖,加鎖成功,執行業務邏輯。這個期間,應用 1 終於執行結束,使用 DEL 成功釋放鎖。

這樣就導致了應用 1 錯誤釋放應用 2 的鎖,另外鎖被釋放之後,其他應用可能再次加鎖成功,這就可能導致業務重複執行。

為了使鎖不被錯誤釋放,我們需要在加鎖時設置隨機字符串,比如 UUID。

SET lock_name uuid NX EX lock_time

釋放鎖時,需要提前獲取當前鎖存儲的值,然後與加鎖時的 uuid 做比較,偽代碼如下:

var value= get lock_name
if value == uuid
	// 釋放鎖成功
else
	// 釋放鎖失敗

上述代碼我們不能通過 Java 代碼運行,因為無法保證上述代碼原子化執行。

幸好 Redis 2.6.0 增加執行 Lua 腳本的功能,lua 代碼可以運行在 Redis 服務器的上下文中,並且整個操作將會被當成一個整體執行,中間不會被其他命令插入。

這就保證了腳本將會以原子性的方式執行,當某個腳本正在運行的時候,不會有其他腳本或 Redis 命令被執行。在其他的別的客戶端看來,執行腳本的效果,要麼是不可見的,要麼就是已完成的。

EVAL 與 EVALSHA

EVAL

Redis 可以使用 EVAL 執行 LUA 腳本,而我們可以在 LUA 腳本中執行判斷求值邏輯。EVAL 執行方式如下:

EVAL script numkeys key [key ...] arg [arg ...]

numkeys 參數用於建明參數,即後面 key 數組的個數。

key [key ...] 代表需要在腳本中用到的所有 Redis key,在 Lua 腳本使用使用數組的方式訪問 key,類似如下 KEYS[1]KEYS[2]。注意 Lua 數組起始位置與 Java 不同,Lua 數組是從 1 開始。

命令最後,是一些附加參數,可以用來當做 Redis Key 值存儲的 Value 值,使用方式如 KEYS 變量一樣,類似如下:ARGV[1]ARGV[2]

用一個簡單例子運行一下 EVAL 命令:

eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2],ARGV[3]}" 2 key1 key2 first second third

運行效果如下:

可以看到 KEYSARGVS內部數組可以不一致。

在 Lua 腳本可以使用下面兩個函數執行 Redis 命令:

  • redis.call()
  • redis.pcall()

兩個函數作用法與作用完全一致,只不過對於錯誤的處理方式不一致,感興趣的小夥伴可以具體點擊以下鏈接,查看錯誤處理一章。

http://doc.redisfans.com/script/eval.html

下面我們統一在 Lua 腳本中使用 redis.call(),執行以下命令:

eval "return redis.call('set',KEYS[1],ARGV[1])" 1 foo 樓下小黑哥

運行效果如下:

EVALSHA

EVAL 命令每次執行時都需要發送 Lua 腳本,但是 Redis 並不會每次都會重新編譯腳本。

當 Redis 第一次收到 Lua 腳本時,首先將會對 Lua 腳本進行 sha1 獲取簽名值,然後內部將會對其緩存起來。後續執行時,直接通過 sha1 計算過後簽名值查找已經編譯過的腳本,加快執行速度。

雖然 Redis 內部已經優化執行的速度,但是每次都需要發送腳本,還是有網絡傳輸的成本,如果腳本很大,這其中花在網絡傳輸的時間就會相應的增加。

所以 Redis 又實現了 EVALSHA 命令,原理與 EVAL 一致。只不過 EVALSHA 只需要傳入腳本經過 sha1計算過後的簽名值即可,這樣大大的減少了傳輸的字節大小,減少了網絡耗時。

EVALSHA命令如下:

evalsha c686f316aaf1eb01d5a4de1b0b63cd233010e63d 1 foo 樓下小黑哥

運行效果如下:

SCRIPT FLUSH 命令用來清除所有 Lua 腳本緩存。

可以看到,如果之前未執行過 EVAL命令,直接執行 EVALSHA 將會報錯。

優化執行 EVAL

我們可以結合使用 EVALEVALSHA,優化程序。下面就不寫偽碼了,以 Jedis 為例,優化代碼如下:

//連接本地的 Redis 服務
Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("1234qwer");

System.out.println("服務正在運行: " + jedis.ping());

String lua_script = "return redis.call('set',KEYS[1],ARGV[1])";
String lua_sha1 = DigestUtils.sha1DigestAsHex(lua_script);

try {
    Object evalsha = jedis.evalsha(lua_sha1, Lists.newArrayList("foo"), Lists.newArrayList("樓下小黑哥"));
} catch (Exception e) {
    Throwable current = e;
    while (current != null) {
        String exMessage = current.getMessage();
        // 包含 NOSCRIPT,代表該 lua 腳本從未被執行,需要先執行 eval 命令
        if (exMessage != null && exMessage.contains("NOSCRIPT")) {
            Object eval = jedis.eval(lua_script, Lists.newArrayList("foo"), Lists.newArrayList("樓下小黑哥"));
            break;
        }

    }
}
String foo = jedis.get("foo");
System.out.println(foo);

上面的代碼看起來還是很複雜吧,不過這是使用原生 jedis 的情況下。如果我們使用 Spring Boot 的話,那就沒這麼麻煩了。Spring 組件執行的 Eval 方法內部就包含上述代碼的邏輯。

不過需要注意的是,如果 Spring-Boot 使用 Jedis 作為連接客戶端,並且使用Redis Cluster 集群模式,需要使用 2.1.9 以上版本的spring-boot-starter-data-redis,不然執行過程中將會拋出:

org.springframework.dao.InvalidDataAccessApiUsageException: EvalSha is not supported in cluster environment.

詳細情況可以參考這個修復的 IssueAdd support for scripting commands with Jedis Cluster

優化分佈式鎖

講完 Redis 執行 LUA 腳本的相關命令,我們來看下如何優化上面的分佈式鎖,使其無法釋放其他應用加的鎖。

以下代碼基於 spring-boot 2.2.7.RELEASE 版本,Redis 底層連接使用 Jedis。

加鎖的 Redis 命令如下:

SET lock_name uuid NX EX lock_time

加鎖代碼如下:

/**
 * 非阻塞式加鎖,若鎖存在,直接返回
 *
 * @param lockName  鎖名稱
 * @param request   唯一標識,防止其他應用/線程解鎖,可以使用 UUID 生成
 * @param leaseTime 超時時間
 * @param unit      時間單位
 * @return
 */
public Boolean tryLock(String lockName, String request, long leaseTime, TimeUnit unit) {
    // 注意該方法是在 spring-boot-starter-data-redis 2.1 版本新增加的,若是之前版本 可以執行下面的方法
    return stringRedisTemplate.opsForValue().setIfAbsent(lockName, request, leaseTime, unit);
}

由於setIfAbsent方法是在 spring-boot-starter-data-redis 2.1 版本新增加,之前版本無法設置超時時間。如果使用之前的版本的,需要如下方法:

/**
 * 適用於 spring-boot-starter-data-redis 2.1 之前的版本
 *
 * @param lockName
 * @param request
 * @param leaseTime
 * @param unit
 * @return
 */
public Boolean doOldTryLock(String lockName, String request, long leaseTime, TimeUnit unit) {
    Boolean result = stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
        RedisSerializer valueSerializer = stringRedisTemplate.getValueSerializer();
        RedisSerializer keySerializer = stringRedisTemplate.getKeySerializer();

        Boolean innerResult = connection.set(keySerializer.serialize(lockName),
                valueSerializer.serialize(request),
                Expiration.from(leaseTime, unit),
                RedisStringCommands.SetOption.SET_IF_ABSENT
        );
        return innerResult;
    });
    return result;
}

解鎖需要使用 Lua 腳本:

-- 解鎖代碼
-- 首先判斷傳入的唯一標識是否與現有標識一致
-- 如果一致,釋放這個鎖,否則直接返回
if redis.call('get', KEYS[1]) == ARGV[1] then
   return redis.call('del', KEYS[1])
else
   return 0
end

這段腳本將會判斷傳入的唯一標識是否與 Redis 存儲的標示一致,如果一直,釋放該鎖,否則立刻返回。

釋放鎖的方法如下:

/**
 * 解鎖
 * 如果傳入應用標識與之前加鎖一致,解鎖成功
 * 否則直接返回
 * @param lockName 鎖
 * @param request 唯一標識
 * @return
 */
public Boolean unlock(String lockName, String request) {
    DefaultRedisScript<Boolean> unlockScript = new DefaultRedisScript<>();
    unlockScript.setLocation(new ClassPathResource("simple_unlock.lua"));
    unlockScript.setResultType(Boolean.class);
    return stringRedisTemplate.execute(unlockScript, Lists.newArrayList(lockName), request);
}

由於公號外鏈無法直接跳轉,關注『程序通事』,回復分佈式鎖獲取源代碼。

Redis 分佈式鎖的缺陷

無法重入

由於上述加鎖命令使用了 SETNX ,一旦鍵存在就無法再設置成功,這就導致後續同一線程內繼續加鎖,將會加鎖失敗。

如果想將 Redis 分佈式鎖改造成可重入的分佈式鎖,有兩種方案:

  • 本地應用使用 ThreadLocal 進行重入次數計數,加鎖時加 1,解鎖時減 1,當計數變為 0 釋放鎖
  • 第二種,使用 Redis Hash 表存儲可重入次數,使用 Lua 腳本加鎖/解鎖

第一種方案可以參考這篇文章分佈式鎖的實現之 redis 篇。第二個解決方案,下一篇文章就會具體來聊聊,敬請期待。

鎖超時釋放

假設線程 A 加鎖成功,鎖超時時間為 30s。由於線程 A 內部業務邏輯執行時間過長,30s 之後鎖過期自動釋放。

此時線程 B 成功獲取到鎖,進入執行內部業務邏輯。此時線程 A 還在執行執行業務,而線程 B 又進入執行這段業務邏輯,這就導致業務邏輯重複被執行。

這個問題我覺得,一般由於鎖的超時時間設置不當引起,可以評估下業務邏輯執行時間,在這基礎上再延長一下超時時間。

如果超時時間設置合理,但是業務邏輯還有偶發的超時,個人覺得需要排查下業務執行過長的問題。

如果說一定要做到業務執行期間,鎖只能被一個線程佔有的,那就需要增加一個守護線程,定時為即將的過期的但未釋放的鎖增加有效時間。

加鎖成功后,同時創建一個守護線程。守護線程將會定時查看鎖是否即將到期,如果鎖即將過期,那就執行 EXPIRE 等命令重新設置過期時間。

說實話,如果要這麼做,真的挺複雜的,感興趣的話可以參考下 redisson watchdog 實現方式。

Redis 分佈式鎖集群問題

為了保證生產高可用,一般我們會採用主從部署方式。採用這種方式,我們可以將讀寫分離,主節點提供寫服務,從節點提供讀服務。

Redis 主從之間數據同步採用異步複製方式,主節點寫入成功后,立刻返回給客戶端,然後異步複製給從節點。

如果數據寫入主節點成功,但是還未複製給從節點。此時主節點掛了,從節點立刻被提升為主節點。

這種情況下,還未同步的數據就丟失了,其他線程又可以被加鎖了。

針對這種情況, Redis 官方提出一種 RedLock 的算法,需要有 N 個Redis 主從節點,解決該問題,詳情參考:

https://redis.io/topics/distlock。

這個算法自己實現還是很複雜的,幸好 redisson 已經實現的 RedLock,詳情參考:redisson redlock

總結

本來這篇文章是想寫 Redis 可重入分佈式鎖的,可是沒想到寫分佈式鎖的實現方案就已經寫了這麼多,再寫下去,文章可能就很長,所以拆分成兩篇來寫。

嘿嘿,這不下星期不用想些什麼了,真是個小機靈鬼~

好了,幫大家再次總結一下本文內容。

簡單的 Redis 分佈式鎖的實現方式還是很簡單的,我們可以直接用 SETNX/DEL 命令實現加解鎖。

不過這種實現方式不夠健壯,可能存在應用宕機,鎖就無法被釋放的問題。

所以我們接着引入以下命令以及 Lua 腳本增強 Redis 分佈式鎖。

SET lock_name anystring NX EX lock_time

最後 Redis 分佈鎖還是存在一些缺陷,在這裏提出一些解決方案,感興趣同學可以自己實現一下。

下篇文章再來將將 Redis 可重入分佈式鎖~

參考資料

  1. 分佈式鎖的實現之 redis 篇
  2. 基於 Redis 的分佈式鎖

歡迎關注我的公眾號:程序通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的博客:studyidea.cn

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

網頁設計最專業,超強功能平台可客製化

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

再生能源興起及疫情助攻 印度碳排放40年來首下降

摘錄自2020年5月12日自由時報報導

印度的二氧化碳排放量出現40年來首次下降,根據印度國家電力公司的數據,二氧化碳排放量在3月份下降了15%,4月份預計可下降30%。其中以燃煤減量為最主要的影響。

截至今年3月的財政年度報告,煤炭的運輸量下降約2%;而火力發電在過去10年中每年增長75%,石油消費需求也同樣下降。根據環境網站「Carbon Brief」的分析,用電下降和再生能源的競爭削弱了國內對化石燃料的需求,加上武漢肺炎(新型冠狀病毒疾病,Covid-19)疫情爆發,印度國內實施全境封鎖,成功改變碳排放量增長的趨勢。

據報導,印度今年3月石油的消耗量下降18%,再生能源供應在過去1年中則有所增加。根據國際能源署(International Energy Agency,IEA)4月底發布的數據,今年第一季度全球燃煤使用量也下降了8%。但分析師則提醒,化石燃料使用率的下降可能不會持續,分析師指出,當全球疫情趨緩,各國重啟經濟,排放量將再次飆升。

公害污染
空氣污染
能源議題
再生能源
污染治理
能源轉型
國際新聞
印度
碳排放
武漢肺炎
化石燃料
疫情看氣候與能源

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

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

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

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

蘇門答臘高速公路通車前夕驚見虎蹤 生態廊道功效恐不足 專家建議另增柵欄

環境資訊中心綜合外電;范震華 翻譯;賴慧玲 審校;稿源:Mongabay

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

【其他文章推薦】

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

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

毀林、武肺、林火三重危機 專家警告:亞馬遜今年火災恐比去年惡化50%

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

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

【其他文章推薦】

※回頭車貨運收費標準

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

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

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

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

真香,擼一個SpringBoot在線代碼修改器

前言

項目上線之後,如果是後端報錯,只能重新編譯打包部署然後重啟;如果僅僅是前端頁面、樣式、腳本修改,只需要替換到就可以了。

小公司的話可能比較自由,可以隨意替換,但是有些公司權限設置的比較嚴格,需要提交申請交給運維去處理。

如果僅僅是一個前端問題,又很緊急,這時候提申請走流程勢必會影響到用戶的正常使用。

今天,擼主給大家推薦一款前端代碼文件編輯器來解決以上問題。

案例

定義實體,用於前端文件樹展示:

@Data
public class SysFile {
    private Integer fileId;
    private String name;
    private Integer parentId;
    private String parentPath;
}

由於項目採用的是SpringBoot框架,打成了war包部署,後端採用以下方式獲取文件列表:

/**
 * 列表
 * @return
 */
@RequestMapping(value = "list", method = RequestMethod.POST)
public Result list() throws FileNotFoundException {
    String filePath = ResourceUtils.getURL("classpath:").getPath();
    List<SysFile> fileList = new ArrayList<>();
    getAllFilePaths(filePath,fileList,0,"");
    return Result.ok(fileList);
}

遞歸獲取某目錄下的所有子目錄以及子文件:

/**
 * 遞歸獲取某目錄下的所有子目錄以及子文件
 * @param filePath
 * @param filePathList
 * @return
 */
private static List<SysFile> getAllFilePaths(String filePath, List<SysFile> filePathList,
                                             Integer level,String parentPath) {
    File[] files = new File(filePath).listFiles();
    if (files == null) {
        return filePathList;
    }
    for (File file : files) {
        int num = filePathList.size()+1;
        SysFile sysFile = new SysFile();
        sysFile.setName(file.getName());
        sysFile.setFileId(num);
        sysFile.setParentId(level);
        if (file.isDirectory()) {
            if(level==0){
                if(file.getName().equals("templates")||
                        file.getName().equals("static")){
                    filePathList.add(sysFile);
                    parentPath = SystemConstant.SF_FILE_SEPARATOR+file.getName();
                    getAllFilePaths(file.getAbsolutePath(), filePathList,num,parentPath);
                    num++;
                }
            }else{
                filePathList.add(sysFile);
                String subParentPath = parentPath+SystemConstant.SF_FILE_SEPARATOR+file.getName();
                getAllFilePaths(file.getAbsolutePath(), filePathList,num,subParentPath);
                num++;
            }
        } else {
            if(level!=0){
                sysFile.setParentPath(parentPath+SystemConstant.SF_FILE_SEPARATOR+file.getName());
                filePathList.add(sysFile);
                num++;
            }
        }
    }
    return filePathList;
}

獲取文件內容:

/**
 * 獲取內容
 * @return
 */
@RequestMapping(value = "getContent", method = RequestMethod.POST)
public Result getContent(String filePath) throws FileNotFoundException {
    String path = ResourceUtils.getURL("classpath:").getPath();
    String content = FileUtil.readUtf8String(path+filePath);
    return Result.ok(content);
}

修改保存:

/**
 * 保存內容
 * @return
 */
@RequestMapping(value = "save", method = RequestMethod.POST)
public Result save(String filePath, String content) throws FileNotFoundException {
    String path = ResourceUtils.getURL("classpath:").getPath();
    /**
     * 生產環境自行解除
     */
    if(active.equals("prod")){
        return Result.error("演示環境禁止插插插!!!");
    }else{
        File file = new File(path+filePath);
        long lastModified = file.lastModified();
        FileUtil.writeUtf8String(content,path+filePath);
        file.setLastModified(lastModified);
        return Result.ok();
    }
}

當然了,如果代碼修改比較多,也可以對文件進行上傳覆蓋操作。

截圖

小結

如果身邊恰好沒有工具連接遠程服務,亦或是自己沒有服務器的權限,這款在線修改器,擼主覺得還是很方便的。但一定要控制好權限,防止普通人員亂修改,還有一定要做好安全日誌記錄。

源碼

https://gitee.com/52itstyle/SPTools

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

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

※回頭車貨運收費標準

抓準時機 澳洲有望在武肺過後追趕全球綠能進度

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

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

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

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

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

LG化學又傳工安意外 南韓工廠爆炸1死2傷

摘錄自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維修中心

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

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

針對的都是首次購車的年輕人,這倆合資小型SUV該怎麼選?

但本田擅長的空間攫取能力則得以比較充分的體現,儘管XR-V的車身三圍尺寸並不算大,但在乘坐空間的表現上,好感度要比同級別的昂科拉更勝一籌。動力:非常經典的對決別克昂科拉與本田XR-V的對比也可以說是很多人十分在意的“渦輪增壓與自然吸氣的對比”,別克昂科拉採用1。

別克昂科拉 VS 本田XR-V?

當下汽車設計的主流也跟汽車消費的主流趨勢一樣,越來越趨於年輕化,別克昂科拉和東風本田XR-V則都是設計偏向年輕時尚的小型SUV,也是很多首次購車人群在合資小型SUV這個細分市場的首要考慮對象車型,那麼這兩款車型各自優勢在哪?又該如何選擇?

由於兩車在指導價格方面差距還是比較大,別克昂科拉明顯偏貴,而且別克昂科拉的細分車型當中有一款四驅配置車型,反觀XR-V全系沒有四驅標配,我們去除別克朗科拉頂配四驅旗艦型,採用兩驅都市精英型(次頂配),與XR-V 1.8L CVT豪華版(頂配)對比,兩者指導價格和配置更加接近。

東風本田-XR-V

1.8L VTi CVT豪華版

指導價格:16.28萬

別克昂科拉

18T 自動兩驅都市精英型

指導價格:16.99萬

外觀:敦實沉穩VS時尚運動

昂科拉的整體外觀變化並不大,主要的變化在於前臉,車標加上飛翼式鍍鉻綬帶的裝飾安置於直瀑式中網上,鍍鉻綬帶還與前大燈模塊內的日間行車燈融為一體,這讓昂科拉的前臉顯得更加具有辨識度;

整車還是維持了昂科拉一貫的敦實形象,這麼一款尺寸不大的小型SUV看上去會讓人有一種沉穩紮實的感覺,腰線和車尾線條飽滿,配合上原廠提供的較為具有活力的配色,昂科拉的造型也透露出一種時尚的動感。

XR-V使用了當下本田家族式的上下雙條幅鍍鉻裝飾作為前臉主體設計元素,配合上銳利的大燈和大面積的黑色塑料裝飾,本田XR-V前臉顯得運動感與攻擊性都更為明顯。

相較於昂科拉的“敦厚”,XR-V的整車線條則顯得有些“扁”,這種效果就會使得車身側面的視覺效果顯得更加修長,車尾設計線條層次感豐富,橫向線條多次運用但不會顯得雜亂和複雜,反而更提升了XR-V年輕的效果。

內飾:各家所長得以充分體現

別克家族近年來的內飾營造手段是體現在全系車型上的,昂科拉亦是如此,乍一眼看上去,別克昂科拉的內飾設計很容易給人以好感,而且在用料的選材和裝配上,別克昂克拉的內飾顯得比較高檔。

而XR-V的內飾設計感或許稍微欠缺,而且由於成本所限,拼接裝飾板材質較硬,在對比體驗上說或許溝通感有所減分。但本田擅長的空間攫取能力則得以比較充分的體現,儘管XR-V的車身三圍尺寸並不算大,但在乘坐空間的表現上,好感度要比同級別的昂科拉更勝一籌。

動力:非常經典的對決

別克昂科拉與本田XR-V的對比也可以說是很多人十分在意的“渦輪增壓與自然吸氣的對比”,別克昂科拉採用1.4T渦輪增壓發動機配合6速手自一體的動力總成。最大馬力143匹,峰值扭矩205牛米。

昂科拉的峰值扭矩平台在發動機達到1800轉的時候得以爆發,對於這個級別的小排量渦輪增壓車型來說中規中矩,兩驅版本的車重1.4噸,動力輸出也不會因此而感到拖沓,換擋效率較高的手自一體變速箱也賦予了昂科拉一定的駕駛樂趣。

本田XR-V所搭載的是一台1.8L自然吸氣發動機,最大馬力136匹,峰值扭矩169牛米,與之配合的是一台CVT無極變速箱,整體駕駛感受保留了一台CVT該有的平順特性,駕駛樂趣的話,小編覺得談不上,但踏實平穩才是一款CVT車型該有的基調。

而且由於XR-V整備質量不大,頂配CVT豪華版車重1.3噸,加之本田CVT變速箱一貫良好的加速性能,XR-V在城市裡通勤的表現也是非常靈活,駕駛起來沒有太大難度。

哪個更值得買?

如果看重的是整車所給予人的質感和高級感方面,別克昂科拉是比較好的選擇,畢竟在車內裝飾用料和整車NVH的控制上,別克昂科拉可以說做到了領先於同級別其他車型的水準,昂科拉所給予人的是更高檔的使用感受。

而如果更看重的是車內更實用的空間表現和駕駛起來的平順性的話,本田XR-V或許更值得考慮,畢竟這兩塊是本田這個品牌所擅長的領域,而且在XR-V上也得以較好的體現,無論是乘坐空間還是載物空間,本田依舊展現了較強的空間利用實力,在動力平順性上,本田的CVT變速箱和自然吸氣發動機配合出來的線性感受也不會使人失望。

全文總結:儘管2017款的昂科拉在12月6日才剛剛上市,但是外觀配置並沒有過多的變化,而且在售價上與2016款幾乎齊平;加之在終端優惠上,別克全系車型當下的終端優惠普遍不小,而反觀日系的XR-V,降價的幅度則比較有限,所以如果從終端優惠上看,或許選擇折扣更大的別克昂科拉也是個不錯的想法。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

網頁設計最專業,超強功能平台可客製化

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