前端開發神器Charles從入門到卸載

前言

本文將帶大家學習使用前端開發神器-charles,從基本的下載安裝到常見配置使用,為大家一一講解。

一、花式誇獎Charles

  • 截取 Http 和 Https 網絡封包。
  • 支持重髮網絡請求,方便後端調試。
  • 支持修改網絡請求參數。
  • 支持網絡請求的截獲並動態修改。
  • 支持模擬慢速網絡。

好,騎上我心愛的小摩托,準備上路…

二、下載與安裝

官網下載傳送門

本文所使用的的版本為 mac V4.5.6版本,不同版本間的具體化差異,大家可留言交流。

Charles破解工具可通過關注公眾號「胡哥有話說」,回復關鍵字charles獲得。

三、簡單入門-抓包所有請求

  1. 打開Charles,勾選Proxy下的macOS Proxy(如果是windows,此處為Windows Proxy)
  2. 點擊Proxy->Start Recording,打開瀏覽器訪問任意頁面,可以在Charles中看到請求了。

很好,現在已經上路了,學習的路上永不堵車…

四、設置過濾請求

通過上面的操作,我們已經抓包了所有的請求,實際開發中可能是專門針對某些接口(如百度域名下的接口),我們可以專門配置過濾接口。

  1. 臨時性過濾配置

    在展示界面的Filter中可進行條件過濾

    同時可在右側的settings中配置使用正則來進行過濾

  2. 永久性過濾配置

    通過Proxy->Recording Settings->include中配置過濾條件

Ok,我們又前進了一大步

五、代理轉發請求

通過CharlesMap RemoteMap Local我們也可以配置代理轉發請求。

Map Remote

Map Remote 遠程映射,是將指定的網絡請求重定向到另一個網址

業務場景:
某些服務端的文件請求時限制某些特定域名(*.baidu.com),我們使用localhost啟動項目時,會導致沒有權限訪問。通過配置Map Remote遠程映射解決問題。

配置路徑:
設置Tools->Map Remote

如圖上的配置,本地啟動的項目地址為:http://localhost:8080(或 http://127.0.0.1:8080),現在再訪問,可以使用路徑 http://test.baidu.com訪問即可。

注意Enable Map Remote一定要勾選,以及相應規則也要勾選,否則不會生效

Map Local

Map Local 本地映射,是指將指定的網絡請求重定向到本地的文件

業務場景:
在本地化的開發中,接口數據Mock;或者是線上環境問題排查時,將請求重定向到本地文件以方便排查。

配置路徑:
設置Tools->Map Local

通過如上圖的配置,請求 aa.baidu.com:443/index時,會被映射到本地 /xx/index.json

注意Enable Map Local一定要勾選,以及相關規則也要勾選,否則不會生效。

六、手機抓包

手機抓包請求也是我們日常開發中需要用到的,那如何利用Charles抓包手機請求呢。

  1. 設置Charles的代理端口號
    通過設置Proxy->Proxy Settings->Proxies->HTTP Proxy下的Port端口號

  2. 查看本地IP地址
    通過CharlesHelp->Local IP Address查看,本機IP為xx.xx.xx.xx

  3. 手機和電腦需要處於同於wifi網絡內

  4. 手機wifi網絡配置

    以華為mate 30為例,選擇對應的wifi,選擇显示高級選項,設置代理為手動
    設置服務器主機名為:xx.xx.xx.xx(剛才查看的電腦IP)
    設置服務器端口為:8888(剛才設置的port)
    點擊保存后,手機的請求就可以在Charles中查看啦…

    注意鏈接時,Charles會彈出授權窗口,要選擇Allow

七、限速設置

通過設置Proxy->Throttle Settings來進行速度限制

注意:一定要勾選 Enable Throttling選項

小結

以上是給大家分享的Charles的常見使用配置,如有相關問題可留言交流。

後記

以上就是胡哥今天給大家分享的內容,喜歡的小夥伴記得點贊收藏呦,關注胡哥有話說,學習前端不迷路,歡迎多多留言交流…

胡哥有話說,專註於大前端技術領域,分享前端系統架構,框架實現原理,最新最高效的技術實踐!

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

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

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

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

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

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

※回頭車貨運收費標準

佛羅里達州九成柑橘染「愛滋」 三分之二果汁停產

摘錄自2019年11月14日法廣社報導

美國的重要柑橘產區佛羅里達州九成柑橘樹感染源自中國的致命病害「黃龍病」,導致樹枝枯萎、葉片扭曲變形、樹幹腐爛;專家將這種植物病害稱之為「柑橘愛滋病」。

佛羅里達州柑橘產業價值90億元,為該州第二大產業,僅次於觀光業;佛州的柳橙汁產量佔全國逾80%。《華盛頓郵報》報導,佛州當局稱黃龍病為「可預期破壞性最大的外來植物病害之一」,已摧毀佛州標誌性的柑橘產業。

隨着每年11月到隔年5月的採收季節到來,「幽靈樹叢」卻遍布佛州各地,迫使農民廢棄部分作物,結束果汁生意,甚至將整座事業轉賣;《華盛頓郵報》報導說,2004年佛州有7000多名農民種植柑橘,如今約5000人已經放棄。

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

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準

RocketMQ入門到入土(一)新手也能看懂的原理和實戰!

學任何技術都是兩步驟:

  1. 搭建環境

  2. helloworld

我也不例外,直接搞起來。

一、RocketMQ的安裝

1、文檔

官方網站

http://rocketmq.apache.org

GitHub

https://github.com/apache/rocketmq

2、下載

wget https://mirror.bit.edu.cn/apache/rocketmq/4.7.0/rocketmq-all-4.7.0-bin-release.zip

我們是基於Centos8來的,面向官方文檔學習,所以下載地址自然也是官方的。

去官方網站找合適的版本進行下載,目前我這裏最新的是4.7.0版本。

http://rocketmq.apache.org/dowloading/releases/

 

https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.7.0/rocketmq-all-4.7.0-bin-release.zip

 

3、準備工作

3.1、解壓

unzip rocketmq-all-4.7.0-bin-release.zip

3.2、安裝jdk

sudo yum install java-1.8.0-openjdk-devel

4、啟動

4.1、啟動namesrv

