美國八大州擬擴建充電站等 提升電動汽車普及率

為增加電動汽車普及率,美國加州、紐約及其它6州政府昨(24) 日聯合發布聲明,將廣設充電站、修改營建法條,務求使一般市民也能輕鬆擁有、享受電動汽車。

加州、紐約、麻州、康乃狄克、馬里蘭、羅德島、佛蒙特、奧勒崗等州,合計銷售佔比已超過美國汽車市場的25%。其中,加州是美國最大的汽車銷售市場。

據紐約時報報導,到2025年,州政府希望零碳排放汽車(包含電動汽車與燃料電池汽車)銷量至少能達到330萬輛的目標。

美國電動汽車製造商特斯拉(Tesla)昨天宣布,成功挖腳蘋果產品設計副總裁Doug Field,他在蘋果公司任職時,曾是MacBook Air、MacBook Pro、與iMac等明星級產品的幕後推手。Field之前還在福特汽車任職過,未來他將領導特斯拉汽車研發部門。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

戴姆勒與歐洲五集團合作 擬在德國大幅增設加氫站

戴姆勒集團日前宣佈,將與法國液化空氣集團、德國林德集團、奧地利石油天然氣集團(OMV)、荷蘭皇家殼牌集團、法國道達爾集團合作,啟動一項名為“H2 流動”的計畫,到2017年將德國現有的15座加氫站增加到100座,到2023年增加到400座。以效刺激氫燃料電池車市場。

除了在城市中心區大量設置加氫站之外,還將在德國主要高速公路沿線設置加氫站,兩座加氫站之間的距離不超過90公里。這項加氫站建設計畫耗資巨大。戴姆勒集團預計,為了修建400座加氫站,大約需要3.5億歐元。

加氫站是發展氫燃料電池車的必備條件,各國都在陸續興建,但此前一直進展緩慢,去年全球僅新增27座加氫站。根據美國能源部最新公佈的資料,目前美國境內只有10座公共加氫站。

由於氫燃料電池車實現了零排放,有利於保護環境,同時降低對石油的依賴,被稱為“終極環保車”。此外,氫燃料電池車續駛里程較長,且幾分鐘內就可以完成加氫工作。目前氫燃料電池車市場處於起步階段,大多採用租賃方式。

今年1月,戴姆勒與福特、日產簽署了戰略聯盟協定,在燃料電池車領域展開深度合作,聯合研發可以共用的燃料電池堆、燃料電池系統及其他燃料電池車部件,並計畫2017年推出各自的量產燃料電池車。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

雷諾擬與LG Chem合作開發時速400公里的電動車

全球最大可充電電池製造商樂金化學公司(LG Chem),和歐洲電動車先驅雷諾汽車(Renault)聯手,希望未來幾年將電動車的最高時速加快一倍。

據韓國總統朴槿惠的首席經濟幕僚趙源東(Cho Won Dong)透露,這兩家公司正在考慮開發最快時速可達400公里的電動車。目前電動車的最高時速為每小時200公里。

為加強雙邊合作,朴槿惠4日還拜會了雷諾在巴黎南方設立的電動車測試中心。

據悉,兩家公司目前還不會簽署了解備忘錄(MOU),但原則上同意互相合作,正在協商細節上的歧異。由於要開發高速電動車,鋰電池是最重要的環節之一,因此對於雷諾來說,與樂金化學的合作十分重要。

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

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

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

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

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

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

※回頭車貨運收費標準

歐盟擬出資400萬歐元升級「雙馬達」電動汽車

據物理學家組織網18日報道,歐盟將出資400萬歐元(約合3307萬元人民幣)資助一項「雙馬達」電動汽車升級項目。這項名為「安全高效電動汽車」(EFUTURE)的項目組正在試圖解決「雙馬達」電動車帶來的安全性及能耗問題。

盡管電動車的銷量在不斷攀升,但目前在歐洲馬路上行駛的電動車總量不超過10萬輛,且其中90%是乘用車系列。相比之下,傳統汽車的數量則超過2.5億輛。

電動汽車之所以沒完全普及開來,「性價比不高」被認為是最重要的原因。在性能方面,由於電池尚處於研發的初級階段,導致電動汽車行駛距離較為短暫。

有些電動車引入了「雙馬達」的概念,,但同時控制兩個馬達,安全性成為了一個重要的挑戰。「雙馬達」電動車需要一種新的系統架構,以及從傳感器到控制單元的一系列的電子設備。

該項目的目標就是設計出新一代電動汽車,用一款智能軟件最大限度地減少能源消耗,同時仍然能夠在安全和能源消耗之間做出動態的優化決策。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

美國汽車監管機構對Model S起火展開調查

據彭博社報導,美國汽車監管機構正調查特斯拉汽車公司(Tesla Motors Inc.)的Model S電動車,此舉恐讓特斯拉股價進一步往下掉。Model S號稱是「全美最安全的汽車」,有觀點認為,這是特斯拉可能展開召回的跡象。

Model S電動車此前在5週內發生3起起火意外。美國國家公路交通安全管理局(NHTSA)昨(19)日在官網宣布將進行調查,並表示會調查汽車底盤撞擊物體的起火風險,調查對象包括1,3108輛Model S。

