Dubbo——Hello world

一、提出需求
  某个电商系统,订单服务需要调用用户服务获取某个用户的所有地址列表,而订单服务和用户服务分别部署在不同的机器上,此时就需要通过RPC的方式。
  我们需要创建两个服务模块:订单服务(服务消费者)和用户服务(服务提供者)

模块 功能
订单服务web模块 创建订单等
用户服务service模块 查询用户地址等

  测试预期结果:订单服务web模块在A服务器,用户服务模块在B服务器,A可以远程调用B的功能。
  关于服务拆分的粒度:服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo 暂未提供分布式事务支持。服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸。不建议使用过于抽象的通用接口,如:Map query(Map),这样的接口没有明确语义,会给后期维护带来不便。

二、搭建服务
 1、创建服务提供者端(用户服务)项目
 项目目录结构:
Dubbo——Hello world_第1张图片
 用户地址实体:

public class UserAddress implements Serializable {
     

	private static final long serialVersionUID = 1L;

	private Integer id;
	private String userAddress; // 用户地址
	private String userId; // 用户id
	private String consignee; // 收货人
	private String phoneNum; // 电话号码
	private String isDefault; // 是否为默认地址 Y-是 N-否

	public UserAddress() {
     
		super();
	}

	public UserAddress(Integer id, String userAddress, String userId, String consignee, String phoneNum,
			String isDefault) {
     
		super();
		this.id = id;
		this.userAddress = userAddress;
		this.userId = userId;
		this.consignee = consignee;
		this.phoneNum = phoneNum;
		this.isDefault = isDefault;
	}
	...
}

 用户服务接口定义:

public interface UserService {
     
	
	/**
	 * 据用户id查收货地址
	 * @param userId
	 * @return
	 */
	public List<UserAddress> getUserAddressList(String userId);
}

 用户服务接口实现:

public class UserServiceImpl implements UserService {
     

	public List<UserAddress> getUserAddressList(String userId) {
     
		UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", "1", "李老师", "010-56253825", "Y");
		UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1", "王老师", "010-56253825", "N");
		return Arrays.asList(address1, address2);
	}
}

 2、创建服务消费者端(订单服务)项目
  由于需要调用用户模块的接口,因此需要在该项目中定义用户的相关实体(UserAddresss)和接口(UserService)
  项目目录结构:UserAddress和UserService与服务提供者中完全相同
Dubbo——Hello world_第2张图片
 订单服务接口定义:

public interface OrderService {
     
	public void initOrder(String userId);
}

 订单服务接口实现:

public class OrderServiceImpl implements OrderService {
     

	UserService userService;

	/**
	 * 初始化订单
	 */
	public void initOrder(String userId) {
     
		// 查询用户的收货地址,需要调用用户服务
		List<UserAddress> userAddressList = userService.getUserAddressList(userId);
		System.out.println(userAddressList);
	}

}

  这里有一个问题:在用户服务项目和订单服务项目中都有完全相同的UserAddress和UserService类,且这两个类很可能在其他的项目中也会被用到,因此可以把这些公共的实体定义和接口定义在另外一个单独的项目中完成,而与服务的实现和消费拆分开,然后采用maven依赖的方式引入到服务的提供者和消费者等项目中。在dubbo的官网中也有这样一段描述:建议将服务接口、服务模型、服务异常等均放在 API 包中,因为服务模型和异常也是 API 的一部分,这样做也符合分包原则:重用发布等价原则(REP)、共同重用原则(CRP)。
 3、抽取出公共API项目
 项目目录结构:
Dubbo——Hello world_第3张图片
 pom.xml:

<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.0modelVersion>
  <groupId>com.bdm.gmallgroupId>
  <artifactId>gmall-apiartifactId>
  <version>0.0.1-SNAPSHOTversion>
project>

 4、将原服务提供者和消费者中的实体定义和接口定义删除,只保留接口实现即可,然后在pom中引入公共API的依赖:

<dependency>
	<groupId>com.bdm.gmallgroupId>
	<artifactId>gmall-apiartifactId>
	<version>0.0.1-SNAPSHOTversion>
dependency>

 服务提供者项目结构:
Dubbo——Hello world_第4张图片
 服务消费者项目结构:
Dubbo——Hello world_第5张图片
  此时在订单项目中调用userService的方法肯定是失败的,因为在订单项目中并没有实现该方法,该方法的实现在用户项目中,这就需要通过RPC的方式来调用该方法,这就是dubbo的用处所在。
使用dubbo主要完成两个任务:
1、将服务提供者提供的服务注册到注册中心(暴露服务)
2、让服务消费者去注册中心订阅服务提供者的服务地址(订阅服务)

三、服务提供者配置
 1、引入dubbo依赖

<dependency>
	<groupId>com.alibabagroupId>
	<artifactId>dubboartifactId>
	<version>2.6.4version>