cd rocketmq-all-4.7.0-bin-release/bin
./mqnamesrv

4.2、啟動broker

cd rocketmq-all-4.7.0-bin-release/bin
./mqbroker -n localhost:9876

常見錯誤以及解決方案:

常見錯誤:啟動broker失敗 Cannot allocate memory

[root@node-113b bin]# ./mqbroker -n localhost:9876
Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000005c0000000, 8589934592, 0) failed
; error='Cannot allocate memory' (errno=12)#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 8589934592 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /usr/local/rocketmq/bin/hs_err_pid1997.log

解決方案:

是由於默認內存分配的太大了,超出了本機內存,直接OOM了。

修改bin/目錄下的如下兩個腳本

runbroker.sh
runserver.sh

在這兩個腳本里都搜索-server -Xms,將其內存分配小點,自己玩的話512MB就足夠了,夠夠的了!

4.3、啟動成功標識

namesrv啟動成功標識:

broker啟動成功標識:

二、RocketMQ控制台的安裝

控制台目前獲取方式有如下兩種:

  1. 第三方網站去下載現成的,比如csdn等。

  2. 官方源碼包自己編譯而成,官方沒有現成的。

我們這裏當然採取官方方式。

1、官方文檔

github倉庫

https://github.com/apache/rocketmq-externals

中文指南

https://github.com/apache/rocketmq-externals/blob/master/rocketmq-console/doc/1_0_0/UserGuide_CN.md

2、下載源碼

https://codeload.github.com/apache/rocketmq-externals/zip/master

3、修改配置(可選)

我們下載完解壓后的文件目錄如下:

修改rocketmq-consolesrcmainresourcesapplication.properties文件的server.port就歐了。默認8080。

4、編譯打包

進入rocketmq-console,然後用maven進行編譯打包

mvn clean package -DskipTests

打包完會在target下生成我們spring boot的jar程序,直接java -jar啟動完事。

5、啟動控制台

將編譯打包好的springboot程序扔到服務器上,執行如下命令進行啟動

java -jar rocketmq-console-ng-1.0.1.jar --rocketmq.config.namesrvAddr=127.0.0.1:9876

如果想後台啟動就nohup &

訪問一下看看效果:

三、測試

rocketmq給我們提供了測試工具和測試類,可以在安裝完很方便的進行測試。

0、準備工作

rocketmq給我們提供的默認測試工具在bin目錄下,叫tools.sh。我們測試前需要配置這個腳本,為他指定namesrv地址才可以,否則測試發送/消費消息的時候會出現如下錯誤 connect to null failed

22:49:02.470 [main] DEBUG i.n.u.i.l.InternalLoggerFactory - Using SLF4J as the default logging framework
RocketMQLog:WARN No appenders could be found for logger (io.netty.util.internal.PlatformDependent0).
RocketMQLog:WARN Please initialize the logger system properly.
java.lang.IllegalStateException: org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to null failed

配置如下:

vim tools.sh
# 在export JAVA_HOME上面添加如下這段代碼
export NAMESRV_ADDR=localhost:9876

1、發送消息

./tools.sh org.apache.rocketmq.example.quickstart.Producer

成功的話會看到嘩嘩嘩的日誌,因為這個類會發送1000條消息到TopicTest這個Topic下。

2、消費消息

./tools.sh org.apache.rocketmq.example.quickstart.Consumer

成功的話會看到嘩嘩嘩的日誌,因為這個類會消費TopicTest下的全部消息。剛發送的1000條都會被消費掉。

3、控制台

發送成功后我們自然也能來到管控台去看消息和消費情況等等等信息

四、架構圖以及角色

1、架構圖

2、角色

2.1、Broker

  • 理解成RocketMQ本身

  • broker主要用於producer和consumer接收和發送消息

  • broker會定時向nameserver提交自己的信息

  • 是消息中間件的消息存儲、轉發服務器

  • 每個Broker節點,在啟動時,都會遍歷NameServer列表,與每個NameServer建立長連接,註冊自己的信息,之後定時上報

2.2、Nameserver

  • 理解成zookeeper的效果,只是他沒用zk,而是自己寫了個nameserver來替代zk

  • 底層由netty實現,提供了路由管理、服務註冊、服務發現的功能,是一個無狀態節點

  • nameserver是服務發現者,集群中各個角色(producer、broker、consumer等)都需要定時向nameserver上報自己的狀態,以便互相發現彼此,超時不上報的話,nameserver會把它從列表中剔除

  • nameserver可以部署多個,當多個nameserver存在的時候,其他角色同時向他們上報信息,以保證高可用,

  • NameServer集群間互不通信,沒有主備的概念

  • nameserver內存式存儲,nameserver中的broker、topic等信息默認不會持久化,所以他是無狀態節點

2.3、Producer

  • 消息的生產者

  • 隨機選擇其中一個NameServer節點建立長連接,獲得Topic路由信息(包括topic下的queue,這些queue分佈在哪些broker上等等)

  • 接下來向提供topic服務的master建立長連接(因為rocketmq只有master才能寫消息),且定時向master發送心跳

2.4、Consumer

  • 消息的消費者

  • 通過NameServer集群獲得Topic的路由信息,連接到對應的Broker上消費消息

  • 由於Master和Slave都可以讀取消息,因此Consumer會與Master和Slave都建立連接進行消費消息

3、核心流程

  • Broker都註冊到Nameserver上

  • Producer發消息的時候會從Nameserver上獲取發消息的topic信息

  • Producer向提供服務的所有master建立長連接,且定時向master發送心跳

  • Consumer通過NameServer集群獲得Topic的路由信息

  • Consumer會與所有的Master和所有的Slave都建立連接進行監聽新消息

五、核心概念

1、Message

消息載體。Message發送或者消費的時候必須指定Topic。Message有一個可選的Tag項用於過濾消息,還可以添加額外的鍵值對。

2、topic

消息的邏輯分類,發消息之前必須要指定一個topic才能發,就是將這條消息發送到這個topic上。消費消息的時候指定這個topic進行消費。就是邏輯分類。

3、queue