上週表示「絕對不會召回汽車」的特斯拉執行長馬斯克(Elon Musk),昨(19)日在NHTSA宣布調查前幾個小時在部落格貼文表示,公司希望這次調查可以消除外界對Model S安全性的「錯誤觀感」。

馬斯克寫道:「雖然我們認為這極度不可能,但如果在乘客防火安全可大幅改善上有任何發現,我們會立刻在新車上改進,並免費維修所有出售車輛。」他還表示,因為這些起火意外,特斯拉將修改損害範圍的保固內容,並更新氣壓避震器,讓汽車在高速行駛時拉大底盤離地高度。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

與特斯拉競爭 BMW、通用等洛杉磯車展推電動車

據華爾街日報報導,美國電動車商特斯拉(Tesla)去年推出Model S後,越來越多的車商參與到豪華電動車市場的競爭。

BMW、通用的凱迪拉克、福斯的保持捷與奧迪,都趁本周揭幕的洛杉磯車展,展出自家新電動車或插電式油電混合車。雖各家車廠的推進技術與Model S不同,但都想爭奪加州消費者的心,此一族群是特斯拉的重要客戶層。

電動車目前在整體車市占有率微不足道。即使日產Leaf電動車需求增加,讓日產擴大Leaf產能,但今年來,電動車僅占美國輕型車銷售約1%。

電動車售價,因鋰離子電池昂貴而難以下降,加上續航力不足,被視為市場接受度不高的主因。但售價7萬美元的特斯拉Model S,因高電池效能讓其充電一次可行駛265英里,因此供不應求。此外政府制定廢氣排放要求趨嚴,也迫使車商生產更多電動車或油電車。

奧迪在洛杉磯車展展出A3 E-Tron插電式油電混合車,準備在2015年上市;但沒有透露何時推出全電動版。通用表示,其ELR油電車因售價7.6萬美元,而會被市場拿來與Model S做比較。BMW在車展上推出13.5萬美元起跳的i8插電式油電跑車,和4萬美元的城市通勤電動小車i3。

而多款豪華插電式新車在明年上市,對特斯拉影響有多大,還要看此一市場規模是否已經變大,抑或大家仍在爭食同一塊大餅。

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

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

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

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

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

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

※回頭車貨運收費標準

SpringCloud Alibaba (三):Sentinel 流量控制組件

SpringCloud Alibaba (三):Sentinel 流量控制組件

Sentinel 是什麼

隨着微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 是面向分佈式服務架構的流量控制組件,主要以流量為切入點,從限流、流量整形、熔斷降級、系統負載保護、熱點防護等多個維度來幫助開發者保障微服務的穩定性。

Sentinel 基本概念

資源

資源是 Sentinel 的關鍵概念。它可以是 Java 應用程序中的任何內容,例如,由應用程序提供的服務,或由應用程序調用的其它應用提供的服務,甚至可以是一段代碼。

只要通過 Sentinel API 定義的代碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下,可以使用方法簽名,URL,甚至服務名稱作為資源名來標示資源。

規則

圍繞資源的實時狀態設定的規則,可以包括流量控制規則、熔斷降級規則以及系統保護規則。所有規則可以動態實時調整。

 

規則的種類

Sentinel 的所有規則都可以在內存態中動態地查詢及修改,修改之後立即生效。同時 Sentinel 也提供相關 API,供您來定製自己的規則策略。

Sentinel 支持以下幾種規則:流量控制規則熔斷降級規則系統保護規則來源訪問控制規則熱點參數規則

流量控制規則 (FlowRule)

流量規則的定義0

重要屬性:

Field 說明 默認值
resource 資源名,資源名是限流規則的作用對象  
count 限流閾值  
grade 限流閾值類型,QPS 或線程數模式 QPS 模式
limitApp 流控針對的調用來源 default,代表不區分調用來源
strategy 調用關係限流策略:直接、鏈路、關聯 根據資源本身(直接)
controlBehavior 流控效果(直接拒絕 / 排隊等待 / 慢啟動模式),不支持按調用關係限流 直接拒絕

同一個資源可以同時有多個限流規則。

通過代碼定義流量控制規則

理解上面規則的定義之後,我們可以通過調用 FlowRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則,比如:

private static void initFlowQpsRule() {
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule1 = new FlowRule();
    rule1.setResource(resource);
    // Set max qps to 20
    rule1.setCount(20);
    rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule1.setLimitApp("default");
    rules.add(rule1);
    FlowRuleManager.loadRules(rules);
}

更多詳細內容可以參考 流量控制

熔斷降級規則 (DegradeRule)

熔斷降級規則包含下面幾個重要的屬性:

Field 說明 默認值
resource 資源名,即限流規則的作用對象  
count 閾值  
grade 熔斷策略,支持秒級 RT/秒級異常比例/分鐘級異常數 秒級平均 RT
timeWindow 降級的時間,單位為 s  

同一個資源可以同時有多個降級規則。

理解上面規則的定義之後,我們可以通過調用 DegradeRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則。

 private static void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();
        rule.setResource(KEY);
        // set threshold rt, 10 ms
        rule.setCount(10);
        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        rule.setTimeWindow(10);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }

更多詳情可以參考 熔斷降級

系統保護規則 (SystemRule)

規則包含下面幾個重要的屬性:

Field 說明 默認值
highestSystemLoad load1 閾值,參考值 -1 (不生效)
avgRt 所有入口流量的平均響應時間 -1 (不生效)
maxThread 入口流量的最大併發數 -1 (不生效)
qps 所有入口資源的 QPS -1 (不生效)
highestCpuUsage 當前系統的 CPU 使用率(0.0-1.0) -1 (不生效)

理解上面規則的定義之後,我們可以通過調用 SystemRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則。

private void initSystemProtectionRule() {
  List<SystemRule> rules = new ArrayList<>();
  SystemRule rule = new SystemRule();
  rule.setHighestSystemLoad(10);
  rules.add(rule);
  SystemRuleManager.loadRules(rules);
}

更多詳情可以參考 系統自適應保護

訪問控制規則 (AuthorityRule)

很多時候,我們需要根據調用方來限制資源是否通過,這時候可以使用 Sentinel 的訪問控制(黑白名單)的功能。黑白名單根據資源的請求來源(origin)限制資源是否通過,若配置白名單則只有請求來源位於白名單內時才可通過;若配置黑名單則請求來源位於黑名單時不通過,其餘的請求通過。

授權規則,即黑白名單規則(AuthorityRule)非常簡單,主要有以下配置項:

  • resource:資源名,即限流規則的作用對象

  • limitApp:對應的黑名單/白名單,不同 origin 用 , 分隔,如 appA,appB

  • strategy:限制模式,AUTHORITY_WHITE 為白名單模式,AUTHORITY_BLACK 為黑名單模式,默認為白名單模式

更多詳情可以參考 來源訪問控制

熱點規則 (ParamFlowRule)

詳情可以參考 熱點參數限流

 

Sentinel控制台

概述

Sentinel 提供一個輕量級的開源控制台,它提供機器發現以及健康情況管理、監控(單機和集群),規則管理和推送的功能。另外,鑒權在生產環境中也必不可少。這裏,我們將會詳細講述如何通過簡單的步驟就可以使用這些功能。

接下來,我們將會逐一介紹如何整合 Sentinel 核心庫和 Dashboard,讓它發揮最大的作用。同時我們也在阿里雲上提供企業級的控制台:AHAS Sentinel 控制台,您只需要幾個簡單的步驟,就能最直觀地看到控制台如何實現這些功能。

Sentinel 控制台包含如下功能:

  • 查看機器列表以及健康情況:收集 Sentinel 客戶端發送的心跳包,用於判斷機器是否在線。

  • 監控 (單機和集群聚合):通過 Sentinel 客戶端暴露的監控 API,定期拉取並且聚合應用監控信息,最終可以實現秒級的實時監控。

  • 規則管理和推送:統一管理推送規則。

  • 鑒權:生產環境中鑒權非常重要。這裏每個開發者需要根據自己的實際情況進行定製。

更詳細內容,訪問 https://github.com/alibaba/Sentinel/wiki

啟動Sentinel控制台

1.在Sentinel的github上下載 sentinel-dashboard.jar

https://github.com/alibaba/Sentinel/releases

2.在sentinel-dashboard.jar所在文件夾運行cmd,在cmd里運行以下啟動命令啟動sentinel-dashboard

java -Dserver.port=8081 -Dcsp.sentinel.dashboard.server=localhost:8081 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.2.jar

3.訪問 localhost:8081,進入Sentinel控制台,默認用戶和密碼都是 sentinel

 

SpringCloud Alibaba 整合 Sentinel

Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結合 Dashboard 可以取得最好的效果。

我們說的資源,可以是任何東西,服務,服務里的方法,甚至是一段代碼。使用 Sentinel 來進行資源保護,主要分為幾個步驟:

  1. 定義資源

  2. 定義規則

  3. 檢驗規則是否生效

先把可能需要保護的資源定義好,之後再配置規則。也可以理解為,只要有了資源,我們就可以在任何時候靈活地定義各種流量控制規則。在編碼的時候,只需要考慮這個代碼是否需要保護,如果需要保護,就將之定義為一個資源。

定義資源

1. 添加依賴

注意:在整合spring-cloud-starter-alibaba-sentinelspring-cloud-starter-openfeign時,feign-core的版本要在10.1.0以上,即導入2.1.0.RELEASE或以上版本的openfeign,否則會報feign.RequestTemplate.path()Ljava/lang/String;異常,因為低版本openfeign的RequestTemplate類里沒有path方法

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>
2.添加配置
server:
  port: 8080
​
spring:
  application:
    name: consumer-8080
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.11.132:8848
    sentinel:
      transport:
        dashboard: localhost:8081 # 將服務註冊到sentinel控制台
​
feign:
  sentinel:
    enabled: true #開啟feign的sentinel支持
​
management:
  endpoints:
    web:
      exposure:
        include: "*"
3.在controller需要保護的方法添加 @SentinelResource 註解,把方法定義為資源
@RestController
public class FeignController {
​
    @Autowired
    private EchoService echoService;
    
    @SentinelResource(value = "echo", blockHandler = "echoBlockHandler", blockHandlerClass = EchoServiceBlockHandler.class)
    @GetMapping("/feign/echo/{string}")
    public String echo(@PathVariable("string")String string){
        return echoService.echo(string);
    }
​
}