dependency>

 2、引入zookeeper的客户端依赖(dubbo2.6版本需引入curator,2.5及以下版本需引入zkclient),如果curator版本太高可能导致测试时报错

<dependency>
	<groupId>org.apache.curatorgroupId>
	<artifactId>curator-frameworkartifactId>
	<version>2.12.0version>
dependency>

 3、配置服务提供者
  ①创建dubbo的配置文件provider.xml,引入dubbo的命名空间并进行相关配置


<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
		http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
	
	
	<dubbo:application name="user-service-provider">dubbo:application>
	
	
	
	<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181">dubbo:registry>
	
	
	<dubbo:protocol name="dubbo" port="20882">dubbo:protocol>

	
	<dubbo:service interface="com.bdm.gmall.service.UserService" ref="userServiceImpl">dubbo:service>
	<bean id="userServiceImpl" class="com.bdm.gmall.service.impl.UserServiceImpl">bean>

beans>

  ②测试

public class MainApplication {
     

	public static void main(String args[]) throws IOException {
     

		ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("provider.xml");
		ioc.start();
		System.in.read();// 这句代码是不让程序退出以便于我们在dubbo-admin中查看服务的情况
	}
}

:在实际开发的时候只需要将dubbo的配置文件引入到Spring的Bean配置文件中:

<import resource="classpath*:provider.xml" />

 查看管理控制台:可看到服务已经注册成功了
Dubbo——Hello world_第6张图片
 也可以看到服务提供者的信息:
Dubbo——Hello world_第7张图片
四、服务消费者配置
 1、引入dubbo和zookeeper依赖:


<dependency>
	<groupId>com.alibabagroupId>
	<artifactId>dubboartifactId>
	<version>2.6.4version>
dependency>

<dependency>
	<groupId>org.apache.curatorgroupId>
	<artifactId>curator-frameworkartifactId>
	<version>2.12.0version>
dependency>

 2、配置服务消费者配置文件,创建consumer.xml文件:


<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
		http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
	
	
	<context:component-scan base-package="com.bdm.gmall.service.impl">context:component-scan>
	
	<dubbo:application name="order-service-consumer">dubbo:application>

	
	<dubbo:registry address="zookeeper://127.0.0.1:2181">dubbo:registry>

	
	<dubbo:reference interface="com.bdm.gmall.service.UserService" id="userService">dubbo:reference>

beans>

 3、将订单服务的实现类稍作改动:加上@Autowired和@Service注解

@Service//是spring的@Service注解而非dubbo的
public class OrderServiceImpl implements OrderService {
     

	@Autowired
	UserService userService;

	/**
	 * 初始化订单
	 */
	public void initOrder(String userId) {
     
		// 查询用户的收货地址,需要调用用户服务
		List<UserAddress> userAddressList = userService.getUserAddressList(userId);
		System.out.println(userAddressList);
	}

}

 4、测试

public class MainApplication {
     

	public static void main(String args[]) throws IOException {
     
		ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("consumer.xml");
		OrderService orderService = ioc.getBean(OrderServiceImpl.class);
		orderService.initOrder("1");
		System.in.read();
	}
}

:在实际开发的时候只需要将dubbo的配置文件引入到Spring的Bean配置文件中:

<import resource="classpath*:provider.xml" />

 可以看到后台打印出:

[UserAddress [id=1, userAddress=北京市昌平区宏福科技园综合楼3层, userId=1, consignee=李老师, phoneNum=010-56253825, isDefault=Y], UserAddress [id=2, userAddress=深圳市宝安区西部硅谷大厦B座3层(深圳分校), userId=1, consignee=王老师, phoneNum=010-56253825, isDefault=N]]

 在管理控制台也可以看到响应变化:
Dubbo——Hello world_第8张图片
Dubbo——Hello world_第9张图片
五、配置监控中心
  在provider.xml和consumer.xml中都做如下配置:

<dubbo:monitor protocol="registry">dubbo:monitor>

  上面是配置监控中心的两种方式,配置的时候可以任选一种:第一种通过protocal=“registry”(值固定为"registry")是让监控中心自己去注册中心发现;第二种则是监控中心直接与服务提供者或者服务消费者通信来完成监控
emsp; 配置好之后就可以在监控中心看到通信的信息:
Dubbo——Hello world_第10张图片
六、疑问
  假如在同一个注册中心有多个服务提供者提供相同的服务,dubbo怎么区分该调用哪一个呢?
  答案:相同的服务意味着服务类的包名和类名完全相同,此时dubbo是区分不出来的,也不需要区分,这种情况下dubbo会在这些相同的服务之间根据负载均衡的策略做负载均衡,因此相同的服务在不同的机器上实现的时候逻辑要保持一致;如果仅仅是类名相同,而包名不同,dubbo会根据包名区分服务,因为服务在消费者端是确定的;也就是说dubbo中的服务是以全类名区分的。

你可能感兴趣的:(Dubbo)