提供一个简单的例子:完成下订单,减库存的功能
一、提供服务暴漏的接口,代码如下:
package com.rm.dubbo.service.api;
import com.rm.dubbo.service.pojo.Orders;
public interface OrdersService {
/**
* @Description: 根据订单id查询订单
*/
public Orders getOrder(String orderId);
/**
* @Description: 下订单
*/
public boolean createOrder(String itemId);
}
用的Mybatis的逆向工程,所以这里生成Orders类和Oreders的条件的类,和对应的mapper.xml的文件,工程如下:
有一个这个api是需要给对应的实现类来引用的。而且在分布式中式服务化的,所以把订单服务放一个子模块,工程的结构如下:
二、加入相应的依赖:
①、由于dubbo2.5.3是阿里维护的最后的一个版本,用的人是最多的。dubbo启动的时候是依赖spring的版本的,dubbo的默认的版本是2.5.6的版本是非常的低的,所以要排除掉。
②、order-service是依赖order-api的,所以也要把order-api的版本控制给引入order-service中。
<dependencies>
<dependency>
<groupId>com.rm.dubbo</groupId>
<artifactId>rm-order-api</artifactId>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
<dependency>
<groupId>com.github.miemiedev</groupId>
<artifactId>mybatis-paginator</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<!-- 引入dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
</dependency>
<!-- zk 客户端依赖 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
写对应的实现类,代码如下:
package com.rm.dubbo.service.impl;
import com.rm.dubbo.mapper.OrdersMapper;
import com.rm.dubbo.service.api.OrdersService;
import com.rm.dubbo.service.pojo.Orders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service("ordersService")
public class OrdersServiceImpl implements OrdersService {
final static Logger log = LoggerFactory.getLogger(OrdersServiceImpl.class);
@Autowired
private OrdersMapper ordersMapper;
@Override
public Orders getOrder(String orderId) {
return ordersMapper.selectByPrimaryKey(orderId);
}
@Override
public boolean createOrder(String itemId) {
// 创建订单
String oid = UUID.randomUUID().toString().replaceAll("-", "");
Orders o = new Orders();
o.setId(oid);
o.setOrderNum(oid);
o.setItemId(itemId);
ordersMapper.insert(o);
log.info("订单创建成功");
return true;
}
}
三、由于需要把这个服务的接口注册到zk上,参考dubbo的官网,需要一个关于dubbo的配置文件,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 服务提供者的名称,唯一标识 -->
<dubbo:application name="order-provider"/>
<!-- 注册中心 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 指定暴露的服务端,默认的协议是dubbo-->
<dubbo:protocol name="dubbo" port="20881"/>
<!-- 定义暴露的服务接口,ref指的是对应的实现类 -->
<dubbo:service interface="com.rm.dubbo.service.api.OrdersService" ref="ordersService"/>
</beans>
对应整个项目的结构如下:
然后测试下,启动这个服务,然后启动zk,控制台的信息打印如下:
其中注册打印的信息如下:dubbo默认的协议的是dubbo,用的是dubbo的统一的标准。
Register: dubbo://169.254.18.74:20881/com.rm.dubbo.service.api.OrdersService?anyhost=true&application=order-provider&dubbo=2.5.3&interface=com.rm.dubbo.service.api.OrdersService&methods=createOrder,getOrder&pid=14544&revision=1.0-SNAPSHOT&side=provider×tamp=1580444734532, dubbo version: 2.5.3, current host: 127.0.0.1
20881:是dubbo的端口号,169.254.18.74:本机的ip地址。com.rm.dubbo.service.api.OrderService:暴漏出接口的全路径。anyhost:true,代表任何机器都可以访问,没有权限。application=orderprovider:代表这个服务定义的名称。dubbo=2.5.3,dubbo的版本用的是2.5.3的版本。interface=com.rm.dubbo.service.api.OrderService&methods=createOrder,getOrder:这个告诉哪一个接口里的哪一个方法。pid:是当前进程的id。revision=1.0-SNAPSHOT:当前项目的版本。sid=provider代表是一种提供服务的角色。timestamp:毫秒值。后面的是dubbo的版本和主机。
然后查看下zk上的注册信息:
configurations节点:配置会话的 providers:配置服务提供者的,数组的原因是一个接口当中会有很多的实例,这样就可以做成负载均衡。 而在providers节点中的内容和控制台打印的内容是一样的,只不过是做了转义。这就是注册生成协议的信息。
同样也提供商品服务,工程结构如下:
提供暴漏出接口中的方法:
package com.rm.dubbo.service.api;
import com.rm.dubbo.service.pojo.Items;
public interface ItemsService {
/**
* @Description: 根据商品id获取商品
*/
public Items getItem(String itemId);
/**
* @Description: 查询商品库存
*/
public int getItemCounts(String itemId);
/**
* @Description: 购买商品成功后减少库存
*/
public void displayReduceCounts(String itemId, int buyCounts);
}
同样也需要把这个服务注册到zk上。
然后提供相应的消费端,工程结构如下:
在接口BuyService接口中,提供相应的买商品的方法,代码如下:
package com.rm.web.service;
public interface BuyService {
/**
* @Description: 购买商品
*/
public void doBuyItem(String itemId);
public boolean displayBuy(String itemId);
}
对应的实现类如下:
package com.rm.web.service.impl;
import com.rm.dubbo.service.api.ItemsService;
import com.rm.dubbo.service.api.OrdersService;
import com.rm.web.service.BuyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service("buyService")
public class BuyServiceImpl implements BuyService {
final static Logger log = LoggerFactory.getLogger(BuyServiceImpl.class);
@Resource
private ItemsService itemService;
@Resource
private OrdersService ordersService;
@Override
public void doBuyItem(String itemId) {
// 减少库存
itemService.displayReduceCounts(itemId, 1);
// 创建订单
ordersService.createOrder(itemId);
}
@Override
public boolean displayBuy(String itemId) {
int buyCounts = 5;
// 1. 判断库存
int stockCounts = itemService.getItemCounts(itemId);
if (stockCounts < buyCounts) {
log.info("库存剩余{}件,用户需求量{}件,库存不足,订单创建失败...",
stockCounts, buyCounts);
return false;
}
// 2. 创建订单
boolean isOrderCreated = ordersService.createOrder(itemId);
// 3. 创建订单成功后,扣除库存
if (isOrderCreated) {
log.info("订单创建成功...");
itemService.displayReduceCounts(itemId, buyCounts);
} else {
log.info("订单创建失败...");
return false;
}
return true;
}
}
然后消费端也需要把自己注册到zk上,也需要订阅上面的两个服务。所以dubbo的配置信息如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="web-consumer"/>
<!-- 注册中心 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 引用暴露的服务ordersService和itemService如果和属性的名称相同,则按照名称来装配,否则按照类型装配。
interface的值指向接口的全路径,这样做之后把接口的实现类完全放到spring容器中去管理了-->
<dubbo:reference id="ordersService" interface="com.rm.dubbo.service.api.OrdersService"/>
<dubbo:reference id="itemService" interface="com.rm.dubbo.service.api.ItemsService" />
</beans>
然后启动消费端:返回的结果是200,代表购买成功。
由于是分布式的项目,数据库做成垂直的拆分。所以分成两个数据库。
这时刷新下数据库。
上面的过程就是dubbo的通过在tomcat容器中启动。一个简单的入门。晚上分享如何去写一个简单的rpc框架。