【一】介绍
Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载。如果不想使用Spring配置,也可以通过使用API的方式进行调用(不推荐)。
【二】项目创建
-
model:数据模型
-
dubbo:需要暴露的接口
dubbo依赖于model模型 -
provider:服务提供者
依赖model模块,依赖dubbo模块 -
consumer:服务消费者
依赖model模块,依赖dubbo模块
【三】maven依赖
- 根pom.xml文件
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dubbo</groupId>
<artifactId>module</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>dubbo</module>
<module>consumer</module>
<module>provider</module>
<module>model</module>
</modules>
<name>module</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba.boot/dubbo-spring-boot-starter -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- model模块的pom.xml
<?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>module</artifactId>
<groupId>com.dubbo</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>model</artifactId>
</project>
- dubbo模块的pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.api</groupId>
<artifactId>dubbo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>dubbo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.dubbo</groupId>
<artifactId>module</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>com.dubbo</groupId>
<artifactId>model</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- provider模块的pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.provider</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.dubbo</groupId>
<artifactId>module</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.api</groupId>
<artifactId>dubbo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.8.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- consumer模块的pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.consumer</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.dubbo</groupId>
<artifactId>module</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.api</groupId>
<artifactId>dubbo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
【四】代码实现
4.1 model:
- User.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain=true)
public class User implements Serializable {
private Integer id;
private String username;
private String password;
}
4.2 dubbo:
- UserService.java
package com.api.dubbo.service;
import com.feng.model.User;
/**
* 用户service
*
* @author fengwen
*/
public interface UserService {
/**
* 登录
*
* @param username
* @param password
* @return
*/
User login(String username, String password);
/**
* 减少用户拥有数量
*
* @param count
* @return
*/
String reduce(int count);
/**
* 减少用户拥有数量
*
* @param count
* @return
*/
String reduce(String id, int count);
}
4.3 provider:
- UserServiceImpl.java
package com.provider.demo.service;
import com.alibaba.dubbo.config.annotation.Service;
import com.api.dubbo.service.UserService;
import com.feng.model.User;
import com.provider.demo.comment.DistributedLock;
import com.provider.demo.comment.RedisLock;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 用户service实现类
* @author fengwen
*/
@org.springframework.stereotype.Service
@Service
public class UserServiceImpl implements UserService {
/**
* 用于测试分布式锁
*/
public static int allCount=10;
/**
* 基于redis做的分布式锁
*/
@Autowired
private RedisLock redisLock;
/**
* 用户登录
* @param username
* @param password
* @return
*/
@Override
public User login(String username, String password) {
User user = new User();
user.setPassword("12345");
user.setUsername("fengwen");
user.setId(10);
return user;
}
/**
* 减少用户拥有数量,这个地方采用的是curator做的分布式锁
* @param count
* @return
*/
@Override
public String reduce(int count) {
DistributedLock lock = new DistributedLock("reduce");
lock.acquireLock();
if (allCount<count){
lock.releaseLock();
return "数量不足";
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
allCount = allCount -count;
lock.releaseLock();
return "减少成功";
}
/**
* 减少用户拥有数量,这个地方采用的是redis做的分布式锁
* @param count
* @param id
* @return
*/
@Override
public String reduce(String id,int count) {
//加锁
long time = System.currentTimeMillis() + 3000 ;
if(!redisLock.lock(id,String.valueOf(time))){ //如果返回
return "并发量太多了,换个姿势再试试!";
}
if (allCount<count){
redisLock.unlock(id,String.valueOf(time));
return "数量不足";
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
allCount = allCount -count;
redisLock.unlock(id,String.valueOf(time));
return "减少成功";
}
}
- UserController.java
package com.provider.demo.controller;
import com.api.dubbo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description
* @Author fengwen
* @Date 2020/1/22 17:34
* @Version V1.0
*/
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
/**
* 减少数量,测试分布式锁
*
* @param count
* @return
*/
@GetMapping("reduce")
public String reduce(@RequestParam("count") int count) {
String result = userService.reduce(count);
return result;
}
/**
* 减少数量,测试分布式锁
*
* @param count
* @return
*/
@GetMapping("reduce2")
public String reduce2(@RequestParam("count") int count) {
String result = userService.reduce(count);
return result;
}
/**
* 减少数量,测试分布式锁
*
* @param count
* @return
*/
@GetMapping("reduce3")
public String reduce3(@RequestParam("id") String id, @RequestParam("count") int count) {
String result = userService.reduce(id, count);
return result;
}
/**
* 减少数量,测试分布式锁
*
* @param count
* @return
*/
@GetMapping("reduce4")
public String reduce4(@RequestParam("id") String id, @RequestParam("count") int count) {
String result = userService.reduce(id, count);
return result;
}
}
- DistributedLock.java
package com.provider.demo.comment;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* @Description
* @Author fengwen
* @Date 2020/1/22 18:12
* @Version V1.0
*/
public class DistributedLock {
public static Logger log = LoggerFactory.getLogger(DistributedLock.class);
//可重入排它锁
private InterProcessMutex interProcessMutex;
//竞争资源标志
private String lockName;
//根节点
private String root = "/distributed/lock/";
private static CuratorFramework curatorFramework;
private static String ZK_URL = "127.0.0.1:2181";
static{
curatorFramework= CuratorFrameworkFactory.newClient(ZK_URL,new ExponentialBackoffRetry(1000,3));
curatorFramework.start();
}
/**
* 实例化
* @param lockName
*/
public DistributedLock(String lockName){
try {
this.lockName = lockName;
interProcessMutex = new InterProcessMutex(curatorFramework, root + lockName);
}catch (Exception e){
log.error("initial InterProcessMutex exception="+e);
}
}
/**
* 获取锁
*/
public void acquireLock(){
try {
//重试2次,每次最大等待2s,也就是最大等待4s
while (true){
boolean acquire = interProcessMutex.acquire(2, TimeUnit.SECONDS);
if (acquire){
log.info("Thread:"+Thread.currentThread().getId()+" acquire distributed lock success");
break;
}
}
} catch (Exception e) {
log.error("distributed lock acquire exception="+e);
}
}
/**
* 释放锁
*/
public void releaseLock(){
try {
if(interProcessMutex != null && interProcessMutex.isAcquiredInThisProcess()){
interProcessMutex.release();
curatorFramework.delete().inBackground().forPath(root+lockName);
log.info("Thread:"+Thread.currentThread().getId()+" release distributed lock success");
}
}catch (Exception e){
log.info("Thread:"+Thread.currentThread().getId()+" release distributed lock exception="+e);
}
}
}
- RedisLock.java
package com.provider.demo.comment;
/**
* @Description
* @Author fengwen
* @Date 2020/1/23 11:37
* @Version V1.0
*/
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* Redis分布式锁
* @author fengwen
*/
@Component
@Slf4j
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 加锁
* @param key
* @param value 当前时间+超时时间
* @return
*/
public boolean lock(String key, String value) {
//相当于SETNX指令,setIfAbsent方法设置了返回true,没有设置返回false
if(redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
//假设currentValue=A 接下来并发进来的两个线程的value都是B 其中一个线程拿到锁,除非从始至终所有都是在并发(实际上这中情况是不存在的),只要开始时有数据有先后顺序,则分布式锁就不会出现“多卖”的现象
String currentValue = redisTemplate.opsForValue().get(key);
//如果锁过期 解决死锁
if (!StringUtils.isEmpty(currentValue)
&& Long.parseLong(currentValue) < System.currentTimeMillis()) {
//获取上一个锁的时间,锁过期后,GETSET将原来的锁替换成新锁
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
return true;
}
}
//拿到锁的就有执行权力,拿不到的只有重新再来,重新再来只得是让用户手动继续抢单
return false;
}
/**
* 解锁
* @param key
* @param value
*/
public void unlock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
}catch (Exception e) {
log.error("【redis分布式锁】解锁异常, {}", e);
}
}
}
- application.yml
spring:
application:
name: dubbo-provider
redis:
host: 47.105.165.182
password:
port: 6379
database: 0
server:
port: 8081
dubbo:
application:
name: dubbo-provider
protocol:
name: dubbo
port: 20880
registry:
address: zookeeper://127.0.0.1:2181
provider:
timeout: 5000
zookeeper:
server: 127.0.0.1:2181
lockPath: /springboot/
4.4 consumer
- UserController.java
package com.consumer.demo.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.api.dubbo.service.UserService;
import com.feng.model.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 消费者
* @author fengwen
*/
@RestController
@RequestMapping(value = "user")
public class UserController {
@Reference
private UserService userService;
@RequestMapping("login")
public User login(String username,String password) {
User user = userService.login(username,password);
return user;
}
}