1個Topic會被分為N個Queue,數量是可配置的。message本身其實是存儲到queue上的,消費者消費的也是queue上的消息。多說一嘴,比如1個topic4個queue,有5個Consumer都在消費這個topic,那麼會有一個consumer浪費掉了,因為負載均衡策略,每個consumer消費1個queue,5>4,溢出1個,這個會不工作。

4、Tag

Tag 是 Topic 的進一步細分,顧名思義,標籤。每個發送的時候消息都能打tag,消費的時候可以根據tag進行過濾,選擇性消費。

5、Message Model

消息模型:集群(Clustering)和廣播(Broadcasting)

6、Message Order

消息順序:順序(Orderly)和併發(Concurrently)

7、Producer Group

消息生產者組

8、Consumer Group

消息消費者組

六、ACK

首先要明確一點:ACK機制是發生在Consumer端的,不是在Producer端的。也就是說Consumer消費完消息后要進行ACK確認,如果未確認則代表是消費失敗,這時候Broker會進行重試策略(僅集群模式會重試)。ACK的意思就是:Consumer說:ok,我消費成功了。這條消息給我標記成已消費吧。

七、消費模式

1、集群模式(Clustering)

1.1、圖解

 

1.2、特點

  • 每條消息只需要被處理一次,broker只會把消息發送給消費集群中的一個消費者

  • 在消息重投時,不能保證路由到同一台機器上

  • 消費狀態由broker維護

2、廣播模式(Broadcasting)

2.1、圖解

 

2.2、特點

  • 消費進度由consumer維護

  • 保證每個消費者都消費一次消息

  • 消費失敗的消息不會重投

八、Java API

說明:

  • RocketMQ服務端版本為目前最新版:4.7.0

  • Java客戶端版本採取的目前最新版:4.7.0

pom如下

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.7.0</version>
</dependency>

1、Producer

發消息肯定要必備如下幾個條件:

  • 指定生產組名(不能用默認的,會報錯)

  • 配置namesrv地址(必須)

  • 指定topic name(必須)

  • 指定tag/key(可選)

驗證消息是否發送成功:消息發送完后可以啟動消費者進行消費,也可以去管控台上看消息是否存在。

1.1、send(同步)

public class Producer {
    public static void main(String[] args) throws Exception {
        // 指定生產組名為my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 啟動Producer
        producer.start();
        // 創建消息對象,topic為:myTopic001,消息內容為:hello world
        Message msg = new Message("myTopic001", "hello world".getBytes());
        // 發送消息到mq,同步的
        SendResult result = producer.send(msg);
        System.out.println("發送消息成功!result is : " + result);
        // 關閉Producer
        producer.shutdown();
        System.out.println("生產者 shutdown!");
    }
}

輸出結果:

發送消息成功!result is : SendResult [sendStatus=SEND_OK, msgId=A9FE854140F418B4AAC26F7973910000, offsetMsgId=7B39B49D00002A9F00000000000589BE, messageQueue=MessageQueue [topic=myTopic001, brokerName=broker-a, queueId=0], queueOffset=7]
生產者 shutdown!

1.2、send(批量)

public class ProducerMultiMsg {
    public static void main(String[] args) throws Exception {
        // 指定生產組名為my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 啟動Producer
        producer.start();

        String topic = "myTopic001";
        // 創建消息對象,topic為:myTopic001,消息內容為:hello world1/2/3
        Message msg1 = new Message(topic, "hello world1".getBytes());
        Message msg2 = new Message(topic, "hello world2".getBytes());
        Message msg3 = new Message(topic, "hello world3".getBytes());
        // 創建消息對象的集合,用於批量發送
        List<Message> msgs = new ArrayList<>();
        msgs.add(msg1);
        msgs.add(msg2);
        msgs.add(msg3);
        // 批量發送的api的也是send(),只是他的重載方法支持List<Message>,同樣是同步發送。
        SendResult result = producer.send(msgs);
        System.out.println("發送消息成功!result is : " + result);
        // 關閉Producer
        producer.shutdown();
        System.out.println("生產者 shutdown!");
    }
}

輸出結果:

發送消息成功!result is : SendResult [sendStatus=SEND_OK, msgId=A9FE854139C418B4AAC26F7D13770000,A9FE854139C418B4AAC26F7D13770001,A9FE854139C418B4AAC26F7D13770002, offsetMsgId=7B39B49D00002A9F0000000000058A62,7B39B49D00002A9F0000000000058B07,7B39B49D00002A9F0000000000058BAC, messageQueue=MessageQueue [topic=myTopic001, brokerName=broker-a, queueId=0], queueOffset=8]
生產者 shutdown!

從結果中可以看到只有一個msgId,所以可以發現雖然是三條消息對象,但是卻只發送了一次,大大節省了client與server的開銷。

錯誤情況:

批量發送的topic必須是同一個,如果message對象指定不同的topic,那麼批量發送的時候會報錯:

Exception in thread "main" org.apache.rocketmq.client.exception.MQClientException: Failed to initiate the MessageBatch
For more information, please visit the url, http://rocketmq.apache.org/docs/faq/
    at org.apache.rocketmq.client.producer.DefaultMQProducer.batch(DefaultMQProducer.java:950)
    at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:898)
    at com.chentongwei.mq.rocketmq.ProducerMultiMsg.main(ProducerMultiMsg.java:29)
Caused by: java.lang.UnsupportedOperationException: The topic of the messages in one batch should be the same
    at org.apache.rocketmq.common.message.MessageBatch.generateFromList(MessageBatch.java:58)
    at org.apache.rocketmq.client.producer.DefaultMQProducer.batch(DefaultMQProducer.java:942)
    ... 2 more

1.3、sendCallBack(異步)

public class ProducerASync {
    public static void main(String[] args) throws Exception {
       // 指定生產組名為my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 啟動Producer
        producer.start();

        // 創建消息對象,topic為:myTopic001,消息內容為:hello world async
        Message msg = new Message("myTopic001", "hello world async".getBytes());
        // 進行異步發送,通過SendCallback接口來得知發送的結果
        producer.send(msg, new SendCallback() {
            // 發送成功的回調接口
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("發送消息成功!result is : " + sendResult);
            }
            // 發送失敗的回調接口
            @Override
            public void onException(Throwable throwable) {
                throwable.printStackTrace();
                System.out.println("發送消息失敗!result is : " + throwable.getMessage());
            }
        });

