搭建服务的提供者和服务的消费者,实现服务消费者跨应用远程调用服务提供者
服务的消费者远程调用服务的提供者, 最起码他自己要得到在服务提供者提供服务的那个类的引用, 那消费者和服务的一人一份,如果是集群就会翻倍,故抽取公共模块,存放公共使用的类和接口
// 自定义的公共模块 <dependency> <groupId>com.changwu</groupId> <artifactId>commom</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.3</version> </dependency>
public class UserServiceImpl implements UserService { public List<UserAddress> getUserAddressList() { UserAddress a1= new UserAddress(1,"张三","北京市朝阳区"); UserAddress a2= new UserAddress(2,"李四","山东济南"); return Arrays.asList(a1,a2); } }
<?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://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- 暴露服务提供者, name=服务名(不能和其他服务名重复) --> <dubbo:application name="user-service-provider" /> <!-- z指定zookeeper的地址 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 指定通信规则,通信协议, 通信端口. 服务消费者和dubbo之间的通信--> <dubbo:protocol name="dubbo" port="20880" /> <!-- 声明需要暴露的服务接口 ref=服务的真正实现, 下面使用<bean>调用--> <dubbo:service interface="com.changwu.service.UserService" ref="userService" /> <!-- 接口的实现 --> <bean id="userService" class="com.changwu.service.impl.UserServiceImpl" /> <dubbo:monitor protocol="registry"></dubbo:monitor> </beans>
schema配置参考手册 , 点击查看全部标签,及详细解释
如下,是一个典型的dubbo.properties配置样例。
dubbo.application.name=foo dubbo.application.owner=bar dubbo.registry.address=10.20.153.10:9090
###set log levels### log4j.rootLogger=info, stdout ###output to console### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n
// 自定义的公共模块 <dependency> <groupId>com.changwu</groupId> <artifactId>commom</artifactId> <version>1.0-SNAPSHOT</version> </dependency> // zk的客户端依赖 curator <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.13.0</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.13</version> </dependency>
编写配置文件自己的服务名,配置注册中心的地址,进而向注册中心拉取服务的列表,配置当前的消费者中使用的哪些接口是通过远程RPC调用实现的, 还可以配置监控中心查看服务的健康情况,彼此的调用情况,别忘了使用Spring注解还得配置包扫描
<?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://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-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --> <dubbo:application name="order-service" /> <!--注册中心地址 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 生成远程服务代理,可以和本地bean一样使用 userService --> <dubbo:reference id="userService" interface="com.changwu.service.UserService" /> <!--添加包扫描--> <context:component-scan base-package="com.changwu"></context:component-scan> <!--配置监控中心,有下面两种方式 1. 去注册中心自动发现, 2. 直连模式--> <dubbo:monitor protocol="registry"></dubbo:monitor> <!-- <dubbo:monitor address="127.0.0.1:7070"></dubbo:monitor>--> </beans>
消费者调用服务提供者的实现
下面的@Service注解使用的Spring原生的
@Service // 没有用dubbo的service注解 public class OrderServiceImpl implements OrderService { @Autowired UserService userService; public List initOrder(String userId) { // 查询用户的地址 System.out.println("userId == "+userId); List<UserAddress> userAddressList = userService.getUserAddressList("1"); for (UserAddress userAddress : userAddressList) { System.out.println(userAddress.getAdress()); } return userAddressList; } }
public class MainApp { public static void main(String[] args) { ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("consumer.xml"); OrderService orderService = ioc.getBean(OrderService.class); orderService.initOrder("1"); try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } } }
同样重复使用类,接口放到中,然后在其它模块引用
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.changwu</groupId> <artifactId>common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- dubbo整合进springboot zookeeper,curate它都已经导入进来了 --> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency>
可以将xml的tag名和属性名组合起来,用‘.’分隔。每行一个属性
# 指定当前的服务, 一般为了防止重复,设置成当前module的名字 dubbo.application.name=provider # 指定注册中心的地址 dubbo.registry.address=127.0.0.1:2181 dubbo.registry.protocol=zookeeper # dubbo 使用的协议 dubbo.protocol.name=dubbo dubbo.protocol.port=20880 # 原配置文件中暴露服务的名字,使用类似下面的方式,但是在SpringBoot中使用 @Service注解(dubbo的) # dubbo.service.interface=com.changwu.service.UserService # 监控中心 dubbo.monitor.protocol=registry server.port=8082
服务提供者对接口进行实现
@Service使用的dubbo的接口
@Component @Service //使用dubbo的Service 对外保留服务 public class UserServiceImpl implements UserService { public List<UserAddress> getUserAddressList(String userId) { UserAddress a1= new UserAddress(1,"张三","北京市朝阳区"); UserAddress a2= new UserAddress(2,"李四","山东济南"); return Arrays.asList(a1,a2); } }
@EnableDubbo @SpringBootApplication public class ProviderApp { public static void main(String[] args) { SpringApplication.run(ProviderApp.class); } }
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.changwu</groupId> <artifactId>common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency>
# 告诉注册中心自己的名字 dubbo.application.name=consumer # 注册中心的地址 dubbo.registry.address=zookeeper://127.0.0.1:2181 # 配置监控中心 dubbo.monitor.protocol=registry # 原来在配置文件中使用 dubbo:reference 配置远程调用的接口,现在用注解 server.port=8081
@Service public class OrderServiceImpl implements OrderService{ //@Autowired @Reference // dubbo的注解 UserService userService; public List<UserAddress> initOrder(String userId) { return userService.getUserAddressList("1"); } }
@EnableDubbo @SpringBootApplication public class MainApp { public static void main(String[] args) { SpringApplication.run(MainApp.class); } }
示例 , dubbo常用的配置示例
服务的消费者在启动是会去检查自己需要远程调用的服务是否已经注册到了 注册中心中, 一旦检查到注册中心没有检查到即使没有调用这个方法也会自动报错而阻止Spring启动 ,在开发时,可以选择手动关掉这个配置
配置某个服务启动时检查 在服务消费者中,关闭某个服务的启动时检查 (没有提供者时报错): <dubbo:reference interface="com.foo.BarService" check="false" /> 统一配置全部服务启动时不检查 关闭所有服务的启动时检查 (没有提供者时报错): <dubbo:consumer check="false" /> 默认是true. 表示注册中心不存在时报错 关闭注册中心启动时检查 (注册订阅失败时报错): <dubbo:registry check="false" />
dubbo.reference.com.foo.BarService.check=false dubbo.reference.check=false dubbo.consumer.check=false dubbo.registry.check=false
java -Ddubbo.reference.com.foo.BarService.check=false java -Ddubbo.reference.check=false java -Ddubbo.consumer.check=false java -Ddubbo.registry.check=false
消费者远程调用服务提供者, 服务提供者的实现逻辑可能存在大量的耗时操作,这是就可能存在大量的线程阻塞在这个方法上, 超时设置解决了这个问题, 一旦在指定的时间内,依然没有返回任何数据,同样会立即返回,保证系统的可用
缺省使用
如下,设置超时时间为3秒
<!-- 生成远程服务代理,可以和本地bean一样使用 userService --> <dubbo:reference id="userService" interface="com.changwu.service.UserService" timeout="3000"/>
从上图中,我标记了他们的作用, 最优先的就是方法级别, 接口次之 , 全局级别最低 , -- 相同级别的消费者设置优先,提供者设置次之
一般超时配置timeout 和 重试次数retries 配合使用
调用失败后,会根据重试的次数再次尝试远程调用, 重试次数不包含第一次,如果我们配置的3, 算上第一次,最多尝试调用四次
如果服务提供者存在集群, 消费者就会轮询集群进行重试
<!-- 生成远程服务代理,可以和本地bean一样使用 userService --> <dubbo:reference id="userService" interface="com.changwu.service.UserService" retries="3" timeout="3000"/>
我们给系统的某些功能进行了升级,但是不能一下把升级后的代码一次性全部适用到集群中的全部机器上, 使用多版本控制,先把集群中的一小部分机器进行升级,没有出现不可用现象再逐步拓展规模,最终完成全部替换
新旧版本的服务提供者全部继承同一个接口 com.foo.BarService , 通过ref指向不同的实现Bean
老版本服务提供者配置: <dubbo:service interface="com.foo.BarService" version="1.0.0" ref="userService1" /> <bean id="userService1" class="xxx"> 新版本服务提供者配置: <dubbo:service interface="com.foo.BarService" version="2.0.0" ref="userService2"/> <bean id="userService2" class="yyy">
老版本服务消费者配置: <dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" /> 新版本服务消费者配置: <dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" /> 如果不需要区分版本,可以按照以下的方式配置 [1]: <dubbo:reference id="barService" interface="com.foo.BarService" version="*" />
如果想在服务的消费者发送远程过程调用其他方法之前进行,进行参数的验证,如,判空处理, 可以选择使用本地存根
如, UserService 是某个服务提供者实现的, 消费者远程调用这个服务,进行如下配置 消费者在本地提供自己的实现,并判断参数的合法性
public class UserServiceImpl implements UserService{ // 注入这个被服务提供者支持的接口 private final UserService userService; // 提供构造函数 public UserServiceImpl(UserService userService) { this.userService = userService; } @Override public List<UserAddress> getUserAddressList(String userId) { // 判空 if (!StringUtils.isEmpty(userId)){ return userService.getUserAddressList(userId); } return null; } }
在 spring 配置文件中按以下方式配置: # 是消费者在本地自己的实现 com.changwu.service.UserServiceImpl <dubbo:service interface="com.changwu.service.UserServiceImpl" stub="true" /> 或 <dubbo:service interface="com.foo.BarService" stub="com.changwu.service.UserServiceImpl" />
如果还想添加诸如方法级别的配置,则保留dubboxml的配置文件
要求使用dubbo为2.6.3