Hazelcast 介绍
Hazelcast ( www.hazelcast.com)是一种内存数据网格 in-memory data grid,提供Java程序员关键任务交易和万亿级内存应用。
Hazelcast的集群属于“无主节点”,这意味着它不是一个客户端 - 服务器系统。有一个集群的领导者,默认是最老的成员,管理数据是如何在系统间分布,但是,如果该节点当机,那么是下面一个旧的节点接管。
你所用的数据结构Maps List和队列都是保存在内存中。如果集群中的一个节点死亡,数据不会丢失,但如果多个节点同时当机,那么你就麻烦了。
不仅仅作为数据缓存使用,目的是内存数据网格,定位更高,功能更丰富的。
优点:
数据结构更多 ,功能更丰富。
集群更方便,管理界面友好。
spring整合更方便。
分为开源版和企业版,企业版气功技术支持。
特点:
完整的IMDG特性(内存数据网格)。
实现了java 的缓存规范。
基于Apache 2 Licenese(开源的)。
只是一个小的jar包(依赖非常少,轻量)。
同时支持嵌入式和 分布式服务端开发。
支持多种语言的客户端。
功能:
内存数据网格,缓存,微服务,session集群,消息系统,内存中的NoSql,应用的伸缩。
由于面向人群多,覆盖面广,功能丰富,所以还是建议把官方文档通读一遍,这里就挑关于java缓存方面作为记录:
1.数据分区:
默认把一块个节点的内存分为271个小格,
当有1个节点的时候 ,所有小分区都用来存储,如果宕机则全体GG,没有备份;
当有两个节点的时候,每个节点有一半的用来保存数据,另一半备份数据, 所有一个宕机了另一个还有备份,数据比一个节点安全。
节点的增多,内存容量大,保存的数据就更多了。
2.分布式数据结构:
在Hazelcast里所有的数据结构都是分布式的,把多个节点当作一个节点使用,最常用的是map,其它的有Queue(队列),MultiMap,Set,List,跟java里的是一样的,因为实现了java里的泛型接口。
支持发布订阅模式的Topic,支持分布式锁,支持集成支持多线程编程环境,分布式事件具体参阅文档。
安装
这里示例嵌入式安装:
1. 引入依赖:
这里使用的gradle方式引入
hazelcast:[
'com.hazelcast:hazelcast:3.8.6',
'com.hazelcast:hazelcast-spring:3.8.6',
]复制代码
2.需要去官网下载管理中心工具 : https://hazelcast.org/download/
3.等依赖引入后,在hazelcast 的jar包目录中找到示例.xml文件复制到 项目的resources
目录中,改名hazelcast.xml
该xml文件的具体内容总览:
xml version="1.0" encoding="UTF-8"?>
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.8.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<group>
<name>devname>
<password>dev-passpassword>
group>
<management-center enabled="true">http://localhost:8080/mancentermanagement-center>
<network>
<port auto-increment="true" port-count="100">5701port>
<outbound-ports>
<ports>0ports>
outbound-ports>
<join>
<multicast enabled="true">
<multicast-group>224.2.2.3multicast-group>
<multicast-port>54327multicast-port>
multicast>
<tcp-ip enabled="false">
<interface>127.0.0.1interface>
<member-list>
<member>127.0.0.1member>
member-list>
tcp-ip>
<aws enabled="false">
<access-key>my-access-keyaccess-key>
<secret-key>my-secret-keysecret-key>
<region>us-west-1region>
<host-header>ec2.amazonaws.comhost-header>
<security-group-name>hazelcast-sgsecurity-group-name>
<tag-key>typetag-key>
<tag-value>hz-nodestag-value>
aws>
<discovery-strategies>
discovery-strategies>
join>
<interfaces enabled="false">
<interface>10.10.1.*interface>
interfaces>
<ssl enabled="false"/>
<socket-interceptor enabled="false"/>
<symmetric-encryption enabled="false">
<algorithm>PBEWithMD5AndDESalgorithm>
<salt>thesaltsalt>
<password>thepasspassword>
<iteration-count>19iteration-count>
symmetric-encryption>
network>
<partition-group enabled="false"/>
<executor-service name="default">
<pool-size>16pool-size>
<queue-capacity>0queue-capacity>
executor-service>
<queue name="default">
<max-size>0max-size>
<backup-count>1backup-count>
<async-backup-count>0async-backup-count>
<empty-queue-ttl>-1empty-queue-ttl>
queue>
<map name="default">
<in-memory-format>BINARYin-memory-format>
<backup-count>1backup-count>
<async-backup-count>0async-backup-count>
<time-to-live-seconds>0time-to-live-seconds>
<max-idle-seconds>0max-idle-seconds>
<eviction-policy>NONEeviction-policy>
<max-size policy="PER_NODE">0max-size>
<eviction-percentage>25eviction-percentage>
<min-eviction-check-millis>100min-eviction-check-millis>
<merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicymerge-policy>
<cache-deserialized-values>INDEX-ONLYcache-deserialized-values>
map>
<multimap name="default">
<backup-count>1backup-count>
<value-collection-type>SETvalue-collection-type>
multimap>
<list name="default">
<backup-count>1backup-count>
list>
<set name="default">
<backup-count>1backup-count>
set>
<jobtracker name="default">
<max-thread-size>0max-thread-size>
<queue-size>0queue-size>
<retry-count>0retry-count>
<chunk-size>1000chunk-size>
<communicate-stats>truecommunicate-stats>
<topology-changed-strategy>CANCEL_RUNNING_OPERATIONtopology-changed-strategy>
jobtracker>
<semaphore name="default">
<initial-permits>0initial-permits>
<backup-count>1backup-count>
<async-backup-count>0async-backup-count>
semaphore>
<reliable-topic name="default">
<read-batch-size>10read-batch-size>
<topic-overload-policy>BLOCKtopic-overload-policy>
<statistics-enabled>truestatistics-enabled>
reliable-topic>
<ringbuffer name="default">
<capacity>10000capacity>
<backup-count>1backup-count>
<async-backup-count>0async-backup-count>
<time-to-live-seconds>0time-to-live-seconds>
<in-memory-format>BINARYin-memory-format>
ringbuffer>
<serialization>
<portable-version>0portable-version>
serialization>
<services enable-defaults="true"/>
<lite-member enabled="false"/>
hazelcast>
复制代码
可以修改的配置:
<group>
<name>devname>
<password>dev-passpassword>
group>复制代码
<management-center enabled="true">http://localhost:8080/mancentermanagement-center>复制代码
<port auto-increment="true" port-count="100">5701port>复制代码
<join>
<multicast enabled="true">
<multicast-group>224.2.2.3multicast-group>
<multicast-port>54327multicast-port>
multicast>
<tcp-ip enabled="false">
<interface>127.0.0.1interface>
<member-list>
<member>127.0.0.1member>
member-list>
tcp-ip>
<aws enabled="false">
<access-key>my-access-keyaccess-key>
<secret-key>my-secret-keysecret-key>
<region>us-west-1region>
<host-header>ec2.amazonaws.comhost-header>
<security-group-name>hazelcast-sgsecurity-group-name>
<tag-key>typetag-key>
<tag-value>hz-nodestag-value>
aws>
<discovery-strategies>
discovery-strategies>
join>复制代码
<interfaces enabled="false">
<interface>10.10.1.*interface>
interfaces>复制代码
使用:
这些配置基本不用改可以直接使用,唯一需要改的就是管理中心配置改为true就行了。
也可以根据实际情况单独配置 ,这里就直接启动了 。
在springBoot成功启动后,启动日志中有如下信息就表示启动成功了:
启动成功后就可以进入管理中心查看状态,需要下载官方管理工具包:hazelcast.org/download/
管理端目录结构以及进入管理端的方法如下图:
这里进入的是默认端口路径的管理端,直接双击运行也行
还可以以其它方式进入具体的哪一个管理端 ,比如指定 端口 路径 ,classpath:
第一次进入管理端的时候需要注册管理员用户密码,直接输入记住就行了。
运行完了就会看到管理端的页面:
至此,SpringBoot项目部署Hazelcast就完成了。
接下来做一个简单spring缓存的使用示例。
分为3步:
1.引入spring依赖, 部署的时候已经引入了
2.配置
3.使用 @Cacheable(读取缓存) @CachePut(更新缓存) @CacheEvict(清空缓存)注解
简单spring缓存使用示例:
1.首先要在springboot启动类上面加上开启缓存功能的注解:
2.在配置文件里指定spring的缓存类型,这里用gradle的 .yml文件作为示例:
3.在需要缓存的service方法上添加@Cacheable
注解即可为该方法开启缓存功能:
注:这里可能会报出一个序列化错误的异常:
原因是数据pojo类存入缓存的时候会序列化,解决办法是需要在pojo类上面实现接口Serializable即可:
再次成功启动即可。
多次发送请求,观察控制台,只有第一次请求有请求日志就说明调用了缓存,缓存功能成功实现, 缓存成功后会在管理端有数据记录:
点击 Map Browser 查看缓存数据:
注:因为spring默认是使用MAP作为缓存格式, 所以当你以ID为参数查询的时候, 就会默认以id为缓存的键。
接下来使用@CachePut(更新缓存) @CacheEvict(清空缓存):
要使用缓存功能多了的话,可以不用在service层里的代码加缓存了, 正确的方式专门做一个类来专门的做缓存功能的类,然后service调用就行了:
缓存功能类:
package com.imooc.seller.service;
import com.imooc.api.ProductRpc;
import com.imooc.entity.Product;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
@Component
public class ProductCache {
static final String CACHE_NAME = "imooc_product";
Logger LOG = LoggerFactory.getLogger(ProductCache.class);
@Autowired
private ProductRpc productRpc;
/**
* 读取缓存
* @param id
* @return
*/
@Cacheable(cacheNames = CACHE_NAME)
public Product readCache(String id){
LOG.info("rpc查询单个产品,请求:{}",id);
Product result = productRpc.findOne(id);
LOG.info("rpc查询单个产品,结果“{}",result);
return result;
}
/**
* 更新缓存
* @param product
* @return
*/
@CachePut(cacheNames = CACHE_NAME, key = "#product.id")
public Product putCache(Product product){
return product;
}
/**
* 清除缓存
* @param id
*/
@CacheEvict(cacheNames = CACHE_NAME)
public void removeCache(String id){
}
}复制代码
然后在service直接注入调用就行了:
@Autowired
private ProductCache productCache;
/**
* 查询单个产品
* @param id
* @return
*/
public Product findOne(String id){
//存入缓存
Product product = productCache.readCache(id);
//判断查询结果如果为空,就删除缓存,这样就只存了不为空的缓存
if(product==null){
productCache.removeCache(id);
}
return product;
}复制代码
这里就实现了查询单个产品的缓存功能了。
实现查询全部产品的缓存:
逻辑分析:在项目初始化的时候就应该把所有数据都放进缓存中。
缓存功能的方法在自己的类里面是没法生效的,所以缓存初始化方法只能放在service中,要让该service实现监听接口,监听在容器初始化完成后触发一个事件,在这个事件里完成缓存数据的方法:
首先 在service实现ApplicationListener(程序监听器)接口泛型ContextRefreshedEvent(容器加载完成事件)类,实现 onApplicationEvent 方法,在该方法里进行缓存操作:
实现接口:
重写onApplicationEvent方法进行缓存操作:
@Autowired
private ProductCache productCache;
/**
* 容器初始化完成后触发缓存全部商品方法
* @param event
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
List products = findAll();
products.forEach(product -> {
productCache.putCache(product);
});
}
/**
* 查询 全部产品 ,
* @return
*/
public List findAll() {
//调用缓存
return productCache.readAllCache();
}复制代码
缓存功能ProductCache类:
package com.imooc.seller.service;
import com.hazelcast.core.HazelcastInstance;
import com.imooc.api.ProductRpc;
import com.imooc.api.domain.ProductRpcReq;
import com.imooc.entity.Product;
import com.imooc.entity.enums.ProductStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class ProductCache {
static final String CACHE_NAME = "imooc_product";
Logger LOG = LoggerFactory.getLogger(ProductCache.class);
@Autowired
private ProductRpc productRpc;
@Autowired
private HazelcastInstance hazelcastInstance;
public List readAllCache() {
Map map = hazelcastInstance.getMap(CACHE_NAME);
List products = null;
if(map.size()>0){
products = new ArrayList<>();
products.addAll(map.values());
}else {
products = findAll();
}
return products;
}
/**
* 查询 全部产品
* @return
*/
public List findAll() {
ProductRpcReq req = new ProductRpcReq();
List status = new ArrayList<>();
status.add(ProductStatus.IN_SELL.name());
req.setStatusList(status);
LOG.info("rpc查询全部产品,请求:{}",req);
List result = productRpc.query(req);
LOG.info("rpc查询 全部产品,结果:{}",result);
return result;
}
/**
* 读取缓存
* @param id
* @return
*/
@Cacheable(cacheNames = CACHE_NAME)
public Product readCache(String id){
LOG.info("rpc查询单个产品,请求:{}",id);
Product result = productRpc.findOne(id);
LOG.info("rpc查询单个产品,结果“{}",result);
return result;
}
/**
* 更新缓存
* @param product
* @return
*/
@CachePut(cacheNames = CACHE_NAME, key = "#product.id")
public Product putCache(Product product){
return product;
}
/**
* 清除缓存
* @param id
*/
@CacheEvict(cacheNames = CACHE_NAME)
public void removeCache(String id){
}
}
复制代码
完成后,启动该项目时会把所有商品放入缓存,观察控制台观察日志,会有查询全部商品的日志信息:
2018-09-11 22:15:24.159 INFO 21336 --- [ main] com.imooc.seller.service.ProductCache : rpc查询全部产品,请求:com.imooc.api.domain.ProductRpcReq@7e15f4d4
2018-09-11 22:15:24.256 DEBUG 21336 --- [ main] c.g.jsonrpc4j.JsonRpcHttpClient : Request {"id":"744939775","jsonrpc":"2.0","method":"query","params":[{"idList":null,"minRewardRate":null,"maxRewardRate":null,"statusList":["IN_SELL"]}]}
2018-09-11 22:15:24.350 DEBUG 21336 --- [ main] c.g.jsonrpc4j.JsonRpcHttpClient : JSON-PRC Response: {"jsonrpc":"2.0","id":"744939775","result":[{"id":"001","name":"rpc","status":"IN_SELL","thresholdAmount":1,"stepAmount":0,"lockTerm":0,"rewardRate":3,"memo":null,"createAt":null,"updateAt":null,"createUser":null,"updateUser":null}]}
2018-09-11 22:15:24.391 INFO 21336 --- [ main] com.imooc.seller.service.ProductCache : rpc查询 全部产品,结果:[Product{id='001', name='rpc', status='IN_SELL', thresholdAmount=1, stepAmount=0, lockTerm=0, rewardRate=3, memo='null', createAt=null, updateAt=null, createUser='null', updateUser='null'}]复制代码
进入hazelcast管理端,有全部数据的缓存数据的话 , 就成功的完成了缓存全部数据的功能。
至此 SpringBoot整合Hazelcats做为缓存就全部完成。