Hazelcast是Hazelcast公司開源的一款分佈式內存數據庫產品,提供彈性可擴展、高性能的分佈式內存計算。並經過提供諸如Map,Queue,ExecutorService,Lock和JCache等Java的許多開發人員友好的分佈式實現。html
Hazelcast特性前端
Hazelcast部署拓撲 在Hazelcast官方提供兩種方式部署集羣(圖片均來自官方文檔): java
如需聚焦異步或高性能大批量任務的緩存服務,嵌入式方式是相對有優點的,最明顯嵌入式方式訪問數據延遲性低。 git
獨立建立Hazelcast集羣,統一管理,全部的應用程序若是須要訪問緩存,可經過Hazelcast客戶端(有java .NET C++的實現)或Memcache客戶端或簡單的REST客戶端訪問。後續demo示例以嵌入式爲例。github
Hazelcast數據分區 在Hazelcast分佈式環境中,默認狀況下,Hazelcast有271個分區。 當啓動第一個成員的時候,成員1在集羣中的分區以下圖: web
當在集羣中新添加一個節點2時,分區圖以下: redis
在圖示中,黑色分區是主分區,藍色分區是副本分區(備份)。第一個成員具備135個主分區(黑色),而且每一個分區都備份在第二個成員(藍色)中。同時,第一個成員還具備第二個成員的主分區的副本分區。spring
隨着成員的增多,Hazelcast將一些主要和副本分區逐個移動到新成員,使全部成員相等和冗餘。只有最小量的分區將被移動到擴展Hazelcast。如下是具備四個成員的Hazelcast集羣中的分區圖示以下: sql
Hazelcast在羣集成員之間平均分配分區。Hazelcast建立分區的備份,並將其分配給成員之間進行冗餘。docker
上述插圖中的分區是爲了方便描述。一般,Hazelcast分區不會按照順序分配(如這些圖所示),而是隨機分佈。Hazelcast在成員間平均分配了分區和備份。
Hazelcast優點
Hazelcast適用場景
下面咱們來使用Spring Boot集成Hazelcast實現分佈式集羣服務看看
首先新建一個Spring Boot的gradle項目,引入Hazelcast相關jar包:
dependencies { compile 'com.hazelcast:hazelcast' compile 'org.springframework.boot:spring-boot-starter-web' }
當Hazelcast包在classpath上,Spring Boot將經過下面兩種方式之一爲咱們建立一個HazelcastInstance實例:
方式一,經過配置屬性指定的Hazelcast.xml文件建立: spring.hazelcast.config = classpath:hazelcast.xml
該方式須要編寫一個hazelcast.xml文件,經過xml文件描述Hazelcast集羣
方式二,經過提供一個com.hazelcast.config.Config javabean到Spring容器中(下面全部demo是基於java config方式)
@Bean public Config hazelCastConfig() { //若是有集羣管理中心,能夠配置 ManagementCenterConfig centerConfig = new ManagementCenterConfig(); centerConfig.setUrl("http://127.0.0.1:8200/mancenter"); centerConfig.setEnabled(true); return new Config() .setInstanceName("hazelcast-instance") .setManagementCenterConfig(centerConfig) .addMapConfig( new MapConfig() .setName("instruments") .setMaxSizeConfig(new MaxSizeConfig(200, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE)) .setEvictionPolicy(EvictionPolicy.LRU) .setTimeToLiveSeconds(20000)); }
上面代碼經過提供Config的bean時候,主要作了以下幾個事:
完整代碼:
@SpringBootApplication public class StartUp { private Logger LOGGER = LoggerFactory.getLogger(StartUp.class); public static void main(String[] args) { SpringApplication.run(StartUp.class, args); } @Bean public Config hazelCastConfig() { //若是有集羣管理中心,能夠配置 ManagementCenterConfig centerConfig = new ManagementCenterConfig(); centerConfig.setUrl("http://127.0.0.1:8200/mancenter"); centerConfig.setEnabled(true); return new Config() .setInstanceName("hazelcast-instance") .setManagementCenterConfig(centerConfig) .addMapConfig( new MapConfig() .setName("instruments") .setMaxSizeConfig(new MaxSizeConfig(200, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE)) .setEvictionPolicy(EvictionPolicy.LRU) .setTimeToLiveSeconds(20000)); } }
下面咱們經過修改server.port分別啓動端口爲8080和8081的成員服務 當啓動完8080成員的時候,能夠在8080控制檯看到以下日誌:
Members [1] { Member [172.17.42.1]:5701 - 0d39dd66-d4fb-4af4-8ddb-e9f4c7bbe5a1 this }
因咱們使用的是組播傳播模式,5701爲節點在組播網絡中分配的端口 當啓動完8081成員的時候,能夠在8081控制檯看到以下日誌:
Members [2] { Member [172.17.42.1]:5701 - 0d39dd66-d4fb-4af4-8ddb-e9f4c7bbe5a1 Member [172.17.42.1]:5702 - a46ceeb4-e079-43a5-9c9d-c74265211bf7 this }
回到8080控制檯,發現多了一行日誌:
Members [2] { Member [172.17.42.1]:5701 - 0d39dd66-d4fb-4af4-8ddb-e9f4c7bbe5a1 this Member [172.17.42.1]:5702 - a46ceeb4-e079-43a5-9c9d-c74265211bf7 }
發現8081成員也加入進來了。兩個控制檯都能看到成員列表。集羣就已經搭建成功。
爲了驗證結果,上面咱們在集羣中已經建立了一個名爲instruments的分佈式map數據結構,如今咱們經過寫個接口證實:
@GetMapping("/greet") public Object greet() { Object value = Hazelcast.getHazelcastInstanceByName("hazelcast-instance").getMap("instruments").get("hello"); if (Objects.isNull(value)) { Hazelcast.getHazelcastInstanceByName("hazelcast-instance").getMap("instruments").put("hello", "world!"); } LOGGER.info("從分佈式緩存獲取到 key=hello,value={}", value); return value; }
首先經過訪問8080服務的/greet,第一次訪問instruments中是沒有key爲hello的鍵值對,會往裏面塞入{"helo":"world!"},而後訪問8081服務的/greet,這個時候應該是能取得改鍵值對的。
完整代碼:
@RestController @SpringBootApplication public class StartUp { private Logger LOGGER = LoggerFactory.getLogger(StartUp.class); public static void main(String[] args) { SpringApplication.run(StartUp.class, args); } @Bean public Config hazelCastConfig() { //若是有集羣管理中心,能夠配置 ManagementCenterConfig centerConfig = new ManagementCenterConfig(); centerConfig.setUrl("http://127.0.0.1:8200/mancenter"); centerConfig.setEnabled(true); return new Config() .setInstanceName("hazelcast-instance") .setManagementCenterConfig(centerConfig) .addMapConfig( new MapConfig() .setName("instruments") .setMaxSizeConfig(new MaxSizeConfig(200, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE)) .setEvictionPolicy(EvictionPolicy.LRU) .setTimeToLiveSeconds(20000)); } @GetMapping("/greet") public Object greet() { Object value = Hazelcast.getHazelcastInstanceByName("hazelcast-instance").getMap("instruments").get("hello"); if (Objects.isNull(value)) { Hazelcast.getHazelcastInstanceByName("hazelcast-instance").getMap("instruments").put("hello", "world!"); } LOGGER.info("從分佈式緩存獲取到 key=hello,value={}", value); return value; } }
重啓8080和8081服務 經過瀏覽器請求http://localhost:8080/greet 查看8080控制檯日誌: 2017-10-23 13:52:27.865 INFO 13848 --- [nio-8080-exec-1] com.hazelcast.StartUp: 從分佈式緩存獲取到 key=hello,value=nul
經過瀏覽器請求http://localhost:8081/greet 查看8081控制檯日誌: 2017-10-23 13:52:40.116 INFO 13860 --- [nio-8081-exec-2] com.hazelcast.StartUp: 從分佈式緩存獲取到 key=hello,value=world
Spring Boot爲Hazelcast提供了明確的緩存支持。若是啓用緩存, HazelcastInstance則會自動包含在CacheManager實現中。因此徹底能夠支持Spring Cache。
以往咱們用Spring Cache都是基於Redis作存儲後端,如今咱們使用Hazelcast來嘗試一下 首先在啓動類上開啓緩存 @EnableCaching
創建個service類,demo爲了方便,寫在一塊兒 完整代碼:
@EnableCaching @RestController @SpringBootApplication public class StartUp { private Logger LOGGER = LoggerFactory.getLogger(StartUp.class); public static void main(String[] args) { SpringApplication.run(StartUp.class, args); } @Bean public Config hazelCastConfig() { //若是有集羣管理中心,能夠配置 ManagementCenterConfig centerConfig = new ManagementCenterConfig(); centerConfig.setUrl("http://127.0.0.1:8200/mancenter"); centerConfig.setEnabled(true); return new Config() .setInstanceName("hazelcast-instance") .setManagementCenterConfig(centerConfig) .addMapConfig( new MapConfig() .setName("instruments") .setMaxSizeConfig(new MaxSizeConfig(200, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE)) .setEvictionPolicy(EvictionPolicy.LRU) .setTimeToLiveSeconds(20000)); } @GetMapping("/greet") public Object greet() { Object value = Hazelcast.getHazelcastInstanceByName("hazelcast-instance").getMap("instruments").get("hello"); if (Objects.isNull(value)) { Hazelcast.getHazelcastInstanceByName("hazelcast-instance").getMap("instruments").put("hello", "world!"); } LOGGER.info("從分佈式緩存獲取到 key=hello,value={}", value); return value; } @Autowired private DemoService demoService; @GetMapping("/cache") public Object cache() { String value = demoService.greet("hello"); LOGGER.info("從分佈式緩存獲取到 key=hello,value={}", value); return value; } } @Service @CacheConfig(cacheNames = "instruments") class DemoService { private Logger LOGGER = LoggerFactory.getLogger(DemoService.class); @Cacheable(key = "#key") public String greet(String key) { LOGGER.info("緩存內沒有取到key={}", key); return "world!"; } }
連續訪問兩次8080服務的/cache接口 第一次控制檯輸出日誌:
2017-10-23 14:10:02.201 INFO 13069 --- [nio-8081-exec-1] com.hazelcast.DemoService: 緩存內沒有取到key=hello 2017-10-23 14:10:02.202 INFO 13069 --- [nio-8081-exec-1] com.hazelcast.StartUp: 從分佈式緩存獲取到 key=hello,value=world!
第二次控制檯輸出日誌: 2017-10-23 14:11:51.966 INFO 13069 --- [nio-8081-exec-3] com.hazelcast.StartUp: 從分佈式緩存獲取到 key=hello,value=world!
第二次比第一次相比少了執行service方法體內容,證實第二次是經過了緩存獲取。
Spring Session已經支持使用Hazelcast做爲會話緩存後端,首先引入Spring Session jar包
dependencies { compile 'com.hazelcast:hazelcast' compile 'org.springframework.boot:spring-boot-starter-web' compile 'org.springframework.session:spring-session' }
要啓用Hazelcast做爲集羣會話緩存後端,有兩種方式 第一種Spring Boot配置文件裏面配置spring.session.*屬性: spring.session.store-type=hazelcast
第二種使用java註解開啓: @EnableHazelcastHttpSession
這裏選擇第二種方式,要證實集羣會話共享,咱們定一個簡單接口打印一下sessionId,經過同一瀏覽器訪問8080和8081服務的該接口,看看不一樣服務請求的時候sessionId是否一致,完整代碼以下:
@EnableCaching @RestController @EnableHazelcastHttpSession @SpringBootApplication public class StartUp { private Logger LOGGER = LoggerFactory.getLogger(StartUp.class); public static void main(String[] args) { SpringApplication.run(StartUp.class, args); } @Bean public Config hazelCastConfig() { //若是有集羣管理中心,能夠配置 ManagementCenterConfig centerConfig = new ManagementCenterConfig(); centerConfig.setUrl("http://127.0.0.1:8200/mancenter"); centerConfig.setEnabled(true); return new Config() .setInstanceName("hazelcast-instance") .setManagementCenterConfig(centerConfig) .addMapConfig( new MapConfig() .setName("instruments") .setMaxSizeConfig(new MaxSizeConfig(200, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE)) .setEvictionPolicy(EvictionPolicy.LRU) .setTimeToLiveSeconds(20000)); } @GetMapping("/greet") public Object greet() { Object value = Hazelcast.getHazelcastInstanceByName("hazelcast-instance").getMap("instruments").get("hello"); if (Objects.isNull(value)) { Hazelcast.getHazelcastInstanceByName("hazelcast-instance").getMap("instruments").put("hello", "world!"); } LOGGER.info("從分佈式緩存獲取到 key=hello,value={}", value); return value; } @Autowired private DemoService demoService; @GetMapping("/cache") public Object cache() { String value = demoService.greet("hello"); LOGGER.info("從分佈式緩存獲取到 key=hello,value={}", value); return value; } @GetMapping("/session") public Object session(HttpSession session) { String sessionId = session.getId(); LOGGER.info("當前請求的sessionId={}", sessionId); return sessionId; } } @Service @CacheConfig(cacheNames = "instruments") class DemoService { private Logger LOGGER = LoggerFactory.getLogger(DemoService.class); @Cacheable(key = "#key") public String greet(String key) { LOGGER.info("緩存內沒有取到key={}", key); return "world!"; } }
訪問8080服務/session接口,控制檯日誌以下: 2017-10-23 14:28:41.991 INFO 14140 --- [nio-8080-exec-2] com.hazelcast.StartUp: 當前請求的sessionId=e75ffc53-90bc-41cd-8de9-e9ddb9c2a5ee
訪問8081服務/session接口,控制檯日誌以下: 2017-10-23 14:28:45.615 INFO 14152 --- [nio-8081-exec-1] com.hazelcast.StartUp: 當前請求的sessionId=e75ffc53-90bc-41cd-8de9-e9ddb9c2a5ee
集羣會話共享生效。
在上面的demo中,在建立Config的時候,設置了一個ManagementCenterConfig配置,該配置是指向一個Hazelcast集羣管理平臺,好比demo中表示在本地啓動了一個管理平臺服務。該功能也是相對其餘NoSql服務的一個優點。
要部署ManagementCenter管理平臺有多種方式 好比經過https://download.hazelcast.com/management-center/management-center-3.8.3.zip地址下載,解壓後啓動; sh ./startManCenter.sh 8200 /mancenter
若是有docker環境,直接能夠docker部署: docker run -ti -p 8200:8080 hazelcast/management-center:latest
部署成功後,訪問http://ip:8200/mancenter,首次訪問會讓你配置個用戶名密碼,進入後 :
在左側菜單欄,能看到現有支持的分佈式數據格式,好比Maps下面名爲instruments的是咱們前面demo本身建立的,名爲spring:session:sessions是咱們用了Hazelcast作集羣會話同步的時候Spring爲咱們建立的。
中間區域能看到全部節點成員的系統相關實時使用率,隨便點擊一個節點進去,能看到當前節點的系統實時使用率:
紅圈裏面的便是上面提到的節點數據分區數,經過左側菜單欄的數據結構進去,能看到當前對應的數據結構的詳細信息和實時吞吐量:
更多內容請參考下方參考資料。 示例代碼能夠經過https://github.com/zggg/hazelcast-in-spring-boot下載。
參考資料
——————————————————分割線——————————————————
我是黑少,直男一枚,微服務硬核玩家,喜歡分享、愛交友人、崇尚「實踐出真知」的理念,以折騰鼓搗代碼爲樂
個人微信:weiweiweiblack (備註:開源中國)
微信公號:黑少微服務,專一微服務技術分享,非技術不八卦!