        producer.shutdown();
        System.out.println("生產者 shutdown!");
    }
}

輸出結果:

生產者 shutdown!
java.lang.IllegalStateException: org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to [124.57.180.156:9876] failed
    at org.apache.rocketmq.client.impl.factory.MQClientInstance.updateTopicRouteInfoFromNameServer(MQClientInstance.java:681)
    at org.apache.rocketmq.client.impl.factory.MQClientInstance.updateTopicRouteInfoFromNameServer(MQClientInstance.java:511)
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.tryToFindTopicPublishInfo(DefaultMQProducerImpl.java:692)
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:556)
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.access$300(DefaultMQProducerImpl.java:97)
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl$4.run(DefaultMQProducerImpl.java:510)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to [124.57.180.156:9876] failed
    at org.apache.rocketmq.remoting.netty.NettyRemotingClient.getAndCreateNameserverChannel(NettyRemotingClient.java:441)
    at org.apache.rocketmq.remoting.netty.NettyRemotingClient.getAndCreateChannel(NettyRemotingClient.java:396)
    at org.apache.rocketmq.remoting.netty.NettyRemotingClient.invokeSync(NettyRemotingClient.java:365)
    at org.apache.rocketmq.client.impl.MQClientAPIImpl.getTopicRouteInfoFromNameServer(MQClientAPIImpl.java:1371)
    at org.apache.rocketmq.client.impl.MQClientAPIImpl.getTopicRouteInfoFromNameServer(MQClientAPIImpl.java:1361)
    at org.apache.rocketmq.client.impl.factory.MQClientInstance.updateTopicRouteInfoFromNameServer(MQClientInstance.java:624)
    ... 10 more
發送消息失敗!result is : org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to [124.57.180.156:9876] failed

為啥報錯了?很簡單,他是異步的,從結果就能看出來,由於是異步的,我還沒發送到mq呢,你就先給我shutdown了。肯定不行,所以我們在shutdown前面sleep 1s在看效果

public class ProducerASync {
    public static void main(String[] args) throws Exception {
       // 指定生產組名為my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 啟動Producer
        producer.start();

        // 創建消息對象,topic為:myTopic001,消息內容為:hello world async
        Message msg = new Message("myTopic001", "hello world async".getBytes());
        // 進行異步發送,通過SendCallback接口來得知發送的結果
        producer.send(msg, new SendCallback() {
            // 發送成功的回調接口
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("發送消息成功!result is : " + sendResult);
            }
            // 發送失敗的回調接口
            @Override
            public void onException(Throwable throwable) {
                throwable.printStackTrace();
                System.out.println("發送消息失敗!result is : " + throwable.getMessage());
            }
        });

        Thread.sleep(1000);

        producer.shutdown();
        System.out.println("生產者 shutdown!");
    }
}

輸出結果:

發送消息成功!result is : SendResult [sendStatus=SEND_OK, msgId=A9FE854106E418B4AAC26F8719B20000, offsetMsgId=7B39B49D00002A9F0000000000058CFC, messageQueue=MessageQueue [topic=myTopic001, brokerName=broker-a, queueId=1], queueOffset=2]
生產者 shutdown!

1.4、sendOneway

public class ProducerOneWay {
    public static void main(String[] args) throws Exception {
        // 指定生產組名為my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 啟動Producer
        producer.start();

        // 創建消息對象,topic為:myTopic001,消息內容為:hello world oneway
        Message msg = new Message("myTopic001", "hello world oneway".getBytes());
        // 效率最高,因為oneway不關心是否發送成功,我就投遞一下我就不管了。所以返回是void
        producer.sendOneway(msg);
        System.out.println("投遞消息成功!,注意這裡是投遞成功,而不是發送消息成功哦!因為我sendOneway也不知道到底成沒成功,我沒返回值的。");
        producer.shutdown();
        System.out.println("生產者 shutdown!");
    }
}

輸出結果:

投遞消息成功!,注意這裡是投遞成功,而不是發送消息成功哦!因為我sendOneway也不知道到底成沒成功,我沒返回值的。
生產者 shutdown!

1.5、效率對比

sendOneway > sendCallBack > send批量 > send單條

很容易理解,sendOneway不求結果,我就負責投遞,我不管你失敗還是成功,相當於中轉站,來了我就扔出去,我不進行任何其他處理。所以最快。

而sendCallBack是異步發送肯定比同步的效率高。

send批量和send單條的效率也是分情況的,如果只有1條msg要發,那還搞毛批量,直接send單條完事。

2、Consumer

每個consumer只能關注一個topic。

發消息肯定要必備如下幾個條件:

  • 指定消費組名(不能用默認的,會報錯)

  • 配置namesrv地址(必須)

  • 指定topic name(必須)

  • 指定tag/key(可選)

2.1、CLUSTERING

集群模式,默認。

比如啟動五個Consumer,Producer生產一條消息后,Broker會選擇五個Consumer中的其中一個進行消費這條消息,所以他屬於點對點消費模式。

public class Consumer {
    public static void main(String[] args) throws Exception {
        // 指定消費組名為my-consumer
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("my-consumer");
        // 配置namesrv地址
        consumer.setNamesrvAddr("124.57.180.156:9876");
        // 訂閱topic:myTopic001 下的全部消息(因為是*,*指定的是tag標籤,代表全部消息,不進行任何過濾)
        consumer.subscribe("myTopic001", "*");
        // 註冊監聽器,進行消息消息。
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                for (MessageExt msg : msgs) {
                    String str = new String(msg.getBody());
                    // 輸出消息內容
                    System.out.println(str);
                }
                // 默認情況下,這條消息只會被一個consumer消費,這叫點對點消費模式。也就是集群模式。
                // ack確認
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        // 啟動消費者
        consumer.start();
        System.out.println("Consumer start");
    }
}

2.2、BROADCASTING

廣播模式。

比如啟動五個Consumer,Producer生產一條消息后,Broker會把這條消息廣播到五個Consumer中,這五個Consumer分別消費一次,每個都消費一次。

// 代碼里只需要添加如下這句話即可:
consumer.setMessageModel(MessageModel.BROADCASTING); 