@SentinelResource 用於定義資源,並提供可選的異常處理和 fallback 配置項。 @SentinelResource 註解包含以下屬性:

  • value:資源名稱,必需項(不能為空)

  • entryType:entry 類型,可選項(默認為 EntryType.OUT

  • blockHandler / blockHandlerClass: blockHandler 對應處理 BlockException 的函數名稱,可選項。blockHandler 函數訪問範圍需要是 public,返回類型需要與原方法相匹配,參數類型需要和原方法相匹配並且最後加一個額外的參數,類型為 BlockException。blockHandler 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 blockHandlerClass 為對應的類的 Class 對象,注意對應的函數必需為 static 函數,否則無法解析。

  • fallback:fallback 函數名稱,可選項,用於在拋出異常的時候提供 fallback 處理邏輯。fallback 函數可以針對所有類型的異常(除了 exceptionsToIgnore裏面排除掉的異常類型)進行處理。fallback 函數簽名和位置要求:

    • 返回值類型必須與原函數返回值類型一致;

    • 方法參數列表需要和原函數一致,或者可以額外多一個 Throwable 類型的參數用於接收對應的異常。

    • fallback 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 fallbackClass 為對應的類的 Class 對象,注意對應的函數必需為 static 函數,否則無法解析。

  • defaultFallback(since 1.6.0):默認的 fallback 函數名稱,可選項,通常用於通用的 fallback 邏輯(即可以用於很多服務或方法)。默認 fallback 函數可以針對所以類型的異常(除了 exceptionsToIgnore 裏面排除掉的異常類型)進行處理。若同時配置了 fallback 和 defaultFallback,則只有 fallback 會生效。defaultFallback 函數簽名要求:

    • 返回值類型必須與原函數返回值類型一致。

    • 方法參數列表需要為空,或者可以額外多一個 Throwable 類型的參數用於接收對應的異常。

    • defaultFallback 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 fallbackClass 為對應的類的 Class 對象,注意對應的函數必需為 static 函數,否則無法解析。

  • exceptionsToIgnore(since 1.6.0):用於指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣拋出。

blockHandler 和 fallback區別

blockHandler 函數會在原方法被限流/降級/系統保護的時候調用,而 fallback 函數會針對所有類型的異常。

如果一個資源同時對 blockHandler 和 fallback 都進行了配置,除了流量控制規則觸發時拋出的 BlockException 會進入 blockHandler 處理邏輯,其他規則觸發時都會進入fallback處理邏輯

4.創建EchoServiceBlockHandler類,建立blockHandler 處理邏輯
public class EchoServiceBlockHandler {
​
    private final static Logger logger = LoggerFactory.getLogger(EchoServiceBlockHandler.class);
​
    public static String echoBlockHandler(String string, BlockException e){
        logger.error("error: "+e);
        return "方法請求降級中";
    }
​
}

在應用程序里定義規則

1.在啟動類中定義規則,並加入容器中
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Consumer8080 {
    public static void main(String[] args) {
        SpringApplication.run(Consumer8080.class, args);
    }
​
    @Bean
    public SentinelResourceAspect sentinelResourceAspect(){
        return new SentinelResourceAspect();
    }
​
    //流量控制規則
    @Bean
    public static void initFlowRule(){
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule flowRule = new FlowRule();
        flowRule.setResource("echo"); //設置資源名,即流量控制規則的作用對象
        flowRule.setCount(2); //設置限流閾值,此為QPS,即每秒最高訪問量
        flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); //限流閾值類型,QPS或線程數
        rules.add(flowRule);
        FlowRuleManager.loadRules(rules);
    }
​
}
2.訪問測試

訪問 http://localhost:8080/feign/echo/hi ,快速刷新,觸發流量控制規則,調用降級邏輯

在Sentinel控制台定義規則

在Sentinel控制台上我們可以簡單、靈活地管理規則以及推送規則,但是要注意的是,在Sentinel控制台上定義的規則是不會被持久化的,僅在內存中生存,當你重啟應用時,在Sentinel控制台上定義的規則將會丟失(應用程序里定義的規則在sentinel控制台上刪除,應用重啟後會重新生效)

 

 

我的個人博客站

 

 

