世上无难事,只要肯放弃

前言

  菜鸡来记录一下学习分布式缓存的过程。


  说起这个缓存我立马就想到了当初学习计算机组成原理的时候学过的什么寄存器,高速缓冲,内存,磁盘,一级缓存,二级缓存,三级缓存之类的东西。我也了解过,一个程序做数据库的缓存也有一级缓存,二级缓存的概念。那么这是不是跟计算机组成原理学习的缓存很相似呢?根据我浅显的认知就大概提一嘴。

  • CPU访问内存比较慢,所以需要高速缓存,让CPU访问高速缓存,而高速缓存与内存进行数据交换。
  • 一个程序要访问数据库,其中与数据库建立连接,查询数据这一过程比较耗时间,所以把第一次查询到的数据进行缓存,下一次查询的时候就直接从缓存中拿到数据,不走数据库,这样能加快程序的速度。

  在搜索如何做缓存的时候,有一篇文章说Ehcache无法实现分布式缓存,就算手动实现分布式缓存,也无法保证数据的强一致性。而Hazelcast是一个很好的分布式缓存解决方案。既然都这么说了,那必须得用一下这个Hazelcast。虽然我是一个连任何缓存都没有用过的菜鸡,但是,世上无难事,只要肯放弃。
  该项目代码存放在码云上:https://gitee.com/siumu/blog_code.git

准备工作

  首先呢,是要先准备数据库。建库、建表、建字段、建数据太麻烦,我就偷个懒,用V部落的sql文件直接导入,用它的user表,该数据库也会和该博客的代码一起上传到码云。这张表长这个样子:

  接下来就是创建应用了。既然是分布式缓存,那肯定要创建多个应用来测试,所以我就创建一个maven多模块的项目,如何创建一个多模块项目,网上有很多教程,很简单,我就不写一遍了,写起来挺费劲的。如果是第一次创建多模块项目,可能会出现很多问题,慢慢解决就好了,学习就要有耐心。
  我创建的项目目录结构如下:

  • hazelcast_demo是父工程
  • hazelcast-model是子模块,主要是创建实体类
  • hazelcast-web是一个web应用,依赖hazelcast-model
  • hazelcast-web-copy是对hazelcast-web应用的一个复制

  这样就能启动两个web应用了。

hazelcast_demo作为父工程,没有src,就只有一个pom文件,项目需要的依赖在这里定义,它的pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><packaging>pom</packaging><modules><module>hazelcast-model</module><module>hazelcast-web</module><module>hazelcast-web-copy</module></modules><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.1.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.xiumu</groupId><artifactId>hazelcast_demo</artifactId><version>0.0.1-SNAPSHOT</version><name>hazelcast_demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><mysql.version>5.1.47</mysql.version><lombok.version>1.18.12</lombok.version><springboot.version>2.3.1.RELEASE</springboot.version><hazelcast.version>4.0</hazelcast.version></properties><dependencyManagement><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope><version>${lombok.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId><version>${springboot.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${springboot.version}</version><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId><version>${springboot.version}</version></dependency><!-- https://mvnrepository.com/artifact/com.hazelcast/hazelcast --><dependency><groupId>com.hazelcast</groupId><artifactId>hazelcast</artifactId><version>${hazelcast.version}</version></dependency><!-- https://mvnrepository.com/artifact/com.hazelcast/hazelcast-spring --><dependency><groupId>com.hazelcast</groupId><artifactId>hazelcast-spring</artifactId><version>${hazelcast.version}</version></dependency></dependencies></dependencyManagement>
</project>

hazelcast-model作为子模块之一,主要是写实体类,我们先引入依赖,再创建实体类,它的POM文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>hazelcast_demo</artifactId><groupId>com.xiumu</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>hazelcast-model</artifactId><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency></dependencies></project>

然后是两个实体类,User类,和Role类,整个项目我只用到了User类,实现一个简单的查询全部,Role类暂时没用上。

@Data
@Entity
public class User implements Serializable {@Idprivate Long id;           //主键private String username;   //用户名private String password;   //密码private String nickname;   //昵称private boolean enabled;   //启用或禁用@Transientprivate List<Role> roles;  //角色,暂时不用private String email;      //邮箱private String userface;   //头像private Timestamp regTime; //注册时间
}
@Data
@Entity
@Table(name = "roles")
public class Role {@Idprivate Long id;       //主键private String name;   //角色名称
}

好了,我们的实体类模块已经创建完成了,该模块的具体目录结构如下:

hazelcast-web作为子模块之一,依赖hazelcast-model,所以它只需要创建respository、service、controller层就可以了。它的POM文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>hazelcast_demo</artifactId><groupId>com.xiumu</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>hazelcast-web</artifactId><dependencies><dependency><groupId>com.xiumu</groupId><artifactId>hazelcast-model</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId></dependency><dependency><groupId>com.hazelcast</groupId><artifactId>hazelcast</artifactId></dependency><dependency><groupId>com.hazelcast</groupId><artifactId>hazelcast-spring</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

然后是创建repository层:

@Repository
public interface UserRepository extends JpaRepository<User,Long> {}

再创建service层:

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public List<User> getAllUser(){return userRepository.findAll();}
}

再创建controller层:

@RestController
@RequestMapping("/hazelcastweb")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/getAllUser")public List<User> getAllUser(){return userService.getAllUser();}
}