2.3、兩種模式對比

  • 集群默認是默認的,廣播模式是需要手動配置。

  • 一條消息:集群模式下的多個Consumer只會有一個Consumer消費。廣播模式下的每一個Consumer都會消費這條消息。

  • 廣播模式下,發送一條消息后,會被當前被廣播的所有Consumer消費,但是後面新加入的Consumer不會消費這條消息,很好理解:村裡面大喇叭喊了全村來領雞蛋,第二天你們村新來個人,那個人肯定聽不到昨天大喇叭喊的消息呀。

3、TAG&&KEY

發送/消費 消息的時候可以指定tag/key來進行過濾消息,支持通配符。*代表消費此topic下的全部消息,不進行過濾。

看下org.apache.rocketmq.common.message.Message源碼可以發現發消息的時候可以指定tag和keys:

public Message(String topic, String tags, String keys, byte[] body) {
    this(topic, tags, keys, 0, body, true);
}

比如:

public class ProducerTagsKeys {
    public static void main(String[] args) throws Exception {
        // 指定生產組名為my-producer
        DefaultMQProducer producer = new DefaultMQProducer("my-producer");
        // 配置namesrv地址
        producer.setNamesrvAddr("124.57.180.156:9876");
        // 啟動Producer
        producer.start();
        // 創建消息對象,topic為:myTopic001,消息內容為:hello world,且tags為:test-tags,keys為test-keys
        Message msg = new Message("myTopic001", "test-tags", "test-keys", "hello world".getBytes());
        // 發送消息到mq,同步的
        SendResult result = producer.send(msg);
        System.out.println("發送消息成功!result is : " + result);
        // 關閉Producer
        producer.shutdown();
        System.out.println("生產者 shutdown!");
    }
}

輸出結果:

發送消息成功!result is : SendResult [sendStatus=SEND_OK, msgId=A9FE854149DC18B4AAC26FA4B7200000, offsetMsgId=7B39B49D00002A9F0000000000058DA6, messageQueue=MessageQueue [topic=myTopic001, brokerName=broker-a, queueId=3], queueOffset=3]
生產者 shutdown!

查看管控台,可以發現tags和keys已經生效了:

 

消費的時候如果指定*那就是此topic下的全部消息,我們可以指定前綴通配符,比如:

// 這樣就只會消費myTopic001下的tag為test-*開頭的消息。
consumer.subscribe("myTopic001", "test-*");

// 代表訂閱Topic為myTopic001下的tag為TagA或TagB的所有消息
consumer.subscribe("myTopic001", "TagA||TagB");

還支持SQL表達式過濾,不是很常用。不BB了。

4、常見錯誤

4.1、sendDefaultImpl call timeout

4.1.1、異常

Exception in thread "main" org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException: sendDefaultImpl call timeout
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:666)
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1342)
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1288)
    at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:324)
    at com.chentongwei.mq.rocketmq.Producer.main(Producer.java:18)

4.1.2、解決

1.如果你是雲服務器,首先檢查安全組是否允許9876這個端口訪問,是否開啟了防火牆,如果開啟了的話是否將9876映射了出去。

2.修改配置文件broker.conf,加上:

brokerIP1=我用的是阿里雲服務器,這裡是我的公網IP

啟動namesrv和broker的時候加上本機IP(我用的是阿里雲服務器,這裡是我的公網IP):

./bin/mqnamesrv -n IP:9876
./bin/mqbroker -n IP:9876 -c conf/broker.conf

4.2、No route info of this topic

4.2.1、異常

Exception in thread "main" org.apache.rocketmq.client.exception.MQClientException: No route info of this topic: myTopic001
See http://rocketmq.apache.org/docs/faq/ for further details.
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendDefaultImpl(DefaultMQProducerImpl.java:684)
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1342)
    at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:1288)
    at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:324)
    at com.chentongwei.mq.rocketmq.Producer.main(Producer.java:18)

4.2.2、解決

很明顯發送成功了,不再是剛才的超時了,但是告訴我們沒有這個topic。那不能每次都手動創建呀,所以啟動broker的時候可以指定參數讓broker為我們自動創建。如下

./bin/mqbroker -n IP:9876 -c conf/broker.conf autoCreateTopicEnable=true

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

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

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

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

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

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

※回頭車貨運收費標準

廣州商場喜迎「全能」電動車充電站 今年將建300個

4月30日,廣州市首個設置在商場內電動車「全能」充電樁在高德置地廣場啟用。這種充電站可為所有型號的電動車充電,每次充電2~3個小時,每小時的費用在4元人民幣至10元人民幣之間,約可行駛25至50公里,一般車輛可走一天。未來車主還可以用手機app軟體即時查詢附近充電樁位置以預約充電。   依威能源方面表示,目前在全國範圍內已完成超過150個電動車智慧充電站的鋪設,今年內將在廣州至少建300個這樣的充電站,地點將設在商場、社區、辦公樓等。   目前廣州有新能源汽車4000多輛,其中公車佔有大部分。廣州市政府已經在大學城、跑馬場、花都區先後配備了500多個充電設施,也在30多個公交站場配備了210個專門為600多部電動汽車、公交汽車充電的設施。此外,特斯拉電動車也在廣州的商場、酒店和社區建了幾個專用充電站。

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

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

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

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

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

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

※回頭車貨運收費標準

一個可以自我進化的微服務框架

你是否遇到過這樣的框架,它非常簡單又是輕量級的,很容易上手,然而當你的項目變得複雜的時候它能自我進化成功能強大的重量級框架,而不需要把整個項目重寫? 我是從來沒見過。

先讓我們來看一下項目的生命周期。通常,當一個新項目開始時,我們不知道它能持續多久,所以我們希望它盡可能簡單。大多數項目都會在短時間內夭折,所以它們並不需要複雜的框架。然而,其中有一些擊中了用戶的痛點並受到歡迎,我們就會不斷地對它們改進,使它們變得越來越複雜。結果就是原來簡單的框架和設計已經遠遠不能滿足需求,剩下的唯一方法就是重寫整個項目,並引入強大的重量級框架。如果項目持續受歡迎,我們可能需要多次重寫整個項目。