自動判斷 中文 中文(簡體) 中文(香港) 中文(繁體) 英語 日語 朝鮮語 德語 法語 俄語 泰語 南非語 阿拉伯語 阿塞拜疆語 比利時語 保加利亞語 加泰隆語 捷克語 威爾士語 丹麥語 第維埃語 希臘語 世界語 西班牙語 愛沙尼亞語 巴士克語 法斯語 芬蘭語 法羅語 加里西亞語 古吉拉特語 希伯來語 印地語 克羅地亞語 匈牙利語 亞美尼亞語 印度尼西亞語 冰島語 意大利語 格魯吉亞語 哈薩克語 卡納拉語 孔卡尼語 吉爾吉斯語 立陶宛語 拉脫維亞語 毛利語 馬其頓語 蒙古語 馬拉地語 馬來語 馬耳他語 挪威語(伯克梅爾) 荷蘭語 北梭托語 旁遮普語 波蘭語 葡萄牙語 克丘亞語 羅馬尼亞語 梵文 北薩摩斯語 斯洛伐克語 斯洛文尼亞語 阿爾巴尼亞語 瑞典語 斯瓦希里語 敘利亞語 泰米爾語 泰盧固語 塔加路語 茨瓦納語 土耳其語 宗加語 韃靼語 烏克蘭語 烏都語 烏茲別克語 越南語 班圖語 祖魯語 自動選擇 中文 中文(簡體) 中文(香港) 中文(繁體) 英語 日語 朝鮮語 德語 法語 俄語 泰語 南非語 阿拉伯語 阿塞拜疆語 比利時語 保加利亞語 加泰隆語 捷克語 威爾士語 丹麥語 第維埃語 希臘語 世界語 西班牙語 愛沙尼亞語 巴士克語 法斯語 芬蘭語 法羅語 加里西亞語 古吉拉特語 希伯來語 印地語 克羅地亞語 匈牙利語 亞美尼亞語 印度尼西亞語 冰島語 意大利語 格魯吉亞語 哈薩克語 卡納拉語 孔卡尼語 吉爾吉斯語 立陶宛語 拉脫維亞語 毛利語 馬其頓語 蒙古語 馬拉地語 馬來語 馬耳他語 挪威語(伯克梅爾) 荷蘭語 北梭托語 旁遮普語 波蘭語 葡萄牙語 克丘亞語 羅馬尼亞語 梵文 北薩摩斯語 斯洛伐克語 斯洛文尼亞語 阿爾巴尼亞語 瑞典語 斯瓦希里語 敘利亞語 泰米爾語 泰盧固語 塔加路語 茨瓦納語 土耳其語 宗加語 韃靼語 烏克蘭語 烏都語 烏茲別克語 越南語 班圖語 祖魯語 有道翻譯 百度翻譯 谷歌翻譯 谷歌翻譯(國內)

翻譯 朗讀 複製 正在查詢,請稍候…… 重試 朗讀 複製 複製 朗讀 複製 via 谷歌翻譯(國內) 譯

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

談談spring-boot-starter-data-redis序列化

在上一篇中springboot 2.X 集成redis中提到了在spring-boot-starter-data-redis中使用JdkSerializationRedisSerializerl來實現序列化,
這裏看下具體是如何實現的。
1.RedisSerializer接口
在spring-data-redis包下,有一個RedisSerializer接口,提供了序列化和反序列化的基本接口。

public interface RedisSerializer<T> {

	/**
	 * Serialize the given object to binary data.
	 *
	 * @param t object to serialize. Can be {@literal null}.
	 * @return the equivalent binary data. Can be {@literal null}.
	 */
	@Nullable
	byte[] serialize(@Nullable T t) throws SerializationException;

	/**
	 * Deserialize an object from the given binary data.
	 *
	 * @param bytes object binary representation. Can be {@literal null}.
	 * @return the equivalent object instance. Can be {@literal null}.
	 */
	@Nullable
	T deserialize(@Nullable byte[] bytes) throws SerializationException;

	/**
	 * Obtain a {@link RedisSerializer} using java serialization.<br />
	 * <strong>Note:</strong> Ensure that your domain objects are actually {@link java.io.Serializable serializable}.
	 *
	 * @return never {@literal null}.
	 * @since 2.1
	 */
	static RedisSerializer<Object> java() {
		return java(null);
	}

	/**
	 * Obtain a {@link RedisSerializer} using java serialization with the given {@link ClassLoader}.<br />
	 * <strong>Note:</strong> Ensure that your domain objects are actually {@link java.io.Serializable serializable}.
	 *
	 * @param classLoader the {@link ClassLoader} to use for deserialization. Can be {@literal null}.
	 * @return new instance of {@link RedisSerializer}. Never {@literal null}.
	 * @since 2.1
	 */
	static RedisSerializer<Object> java(@Nullable ClassLoader classLoader) {
		return new JdkSerializationRedisSerializer(classLoader);
	}

	/**
	 * Obtain a {@link RedisSerializer} that can read and write JSON using
	 * <a href="https://github.com/FasterXML/jackson-core">Jackson</a>.
	 *
	 * @return never {@literal null}.
	 * @since 2.1
	 */
	static RedisSerializer<Object> json() {
		return new GenericJackson2JsonRedisSerializer();
	}

	/**
	 * Obtain a simple {@link java.lang.String} to {@literal byte[]} (and back) serializer using
	 * {@link java.nio.charset.StandardCharsets#UTF_8 UTF-8} as the default {@link java.nio.charset.Charset}.
	 *
	 * @return never {@literal null}.
	 * @since 2.1
	 */
	static RedisSerializer<String> string() {
		return StringRedisSerializer.UTF_8;
	}
}

可以看到byte[] serialize(@Nullable T t)和T deserialize(@Nullable byte[] bytes)就是序列化和反序列化接口,並且下面還定義了java的JdkSerializationRedisSerializer序列化、json的GenericJackson2JsonRedisSerializer和string的StringRedisSerializer.UTF_8.
2.1 JdkSerializationRedisSerializer序列化

public class JdkSerializationRedisSerializer implements RedisSerializer<Object> {

	private final Converter<Object, byte[]> serializer;
	private final Converter<byte[], Object> deserializer;

	/**
	 * Creates a new {@link JdkSerializationRedisSerializer} using the default class loader.
	 */
	public JdkSerializationRedisSerializer() {
		this(new SerializingConverter(), new DeserializingConverter());
	}

	/**
	 * Creates a new {@link JdkSerializationRedisSerializer} using a {@link ClassLoader}.
	 *
	 * @param classLoader the {@link ClassLoader} to use for deserialization. Can be {@literal null}.
	 * @since 1.7
	 */
	public JdkSerializationRedisSerializer(@Nullable ClassLoader classLoader) {
		this(new SerializingConverter(), new DeserializingConverter(classLoader));
	}

