基于springboot+dubbo的简易分布式小Demo

 

  新手都能看懂!手把手教你使用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 }
View Code

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 }
View Code

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>
View Code

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 }
View Code
 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 &lt;=#{hehelevel}
 9     </select>
10     <select id="selectCount" resultType="int">
11         select count(1) from hero
12     </select>
13 </mapper>
View Code

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 }
View Code

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 }
View Code

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
View Code

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>
View Code

消费者

新建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>
View Code

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 }
View Code

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 }
View Code

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>
View Code
 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>
View Code

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
View Code

启动测试

测试截图省略

测试要点:

①服务注册是否成功

②服务发现、调用是否成功

③缓存是否生效

④缓存有效期是否生效

⑤新增记录是否清除缓存

 

posted @ 2020-03-20 19:02  zomicc  阅读(323)  评论(0编辑  收藏  举报