這時一個能自我進化的框架就展現出優勢。我們可以在項目開始時使用這個輕量級框架,並只在確實需要時才將其進化為重量級框架, 在這個過程中我們不需要重寫整個項目或更改任何業務邏輯代碼,當然你需要對創建結構(struct)的代碼(也叫程序容器)做一些修改。但這個修改比起修改業務邏輯或重寫整個項目不知要容易多少倍。

這聽起來太棒了,但有這樣的東西嗎?很長一段時間以來,我都認為這是不可能的,直到最近竟然找到了一個。

去年,我創建了一個基於清晰架構(Clean Architecture)的框架,並寫了一系列關於它的文章。請查看”清晰架構(Clean Architecture)的Go微服務” 。它使用工廠方法設計模式來創建對象(結構),功能非常強大,但有點重。我希望能把它改的輕一些,這樣簡單的項目也能使用。但我發現任何強大的框架都是重量級的。沒有一個框架是輕量級的但同時又非常強大,正如魚與熊掌不可兼得。我在這上面花了不少時間,最後終於找到了一個方法,就是讓框架能夠自我進化。

解決方案

我們可以將一個項目的代碼分為兩部分,一部分是業務邏輯(Business Logic),其中所有調用都基於接口,不涉及具體對象(結構)。另一部分是為這些接口創建具體對象(結構(struct)),我們可以稱之為程序容器(Application Container)(詳情參見”清晰架構(Clean Architecture)的Go微服務: 程序容器(Application Container)”) 。這樣,我們就可以讓業務邏輯保持不變,而使程序容器自我進化。大多數程序容器都用依賴注入來將對象(結構)注入到業務邏輯中,“Spring”就是一個很好的例子。但是,要使框架能夠自我進化,關鍵是不能直接使用依賴注入作為這兩部分之間的接口。相反,你必須使用一個非常簡單的接口。當然,你依然可以使用依賴注入,但這隻是在程序容器內部,因此只是程序容器的實現細節。

下面就是框架的結構圖.

程序容器和業務邏輯之間的接口

程序容器和業務邏輯之間的接口應該非常簡單。唯一的功能就是讓業務邏輯能獲取具體對象(結構)。在清晰架構中,大多數情況下你只需要獲取用例(Use Case)。

下面就是程序容器的接口:

type Container interface {
	// BuildUseCase creates concrete types for use case and it's included types.
	// For each call, it will create a new instance, which means it is not a singleton
	BuildUseCase(code string) (interface{}, error)

	// This should only be used by container and it's sub-package
	// Get instance by code from container.
	Get(code string) (interface{}, bool)

	// This should only be used by container and it's sub-package
	// Put value into container with code as the key.
	Put(code string, value interface{})

}

如何讓程序容器進化

我定義了三種模式的程序容器,從最簡單到最複雜,你可以直接使用。你也可以定義新的程序容器模式,只要它遵循上面的接口即可。你可以隨時將程序容器替換為其他模式,而無需更改業務邏輯代碼。

初級模式

這是最簡單的模式,它不涉及任何設計模式。它的最大優點是簡單,易學,易用。絕大多數的項目都可以從此模式開始。使用這種模式可以在一天之內創建整個項目。如果項目很簡單,在一小時內完成都是有可能的。如果你不再需要這個項目,就可以一點也不可惜地丟棄它。缺點是它提供的功能非常簡單,所有配置信息都是以硬編碼的形式寫在程序中,既不靈活也不強大。最適合POC(概念驗證)類型的項目。具體實例可查看 “訂單服務” 。這是一個事件驅動的微服務項目,旨在提供訂單服務。

以下是初級模式的結構圖,框內是程序容器:

增強模式

這種模式類似於初級模式,主要改進是增加了配置參數管理。在這種模式下,配置參數不再是硬編碼在代碼中的,它們是在結構(struts)中定義的。你也可以對它們進行校驗。更改程序配置要容易得多,你可以在單個文件里看到項目的所有配置參數,從而掌握整個程序的全貌。該框架仍然非常簡單,不涉及任何設計模式。當項目已經穩定並且需要某種結構時,可以切換到這種模式。具體實例可查看”支付服務”. 這是一個事件驅動的微服務項目,旨在提供支付服務。

以下是增強模式的結構圖,框內是程序容器:

高級模式

當你有一個複雜項目時,你需要一個功能強大的框架來與之匹配。你可能會有一些比較複雜的需求,如更改所用的數據庫或動態更改配置參數(不需更改代碼)。這時,你可以將項目升級為高級模式。它將在程序容器中使用依賴注入。具體實例可查看”Service template 1″。 這是一個清晰架構(Clean Architecture)的微服務框架。

以下是高級模式的結構圖,框內是程序容器,它的文件結構看起來有很大的不同。

如何升級

假設你有一個新項目,最容易的啟動方式的是複製整個“訂單服務”項目,然後將裏面的結構(struct)更改為你的結構,並完成業務邏輯代碼。在這個過程中,你可以保留“訂單服務”項目的目錄結構和一些接口。過了一段時間,你發現需要升級到高級模式。這時,最簡單的方法是從“servicetmp1”項目中複製“app”文件夾,並替換你的項目中的“ app”文件夾,然後對程序容器進行相應的修改。完成之後,你無需更改業務邏輯中的任何代碼,一切都應該可以正常工作。如果你了解這個框架,整個過程應該不會超過一天時間,甚至更短都有可能。

此方案的關鍵元素

要想框架能夠自我進化,它必須按照特定的方式進行設計和創建。以下是框架的四個關鍵元素。

  • 程序結構
  • 程序容器
  • 基於接口的業務邏輯
  • 可插拔的第三方接口庫

基於接口(Interface)的業務邏輯

前面已經講了程序結構和程序容器,這裏主要講解業務邏輯。基於接口的業務邏輯是框架能自我進化的關鍵。在應用程序的業務邏輯部分,你可能有不同類型的元素,例如“用例(use case)”,“域模型(domain model)”,“存儲庫(repository)”和“域服務(domain service)”。除了“域模型(domain model)”或“域事件(domain event)”之外,業務邏輯中的幾乎所有元素都應該是接口(而不是結構(struct))。有關程序設計和項目結構的詳細信息,請查看”清晰架構(Clean Architecture)的Go微服務: 程序設計”

內部接口

