paulwong

          #

          快速理解聚合根、實(shí)體、值對(duì)象的區(qū)別和聯(lián)系

          posted @ 2017-02-19 08:32 paulwong 閱讀(1293) | 評(píng)論 (0)編輯 收藏

          基于Spring Boot, Axon CQRS/ES,和Docker構(gòu)建微服務(wù)

          這是一個(gè)使用Spring Boot和Axon以及Docker構(gòu)建的Event Sorucing源碼項(xiàng)目,技術(shù)特點(diǎn):
          1.使用Java 和Spring Boot實(shí)現(xiàn)微服務(wù);
          2.使用命令和查詢職責(zé)分離 (CQRS) 和 Event Sourcing (ES) 的框架Axon Framework v2, MongoDB 和 RabbitMQ;
          3.使用Docker構(gòu)建 交付和運(yùn)行;
          4.集中配置和使用Spring Cloud服務(wù)注冊(cè);
          5.使用Swagger 和 SpringFox 提供API文檔

          項(xiàng)目源碼:GitHub

          工作原理:
          這個(gè)應(yīng)用使用CQRS架構(gòu)模式構(gòu)建,在CQRS命令如ADD是和查詢VIEW(where id=1)分離的,在這個(gè)案例中領(lǐng)域部分代碼已經(jīng)分離成兩個(gè)組件:一個(gè)是屬于命令這邊的微服務(wù)和屬性查詢這邊的微服務(wù)。

          微服務(wù)是單個(gè)職責(zé)的功能,自己的數(shù)據(jù)存儲(chǔ),每個(gè)能彼此獨(dú)立擴(kuò)展部署。

          屬于命令這邊的微服務(wù)和屬性查詢這邊的微服務(wù)都是使用Spring Boot框架開(kāi)發(fā)的,在命令微服務(wù)和查詢微服務(wù)之間通訊是事件驅(qū)動(dòng),事件是通過(guò)RabbitMQ消息在微服務(wù)組件之間傳遞,消息提供了一種進(jìn)程節(jié)點(diǎn)或微服務(wù)之間可擴(kuò)展的事件載體,包括與傳統(tǒng)遺留系統(tǒng)或其他系統(tǒng)的松耦合通訊都可以通過(guò)消息進(jìn)行。

          請(qǐng)注意,服務(wù)之間不能彼此共享數(shù)據(jù)庫(kù),這是很重要,因?yàn)槲⒎?wù)應(yīng)該是高度自治自主的,這樣反過(guò)來(lái)有助于服務(wù)能夠彼此獨(dú)立地?cái)U(kuò)展伸縮規(guī)模。

          CQRS中命令是“改變狀態(tài)的動(dòng)作”。命令的微服務(wù)包含所有領(lǐng)域邏輯和業(yè)務(wù)規(guī)則,命令被用于增加新的產(chǎn)品或改變它們的狀態(tài),這些命令針對(duì)某個(gè)具體產(chǎn)品的執(zhí)行會(huì)導(dǎo)致事件Event產(chǎn)生,這會(huì)通過(guò)Axon框架持久化到MongoDB中,然后通過(guò)RabbitMQ傳播給其他節(jié)點(diǎn)進(jìn)程或微服務(wù)。

          在event-sourcing中,事件是狀態(tài)改變的原始記錄,它們用于系統(tǒng)來(lái)重新建立實(shí)體的當(dāng)前狀態(tài)(通過(guò)重新播放過(guò)去的事件到當(dāng)前就可以構(gòu)建當(dāng)前的狀態(tài)),這聽(tīng)上去會(huì)很慢,但是實(shí)際上,事件都很簡(jiǎn)單,執(zhí)行非常快,也能采取‘快照’策略進(jìn)行優(yōu)化。

          請(qǐng)注意,在DDD中,實(shí)體是指一個(gè)聚合根實(shí)體。

          上面是命令這邊的微服務(wù),下面看看查詢這邊的微服務(wù):
          查詢微服務(wù)一般扮演一種事件監(jiān)聽(tīng)器和視圖角色,它監(jiān)聽(tīng)到命令那邊發(fā)出的事件,然后處理它們以符合查詢這邊的要求。

          在這個(gè)案例中,查詢這邊只是簡(jiǎn)單建立和維持了一個(gè) ‘materialised view’或‘projection’ ,其中保留了產(chǎn)品的最新?tīng)顟B(tài),也就是產(chǎn)品id和描述以及是否被賣出等等信息,查詢這邊能夠被復(fù)制多次以方便擴(kuò)展,消息可以保留在RabbitMQ隊(duì)列中實(shí)現(xiàn)持久保存,這種臨時(shí)保存消息方式可以防止查詢這邊微服務(wù)當(dāng)機(jī)。

          命令微服務(wù)和查詢微服務(wù)兩者都有REST API,提供外界客戶端訪問(wèn)。

          下面看看如何通過(guò)Docker運(yùn)行這個(gè)案例,需要 Ubuntu 16.04:
          1.Docker ( v1.8.2)
          2.Docker-compose ( v1.7.1)

          在一個(gè)空目錄,執(zhí)行下面命令下載docker-compose:

          $ wget https://raw.githubusercontent.com/benwilcock/microservice-sampler/master/docker-compose.yml
          注意:不要更改文件名稱。

          啟動(dòng)微服務(wù):只是簡(jiǎn)單一個(gè)命令:

          $ docker-compose up

          你會(huì)看到許多下載信息和日志輸出在屏幕上,這是Docker image將被下載和運(yùn)行。一共有六個(gè)docker,分別是: ‘mongodb’, ‘rabbitmq’, ‘config’, ‘discovery’, ‘product-cmd-side’, 和 ‘product-qry-side’.

          使用下面命令進(jìn)行測(cè)試增加一個(gè)新產(chǎn)品:

          $ curl -X POST -v --header "Content-Type: application/json" --header "Accept: */*" "http://localhost:9000/products/add/1?name=Everything%20Is%20Awesome"

          查詢這個(gè)新產(chǎn)品:

          $ curl http://localhost:9001/products/1

          Microservices With Spring Boot, Axon CQRS/ES, and Docker

          posted @ 2017-02-18 22:00 paulwong 閱讀(1866) | 評(píng)論 (0)編輯 收藏

          DDD資源

          微服務(wù)+DDD
          https://github.com/benwilcock/microservice-sampler



          https://github.com/AxonFramework/AxonBank

          http://www.cnblogs.com/netfocus/p/4150084.html

          posted @ 2017-02-18 21:53 paulwong 閱讀(479) | 評(píng)論 (0)編輯 收藏

          使用 Docker 搭建 Java Web 運(yùn)行環(huán)境

               摘要: Docker 是 2014 年最為火爆的技術(shù)之一,幾乎所有的程序員都聽(tīng)說(shuō)過(guò)它。Docker 是一種“輕量級(jí)”容器技術(shù),它幾乎動(dòng)搖了傳統(tǒng)虛擬化技術(shù)的地位,現(xiàn)在國(guó)內(nèi)外已經(jīng)有越來(lái)越多的公司開(kāi)始逐步使用 Docker 來(lái)替換現(xiàn)有的虛擬化平臺(tái)了。作為一名 Java 程序員,我們是時(shí)候一起把 Docker 學(xué)起來(lái)了!本文會(huì)對(duì)虛擬化技術(shù)與 Docker 容器技術(shù)做一個(gè)對(duì)比,然后引出一些 ...  閱讀全文

          posted @ 2016-10-15 19:57 paulwong 閱讀(1623) | 評(píng)論 (0)編輯 收藏

          使用Spring Cloud Security OAuth2搭建授權(quán)服務(wù)

          Spring Cloud Security OAuth2 是 Spring 對(duì) OAuth2 的開(kāi)源實(shí)現(xiàn),優(yōu)點(diǎn)是能與Spring Cloud技術(shù)線無(wú)縫集成,如果全部使用默認(rèn)配置,開(kāi)發(fā)者只需要添加注解就能完成 OAuth2 授權(quán)服務(wù)的搭建。

          1. 添加依賴

          授權(quán)服務(wù)是基于Spring Security的,因此需要在項(xiàng)目中引入兩個(gè)依賴:

          <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-security</artifactId>
          </dependency>

          <dependency>
                   <groupId>org.springframework.cloud</groupId>
                   <artifactId>spring-cloud-starter-oauth2</artifactId>
           </dependency>


          前者為 Security,后者為Security的OAuth2擴(kuò)展。

          2. 添加注解和配置

          在啟動(dòng)類中添加@EnableAuthorizationServer注解:

          @SpringBootApplication
          @EnableAuthorizationServer
          public class AlanOAuthApplication {
              public static void main(String[] args) {
                  SpringApplication.run(AlanOAuthApplication.class, args);
              }
          }


          完成這些我們的授權(quán)服務(wù)最基本的骨架就已經(jīng)搭建完成了。但是要想跑通整個(gè)流程,我們必須分配 client_idclient_secret才行。Spring Security OAuth2的配置方法是編寫@Configuration類繼承AuthorizationServerConfigurerAdapter,然后重寫void configure(ClientDetailsServiceConfigurer clients)方法,如:

          @Override
              public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                  clients.inMemory() // 使用in-memory存儲(chǔ)
                          .withClient("client") // client_id
                          .secret("secret") // client_secret
                          .authorizedGrantTypes("authorization_code") // 該client允許的授權(quán)類型
                          .scopes("app"); // 允許的授權(quán)范圍
              }


          3. 授權(quán)流程

          訪問(wèn)授權(quán)頁(yè)面:

          localhost:8080/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.baidu.com


          此時(shí)瀏覽器會(huì)讓你輸入用戶名密碼,這是因?yàn)?Spring Security 在默認(rèn)情況下會(huì)對(duì)所有URL添加Basic Auth認(rèn)證。默認(rèn)的用戶名為user, 密碼是隨機(jī)生成的,在控制臺(tái)日志中可以看到。

          oauth2

          畫風(fēng)雖然很簡(jiǎn)陋,但是基本功能都具備了。點(diǎn)擊Authorize后,瀏覽器就會(huì)重定向到百度,并帶上code參數(shù):

          這里寫圖片描述

          拿到code以后,就可以調(diào)用

          POST/GET http://client:secret@localhost:8080/oauth/token
          • 1

          來(lái)?yè)Q取access_token了:

          curl -X POST -H "Cache-Control: no-cache" -H "Content-Type: application/x-www-form-urlencoded" -d 'grant_type=authorization_code&code=Li4NZo&redirect_uri=http://www.baidu.com' "http://client:secret@localhost:8080/oauth/token"

          返回如下:

          {
            "access_token": "32a1ca28-bc7a-4147-88a1-c95abcc30556",
            "token_type": "bearer",
            "expires_in": 2591999,
            "scope": "app"
          }

          到此我們最最基本的授權(quán)服務(wù)就搭建完成了。然而,這僅僅是個(gè)demo,如果要在生產(chǎn)環(huán)境中使用,還需要做更多的工作。

          4. 使用MySQL存儲(chǔ)access_token和client信息

          把授權(quán)服務(wù)器中的數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫(kù)中并不難,因?yàn)?Spring Cloud Security OAuth 已經(jīng)為我們?cè)O(shè)計(jì)好了一套Schema和對(duì)應(yīng)的DAO對(duì)象。但在使用之前,我們需要先對(duì)相關(guān)的類有一定的了解。

          4.1 相關(guān)接口

          Spring Cloud Security OAuth2通過(guò)DefaultTokenServices類來(lái)完成token生成、過(guò)期等 OAuth2 標(biāo)準(zhǔn)規(guī)定的業(yè)務(wù)邏輯,而DefaultTokenServices又是通過(guò)TokenStore接口完成對(duì)生成數(shù)據(jù)的持久化。在上面的demo中,TokenStore的默認(rèn)實(shí)現(xiàn)為InMemoryTokenStore,即內(nèi)存存儲(chǔ)。 對(duì)于Client信息,ClientDetailsService接口負(fù)責(zé)從存儲(chǔ)倉(cāng)庫(kù)中讀取數(shù)據(jù),在上面的demo中默認(rèn)使用的也是InMemoryClientDetialsService實(shí)現(xiàn)類。說(shuō)到這里就能看出,要想使用數(shù)據(jù)庫(kù)存儲(chǔ),只需要提供這些接口的實(shí)現(xiàn)類即可。慶幸的是,框架已經(jīng)為我們寫好JDBC實(shí)現(xiàn)了,即JdbcTokenStoreJdbcClientDetailsService

          4.2 建表

          要想使用這些JDBC實(shí)現(xiàn),首先要建表。框架為我們提前設(shè)計(jì)好了schema, 在github上:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

          在使用這套表結(jié)構(gòu)之前要注意的是,對(duì)于MySQL來(lái)說(shuō),默認(rèn)建表語(yǔ)句中主鍵是varchar(255)類型,在mysql中執(zhí)行會(huì)報(bào)錯(cuò),原因是mysql對(duì)varchar主鍵長(zhǎng)度有限制。所以這里改成128即可。其次,語(yǔ)句中會(huì)有某些字段為LONGVARBINARY類型,它對(duì)應(yīng)mysql的blob類型,也需要修改一下。

          4.3 配置

          數(shù)據(jù)庫(kù)建好后,下一步就是配置框架使用JDBC實(shí)現(xiàn)。方法還是編寫@Configuration類繼承AuthorizationServerConfigurerAdapter

          @Autowired
              private AuthenticationManager authenticationManager;

              @Autowired
              private DataSource dataSource;
              @Bean // 聲明TokenStore實(shí)現(xiàn)
              public TokenStore tokenStore() {
                  return new JdbcTokenStore(dataSource);
              }
              @Bean // 聲明 ClientDetails實(shí)現(xiàn)
              public ClientDetailsService clientDetails() {
                  return new JdbcClientDetailsService(dataSource);
              }
              @Override // 配置框架應(yīng)用上述實(shí)現(xiàn)
              public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                  endpoints.authenticationManager(authenticationManager);
                  endpoints.tokenStore(tokenStore());

                  // 配置TokenServices參數(shù)
                  DefaultTokenServices tokenServices = new DefaultTokenServices();
                  tokenServices.setTokenStore(endpoints.getTokenStore());
                  tokenServices.setSupportRefreshToken(false);
                  tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
                  tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
                  tokenServices.setAccessTokenValiditySeconds( (int) TimeUnit.DAYS.toSeconds(30)); // 30天
                  endpoints.tokenServices(tokenServices);
              }

          完成這些后,框架就會(huì)將中間產(chǎn)生的數(shù)據(jù)寫到mysql中了。oauth_client_details是client表,可以直接在該表中添加記錄來(lái)添加client: 
          這里寫圖片描述

          4.4 需要注意的地方

          這里不得不說(shuō) Spring 設(shè)計(jì)有一個(gè)奇葩地的方。注意看oauth_access_token表是存放訪問(wèn)令牌的,但是并沒(méi)有直接在字段中存放token。Spring 使用OAuth2AccessToken來(lái)抽象與令牌有關(guān)的所有屬性,在寫入到數(shù)據(jù)庫(kù)時(shí),Spring將該對(duì)象通過(guò)JDK自帶的序列化機(jī)制序列成字節(jié)直接保存到了該表的token字段中。也就是說(shuō),如果只看數(shù)據(jù)表你是看不出access_token的值是多少,過(guò)期時(shí)間等信息的。這就給資源服務(wù)器的實(shí)現(xiàn)帶來(lái)了麻煩。我們的資源提供方并沒(méi)有使用Spring Security,也不想引入 Spring Security 的任何依賴,這時(shí)候就只能將 DefaultOAuth2AccessToken的源碼copy到資源提供方的項(xiàng)目中,然后讀取token字段并反序列化還原對(duì)象來(lái)獲取token信息。但是如果這樣做還會(huì)遇到反序列化兼容性的問(wèn)題,具體解決方法參考我另一篇博文:http://blog.csdn.net/neosmith/article/details/52539614

          5. 總結(jié)

          至此一個(gè)能在生產(chǎn)環(huán)境下使用的授權(quán)服務(wù)就搭建好了。其實(shí)我們?cè)趯?shí)際使用時(shí)應(yīng)該適當(dāng)定制JdbcTokenStoreClientDetailsService來(lái)實(shí)適應(yīng)業(yè)務(wù)需要,甚至可以直接從0開(kāi)始實(shí)現(xiàn)接口,完全不用框架提供的實(shí)現(xiàn)。另外,Spring 直接將DefaultOAuth2AccessToken序列化成字節(jié)保存到數(shù)據(jù)庫(kù)中的設(shè)計(jì),我認(rèn)為是非常不合理的。或許設(shè)計(jì)者的初衷是保密access_token,但是通過(guò)加密的方法也可以實(shí)現(xiàn),完全不應(yīng)該直接扔字節(jié)。不過(guò)通過(guò)定制TokenStore接口,我們可以使用自己的表結(jié)構(gòu)而不拘泥于默認(rèn)實(shí)現(xiàn)。

          http://blog.csdn.net/tracker_w/article/category/6360121

          http://blog.csdn.net/neosmith/article/details/52539927

          posted @ 2016-09-16 18:22 paulwong 閱讀(8753) | 評(píng)論 (0)編輯 收藏

          使用Spring Cloud Feign作為HTTP客戶端調(diào)用遠(yuǎn)程HTTP服務(wù)

          在Spring Cloud Netflix棧中,各個(gè)微服務(wù)都是以HTTP接口的形式暴露自身服務(wù)的,因此在調(diào)用遠(yuǎn)程服務(wù)時(shí)就必須使用HTTP客戶端。我們可以使用JDK原生的URLConnection、Apache的Http Client、Netty的異步HTTP Client, Spring的RestTemplate。但是,用起來(lái)最方便、最優(yōu)雅的還是要屬Feign了。

          Feign簡(jiǎn)介

          Feign是一種聲明式、模板化的HTTP客戶端。在Spring Cloud中使用Feign, 我們可以做到使用HTTP請(qǐng)求遠(yuǎn)程服務(wù)時(shí)能與調(diào)用本地方法一樣的編碼體驗(yàn),開(kāi)發(fā)者完全感知不到這是遠(yuǎn)程方法,更感知不到這是個(gè)HTTP請(qǐng)求。比如:

          @Autowired
          private AdvertGropRemoteService service; // 遠(yuǎn)程服務(wù)

          public AdvertGroupVO foo(Integer groupId) {
              return service.findByGroupId(groupId); // 通過(guò)HTTP調(diào)用遠(yuǎn)程服務(wù)
          }

          開(kāi)發(fā)者通過(guò)service.findByGroupId()就能完成發(fā)送HTTP請(qǐng)求和解碼HTTP返回結(jié)果并封裝成對(duì)象的過(guò)程。

          Feign的定義

          為了讓Feign知道在調(diào)用方法時(shí)應(yīng)該向哪個(gè)地址發(fā)請(qǐng)求以及請(qǐng)求需要帶哪些參數(shù),我們需要定義一個(gè)接口:

          @FeignClient(name = "ea")  //  [A]
          public interface AdvertGroupRemoteService {

              @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) // [B]
              AdvertGroupVO findByGroupId(@PathVariable("groupId") Integer adGroupId) // [C]

              @RequestMapping(value = "/group/{groupId}", method = RequestMethod.PUT)
              void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName)

          A: @FeignClient用于通知Feign組件對(duì)該接口進(jìn)行代理(不需要編寫接口實(shí)現(xiàn)),使用者可直接通過(guò)@Autowired注入。

          B: @RequestMapping表示在調(diào)用該方法時(shí)需要向/group/{groupId}發(fā)送GET請(qǐng)求。

          C: @PathVariableSpringMVC中對(duì)應(yīng)注解含義相同。

          Spring Cloud應(yīng)用在啟動(dòng)時(shí),F(xiàn)eign會(huì)掃描標(biāo)有@FeignClient注解的接口,生成代理,并注冊(cè)到Spring容器中。生成代理時(shí)Feign會(huì)為每個(gè)接口方法創(chuàng)建一個(gè)RequetTemplate對(duì)象,該對(duì)象封裝了HTTP請(qǐng)求需要的全部信息,請(qǐng)求參數(shù)名、請(qǐng)求方法等信息都是在這個(gè)過(guò)程中確定的,F(xiàn)eign的模板化就體現(xiàn)在這里。

          在本例中,我們將Feign與Eureka和Ribbon組合使用,@FeignClient(name = "ea")意為通知Feign在調(diào)用該接口方法時(shí)要向Eureka中查詢名為ea的服務(wù),從而得到服務(wù)URL。

          Feign的Encoder、Decoder和ErrorDecoder

          Feign將方法簽名中方法參數(shù)對(duì)象序列化為請(qǐng)求參數(shù)放到HTTP請(qǐng)求中的過(guò)程,是由編碼器(Encoder)完成的。同理,將HTTP響應(yīng)數(shù)據(jù)反序列化為java對(duì)象是由解碼器(Decoder)完成的。

          默認(rèn)情況下,F(xiàn)eign會(huì)將標(biāo)有@RequestParam注解的參數(shù)轉(zhuǎn)換成字符串添加到URL中,將沒(méi)有注解的參數(shù)通過(guò)Jackson轉(zhuǎn)換成json放到請(qǐng)求體中。注意,如果在@RequetMapping中的method將請(qǐng)求方式指定為POST,那么所有未標(biāo)注解的參數(shù)將會(huì)被忽略,例如:

          @FeignClient(name = "ea")  //  [A]
          public interface AdvertGroupRemoteService {

              @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) // [B]
              AdvertGroupVO findByGroupId(@PathVariable("groupId") Integer adGroupId) // [C]

              @RequestMapping(value = "/group/{groupId}", method = RequestMethod.PUT)
              void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName)

          此時(shí)因?yàn)槁暶鞯氖荊ET請(qǐng)求沒(méi)有請(qǐng)求體,所以obj參數(shù)就會(huì)被忽略。

          在Spring Cloud環(huán)境下,F(xiàn)eign的Encoder*只會(huì)用來(lái)編碼沒(méi)有添加注解的參數(shù)*。如果你自定義了Encoder, 那么只有在編碼obj參數(shù)時(shí)才會(huì)調(diào)用你的Encoder。對(duì)于Decoder, 默認(rèn)會(huì)委托給SpringMVC中的MappingJackson2HttpMessageConverter類進(jìn)行解碼。只有當(dāng)狀態(tài)碼不在200 ~ 300之間時(shí)ErrorDecoder才會(huì)被調(diào)用。ErrorDecoder的作用是可以根據(jù)HTTP響應(yīng)信息返回一個(gè)異常,該異常可以在調(diào)用Feign接口的地方被捕獲到。我們目前就通過(guò)ErrorDecoder來(lái)使Feign接口拋出業(yè)務(wù)異常以供調(diào)用者處理。

          Feign的HTTP Client

          Feign在默認(rèn)情況下使用的是JDK原生的URLConnection發(fā)送HTTP請(qǐng)求,沒(méi)有連接池,但是對(duì)每個(gè)地址會(huì)保持一個(gè)長(zhǎng)連接,即利用HTTP的persistence connection 。我們可以用Apache的HTTP Client替換Feign原始的http client, 從而獲取連接池、超時(shí)時(shí)間等與性能息息相關(guān)的控制能力。Spring Cloud從Brixtion.SR5版本開(kāi)始支持這種替換,首先在項(xiàng)目中聲明Apache HTTP Client和feign-httpclient依賴:

          @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET)
          void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);

          然后在application.properties中添加:

          feign.httpclient.enabled=true

          總結(jié)

          通過(guò)Feign, 我們能把HTTP遠(yuǎn)程調(diào)用對(duì)開(kāi)發(fā)者完全透明,得到與調(diào)用本地方法一致的編碼體驗(yàn)。這一點(diǎn)與阿里Dubbo中暴露遠(yuǎn)程服務(wù)的方式類似,區(qū)別在于Dubbo是基于私有二進(jìn)制協(xié)議,而Feign本質(zhì)上還是個(gè)HTTP客戶端。如果是在用Spring Cloud Netflix搭建微服務(wù),那么Feign無(wú)疑是最佳選擇。

          http://blog.csdn.net/tracker_w/article/category/6360121
          http://blog.csdn.net/neosmith/article/details/52449921

          posted @ 2016-09-16 18:13 paulwong 閱讀(2655) | 評(píng)論 (0)編輯 收藏

          微服務(wù)框架Spring Cloud

          2016

          posted @ 2016-09-11 20:49 paulwong 閱讀(953) | 評(píng)論 (0)編輯 收藏

          JHipster

          基于SPRING CLOUD的微服務(wù)框架
          http://jhipster.cn/

          posted @ 2016-09-11 16:40 paulwong 閱讀(604) | 評(píng)論 (0)編輯 收藏

          Spring Boot 性能優(yōu)化

          摘要
          Spring 框架給企業(yè)軟件開(kāi)發(fā)者提供了常見(jiàn)問(wèn)題的通用解決方案,包括那些在未來(lái)開(kāi)發(fā)中沒(méi)有意識(shí)到的問(wèn)題。但是,它構(gòu)建的 J2EE 項(xiàng)目變得越來(lái)越臃腫,逐漸被 Spring Boot 所替代。Spring Boot 讓我們創(chuàng)建和運(yùn)行項(xiàng)目變得更為迅速,現(xiàn)在已經(jīng)有越來(lái)越多的人使用它。我們已經(jīng)在幾個(gè)項(xiàng)目中使用了 Spring Boot ,今天我們就來(lái)一起討論一下如何改進(jìn) Spring Boot 應(yīng)用的性能。

          Spring 框架給企業(yè)軟件開(kāi)發(fā)者提供了常見(jiàn)問(wèn)題的通用解決方案,包括那些在未來(lái)開(kāi)發(fā)中沒(méi)有意識(shí)到的問(wèn)題。但是,它構(gòu)建的 J2EE 項(xiàng)目變得越來(lái)越臃腫,逐漸被 Spring Boot 所替代。Spring Boot 讓我們創(chuàng)建和運(yùn)行項(xiàng)目變得更為迅速,現(xiàn)在已經(jīng)有越來(lái)越多的人使用它。我們已經(jīng)在幾個(gè)項(xiàng)目中使用了 Spring Boot ,今天我們就來(lái)一起討論一下如何改進(jìn) Spring Boot 應(yīng)用的性能。

          首先,從之前我在開(kāi)發(fā)中遇到的一個(gè)問(wèn)題說(shuō)起。在一次查看項(xiàng)目運(yùn)行日志的時(shí)候,我偶然發(fā)現(xiàn)了一個(gè)問(wèn)題,日志里顯示這個(gè)項(xiàng)目總是加載 Velocity 模板引擎,但實(shí)際上這個(gè)項(xiàng)目是一個(gè)沒(méi)有 web 頁(yè)面的 REST Service 項(xiàng)目。于是我花了一點(diǎn)時(shí)間去尋找產(chǎn)生這個(gè)問(wèn)題的原因,以及如何改進(jìn) Spring Boot 應(yīng)用的性能。在查找了相關(guān)的資料后,我得出的結(jié)論如下:

          組件自動(dòng)掃描帶來(lái)的問(wèn)題

          默認(rèn)情況下,我們會(huì)使用 @SpringBootApplication 注解來(lái)自動(dòng)獲取的應(yīng)用的配置信息,但這樣也會(huì)給應(yīng)用帶來(lái)一些副作用。使用這個(gè)注解后,會(huì)觸發(fā)自動(dòng)配置( auto-configuration )和 組件掃描 ( component scanning),這跟使用 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 三個(gè)注解的作用是一樣的。這樣做給開(kāi)發(fā)帶來(lái)方便的同時(shí),也會(huì)有兩方面的影響:

          1、會(huì)導(dǎo)致項(xiàng)目啟動(dòng)時(shí)間變長(zhǎng)。當(dāng)啟動(dòng)一個(gè)大的應(yīng)用程序,或?qū)⒆龃罅康募蓽y(cè)試啟動(dòng)應(yīng)用程序時(shí),影響會(huì)特別明顯。

          2、會(huì)加載一些不需要的多余的實(shí)例(beans)。

          3、會(huì)增加 CPU 消耗。

          針對(duì)以上兩個(gè)情況,我們可以移除 @SpringBootApplication 和 @ComponentScan 兩個(gè)注解來(lái)禁用組件自動(dòng)掃描,然后在我們需要的 bean 上進(jìn)行顯式配置:

          // 移除 @SpringBootApplication and @ComponentScan, 用 @EnableAutoConfiguration 來(lái)替代
          @Configuration
          @EnableAutoConfiguration
          public class SampleWebUiApplication {

              
          // 

              
          // 用 @Bean 注解明確顯式配置,以便被 Spring 掃描到
              @Bean
              
          public MessageController messageController(MessageRepository messageRepository) {
                  
          return new MessageController(messageRepository);
              }

          如何避免組件自動(dòng)掃描帶來(lái)的問(wèn)題

          我們?cè)谏厦嫣岬剑?#64;SpringBootApplication 注解的作用跟 @EnableAutoConfiguration 注解的作用是相當(dāng)?shù)模蔷鸵馕吨材軒?lái)上述的三個(gè)問(wèn)題。要避免這些問(wèn)題,我們就要知道我們需要的組件列表是哪些,可以用 -Ddebug 的方式來(lái)幫助我們明確地定位:

          mvn spring-boot:run -Ddebug … ========================= AUTO-CONFIGURATION REPORT =========================   Positive matches: -----------------     DispatcherServletAutoConfiguration       - @ConditionalOnClass classes found: org.springframework.web.servlet.DispatcherServlet (OnClassCondition)       - found web application StandardServletEnvironment (OnWebApplicationCondition)  ... 

          接著拷貝 Positive matches 中列出的信息:

          DispatcherServletAutoConfiguration 
          EmbeddedServletContainerAutoConfiguration
          ErrorMvcAutoConfiguration
          HttpEncodingAutoConfiguration
          HttpMessageConvertersAutoConfiguration
          JacksonAutoConfiguration
          JmxAutoConfiguration
          MultipartAutoConfiguration
          ServerPropertiesAutoConfiguration
          PropertyPlaceholderAutoConfiguration
          ThymeleafAutoConfiguration
          WebMvcAutoConfiguration
          WebSocketAutoConfiguration

          然后來(lái)更新項(xiàng)目配置,顯式地引入這些組件,引入之后,再運(yùn)行一下應(yīng)用確保沒(méi)有錯(cuò)誤發(fā)生:

          @Configuration
          @Import({
                  DispatcherServletAutoConfiguration.
          class,
                  EmbeddedServletContainerAutoConfiguration.
          class,
                  ErrorMvcAutoConfiguration.
          class,
                  HttpEncodingAutoConfiguration.
          class,
                  HttpMessageConvertersAutoConfiguration.
          class,
                  JacksonAutoConfiguration.
          class,
                  JmxAutoConfiguration.
          class,
                  MultipartAutoConfiguration.
          class,
                  ServerPropertiesAutoConfiguration.
          class,
                  PropertyPlaceholderAutoConfiguration.
          class,
                  ThymeleafAutoConfiguration.
          class,
                  WebMvcAutoConfiguration.
          class,
                  WebSocketAutoConfiguration.
          class,
          })
          public class SampleWebUiApplication {}


          在上面的代碼中,我們可以刪掉我們不需要的組件信息,來(lái)提高應(yīng)用的性能,比如在我的項(xiàng)目中,不需要 JMX 和 WebSocket 功能,我就刪掉了它們。刪掉之后,再次運(yùn)行項(xiàng)目,確保一切正常。

          將Servlet容器變成Undertow

          默認(rèn)情況下,Spring Boot 使用 Tomcat 來(lái)作為內(nèi)嵌的 Servlet 容器。我們可以啟動(dòng)項(xiàng)目,然后用 VisualVM 或者 JConsole 來(lái)查看應(yīng)用所占的內(nèi)存情況:

          Spring Boot 性能優(yōu)化

          以上是我使用 Spring Boot 的默認(rèn)方式啟動(dòng)應(yīng)用后,用 VisualVM 監(jiān)控到的內(nèi)存的占用情況:堆內(nèi)存占用 110M,16 個(gè)線程被開(kāi)啟。

          可以將 Web 服務(wù)器切換到 Undertow 來(lái)提高應(yīng)用性能。Undertow 是一個(gè)采用 Java 開(kāi)發(fā)的靈活的高性能 Web 服務(wù)器,提供包括阻塞和基于 NIO 的非堵塞機(jī)制。Undertow 是紅帽公司的開(kāi)源產(chǎn)品,是 Wildfly 默認(rèn)的 Web 服務(wù)器。首先,從依賴信息里移除 Tomcat 配置:

          <exclusions>
                  
          <exclusion>
                          
          <groupId>org.springframework.boot</groupId>
                          
          <artifactId>spring-boot-starter-tomcat</artifactId>
                  
          </exclusion>
          </exclusions>


          然后添加 Undertow:

          <dependency>
                  
          <groupId>org.springframework.boot</groupId>
                  
          <artifactId>spring-boot-starter-undertow</artifactId>
          </dependency>


          啟動(dòng)項(xiàng)目后,用 VisualVM 監(jiān)控到的信息顯示:堆內(nèi)存占用 90M,13個(gè)線程被開(kāi)啟。

          Spring Boot 性能優(yōu)化

          總結(jié)

          這些都是我們?cè)陧?xiàng)目開(kāi)發(fā)中使用到的一些優(yōu)化 Spring Boot 應(yīng)用的小技巧,對(duì)于大的應(yīng)用性能的提高還是很明顯的。大家可以嘗試一下,然后告訴我們你的測(cè)試結(jié)果。

          最后,附上代碼,大家可以去這里下載:spring-boot-performance

          文中大部分內(nèi)容參考英國(guó)一個(gè)架構(gòu)師的博客 和 DZone 近期發(fā)布的文章,在此感謝兩位大牛。參考文章及鏈接:

          (1)Spring Boot 性能優(yōu)化:Spring Boot Performance

          (2)Spring Boot 內(nèi)存優(yōu)化:Spring Boot Memory Performance

          (3)https://www.techempower.com/benchmarks/

          (4)Spring 應(yīng)用程序優(yōu)化:Optimizing Spring Framework for App Engine Applications

          posted @ 2016-09-11 16:37 paulwong 閱讀(839) | 評(píng)論 (0)編輯 收藏

          spring cloud項(xiàng)目讀取配置管理

          摘要
          spring cloud config server配置好了數(shù)據(jù)庫(kù)連接信息,這個(gè)項(xiàng)目讀取config,獲取連接信息。這里以mybtis作為列子。從服務(wù)器讀取jdbc信息后,運(yùn)行mybatis程序。

          確認(rèn)服務(wù)是否成功
          http://localhost:8888/demo-config/test
          {"name":"demo-config","profiles":["test"],"label":"master","version":"02d28ad4925aa9bd1bf8a48d2edbf04ce61aa45a","propertySources":[{"name":"https://git.oschina.net/penghaozhong/demo.git/demo-config-repo/demo-config-test.properties","source":{"jdbc.url":"jdbc:mysql://localhost:3306/demo?characterEncoding=UTF-8","jdbc.username":"root","jdbc.driver":"com.mysql.jdbc.Driver","jdbc.password":"xxxxxx","jdbc.type":"mysql"}}]}

              2. 配置讀取配置文件

            



          在bootstrap.properties中添加讀取配置管理的地址。

          3. 讀取配置文件屬性,這里采用@ConfigurationProperties
          /**
           * 讀取數(shù)據(jù)庫(kù)配置文件
           * 
          @author penghaozhong
           *
           
          */
          @ConfigurationProperties(prefix = DataSourceProperties.PREFIX, ignoreUnknownFields = false)
          public  class  DataSourceProperties {
              
                  public DataSourceProperties() {
                  super();
              }
                  //對(duì)應(yīng)配置文件里的配置鍵
                  public final static String PREFIX="jdbc";    
                  
                  private String type; 
                  private String driver; 
                  private String url; 
                  private String username; 
                  private String password;
                  
                  public String getType() {
                      return type;
                  }
                  public void setType(String type) {
                      this.type = type;
                  }
                  public String getDriver() {
                      return driver;
                  }
                  public void setDriver(String driver) {
                      this.driver = driver;
                  }
                  public String getUrl() {
                      return url;
                  }
                  public void setUrl(String url) {
                      this.url = url;
                  }
                  public String getUsername() {
                      return username;
                  }
                  public void setUsername(String username) {
                      this.username = username;
                  }
                  public String getPassword() {
                      return password;
                  }
                  public void setPassword(String password) {
                      this.password = password;
                  }

          }


          4. 配置mybatis
          @Configuration
          @MapperScan("com.phz.test.spring.cloud.demo")
          @EnableConfigurationProperties(DataSourceProperties.class)
          @EnableTransactionManagement
          public class MybatisDataSource {

              // mybaits mapper xml搜索路徑
              private final static String MAPPERLOCATIONS = "classpath:/mappings/**/*.xml";
              private final static String CONFIGLOCATION = "classpath:/mybatis-config.xml";

              @Autowired
              private  DataSourceProperties dataSourceProperties;
              private DruidDataSource datasource = null;

              @Bean(destroyMethod = "close")
              public  DataSource dataSource(){
                  datasource = new DruidDataSource();  
                  datasource.setUrl(dataSourceProperties.getUrl());
                  datasource.setDbType(dataSourceProperties.getType());
                  datasource.setDriverClassName(dataSourceProperties.getDriver());
                  datasource.setUsername(dataSourceProperties.getUsername());
                  datasource.setPassword(dataSourceProperties.getPassword());
                  return datasource;
              }

              @PreDestroy
              public void close() {
                  if(datasource != null){
                      datasource.close();
                  }
              }

              @Bean
              public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
                  SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
                  sqlSessionFactoryBean.setDataSource(dataSource());
                  PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
                  sqlSessionFactoryBean.setMapperLocations(resolver.getResources(MAPPERLOCATIONS));
                  sqlSessionFactoryBean.setConfigLocation(resolver.getResource(CONFIGLOCATION));
                  sqlSessionFactoryBean.setTypeAliasesPackage("com.phz.test.spring.cloud.demo.entity");
                  return sqlSessionFactoryBean.getObject();
              }

              @Bean
              public PlatformTransactionManager transactionManager() {
                  return new DataSourceTransactionManager(dataSource());
              }

          }











          posted @ 2016-09-11 16:26 paulwong 閱讀(2005) | 評(píng)論 (0)編輯 收藏

          僅列出標(biāo)題
          共115頁(yè): First 上一頁(yè) 28 29 30 31 32 33 34 35 36 下一頁(yè) Last 
          主站蜘蛛池模板: 衡南县| 白沙| 大新县| 紫云| 凭祥市| 庆阳市| 溆浦县| 商南县| 湖州市| 龙泉市| 永丰县| 荔浦县| 平武县| 噶尔县| 达孜县| 青铜峡市| 德州市| 吉安市| 迁西县| 芒康县| 永兴县| 通州市| 化德县| 云南省| 兴城市| 隆德县| 怀集县| 缙云县| 筠连县| 吉安县| 福贡县| 四平市| 通城县| 武邑县| 社旗县| 眉山市| 忻城县| 衡阳县| 庆云县| 林口县| 枣庄市|