干貨|Spring Cloud Bus 消息總線介紹

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

繼上一篇?干貨|Spring Cloud Stream 體系及原理介紹?之后,本期我們來了解下 Spring Cloud 體系中的另外一個組件 Spring Cloud Bus (建議先熟悉 Spring Cloud Stream,不然無法理解 Spring Cloud Bus 內部的代碼)。

Spring Cloud Bus?對自己的定位是 Spring Cloud 體系內的消息總線,使用 message broker 來連接分布式系統的所有節點。Bus 官方的?Reference 文檔?比較簡單,簡單到連一張圖都沒有。

這是最新版的 Spring Cloud Bus 代碼結構(代碼量比較少):

?

v2-7e5817acf1493653a5a7c857d288f0ba_hd.jpg

?

Bus 實例演示

在分析 Bus 的實現之前,我們先來看兩個使用 Spring Cloud Bus 的簡單例子。

所有節點的配置新增

Bus 的例子比較簡單,因為 Bus 的 AutoConfiguration 層都有了默認的配置,只需要引入消息中間件對應的 Spring Cloud Stream 以及 Spring Cloud Bus 依賴即可,之后所有啟動的應用都會使用同一個 Topic 進行消息的接收和發送。

Bus 對應的 Demo 已經放到了 github 上:?https://github.com/fangjian0423/rocketmq-binder-demo/tree/master/rocketmq-bus-demo?。 該 Demo 會模擬啟動 5 個節點,只需要對其中任意的一個實例新增配置項,所有節點都會新增該配置項。

訪問任意節點提供的 Controller 提供的獲取配置的地址(key為hangzhou):

curl -X GET 'http://localhost:10001/bus/env?key=hangzhou'

所有節點返回的結果都是 unknown,因為所有節點的配置中沒有?hangzhou?這個 key。

Bus 內部提供了?EnvironmentBusEndpoint?這個 Endpoint 通過 message broker 用來新增/更新配置。

訪問任意節點該 Endpoint 對應的 url?/actuator/bus-env?name=hangzhou&value=alibaba?進行配置項的新增(比如訪問 node1 的url):

curl -X POST 'http://localhost:10001/actuator/bus-env?name=hangzhou&value=alibaba' -H 'content-type: application/json'

然后再次訪問所有節點?/bus/env?獲取配置:

$ curl -X GET 'http://localhost:10001/bus/env?key=hangzhou'
unknown%
~ ?
$ curl -X GET 'http://localhost:10002/bus/env?key=hangzhou'
unknown%
~ ?
$ curl -X GET 'http://localhost:10003/bus/env?key=hangzhou'
unknown%
~ ?
$ curl -X GET 'http://localhost:10004/bus/env?key=hangzhou'
unknown%
~ ?
$ curl -X GET 'http://localhost:10005/bus/env?key=hangzhou'
unknown%
~ ?
$ curl -X POST 'http://localhost:10001/actuator/bus-env?name=hangzhou&value=alibaba' -H 'content-type: application/json'~ ?
$ curl -X GET 'http://localhost:10005/bus/env?key=hangzhou'
alibaba%
~ ?
$ curl -X GET 'http://localhost:10004/bus/env?key=hangzhou'
alibaba%
~ ?
$ curl -X GET 'http://localhost:10003/bus/env?key=hangzhou'
alibaba%
~ ?
$ curl -X GET 'http://localhost:10002/bus/env?key=hangzhou'
alibaba%
~ ?
$ curl -X GET 'http://localhost:10001/bus/env?key=hangzhou'
alibaba%

可以看到,所有節點都新增了一個 key 為?hangzhou?的配置,且對應的 value 是?alibaba。這個配置項是通過 Bus 提供的?EnvironmentBusEndpoint?完成的。

這里引用?程序猿DD?畫的一張圖片,Spring Cloud Config 配合 Bus 完成所有節點配置的刷新來描述之前的實例(本文實例不是刷新,而是新增配置,但是流程是一樣的):

?

v2-6219c057862c9d40feae8173c3abef2d_hd.jpg

?

部分節點的配置修改

比如在 node1 上指定 destination 為 rocketmq-bus-node2 (node2 配置了 spring.cloud.bus.id 為?rocketmq-bus-node2:10002,可以匹配上) 進行配置的修改:

curl -X POST 'http://localhost:10001/actuator/bus-env/rocketmq-bus-node2?name=hangzhou&value=xihu' -H 'content-type: application/json'

訪問?/bus/env?獲取配置(由于在 node1 上發送消息,Bus 也會對發送方的節點 node1 進行配置修改):

~ ?
$ curl -X POST 'http://localhost:10001/actuator/bus-env/rocketmq-bus-node2?name=hangzhou&value=xihu' -H 'content-type: application/json'~ ?
$ curl -X GET 'http://localhost:10005/bus/env?key=hangzhou'
alibaba%
~ ?
$ curl -X GET 'http://localhost:10004/bus/env?key=hangzhou'
alibaba%
~ ?
$ curl -X GET 'http://localhost:10003/bus/env?key=hangzhou'
alibaba%
~ ?
$ curl -X GET 'http://localhost:10002/bus/env?key=hangzhou'
xihu%
~ ?
$ curl -X GET 'http://localhost:10001/bus/env?key=hangzhou'
xihu%

可以看到,只有 node1 和 node2 修改了配置,其余的 3 個節點配置未改變。

Bus 的實現

Bus 概念介紹

事件

Bus 中定義了遠程事件?RemoteApplicationEvent,該事件繼承了 Spring 的事件?ApplicationEvent,而且它目前有 4 個具體的實現:

?

v2-d0931dd4d4f61dbac92d52e290eef4a3_hd.jpg

?

  • EnvironmentChangeRemoteApplicationEvent: 遠程環境變更事件。主要用于接收一個?Map<String, String>?類型的數據并更新到 Spring 上下文中?Environment?中的事件。文中的實例就是使用這個事件并配合?EnvironmentBusEndpoint?和?EnvironmentChangeListener?完成的。
  • AckRemoteApplicationEvent: 遠程確認事件。Bus 內部成功接收到遠程事件后會發送回?AckRemoteApplicationEvent?確認事件進行確認。
  • RefreshRemoteApplicationEvent: 遠程配置刷新事件。配合?@RefreshScope?以及所有的?@ConfigurationProperties?注解修飾的配置類的動態刷新。
  • UnknownRemoteApplicationEvent:遠程未知事件。Bus 內部消息體進行轉換遠程事件的時候如果發生異常會統一包裝成該事件。

Bus 內部還存在一個非?RemoteApplicationEvent?事件 -?SentApplicationEvent?消息發送事件,配合 Trace 進行遠程消息發送的記錄。

這些事件會配合?ApplicationListener?進行操作,比如?EnvironmentChangeRemoteApplicationEvent配了?EnvironmentChangeListener?進行配置的新增/修改:

public class EnvironmentChangeListenerimplements ApplicationListener<EnvironmentChangeRemoteApplicationEvent> {private static Log log = LogFactory.getLog(EnvironmentChangeListener.class);@Autowiredprivate EnvironmentManager env;@Overridepublic void onApplicationEvent(EnvironmentChangeRemoteApplicationEvent event) {Map<String, String> values = event.getValues();log.info("Received remote environment change request. Keys/values to update "+ values);for (Map.Entry<String, String> entry : values.entrySet()) {env.setProperty(entry.getKey(), entry.getValue());}}
}

收到其它節點發送來的?EnvironmentChangeRemoteApplicationEvent?事件之后調用?EnvironmentManager#setProperty?進行配置的設置,該方法內部針對每一個配置項都會發送一個?EnvironmentChangeEvent?事件,然后被?ConfigurationPropertiesRebinder?所監聽,進行 rebind 操作新增/更新配置。

Actuator Endpoint

Bus 內部暴露了 2 個 Endpoint,分別是?EnvironmentBusEndpoint?和?RefreshBusEndpoint,進行配置的新增/修改以及全局配置刷新。它們對應的 Endpoint id 即 url 是?bus-env?和?bus-refresh

配置

Bus 對于消息的發送必定涉及到 Topic、Group 之類的信息,這些內容都被封裝到了?BusProperties中,其默認的配置前綴為?spring.cloud.bus,比如:

  • spring.cloud.bus.refresh.enabled?用于開啟/關閉全局刷新的 Listener。
  • spring.cloud.bus.env.enabled?用于開啟/關閉配置新增/修改的 Endpoint。
  • spring.cloud.bus.ack.enabled?用于開啟開啟/關閉?AckRemoteApplicationEvent?事件的發送。
  • spring.cloud.bus.trace.enabled?用于開啟/關閉消息記錄 Trace 的 Listener。