在業務邏輯中有兩種不同類型的接口。一種是內部接口,另一種是外部接口。內部接口是在應用程序內部使用的接口(通常不能與其他程序共享),例如“用例”,它是清晰架構中的重要元素。以下是“RegistrationUseCaseInterface”用例的接口。

type RegistrationUseCaseInterface interface {
	RegisterUser(user *model.User) (resultUser *model.User, err error)

	UnregisterUser(username string) error
	
	ModifyUser(user *model.User) error
	
	ModifyAndUnregister(user *model.User) error
}

可插拔的第三方接口庫

通常業務邏輯需要與外部世界交互並使用它們提供的服務,例如,日誌服務、消息服務等等。這些都是外部接口,常常可以被很多應用程序共享。在領域驅動設計中,它們被稱為“應用服務(application service)”。 通常有許多庫或應用程序可以提供這樣的服務, 但你不希望將應用程序與它們中的任何一個綁定。最好是能隨時替換任何服務而又不需要更改代碼。

問題是每個服務都有自己的接口。理想的情況是,我們已經有了標準接口,所有不同的服務提供者都遵循相同的接口。這將是開發者的夢想成真。Java有一個“JDBC”的接口,它隱藏了每個數據庫的實現細節,使我們能按照統一的方式處理不同的SQL數據庫。不幸的是,這種成功並沒有擴展到其他領域。

要想讓框架變得很輕量的一個關鍵是把服務都變成標準接口,並把它們移到框架之外,使之成為第三方庫,其中不僅包含了標準接口,同時也封裝了支持這個接口的庫。這樣這個第三方庫就變成了可插拔的標準組件。為了讓應用程序基於接口設計,我創建了三個通用接口分別用於日誌記錄、消息傳遞和事務管理。創建一個好的標準接口是非常困難的,由於我在上面這些領域都不是專家,因此這些自建的接口離標準接口有一定差距。但對於我的應用程序來說,這已經足夠。我希望各個領域的專家能儘快制定出標準接口。在沒有標準接口之前,可以自定義接口,為以後切換到標準接口做好準備。

下面是日誌的通用接口:

type Logger interface {
	Errorf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	Fatal(args ...interface{})
	Infof(format string, args ...interface{})
	Info(args ...interface{})
	Warnf(format string, args ...interface{})
	Debugf(format string, args ...interface{})
	Debug(args ...interface{})
}

這個第三方庫的結構是與框架或應用程序的結構相匹配的,這樣才能與框架很好地對接。關於如何創建一個第三方庫,我會單獨寫一篇文章[“事件驅動的微服務-創建第三方庫”]來講解。

框架(framework)或者庫(Lib)?

框架和庫之間的爭論已經持續了很久了。大多數人更喜歡庫而不是框架,因為它是輕量級的並更加靈活。但為什麼我要創建一個框架而不是一個庫呢? 因為你仍然需要一個框架來將所有不同的庫組織在一起(不論它是自建的或是第三方的)。因此你通常要用很多庫,但只要一個框架。問題是有用的框架都太重了,我們需要一個輕量級的好用的框架。

因為業務邏輯中的元素都是基於接口的,我們可以把框架視為總線(接口總線),將任何基於接口的服務插入其中。這就是所謂的可插拔框架,它實現了框架與庫的完美結合。

在這個框架之下,一個應用程序的生態由三部分組成,一個是可進化的框架;另一個是可插拔的第三方標準接口(這個接口是可以不依賴於任何框架而單獨使用的),例如上面提到的日誌接口;最後是支持標準接口的具體實現庫,例如對日誌功能來講就是”zap” 或”Logrus”。 而可進化的框架就成了把它們串接起來的主線。

與其它框架的比較

本文的框架是基於清晰架構(Clean Architecture) 的。你可以在很多其他框架中看到相似的元素,比如Java中的“Spring”,它也有程序容器並大量地使用了依賴注入。本框架唯一的新東西是自我進化。

通常,大多數框架都試圖通過使用多種設計模式來應對未來的不確定性。而它需要複雜的邏輯,這就不可避免地將這種複雜性寫入到代碼中。這就使得多數有用的框架都很重,不論學習和使用都難度較高。但如果未來的情況與預計的並不相符,那麼這種內置的複雜性就得不到利用,而變成巨大的負擔。“Spring”就是一個很好的例子,它非常強大但也很重,適合複雜的項目,但是對於簡單的項目就很浪費。本框架在設計時徹底改變了思路,不對未來做任何假設,因此就不需預先在代碼中引入複雜的設計模式。你可以從最簡單的框架開始,只有當你的程序變得很複雜並需要與之匹配的框架時,才進化成複雜的框架。當然你的程序必須遵從一定的設計結構,這裏面的關鍵是基於接口的設計。當前,我們已進入了微服務時代,大多數項目都是小的服務,這對能夠自我進化框架的需求就變得更為強烈。

應用程序如何使用框架?

在清晰架構中,“用例”是一個關鍵組件。如果你想了解一個應用程序,就從這裏開始。業務邏輯只需要獲得用例一個接口,就可以完成需要的任何操作,因為所有其它需要的接口都包含在“用例”中。

在業務邏輯中,“用例”被定義成接口而不是結構(struct)。在運行時,你需要獲得用例的具體實現結構(struct)並將其注入到業務邏輯中。它的步驟是這樣的,首先創建容器,然後構建具體的用例,最後調用“用例”中的函數。

如何調用“用例”

下面是構建程序容器的代碼。

func buildContainer(filename string) (container.Container, error) {
	container, err := app.InitApp(filename)
	if err != nil {
		return nil, errors.Wrap(err, "")
	}
	return container, nil
}

下面是程序容器中的函數”InitApp()”(在文件”app.go”里),調用它來初始化容器。

func InitApp(filename...string) (container.Container, error) {
	err := initLogger()
	if err != nil {
		return nil, err
	}
	return initContainer()
}

下面是用來創建”Registration”用例的幫助函數,它在文件”serviceTmplContainer.go”里。

func GetRegistrationUseCase(c container.Container) (usecase.RegistrationUseCaseInterface, error) {
	key := config.REGISTRATION
	value, err := c.BuildUseCase(key)
	if err != nil {
		//logger.Log.Errorf("%+v\n", err)
		return nil, errors.Wrap(err, "")
	}
	return value.(usecase.RegistrationUseCaseInterface), nil
}