	/**
	 * Creates a new {@link JdkSerializationRedisSerializer} using a {@link Converter converters} to serialize and
	 * deserialize objects.
	 *
	 * @param serializer must not be {@literal null}
	 * @param deserializer must not be {@literal null}
	 * @since 1.7
	 */
	public JdkSerializationRedisSerializer(Converter<Object, byte[]> serializer, Converter<byte[], Object> deserializer) {

		Assert.notNull(serializer, "Serializer must not be null!");
		Assert.notNull(deserializer, "Deserializer must not be null!");

		this.serializer = serializer;
		this.deserializer = deserializer;
	}

	public Object deserialize(@Nullable byte[] bytes) {

		if (SerializationUtils.isEmpty(bytes)) {
			return null;
		}

		try {
			return deserializer.convert(bytes);
		} catch (Exception ex) {
			throw new SerializationException("Cannot deserialize", ex);
		}
	}

	@Override
	public byte[] serialize(@Nullable Object object) {
		if (object == null) {
			return SerializationUtils.EMPTY_ARRAY;
		}
		try {
			return serializer.convert(object);
		} catch (Exception ex) {
			throw new SerializationException("Cannot serialize", ex);
		}
	}
}

在JdkSerializationRedisSerializer構造方法中,傳入了Converter的兩個對象,serialize的序列化就使用SerializingConverter的convert方法

public byte[] convert(Object source) {
	try  {
		return this.serializer.serializeToByteArray(source);
	}
	catch (Throwable ex) {
		throw new SerializationFailedException("Failed to serialize object using " +
				this.serializer.getClass().getSimpleName(), ex);
	}
}

Serializer 接口

void serialize(T object, OutputStream outputStream) throws IOException;

default byte[] serializeToByteArray(T object) throws IOException {
	ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
	serialize(object, out);
	return out.toByteArray();
}

在這裏JdkSerializationRedisSerializer中,使用的是DefaultSerializer,它實現了serialize方法:

public class DefaultSerializer implements Serializer<Object> {

	/**
	 * Writes the source object to an output stream using Java serialization.
	 * The source object must implement {@link Serializable}.
	 * @see ObjectOutputStream#writeObject(Object)
	 */
	@Override
	public void serialize(Object object, OutputStream outputStream) throws IOException {
		if (!(object instanceof Serializable)) {
			throw new IllegalArgumentException(getClass().getSimpleName() + " requires a Serializable payload " +
					"but received an object of type [" + object.getClass().getName() + "]");
		}
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
		objectOutputStream.writeObject(object);
		objectOutputStream.flush();
	}

}

可以看到使用了ObjectOutputStream的writeObject方法來實現的,下面會繼續調用writeObject0方法,相關可以查看ObjectOutputStream的序列化和反序列化。
JdkSerializationRedisSerializer的反序列化方式轉化類型有區別,這裏就不詳細介紹了。

2.2 GenericJackson2JsonRedisSerializer序列化
GenericJackson2JsonRedisSerializer主要使用ObjectMapper來實現。

@Override
public byte[] serialize(@Nullable Object source) throws SerializationException {

	if (source == null) {
		return SerializationUtils.EMPTY_ARRAY;
	}

	try {
		return mapper.writeValueAsBytes(source);
	} catch (JsonProcessingException e) {
		throw new SerializationException("Could not write JSON: " + e.getMessage(), e);
	}
}

@Override
public Object deserialize(@Nullable byte[] source) throws SerializationException {
	return deserialize(source, Object.class);
}
public <T> T deserialize(@Nullable byte[] source, Class<T> type) throws SerializationException {

	Assert.notNull(type,
			"Deserialization type must not be null! Please provide Object.class to make use of Jackson2 default typing.");

	if (SerializationUtils.isEmpty(source)) {
		return null;
	}

	try {
		return mapper.readValue(source, type);
	} catch (Exception ex) {
		throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
	}
}

查看writeValueAsBytes方法,並且繼續向下,可以看到使用了jackson相關包進行json化數據。

private final void _serialize(JsonGenerator gen, Object value,
		JsonSerializer<Object> ser, PropertyName rootName)
	throws IOException
{
	try {
		gen.writeStartObject();
		gen.writeFieldName(rootName.simpleAsEncoded(_config));
		ser.serialize(value, gen, this);
		gen.writeEndObject();
	} catch (Exception e) {
		throw _wrapAsIOE(gen, e);
	}
}

2.3 StringRedisSerializer
StringRedisTemplate中使用了UTF_8的編碼格式。

public class StringRedisSerializer implements RedisSerializer<String> {

	private final Charset charset;

	/**
	 * {@link StringRedisSerializer} to use 7 bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode
	 * character set.
	 *
	 * @see StandardCharsets#US_ASCII
	 * @since 2.1
	 */
	public static final StringRedisSerializer US_ASCII = new StringRedisSerializer(StandardCharsets.US_ASCII);

	/**
	 * {@link StringRedisSerializer} to use ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
	 *
	 * @see StandardCharsets#ISO_8859_1
	 * @since 2.1
	 */
	public static final StringRedisSerializer ISO_8859_1 = new StringRedisSerializer(StandardCharsets.ISO_8859_1);

