新手都能看懂!手把手教你使用SpringBoot+Dubbo搭建一个简单的分布式服务。
此文使用springboot+dubbo+zookeeper+redis搭建一个简易的分布式服务。
环境准备
1、zookeeper环境安装;
2、redis环境安装;
3、mysql环境安装。
实现的业务功能
1、根据一定条件查询hero记录,并生成相应缓存;
2、查询记录数,进行缓存,并设置有效期;
3、新增hero记录,清空所有记录缓存,因为记录要求实时性;但不清空记录数缓存,这个不要求实时性。
代码实现
基础项目搭建
新建maven项目dubbo-interface。定义公共内容。
1、实体类:
1 package com.zomi.bean; 2 3 import java.io.Serializable; 4 import java.util.Date; 5 6 public class Hero implements Serializable{ 7 8 private static final long serialVersionUID = 1L; 9 10 private int id ; 11 private String name ; 12 private String remark ; 13 private int level ; 14 private Date date ; 15 //省去getter、setter、toString 16 }
2、功能接口:
1 package com.zomi.service; 2 3 import java.util.List; 4 5 import com.zomi.bean.Hero; 6 7 public interface HeroService { 8 9 void addHero(Hero hero); 10 List<Hero> findHeros(int level); 11 int findHerosCount(); 12 }
3、maven install 打包,供之后项目使用。
提供者
新建spring boot工程dubbo-provider
1、添加pom依赖
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-web</artifactId> 4 </dependency> 5 <!-- 引入公共接口项目 --> 6 <dependency> 7 <groupId>com.zomi</groupId> 8 <artifactId>dubbo-interface</artifactId> 9 <version>0.0.1-SNAPSHOT</version> 10 </dependency> 11 <!-- 引入dubbo的依赖 --> 12 <dependency> 13 <groupId>com.alibaba.spring.boot</groupId> 14 <artifactId>dubbo-spring-boot-starter</artifactId> 15 <version>2.0.0</version> 16 </dependency> 17 <!-- 引入zookeeper的依赖 --> 18 <dependency> 19 <groupId>com.101tec</groupId> 20 <artifactId>zkclient</artifactId> 21 <version>0.10</version> 22 </dependency> 23 <!-- slf4j依赖 --> 24 <dependency> 25 <groupId>org.slf4j</groupId> 26 <artifactId>slf4j-log4j12</artifactId> 27 <scope>test</scope> 28 </dependency> 29 <!-- 添加redis-springboot整合依赖 --> 30 <dependency> 31 <groupId>org.springframework.boot</groupId> 32 <artifactId>spring-boot-starter-data-redis</artifactId> 33 </dependency> 34 <!-- 添加mybatis-springboot整合依赖 --> 35 <dependency> 36 <groupId>org.mybatis.spring.boot</groupId> 37 <artifactId>mybatis-spring-boot-starter</artifactId> 38 <version>1.3.2</version> 39 </dependency> 40 <!-- 添加mysql驱动依赖 --> 41 <dependency> 42 <groupId>mysql</groupId> 43 <artifactId>mysql-connector-java</artifactId> 44 </dependency> 45 <!-- druid依赖 --> 46 <dependency> 47 <groupId>com.alibaba</groupId> 48 <artifactId>druid</artifactId> 49 <version>1.1.21</version> 50 </dependency>
2、dao层
1 package com.zomi.mapper; 2 3 import java.util.List; 4 import org.apache.ibatis.annotations.Mapper; 5 import com.zomi.bean.Hero; 6 7 @Mapper 8 public interface HeroMapper { 9 void insertHero(Hero hero); 10 List<Hero> selectHerosByLevel(int level); 11 int selectCount(); 12 }
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > 3 <mapper namespace="com.zomi.mapper.HeroMapper" > 4 <insert id="insertHero"> 5 insert into Hero(name,remark,level,date) values(#{name},#{remark},#{level},#{date}) 6 </insert> 7 <select id="selectHerosByLevel" resultType="hero"> 8 select id,name,remark,level,date from hero where level <=#{hehelevel} 9 </select> 10 <select id="selectCount" resultType="int"> 11 select count(1) from hero 12 </select> 13 </mapper>
3、Service层
1 package com.zomi.service.impl; 2 3 import java.util.List; 4 import java.util.concurrent.TimeUnit; 5 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.cache.annotation.CacheEvict; 8 import org.springframework.cache.annotation.Cacheable; 9 import org.springframework.data.redis.core.BoundValueOperations; 10 import org.springframework.data.redis.core.RedisTemplate; 11 import org.springframework.stereotype.Component; 12 import org.springframework.transaction.annotation.Transactional; 13 14 import com.alibaba.dubbo.config.annotation.Service; 15 import com.zomi.bean.Hero; 16 import com.zomi.mapper.HeroMapper; 17 import com.zomi.service.HeroService; 18 19 @Service //注意这个是dubbo里边的注解,而不是springframework里边的注解 20 @Component 21 public class HeroServiceImpl implements HeroService { 22 23 @Autowired 24 HeroMapper mapper ; 25 @Autowired 26 private RedisTemplate<Object, Object> redisTemplate ; 27 28 @CacheEvict(value="realTimeCache", allEntries=true) 29 @Transactional(rollbackFor=Exception.class) //事务控制 30 @Override 31 public void addHero(Hero hero) { 32 mapper.insertHero(hero); 33 } 34 35 @Cacheable(value="realTimeCache", key="'hero_'+#level") 36 @Override 37 public List<Hero> findHeros(int level) { 38 return mapper.selectHerosByLevel(level); 39 } 40 41 /** 42 * redis高并发可能存在的三个问题:(此处针对热点缓存使用到了双重检测锁) 43 * 1)缓存穿透:默认值 44 * 2)缓存雪崩:提前规划 45 * 3)热点缓存:双重检测锁 46 */ 47 @Override 48 public int findHerosCount() { 49 //获取redis操作对象 50 BoundValueOperations<Object, Object> ops = redisTemplate.boundValueOps("count"); 51 //从缓存中读取数据 52 Object count = ops.get(); 53 if(count == null) { 54 synchronized(this) { 55 count = ops.get(); 56 if(count == null) { 57 //如果缓存没有从db中读取数据 58 count = mapper.selectCount(); 59 //将数据将入到缓存中 60 ops.set(count, 10, TimeUnit.SECONDS); 61 } 62 } 63 } 64 return (int) count; 65 } 66 67 }
4、启动类
1 package com.zomi; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cache.annotation.EnableCaching; 6 import org.springframework.transaction.annotation.EnableTransactionManagement; 7 8 import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration; 9 10 @EnableTransactionManagement //开启dubbo的事务管理,否则服务会注册不上 11 @EnableDubboConfiguration //开启dubbo的自动配置 12 @SpringBootApplication 13 @EnableCaching //开启缓存功能 14 public class DubboProviderApplication { 15 16 public static void main(String[] args) { 17 SpringApplication.run(DubboProviderApplication.class, args); 18 } 19 20 }
5、配置文件
1 #配置端口、服务暴露名称 2 server: 3 port: 8000 4 5 #配置服务暴露名称、dubbo注册中心地址 6 spring: 7 application: 8 name: dubbo-provider 9 dubbo: 10 registry: zookeeper://zkOS:2181 11 12 #配置数据源、redis缓存 13 datasource: 14 type: com.alibaba.druid.pool.DruidDataSource 15 driver-class-name: com.mysql.jdbc.Driver 16 url: jdbc:mysql://aliyun:3306/hero?characterEncoding=utf8 17 username: root 18 password: "这里填你的密码" 19 mvc: 20 date-format: yyyyMMdd 21 22 redis: 23 host: aliyun 24 port: 6379 25 password: "这里填你的密码" 26 27 cache: 28 type: redis 29 cache-names: realTimeCache #缓存命名空间列表,可以执行多个 30 31 #mybatis配置 32 mybatis: 33 mapper-locations: 34 - classpath:com/zomi/mapper/*.xml 35 type-aliases-package: com.zomi.bean
6、添加上日志配置。通过日志可以监控到sql语句、同时可以判断是否使用了缓存。
注意:此日志文件必须在resources路径下,与application.yml同级,且命名为logback.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <configuration> 3 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 4 <encoder> 5 <!-- %level %msg%n --> 6 <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> 7 </encoder> 8 </appender> 9 <root level="WARN"> 10 <appender-ref ref="STDOUT"/> 11 </root> 12 13 <logger name="com.zomi.mapper" level="DEBUG"></logger> 14 </configuration>
消费者
新建springboot工程dubbo-consumer
1、添加pom依赖
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-web</artifactId> 4 </dependency> 5 <!-- 引入公共接口项目 --> 6 <dependency> 7 <groupId>com.zomi</groupId> 8 <artifactId>dubbo-interface</artifactId> 9 <version>0.0.1-SNAPSHOT</version> 10 </dependency> 11 <!-- 引入dubbo的依赖 --> 12 <dependency> 13 <groupId>com.alibaba.spring.boot</groupId> 14 <artifactId>dubbo-spring-boot-starter</artifactId> 15 <version>2.0.0</version> 16 </dependency> 17 <!-- 引入zookeeper的依赖 --> 18 <dependency> 19 <groupId>com.101tec</groupId> 20 <artifactId>zkclient</artifactId> 21 <version>0.10</version> 22 </dependency> 23 <!-- slf4j依赖 --> 24 <dependency> 25 <groupId>org.slf4j</groupId> 26 <artifactId>slf4j-log4j12</artifactId> 27 <scope>test</scope> 28 </dependency>
2、controller层
1 package com.zomi.controller; 2 3 import java.util.List; 4 5 import org.springframework.stereotype.Controller; 6 import org.springframework.ui.Model; 7 import org.springframework.web.bind.annotation.PostMapping; 8 import org.springframework.web.bind.annotation.RequestMapping; 9 import org.springframework.web.bind.annotation.ResponseBody; 10 11 import com.alibaba.dubbo.config.annotation.Reference; 12 import com.zomi.bean.Hero; 13 import com.zomi.service.HeroService; 14 15 @Controller 16 public class HeroController { 17 18 @Reference(check=false) 19 HeroService service; 20 21 @PostMapping("hero") 22 public String addHero(Hero hero,Model model) { 23 model.addAttribute("hero", hero); 24 service.addHero(hero); 25 return "/WEB-INF/welcome.jsp"; 26 } 27 28 @RequestMapping("findHeros") 29 public @ResponseBody List<Hero> findHeros(int level){ 30 return service.findHeros(level); 31 } 32 33 @RequestMapping("findHerosCount") 34 public @ResponseBody int findHerosCount() { 35 return service.findHerosCount(); 36 } 37 38 }
3、启动类
1 @EnableDubboConfiguration 2 @SpringBootApplication 3 public class DubboConsumerApplication { 4 5 public static void main(String[] args) { 6 SpringApplication.run(DubboConsumerApplication.class, args); 7 } 8 9 }
4、jsp页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <title>英雄注册</title> 8 </head> 9 <body> 10 注册成功!<br> 11 ${hero} 12 </body> 13 </html>
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <title>英雄注册</title> 8 </head> 9 <body> 10 <form action="hero" method="post"> 11 英雄名:<input type="text" name="name"><br> 12 定位:<input type="text" name="remark"><br> 13 熟练度:<input type="text" name="level"><br> 14 日期:<input type="text" name="date"><br> 15 <input type="submit" value="战斗"> 16 </form> 17 <hr> 18 <form action="findHeros" method="post"> 19 熟练度:<input type="text" name="level"><br> 20 <input type="submit" value="查询"> 21 </form> 22 <hr> 23 <a href="findHerosCount">预估英雄数量</a> 24 </body> 25 </html>
5、配置文件
1 #配置端口、服务暴露名称 2 server: 3 port: 8888 4 #服务暴露名称 5 spring: 6 application: 7 name: dubbo-consumer 8 #配置dubbo注册中心地址 9 dubbo: 10 registry: zookeeper://zkOS:2181 11
启动测试
测试要点:
②服务发现、调用是否成功
③缓存是否生效
④缓存有效期是否生效
⑤新增记录是否清除缓存