然后是创建一个启动类:

@SpringBootApplication
@EntityScan("com.xiumu.hazelcastmodel.entity")
public class HazelcastWebApplication {public static void main(String[] args) {SpringApplication.run(HazelcastWebApplication.class,args);}
}

最后是配置文件application.yml

server:port: 8080
spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql:///vueblog?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootjpa:hibernate:ddl-auto: noneshow-sql: true

这样一个应用就算是创建成功了,理论上是可以启动成功的。但是我没有试过,我是把所有的文件准备完之后才启动的。接下来就是引入hazelcast,我们找到它的jar包,这里面有一个hazelcast-default.yaml文件。如下图所示:


将它复制出来,并改个文件名,改成hazelcast.yaml,放到application.yml文件的旁边。我在网上找的文档都是复制旁边那个xml,但是既然这里有yaml,那肯定也没问题。这个文件很长,但是先不用管放在resources文件夹下就行了。然后怎么启用缓存呢?很简单,就是在启动类上加上注解@EnableCaching,所以最后的启动类长这样:

@EnableCaching
@SpringBootApplication
@EntityScan("com.xiumu.hazelcastmodel.entity")
public class HazelcastWebApplication {public static void main(String[] args) {SpringApplication.run(HazelcastWebApplication.class,args);}
}

然后在service层里把那个方法上加上注解@Cacheable,缓存它的返回值。最后的service层是这样的

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Cacheable("allUser")public List<User> getAllUser(){return userRepository.findAll();}
}

这样我们基本上就配置完了,关于那个hazelcast.yaml配置文件一点都不需要改动。该模块最后的目录结构就是这样子了:

接下来我们需要将这个应用复制一份,用两个应用来测试,hazelcast-web-copy就是对hazelcast-web的一个复制,它俩的配置文件,repository,service,controller,启动类几乎都一模一样,这是它的目录结构:

注意这个路径问题和启动类的名字,其它的注解,配置啥的一模一样,啊对了,还有注意它配置的端口号。它的端口号要改成8081,controller得请求路径也要改一改,区分一下两个请求。

开始试验

  接下来就是对项目进行测试了,没啥说的,启动就行了。我们先启动hazelcast-web应用,先不说别的,这个日志就很显眼:

这个日志就说明了我们创建了一个节点,接下来我们再启动另外一个应用hazel-cast-copy,我们就会看到以下日志:

现在节点变成了两个,接下来我们进行测试一下。
  测试之前我要安利一个插件叫RestfulToolkit。我们测试接口一般都是用postman,但是用postman还要打开客户端,就很麻烦,而这个工具是IDEA的一个插件,不用切换窗口,直接就在IDEA里就可以测试接口,如图所示:

我们请求第一个接口,可以看到,获得返回值,并且打印出来查询的sql语句。

接下来我们请求第二个接口,如图所示

我们看到,它初始化了dispatcherServlet,确实接收到了请求,但是并没有走数据库,说明我们这个分布式缓存还是成功的。

中期总结

  我听说如果要是用redis做集群,还要安装redis,然后再做主从配置,哨兵什么的。但是试验了Hazelcast之后我们发现,它只需要引入jar包,没有主从配置,一个应用就是一个节点,自动集群。
  我们打开它的配置文件hazelcast.yaml,找到下图这个地方:

  注意,这个network,一看就是网络的意思,那么它肯定就是配置我们这个集群的网络,接下来是port,这不就是端口号的意思嘛,auto-increment,port-count,port这三个配置意思就是,我从5701这个端口开始创建节点,如果5701端口被占用,那么就自动加1,使用5702端口,一直被占用就一直增加,直到我们端口号增加了100个还是被占用,那么就该抛出异常了。我们看这个日志就能看到第一个应用的缓存节点使用5701端口号,第二个应用的缓存节点使用5702端口号。
  接下来再往下看,这里有join,这不就是加入的意思嘛,那么这里肯定就是配置节点是怎么自动加入集群的。multicast意思就是使用组播协议来加入集群,我也是第一次听说这个东西,用我浅薄的认知来说一下,就是说节点启动的时候就向外广播,如果有其它节点回应,那节点就自动加入集群,如果没有那就它自己呗,等着别的新启动的节点给它广播。我们默认就是使用这个方式。我们找到这个日志:

这里有一个Creating MulticastJoiner,这就是使用组播协议来进行集群。
  跟multicast平行的是这个tcp-ip,说明它也是集群的一种方式,只不过默认是关闭的,interface这里是第一个节点启动的IP,也可以说它是个主节点吧,member-list这不就是成员的IP地址嘛,是其他后续启动的节点的IP地址。其中的减号 " - ",表示这是一个数组,成员可以有很多IP地址,可以这么写

tcp-ip:enabled: falseinterface: 127.0.0.1member-list:- 127.0.0.1- 127.0.0.2- 127.0.0.3- 127.0.0.4- 127.0.0.5

  好了,接下来的试验我们就需要用到tcp-ip这个配置了。
  问题是这样的,我们这两个应用是在同一个机器上运行的,那么在不同的机器上如何使用hazelcast做分布式缓存呢?

不同机器上使用Hazelcast实现分布式缓存

准备工作

  代码写起来倒是简单,准备工作倒是很费时间。
  首先我们要准备两台机器,但是我只有一个电脑,所以就使用了虚拟机:centos7,网络呢使用桥接模式,这样有不同的ip地址,代表主机与虚拟机是两台机器。

虚拟机与主机的准备工作

  • 首先,要测试虚拟机与主机之间可以ping通,不能的话就要解决这个问题。
  • 第二,我没有远程数据库,用的是本地数据库,所以要让虚拟机可以访问主机的数据库,也就是创建一个用户允许虚拟机的IP可以远程访问,当然我偷了个懒,直接设置root用户允许任何IP进行远程登录。
  • 第三,虚拟机要有运行java程序的环境,还要开放8081的端口号,可以让外界访问,因为我上传的应用程序就是占用的8081端口号。
  • 查看端口开放 firewall-cmd --zone=public --list-port
  • 开放8081端口firewall-cmd --zone=public --add-port=8081/tcp --permanent

修改设置

  首先我们要把组播模式关掉,打开tcp-ip模式,因为我还没摸索出来组播模式怎么配置不同机器之间的集群,然后配置我们的主机IP地址,和虚拟机IP地址,千万不要写127.0.0.1。具体修改后的配置如下

也不要忘了修改数据库的连接信息,要写上主机的IP地址,就像下图这样:

打包上传

  将hazelcast-web-copy打包,上传至虚拟机。我将它打包成jar。

开始测试

  接下来就可以测试了,首先我们启动主机上的两个应用,然后我们就可以看到打印的日志,表示我们使用tcp-ip模式来集群,并且现在有两个节点了。

  然后我们再启动虚拟机上的应用,使用java -jar 包名命令就可以启动虚拟机上的这个项目启动之后,部分日志如图所示:

  我们发现有三个节点了,那么它到底能不能实现分布式缓存呢?这就需要验证一番了,我们看这三个节点的日志就知道,现在还没有初始化dispatcherServlet,我们先给本机的8081端口发送一次请求。
  我们看打印日志就发现它初始化了dispatcherServlet走了一次数据库,打印出了sql语句
接下来我们给虚拟机的应用发送一次请求,结果如下:

我们看到它收到了请求,返回了数据,但是并没有走数据库。那我们继续给主机的8080端口发送一次请求。同样可以看到日志,它并没有走数据库。这就证明了我们的分布式缓存是成功的。

总结

  通过这次试验,我们发现Hazelcast实现分布式缓存还是很简单的,只不过是我比较菜,整了两三天才摸索出来。说起缓存我们总是提起redis,ehcache,memcache,但是这个Hazelcast倒是很少有人提及,所以网上相关的博客很少,我遇到的问题有时候都搜索不到,甚至把网上的方法试了个遍都不行。可能是我还不是很会搜索问题吧。
  有的博客写的就很神奇,最让我记忆深刻的就是要让我在启动类上加上@EnableHazelcastCaching这个注解,我完全都搜不到这个注解在哪,我现在想搜这个博客也搜索不到了,当然也有可能这是人家自定义的注解,手写了一个spring-boot-starter-hazelcast包。有的说让我设置store-type的,或者把实体类序列化的,等等各种情况,到最后我还是看到一个英文博客里使用了hazelcast-spring这个Jar包,我把它加上就好了。
  刚开始搜这个Hazelcast如何使用的时候,大家的博客都说这个很简单,就引入一个hazelcast就可以使用,我深信不疑,因为确实看到了日志显示有集群,有三个节点,可是,访问任何一个节点都要走一次数据库,我还以为是我的使用有什么问题,毕竟没有使用过别的缓存框架,不是很会用。后来做成这个demo之后我又搜索了一下关于它的博客,发现确实是我眼瞎,有的博客也引入了两个jar包,而且是用gradle构建的项目,不知道为啥我居然没有看到它引入了两个jar包。可能是我意识里被这个只需要引入一个jar给洗脑了,下意识给忽略了。
  好了,闲聊到此,总的来说,对于该工具更加细节的使用还是得去实践中学习。