	/**
	 * {@link StringRedisSerializer} to use 8 bit UCS Transformation Format.
	 *
	 * @see StandardCharsets#UTF_8
	 * @since 2.1
	 */
	public static final StringRedisSerializer UTF_8 = new StringRedisSerializer(StandardCharsets.UTF_8);

	/**
	 * Creates a new {@link StringRedisSerializer} using {@link StandardCharsets#UTF_8 UTF-8}.
	 */
	public StringRedisSerializer() {
		this(StandardCharsets.UTF_8);
	}

	/**
	 * Creates a new {@link StringRedisSerializer} using the given {@link Charset} to encode and decode strings.
	 *
	 * @param charset must not be {@literal null}.
	 */
	public StringRedisSerializer(Charset charset) {

		Assert.notNull(charset, "Charset must not be null!");
		this.charset = charset;
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.serializer.RedisSerializer#deserialize(byte[])
	 */
	@Override
	public String deserialize(@Nullable byte[] bytes) {
		return (bytes == null ? null : new String(bytes, charset));
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.serializer.RedisSerializer#serialize(java.lang.Object)
	 */
	@Override
	public byte[] serialize(@Nullable String string) {
		return (string == null ? null : string.getBytes(charset));
	}

	@Override
	public Class<?> getTargetType() {
		return String.class;
	}
}

當你的redis數據庫裏面本來存的是字符串數據或者你要存取的數據就是字符串類型數據的時候,可以使用這種方式,非常簡便。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

.NETCore微服務探尋(三) – 分佈式日誌

前言

一直以來對於.NETCore微服務相關的技術棧都處於一個淺嘗輒止的了解階段,在現實工作中也對於微服務也一直沒有使用的業務環境,所以一直也沒有整合過一個完整的基於.NETCore技術棧的微服務項目。正好由於最近剛好辭職,有了時間可以寫寫自己感興趣的東西,所以在此想把自己了解的微服務相關的概念和技術框架使用實現記錄在一個完整的工程中,由於本人技術有限,所以錯誤的地方希望大家指出。

目錄

  • .NETCore微服務探尋(一) – 網關
  • .NETCore微服務探尋(二) – 認證與授權

項目地址:https://github.com/yingpanwang/fordotnet/tree/dev

為什麼需要分佈式日誌

在項目的運行運行過程中,不可避免的是由於系統原因或者業務原因產生的警告或異常,這時我們需要根據產生的異常或警告信息快速排查出現的問題並修復,但是由於多個服務產生的過於龐雜的信息使那些以往通過直接寫入日誌文件的方式已經無法滿足快速排查的需求了,因為直接寫入日誌文件只能根據事先制定好的規則查看日誌信息,但是由於體量過大導致排查起來異常麻煩,例如,如果問題出現在 6月20日的凌晨1點 日誌文件對應的是 log-2020-06-20 ,那麼導致這個問題產生的原因可能20日之前的前置問題已經產生,如果我們需要排查的話,由於無法宏觀分析問題的出現原因,那麼需要日誌文件逐個查看導致效率低下。

如果採用分佈式日誌的話,首先由於日誌由日誌中心統一存儲,不需要寫入本地文件減少了IO(不包括由於項目與日誌收集中心通訊失敗而導致本地補償產生的日誌),其次搭配其他的可視乎,管理,分析組件,可以有一個良好的日誌管理與可視化,排查時可以通過相關的信息篩選,過濾無關信息,從宏觀信息中精準查詢指定信息,從而提高排查效率。

怎麼給項目接入分佈式日誌系統

  • Exceptionless Asp.Net Core 開源分佈式日誌組件

  • Log組件+ Elasticsearch+ Kibana 這種模式採用的時通過擴展已有日誌組件(Log4Net,Serilog,NLog等),通過Elasticsearch使用或不適用隊列的模式收集日誌,然後通過Kibana可視化管理分析組件 實現日誌的收集分析

    目前我所了解的搭建分佈式日誌系統的方式有兩種,但他們的方式其實底層都差不多 主要依賴Elasticsearch作為日誌的收集,然後搭配可視化的插件,這裏我選擇的時採用的是第二種方式,因為對代碼的侵入性較小,可以比較靈活的根據實際的業務需要添加日誌組件的相關插件。

首先安裝並運行Elasticsearch(es) 和 Kibana

這裏不詳細講 es/kibana 的配置,安裝好jdk以後 直接運行bin目錄下的elasticsearch.bat/kibana.bat 就可以了

注意 :
1.es 運行依賴jdk
2.Kibana 運行需要 對應es 對應的版本

其次擴展已有日誌組件使其通過es支持日誌收集

由於項目中我採用的時Serilog所以下面的代碼都是以Serilog為主,但由於是實現Asp.Net Core中的ILogger,使用實際差距不大\

1.根據需要安裝依賴組件

必須(二選一)

  • Serilog Serilog 基本庫
  • Serilog.AspNetCore AspNetCore框架整合庫,包含Serlog基本庫和控制台日誌實現

可選

  • Serilog.Extensions.Logging 包含了注入Serilog的擴展方法
  • Serilog.Sinks.Async 實現了日誌異步收集
  • Serilog.Sinks.Console 實現了控制台日誌
  • Serilog.Settings.Configuration 如果需要通過json配置文件配置Serilog的話需要安裝此庫
  • Serilog.Sinks.Elasticsearch 實現了Elasticsearch收集

2.初始化Serilog,並添加至AspNetCore的ILoggerFactory

這裏要添加serilog的方式有幾種,常用的是通過硬編碼的情況,另外是通過 xml,json等配置文件的方式,這裏都做一個簡單的示例,更詳細的配置信息可以查看Serilog.Sinks github倉庫中的介紹,非常詳細,這裏不做過多介紹
Serilog.Settings.Configuration項目地址

1.硬編碼的方式


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ForDotNet.Common.Consul.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Sinks.Elasticsearch;

namespace ForDotNet.Web.Api
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;

            //初始化Serilog
            Log.Logger = new LoggerConfiguration()
                    .Enrich.FromLogContext()
                    .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}")
                    .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
                    {
                        AutoRegisterTemplate = true,
                        IndexFormat = "Api1-{0:yyyy-MM-dd}",// es index模板
                    })
                    .CreateLogger();
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // 添加當前項目服務發現
            services.AddConsulServiceDiscovery();

            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env,ILoggerFactory loggerFactory,IHostApplicationLifetime life)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            // 添加serilog
            loggerFactory.AddSerilog();

            app.UseConsulServiceDiscovery(life);

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}