消息發送涉及到的 Topic 默認用的是?springCloudBus,可以配置進行修改,Group 可以設置成廣播模式或使用 UUID 配合 offset 為 lastest 的模式。

每個 Bus 應用都有一個對應的 Bus id,官方取值方式較復雜:

${vcap.application.name:${spring.application.name:application}}:${vcap.application.instance_index:${spring.application.index:${local.server.port:${server.port:0}}}}:${vcap.application.instance_id:${random.value}}

建議手動配置 Bus id,因為 Bus 遠程事件中的 destination 會根據 Bus id 進行匹配:

spring.cloud.bus.id=${spring.application.name}-${server.port}

Bus 底層分析

Bus 的底層分析無非牽扯到這幾個方面:

  • 消息是如何發送的;
  • 消息是如何接收的;
  • destination 是如何匹配的;
  • 遠程事件收到后如何觸發下一個 action;

BusAutoConfiguration?自動化配置類被?@EnableBinding(SpringCloudBusClient.class)?所修飾。

@EnableBinding?的用法在上期文章?干貨|Spring Cloud Stream 體系及原理介紹?中已經說明,且它的 value 為?SpringCloudBusClient.class,會在?SpringCloudBusClient?中基于代理創建出 input 和 output 的?DirectChannel

public interface SpringCloudBusClient {String INPUT = "springCloudBusInput";String OUTPUT = "springCloudBusOutput";@Output(SpringCloudBusClient.OUTPUT)MessageChannel springCloudBusOutput();@Input(SpringCloudBusClient.INPUT)SubscribableChannel springCloudBusInput();
}

springCloudBusInput 和 springCloudBusOutput 這兩個 Binding 的屬性可以通過配置文件進行修改(比如修改 topic):

spring.cloud.stream.bindings:springCloudBusInput:destination: my-bus-topicspringCloudBusOutput:destination: my-bus-topic

消息的接收的發送:

// BusAutoConfiguration@EventListener(classes = RemoteApplicationEvent.class) // 1
public void acceptLocal(RemoteApplicationEvent event) {if (this.serviceMatcher.isFromSelf(event)&& !(event instanceof AckRemoteApplicationEvent)) { // 2this.cloudBusOutboundChannel.send(MessageBuilder.withPayload(event).build()); // 3}
}@StreamListener(SpringCloudBusClient.INPUT) // 4
public void acceptRemote(RemoteApplicationEvent event) {if (event instanceof AckRemoteApplicationEvent) {if (this.bus.getTrace().isEnabled() && !this.serviceMatcher.isFromSelf(event)&& this.applicationEventPublisher != null) { // 5this.applicationEventPublisher.publishEvent(event);}// If it's an ACK we are finished processing at this pointreturn;}if (this.serviceMatcher.isForSelf(event)&& this.applicationEventPublisher != null) { // 6if (!this.serviceMatcher.isFromSelf(event)) { // 7this.applicationEventPublisher.publishEvent(event);}if (this.bus.getAck().isEnabled()) { // 8AckRemoteApplicationEvent ack = new AckRemoteApplicationEvent(this,this.serviceMatcher.getServiceId(),this.bus.getAck().getDestinationService(),event.getDestinationService(), event.getId(), event.getClass());this.cloudBusOutboundChannel.send(MessageBuilder.withPayload(ack).build());this.applicationEventPublisher.publishEvent(ack);}}if (this.bus.getTrace().isEnabled() && this.applicationEventPublisher != null) { // 9// We are set to register sent events so publish it for local consumption,// irrespective of the originthis.applicationEventPublisher.publishEvent(new SentApplicationEvent(this,event.getOriginService(), event.getDestinationService(),event.getId(), event.getClass()));}
}
  1. 利用 Spring 事件的監聽機制監聽本地所有的?RemoteApplicationEvent?遠程事件(比如?bus-env會在本地發送?EnvironmentChangeRemoteApplicationEvent?事件,bus-refresh?會在本地發送?RefreshRemoteApplicationEvent?事件,這些事件在這里都會被監聽到)。
  2. 判斷本地接收到的事件不是?AckRemoteApplicationEvent?遠程確認事件(不然會死循環,一直接收消息,發送消息...)以及該事件是應用自身發送出去的(事件發送方是應用自身),如果都滿足執行步驟 3。
  3. 構造 Message 并將該遠程事件作為 payload,然后使用 Spring Cloud Stream 構造的 Binding name 為 springCloudBusOutput 的 MessageChannel 將消息發送到 broker。
  4. @StreamListener?注解消費 Spring Cloud Stream 構造的 Binding name 為 springCloudBusInput 的 MessageChannel,接收的消息為遠程消息。
  5. 如果該遠程事件是?AckRemoteApplicationEvent?遠程確認事件并且應用開啟了消息追蹤 trace 開關,同時該遠程事件不是應用自身發送的(事件發送方不是應用自身,表示事件是其它應用發送過來的),那么本地發送?AckRemoteApplicationEvent?遠程確認事件表示應用確認收到了其它應用發送過來的遠程事件,流程結束。
  6. 如果該遠程事件是其它應用發送給應用自身的(事件的接收方是應用自身),那么進行步驟 7 和 8,否則執行步驟 9。
  7. 該遠程事件不是應用自身發送(事件發送方不是應用自身)的話,將該事件以本地的方式發送出去。應用自身一開始已經在本地被對應的消息接收方處理了,無需再次發送。
  8. 如果開啟了?AckRemoteApplicationEvent?遠程確認事件的開關,構造?AckRemoteApplicationEvent?事件并在遠程和本地都發送該事件(本地發送是因為步驟 5 沒有進行本地?AckRemoteApplicationEvent?事件的發送,也就是自身應用對自身應用確認; 遠程發送是為了告訴其它應用,自身應用收到了消息)。
  9. 如果開啟了消息記錄 Trace 的開關,本地構造并發送?SentApplicationEvent?事件

?

v2-ce0a04e1a712a01f76ba689d646cbe3e_hd.jpg

?

bus-env?觸發后所有節點的?EnvironmentChangeListener?監聽到了配置的變化,控制臺都會打印出以下信息:

o.s.c.b.event.EnvironmentChangeListener  : Received remote environment change request. Keys/values to update {hangzhou=alibaba}

如果在本地監聽遠程確認事件?AckRemoteApplicationEvent,都會收到所有節點的信息,比如 node5 節點的控制臺監聽到的?AckRemoteApplicationEvent?事件如下:

ServiceId [rocketmq-bus-node5:10005] listeners on {"type":"AckRemoteApplicationEvent","timestamp":1554124670484,"originService":"rocketmq-bus-node5:10005","destinationService":"**","id":"375f0426-c24e-4904-bce1-5e09371fc9bc","ackId":"750d033f-356a-4aad-8cf0-3481ace8698c","ackDestinationService":"**","event":"org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent"}
ServiceId [rocketmq-bus-node5:10005] listeners on {"type":"AckRemoteApplicationEvent","timestamp":1554124670184,"originService":"rocketmq-bus-node1:10001","destinationService":"**","id":"91f06cf1-4bd9-4dd8-9526-9299a35bb7cc","ackId":"750d033f-356a-4aad-8cf0-3481ace8698c","ackDestinationService":"**","event":"org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent"}
ServiceId [rocketmq-bus-node5:10005] listeners on {"type":"AckRemoteApplicationEvent","timestamp":1554124670402,"originService":"rocketmq-bus-node2:10002","destinationService":"**","id":"7df3963c-7c3e-4549-9a22-a23fa90a6b85","ackId":"750d033f-356a-4aad-8cf0-3481ace8698c","ackDestinationService":"**","event":"org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent"}
ServiceId [rocketmq-bus-node5:10005] listeners on {"type":"AckRemoteApplicationEvent","timestamp":1554124670406,"originService":"rocketmq-bus-node3:10003","destinationService":"**","id":"728b45ee-5e26-46c2-af1a-e8d1571e5d3a","ackId":"750d033f-356a-4aad-8cf0-3481ace8698c","ackDestinationService":"**","event":"org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent"}
ServiceId [rocketmq-bus-node5:10005] listeners on {"type":"AckRemoteApplicationEvent","timestamp":1554124670427,"originService":"rocketmq-bus-node4:10004","destinationService":"**","id":"1812fd6d-6f98-4e5b-a38a-4b11aee08aeb","ackId":"750d033f-356a-4aad-8cf0-3481ace8698c","ackDestinationService":"**","event":"org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent"}

那么回到本章節開頭提到的 4 個問題,我們分別做一下解答:

  • 消息是如何發送的: 在?BusAutoConfiguration#acceptLocal?方法中通過 Spring Cloud Stream 發送事件到?springCloudBus?topic 中。
  • 消息是如何接收的: 在?BusAutoConfiguration#acceptRemote?方法中通過 Spring Cloud Stream 接收?springCloudBus?topic 的消息。
  • destination 是如何匹配的: 在?BusAutoConfiguration#acceptRemote?方法中接收遠程事件方法里對 destination 進行匹配。
  • 遠程事件收到后如何觸發下一個 action: Bus 內部通過 Spring 的事件機制接收本地的?RemoteApplicationEvent?具體的實現事件再做下一步的動作(比如?EnvironmentChangeListener?接收了?EnvironmentChangeRemoteApplicationEvent?事件,?RefreshListener?接收了?RefreshRemoteApplicationEvent?事件)。

總結

Spring Cloud Bus 自身內容還是比較少的,不過還是需要提前了解 Spring Cloud Stream 體系以及 Spring 自身的事件機制,在此基礎上,才能更好地理解 Spring Cloud Bus 對本地事件和遠程事件的處理邏輯。

目前 Bus 內置的遠程事件較少,大多數為配置相關的事件,我們可以繼承?RemoteApplicationEvent并配合?@RemoteApplicationEventScan?注解構建自身的微服務消息體系。

?

原文鏈接

本文為云棲社區原創內容,未經允許不得轉載。

轉載于:https://my.oschina.net/u/3889140/blog/3038196

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/387833.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/387833.shtml
英文地址,請注明出處:http://en.pswp.cn/news/387833.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一類動詞二類動詞三類動詞_基于http動詞的完全無效授權技術

一類動詞二類動詞三類動詞Authorization is a basic feature of modern web applications. It’s a mechanism of specifying access rights or privileges to resources according to user roles. In case of CMS like applications, it needs to be equipped with advanced l…

主成份分析(PCA)詳解

主成分分析法&#xff08;Principal Component Analysis&#xff09;大多在數據維度比較高的時候&#xff0c;用來減少數據維度&#xff0c;因而加快模型訓練速度。另外也有些用途&#xff0c;比如圖片壓縮&#xff08;主要是用SVD&#xff0c;也可以用PCA來做&#xff09;、因…

thinkphp5記錄

ThinkPHP5 隱藏index.php問題 thinkphp模板輸出cookie,session中… 轉載于:https://www.cnblogs.com/niuben/p/10056049.html

portainer容器可視化管理部署簡要筆記

參考鏈接&#xff1a;https://www.portainer.io/installation/ 1、單個宿主機部署in Linux&#xff1a;$ docker volume create portainer_data$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer 2、單…

證明您履歷表經驗的防彈五步法

How many times have you gotten the question “Tell me more about your work experience at …” or “Describe an experience when you had to overcome a technical challenge”? Is your answer solid and bullet-proof every single time you have to respond? If no…

2018-2019-1 20165231 實驗四 外設驅動程序設計

博客鏈接&#xff1a;https://www.cnblogs.com/heyanda/p/10054680.html 轉載于:https://www.cnblogs.com/Yhooyon/p/10056173.html

如何安裝pylab:python如何導入matplotlib模塊

pylab是python下挺不錯的一個畫圖模塊&#xff0c;使用也非常簡單&#xff0c;記得Mit的計算機科學及編程導論有節課也是用到了這個工具&#xff0c;但這個工具安裝不象用起來那么方便&#xff0c;小編就圖文全程直播下吧 工具/原料 python2.7.10win10 32位方法/步驟 1缺省狀態…

微信掃描二維碼和瀏覽器掃描二維碼 ios和Android 分別進入不用的提示頁面

實現微信掃描二維碼和瀏覽器掃描二維碼 ios和Android 分別進入不用的提示頁面 而進入商城下載該項目 詳情地址&#xff1a;gitee.com/DuJiaHui123… 1.創建完之后 替換文件里面的ios項目地址和Android地址 2.網頁上線 3.百度搜索 二維碼生成 把上線后的地址生成二維碼 4.可以把…

詳解getchar()函數與緩沖區

1、首先&#xff0c;我們看一下這段代碼&#xff1a; 它的簡單意思就是從鍵盤讀入一個字符&#xff0c;然后輸出到屏幕。理所當然&#xff0c;我們輸入1&#xff0c;輸出就是1&#xff0c;輸入2&#xff0c;輸出就是2。 那么我們如果輸出的是12呢&#xff1f; 它的輸出是1。 這…

windows下python安裝Numpy、Scipy、matplotlib模塊

python 2.7 針對2.7的軟件。numpy :http://sourceforge.net/projects/numpy/files/NumPy/1.8.1/ 下載下面的numpy-1.8.2-win32-superpack-python2.7 scipy: http://sourceforge.net/projects/scipy/files/matplotlib:matplotlib-1.1.0.win32-py2.7 以上都是exe文件&#xff0…

restTemplate使用和踩坑總結

日常工作中肯定會遇到服務之間的調用&#xff0c;尤其是現在都是微服務的架構&#xff0c;所以總結一下restTemplate的最常用的用法以及自己踩過的坑。 restTemplate的使用 restTemplate底層調用的是Execute方法&#xff0c;而Execute底層調用的是doExecute&#xff0c;它是基于…

常見編碼總結

本文總結自&#xff1a;https://blog.csdn.net/zmx729618/article/details/51821024 1. ISO 8859-1 字節數&#xff1a;1 范圍&#xff1a;0-255&#xff08;編碼范圍是0x00-0xFF&#xff09;&#xff0c;其中0x00-0x7F之間完全和ASCII一致&#xff08;ASCII是7位編碼&#xff…

啟動一個Java進程

windows版本 startup.bat -------------------------------------------------------- rem --------------------------------------------------------------------------- rem Start SMS Server by zhangjin rem --------------------------------------------------------…

Flask框架從入門到精通之參數配置(二)

知識點&#xff1a; 1、參數配置 一、概況 上一篇我們已經把Flask第一個程序運行起來了&#xff0c;那么這一篇主要講一下Flask參數的配置。 二、配置參數 Flask參數配置方式有很多種&#xff0c;每一種都可以達到結果&#xff0c;在合適的場景選擇合適的配置方式。 配置文件 在…

BP神經網絡python簡單實現

BP神經網絡的原理在網上有很詳細的說明&#xff0c;這里就不打算細說&#xff0c;這篇文章主要簡單的方式設計及實現BP神經網絡&#xff0c;并簡單測試下在恒等計算&#xff08;編碼&#xff09;作測試。 BP神經網絡模型圖如下 BP神經網絡基本思想 BP神經網絡學習過程由信息的…

golang的reflection(轉)(一)

2019獨角獸企業重金招聘Python工程師標準>>> 反射reflection 可以大大提高程序的靈活性&#xff0c;使得interface{}有更大的發揮余地反射可以使用TypeOf和ValueOf函數從接口中獲取目標對象信息反射會將匿名字段作為獨立字段&#xff08;匿名字段的本質&#xff09;…

idea教程--Maven 骨架介紹

簡單的說&#xff0c;Archetype是Maven工程的模板工具包。一個Archetype定義了要做的相同類型事情的初始樣式或模型。這個名稱給我們提供來了一個一致的生成Maven工程的方式。Archetype會幫助作者給用戶創建Maven工程模板&#xff0c;并給用戶提供生成相關工程模板版本的參數化…

datatables.js 簡單使用--多選框和服務器端分頁

說明&#xff1a;datatables是一款jQuery表格插件。感覺EasyUI的datagrid更易用 內容&#xff1a;多選框和服務器端分頁 緣由&#xff1a;寫這篇博客的原因是datatables的文檔寫的不怎么樣&#xff0c;找東西很麻煩 環境&#xff1a;asp.net mvc , vs2015sqlserver2012 顯示效…

python異常(高級) Exception

異常(高級) Exception 異常回顧:     try-except 語句 捕獲(接收)異常通知,把異常流程變為正常流程     try-finally 語句 執行必須要執行的語句.     raise 語句 發送異常通知,同時進入異常流程     assert 語句 發送AssertionError異常     with 語句 wi…

反射賦值

目前例子為NPOI Excel導入 入庫時調用 var file file1.PostedFile.InputStream;var fileExt System.IO.Path.GetExtension(file1.FileName);IWorkbook workbook;if (fileExt ".xlsx")workbook new XSSFWorkbook(file);elseworkbook new HSSFWorkbook(file);DB.D…