下面是調用”Registration”用例的代碼,它先調用”GetRegistrationUseCase”來得到用例,然後再調用“用例”裏面的”RegisterUser()”函數。

func testRegisterUser(container container.Container) {
	ruci, err := containerhelper.GetRegistrationUseCase(container)
	if err != nil {
		logger.Log.Fatal("registration interface build failed:%+v\n", err)
	}
	created, err := time.Parse(timea.FORMAT_ISO8601_DATE, "2018-12-09")
	if err != nil {
		logger.Log.Errorf("date format err:%+v\n", err)
	}

	user := model.User{Name: "Brian", Department: "Marketing", Created: created}

	resultUser, err := ruci.RegisterUser(&user)
	if err != nil {
		logger.Log.Errorf("user registration failed:%+v\n", err)
	} else {
		logger.Log.Info("new user registered:", resultUser)
	}
}

結論

本文介紹了一個能夠自我進化的輕量級的清晰架構框架。當創建一個新項目時你可以從最簡單的輕量級的框架開始。當此項目不斷髮展變得複雜時,框架可以自我進化為一個功能強大的重量級框架。在此過程中,不需要更改任何業務代碼。目前它有三種模式,分別是初級模式,增強模式和高級模式。最複雜的是高級模式,它基於依賴注入,非常強大。我創建了三個簡單的應用程序來說明展示如何使用它,每個程序對應一種模式。

源碼:

完整的源碼:

  • “servicetmpl1”
  • “Order Service”
  • “Payment Service”

索引:

1 “清晰架構(Clean Architecture)的Go微服務”

2 “清晰架構(Clean Architecture)的Go微服務: 程序容器(Application Container)”

3 “訂單服務”

4 “支付服務”

5 “Service template 1”

6 “zap”

7 “Logrus”

8 “清晰架構(Clean Architecture)的Go微服務: 程序設計”

9 [“事件驅動的微服務-創建第三方庫”]

10 The Clean Architcture

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

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

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

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

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

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

※回頭車貨運收費標準

中國就愛電動車,豐田氫燃料電池大計只好轉彎

豐田(Toyota)正全力推動氫燃料電池車,與過去盟友特斯拉(Tesla)分道揚鑣,鼓吹氫燃料電池才是未來汽車的主流,甚至不惜出售 5,000 億日圓特別股籌資以發展氫燃料電池車,不過,凡事遇到中國政府都得轉彎,豐田的大計也一樣,由於中國政府政策就是要電動車,豐田在屋簷下也不得不低頭。  
 
 
 
 
 
 
 豐田也曾經加入電動車陣營,自 2010 年起更與特斯拉合作,並進行資本投資,投資特斯拉 5,000 萬美元取得 2.4% 股份,雙方自 2010 年起合作打造豐田第二代 RAV4 EV 全電動車,然而,之後豐田轉向氫燃料電池,與特斯拉的合作於 2014 年結束,RAV4 EV 電動車於 2014 年 8 月停產,2014 年 10 月傳出豐田出售特斯拉持股,而在那之前,豐田就已經與特斯拉惡言相向,豐田唱衰電動車沒有未來,特斯拉的馬斯克(Musk)則說氫比較適合用來推動火箭,到 2015 年 1 月又直批氫燃料電池愚蠢。     豐田不理會舊合作夥伴的閒言閒語,鐵了心走氫燃料電池之路,宣布推出「未來」(MIRAI)氫燃料電池車,並全力押注在氫燃料電池車之上。2015 年 4 月 28 日,豐田宣布將發行 5,000 億日圓特別股籌資,特別股售價將高於普通股 2 成,並且有 5 年閉鎖期,股息自 0.5% 開始,每年提升 0.5% 直到 2.5%,雖然並不起眼,但在日本的低利環境下算是條件不差,特別股日後可選擇原價賣回,或轉換為普通股。   不過,任何事遇上中國政府,往往只能轉彎,豐田與電動車決裂,另立氫燃料電池車旗幟的大計,遇到中國政策,也只能低頭。   中國政府正推動本土電動車製造,要求國際大廠與其中國合作夥伴參與其中,各大廠為了討好中國政府,紛紛加入,《彭博新能源財經》估計 2015 年有 40 款電動車將在中國上市,豐田也無法例外,將於 2015 年與中國合作夥伴廣州汽車、中國第一汽車集團合作推出 Leahead 以及 Ranz 全電動車品牌。   豐田首席工程師田中良和(Yoshikazu Tanaka)於 2015 年 4 月 16 日表示,電動車的普及需要仰賴快速充電站,而快速充電對於電網產生很大負擔,他不認為電動車會普及,言猶在耳,豐田中國區董事長大西弘致卻於 4 月 21 日表示,在習近平全力推動電動車政策下,他相信包括充電站在內的基礎設施將發展快速。  
 

    豐田的自相矛盾,也再度顯示在中國做生意的政治風險,不過,產業界認為豐田在中國續推電動車,只是為了迎合上意作作秀,賣個一定數量交差即可,不會全心推動,畢竟豐田已經全力壓寶氫燃料電池車,沒有
 
 
 
 回頭路了,這也顯示用政策強迫推動產業,即使干預力量強如中國政府,仍有極限。     本文全文授權轉載自《科技新報》─〈〉

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

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

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

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

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

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

※回頭車貨運收費標準

為電動車佈局?鴻海2250萬美元入股上海嘉銘

根據《經濟日報》5月19日報導,鴻海集團昨日代子公司富泰華工業(深圳)有限公司發佈公告,取得嘉銘融資租賃(上海)有限公司股權,預計投資金額為2,250萬美元,持股比例將達75%。   市場猜測,鴻海集團投資此融資租賃公司,應該與電動車佈局有關。現在中國大陸融資租賃、分時租賃是電動車銷售的主要動力,大陸日前也有報告指出,目前,新能源汽車領域融資租賃的滲透率約為14%。預計到2025年,新能源汽車銷量將不低於500萬輛,融資租賃滲透率至少達到22%。汽車金融及租賃服務是新能源汽車業價值鏈中最具有獲利能力的環節,毛利率甚至可望高達30%左右。

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

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

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

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

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

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

※回頭車貨運收費標準