2.通過配置文件的方式

準備需要配置的信息,這裏我們使用的是appsettings.json文件


{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "ServiceOptions": {
    "ServiceIP": "localhost",
    "ServiceName": "Auth",
    "Port": 5800,
    "HealthCheckUrl": "/api/health",
    "ConsulOptions": {
      "Scheme": "http",
      "ConsulIP": "localhost",
      "Port": 8500
    }
  },
  "Serilog": {
    "WriteTo": [
      {
        "Name": "Elasticsearch",
        "Args": {
          "nodeUris": "http://localhost:9200;http://remotehost:9200/",
          "indexFormat": "auth-{0:yyyy-MM-dd}",
          "autoRegisterTemplate": true
        }
      }
    ]
  }
}

然後更改Serilog的相關初始化代碼為


public Startup(IConfiguration configuration,IHostEnvironment hostEnvironment)
        {

           // 讀取配置文件
           var builder = new ConfigurationBuilder()
               .SetBasePath(hostEnvironment.ContentRootPath)
               .AddJsonFile("appsettings.json", true, true)
               .AddJsonFile($"appsettings.{hostEnvironment.EnvironmentName}.json", true, true)
               .AddEnvironmentVariables();

            Configuration = builder.Build();

            Log.Logger = new LoggerConfiguration()
                    .ReadFrom.Configuration(Configuration)
                    .CreateLogger();
        }

3.將日誌記錄操作轉為異步

由於serilog.sinks實現大多都是同步的方式實現,所以如果需要以異步的方式收集日誌的話需要引用Serilog.Sinks.Async這個庫,並更改相關代碼。詳細請見Serilog.Sinks.Async官方倉庫,
同樣,異步的方式也可以通過配置文件實現,可以查看官方倉庫,這裏只使用硬編碼的方式。


public Startup(IConfiguration configuration,IHostEnvironment hostEnvironment)
        {

           // 讀取配置文件
           var builder = new ConfigurationBuilder()
               .SetBasePath(hostEnvironment.ContentRootPath)
               .AddJsonFile("appsettings.json", true, true)
               .AddJsonFile($"appsettings.{hostEnvironment.EnvironmentName}.json", true, true)
               .AddEnvironmentVariables();

            Configuration = builder.Build();

            Log.Logger = new LoggerConfiguration()
                    .WriteTo.Async(configure =>
                    {
                        configure
                        .Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code);

                        configure
                        .Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
                        {
                            AutoRegisterTemplate = true,
                            IndexFormat = "auth-{0:yyyy-MM-dd}",

                        });
                    })
                    .CreateLogger();
        }

3.運行並查看

啟動Elasticserach,Kibana,項目后

查看控制台,發現同樣的日誌輸出了兩遍,這是因為AspNetCore默認實現的LoggerProvider 沒有清除所以會導致 打印輸出,我們在啟動時清除默認Provider即可

清除LoggerProvider

然後運行並查看日誌,是不是清爽了很多

然後我們訪問Kiabana查看我們剛剛收集的日誌

訪問 http://localhost:5601 Kibana默認項目地址,不同Kibana版本頁面會有差異

點擊Management建立我們的日誌收集模型

這裏由於我已經建立過其他的模塊的信息,所以可以看到我已經創建的信息,這裏我們點擊創建新的信息

這裏需要輸入 正則表達式 匹配收集的信息,這裏我們輸入我們定義的模板開頭的api1並點擊下一步

這裏選擇@timestamp作為索引模式,也可以選擇不添加,然後創建

創建完成后 去到 Discover 模塊

在左邊選擇需要查看的index

就可以看到我們的日誌已經收集到es中了,可以通過kibana查看了

如果覺得字段過於複雜的話 可以在左邊選擇過濾的字段查看 我這裏已經只選擇了 leve 和 message

好了 ,以上我就我分享的 建立分佈式日誌的內容了,如果有紕漏及錯誤 希望大家指出,謝謝

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

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

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

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

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

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

※回頭車貨運收費標準

【你來報報】「膠」備競賽 Plastic or Planet?

文:朱漢強(綠惜地球環境倡議總監)

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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