springboot整合Hazelcast实现分布式缓存相关推荐

  1. hazelcast 搭建_SpringBoot整合Hazelcast实现分布式缓存

    原标题:SpringBoot整合Hazelcast实现分布式缓存 一. 分布式缓存代码实现步骤 1. 创建web项目 我们按照之前的经验,创建一个web程序,并将之改造成Spring Boot项目,具 ...

  2. 关于SpringBoot整合Shiro并入redis缓存

    关于SpringBoot整合Shiro并入redis缓存 最近做一个小项目加入shiro权限框架, Shiro是Apache下的一个开源项目,提供了认证.授权.加密.会话管理,与spring Secu ...

  3. springboot整合redis实现分布式锁思想

    思路 所有响应获取锁的线程都先尝试往redis中创建一个缓存数据,所有线程的key必须相同.使用的是redis的setnx命令.就只有一个线程能够创建成功,创建成功的线程就成功获取锁. 没有获取锁的线 ...

  4. 8分钟带你学会SpringBoot整合Redis来实现缓存技术

    1.概述 随着互联网技术的发展,对技术要求也越来越高,所以在当期情况下项目的开发中对数据访问的效率也有了很高的要求,所以在项目开发中缓存技术使用的也越来越多,因为它可以极大的提高系统的访问速度,关于缓 ...

  5. springboot整合redisson实现分布式锁

    一.介绍Redisson Redisson是Redis官方推荐的Java版的Redis客户端(Jedis.letture也是官方推荐的java版本redis客户端程序).它提供的功能非常多,也非常强大 ...

  6. 5分钟实现SpringBoot整合Dubbo构建分布式服务

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:jaycekon cnblogs.com/jaycekon/ ...

  7. Springboot整合TX-LCN实现分布式事务

    文章目录 前言 名词解释 TM 配置 TC 配置 LCN (Lock Confirm Notify) 模式 TCC (Try Confirm Cancel) 模式 参考链接 前言 TX-LCN 是一款 ...

  8. springboot整合curator实现分布式锁模拟抢购场景

    文章目录 1.环境说明 2.maven依赖 3.代码实现 3.1.Redis 3.1.1.配置文件 3.1.2.redisTemplate配置 3.2.Curator 3.1.1.配置文件 3.1.2 ...

  9. SpringBoot集成Redission实现分布式缓存

    github地址:https://github.com/redisson/redisson 一.源码分析 trylock tryAcquire 针对过期时间做不同的转发处理 tryLockInnerA ...

最新文章

  1. CCNA配置试验之三 EIGRP协议的配置
  2. MT76x8的多网口与GPIO复用配置
  3. Virtual Box 安装过程(卸载Vmware后)
  4. Horspool 字符串快速查找算法
  5. [react] 举例说明在react中怎么使用样式
  6. git基础-远程仓库的使用
  7. 脚本启动慢_Linux 常用运维脚本,建议收藏
  8. 打印时候复选框勾选不见了_checkbox 选中未显示对号勾选的问题
  9. java实体类的功能_(转载) java实体类的作用
  10. centos修改jdk之后无法生效问题
  11. 常用算法之-快速排序
  12. 野火RT1052关于 W25Q256JV NORFLASH异常锁死的解锁处理
  13. 如何设计一个应用软件
  14. 解决word各级标题序号后面有长空格
  15. 攻防演练中防守方的骚姿势
  16. 2011微软校园招聘职位一览表
  17. js首次修改html无效,浅谈jQuery添加的HTML,JS失效的问题
  18. 单元格颜色公式之明细数据项隔行底纹
  19. C语言递归(pta递归求简单交错幂级数的部分和)
  20. 计算机信息检索在医学中的应用,探索医学文献检索在医学期刊编辑中的重要性及应用...

热门文章

  1. 学习笔记《最优化理论与算法》(一)
  2. linux 静态链接 mysql glibc 库的悲催过程
  3. cuda安装出现 Log file not open. Segmentation fault (core dumped) 错误
  4. 用java程序写一个闹钟_java 使用swing制作一个小闹钟,包含以下功能:
  5. 2020年塔式起重机司机多少分及格及塔式起重机司机考试技巧
  6. 国产品牌的蓝牙耳机哪个好?国产最好的蓝牙耳机排行
  7. word写论文小技巧
  8. Tomcat(二):手写嵌入式tomcat
  9. 对HMM-GMM模型的理解
  10. 手把手教你用.NET Core写爬虫 1