Dubbo配置
dubbo.application
对应 org.apache.dubbo.config.ApplicationConfig, 代表当前应用的信息
- name: 当前应用程序的名称,在dubbo-admin中我们也可以看到,这个代表这个应用名称。我们 在真正时是时也会根据这个参数来进行聚合应用请求。
- owner: 当前应用程序的负责人,可以通过这个负责人找到其相关的应用列表,用于快速定位到责 任人。
- qosEnable : 是否启动QoS 默认true
- qosPort : 启动QoS绑定的端口 默认22222
- qosAcceptForeignIp: 是否允许远程访问 默认是false
这里需要注意的是:配置项类似qosEnable,对应的xml写法和properties文件写法,不一样
xml配置案例
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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">
<dubbo:application name="service-consumer">
<dubbo:parameter key="qos.enable" value="true"/>
<dubbo:parameter key="qos.port" value="33333"/>
<dubbo:parameter key="qos.accept.foreign.ip" value="true"/>
</dubbo:application>
<dubbo:registry address="zookeeper://81.xx.xx.80:2181?timeout=30000"/>
<dubbo:reference id="helloService" interface="com.lagou.service.HelloService" />
</beans>
注解形式配置文件
dubbo.application.name=service-consumer33355555
dubbo.application.owner=444
dubbo.registry.address=zookeeper://81.xx.xx.80:2181?timeout=30000
dubbo.application.qosEnable=true
dubbo.application.qosPort=33333
dubbo.application.qosAcceptForeignIp=false
小坑:在第一次进行绑定端口的参数配置的时候,发现qosPort没有生效,代码追踪到 org.springframework.beans.AbstractPropertyAccessor中的setPropertyValues方法,发现在properties中,不能配置 dubbo.application.qos.port=33333 必须配置成 dubbo.application.qosPort=33333才行
那么,我们在xml中时,配置成了 <dubbo:parameter key="qos.port" value="33334"/>,究竟是怎么转换的呢?
答案应该在解析xml文件的时候,就针对这个配置文件进行自动拼接了,即 qos.port -> qosPort
JDK SPI
Java中如果想要使用SPI功能,先提供标准服务接口,然后再提供相关接口实现和调用者。这样就可以通 过SPI机制中约定好的信息进行查询相应的接口实现。
SPI遵循如下约定:
1、当服务提供者提供了接口的一种具体实现后,在META-INF/services目录下创建一个以“接口全 限定名”为命名的文件,内容为实现类的全限定名;
2、接口实现类所在的jar包放在主程序的classpath中;
3、主程序通过java.util.ServiceLoader动态装载实现模块,它通过扫描META-INF/services目录下 的配置文件找到实现类的全限定名,把类加载到JVM;
4、SPI的实现类必须携带一个无参构造方法;
案例展示,一个简单的SPI例子
-
创建 api项目,定义一个接口 HelloService
-
创建impl项目,编写接口实现类 HumanHelloService
在资源目录下,创建一个META-INF.services包,并且创建一个文件,文件名就为:类名的全限定名,
在此文件中,填入两个实现类的完全限定名
- 创建main项目,引入两个两个项目
创建一个测试项目,使用ServiceLoader去加载系统中,所有实现了HelloService的类
final ServiceLoader<HelloService> helloServices=ServiceLoader.load(HelloService.class);
Dubbo SPI
下面使用dubbo进行 SPI的案例演示,下图为全项目结构
- 创建API项目,引入dubbo的坐标,在接口上面,添加dubbo的SPI注解
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.5</version>
</dependency>
</dependencies>
- 创建impl项目,编写接口实现类 HumanHelloService,DogHelloService,创建一个META-INF.services包,并且创建一个文件,文件名就为:类名的全限定名, 在此文件中,填入两个实现类的完全限定名
注意:这里在完全限定名之前,需要带上一个key
3)创建main测试项目,引入两个工程
使用extensionLoader,通过接口,加载实现类
//获取扩展加载器
ExtensionLoader<HelloService> extensionLoader=ExtensionLoader.getExtensionLoader(HelloService.class);
//遍历所有支持的扩展点 META-INF.dubbo
Set<String> extensions=extensionLoader.getSupportedExtensions();
for (String extension : extensions) {
String result = extensionLoader.getExtension(extension).sayHello();
System.out.println(result);
}
- 运行结果
Dubbo 为什么单独实现 SPI?
-
JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加 载,会很浪费资源
-
如果有扩展点加载失败,则所有扩展点无法使用
-
提供了对扩展点包装的功能(Adaptive),并且还支持通过set的方式对其他的扩展点进行注入
Dubbo 动态选择组件
- 创建接口,在sayHello中增加 Adaptive 注解,并且在参数中 提供URL参数.注意这里的URL参数的类为 org.apache.dubbo.common.URL
其中@SPI可以指定一个字符串参数,用于指明该SPI的默认实现。
-
实现类实现接口
-
编写DubboAdaptiveMain
注意点:
-
因为在这里只是临时测试,所以为了保证URL规范,前面的信息均为测试值即可,关键的点在于 hello.service 参数,这个参数的值指定的就是具体的实现方式。关于为什么叫 hello.service 是因为这个接口的名称,其中后面的大写部分被dubbo自动转码为 . 分割。 我们的接口名称为HelloService,所以这里的URL地址参数就为 hello.service
-
通过 getAdaptiveExtension 来提供一个统一的类来对所有的扩展点提供支持(底层对所有的扩展 点进行封装)。
-
调用时通过参数中增加 URL 对象来实现动态的扩展点使用。
-
如果URL没有提供该参数,则该方法会使用默认在 SPI 注解中声明的实现。
Dubbo负载均衡
如果想要实现负载均衡,则需要在消费者引用服务的注解上面,增加 loadbalance参数
注意:这里传入的是全部小写的字母
- random 随机
- roundrobin 轮询
- leastactive 最少活跃调用数
- consistenthash 一致性Hash
Dubbo 自定负载均衡器
-
创建一个工程loadbalance项目,新建一个类,继承dubbo的-
LoadBalance接口 -
使用SPI的方式,新建一个文件名为:
org.apache.dubbo.rpc.cluster.LoadBalance,文件内部填入新创建类的引用路径onlyFirst=com.lagou.loadbalance.OnlyFirstLoadBalance -
在Consumer项目中,直接引入即可
-
演示效果
Dubbo future异步调用
dubbo支持异步调用,使用异步调用的时候,使用xml配置的方法,在reference中,针对method,增加async参数。在main方法中,使用future模式进行获取数据 Future<Object> future= RpcContext.getContext().getFuture();
consumer.xml
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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">
<dubbo:application name="service-consumer"/>
<dubbo:registry address="zookeeper://xx.xx.xx.80:2181?timeout=30000"/>
<dubbo:reference id="helloService" interface="com.lagou.service.HelloService">
<dubbo:method name="sayHello" async="true"></dubbo:method>
</dubbo:reference>
</beans>
XmlCosumerMain.java
import com.lagou.service.HelloService;
import org.apache.dubbo.rpc.RpcContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class XMLConsumerMain {
public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
HelloService service = context.getBean(HelloService.class);
System.out.println("启动了消费端...");
while (true) {
System.in.read();
String world = service.sayHello("world", 100);
//利用future模式 来获取
Future<Object> future= RpcContext.getContext().getFuture();
System.out.println(world);
System.out.println("future:"+future.get());
}
}
}
Dubbo控制台部署
1.从git 上下载项目 github.com/apache/dubb… 下载master分支
2.进入dubbo-admin项目,修改项目下的dubbo.properties文件
- 注意dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号
- dubbo.registry.address=zookeeper://zk所在机器ip:zk端口
- dubbo.admin.root.password=root
- dubbo.admin.guest.password=guest
3.切换到项目所在的路径,使用mvn 打包 mvn clean package -Dmaven.test.skip=true
4.进入dubbo-admin下面的target目录,java 命令运行 java -jar 对应的jar包
控制台启动完毕后,页面访问 http://localhost:7001/
正式进入主界面
zookeeper图像化客户端
-
进入到文件夹的build目录
-
java -jar zookeeper-dev-ZooInspector.jar 启动客户端,填入zookeeper地址
-
看到了zookeeper的节点
服务降级
1. 在 dubbo 管理控制台配置服务降级
屏蔽和容错
- mock=force:return+null
表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
确认屏蔽后,显示已屏蔽,屏蔽的按钮显示为 恢复
可以看到消费者直接返回 null
- mock=fail:return+null
表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
在明确调用失败后,返回result,而不会直接中断程序,所有的接口都应该支持容错,
2. 指定返回简单值或者null
<dubbo:reference id="xxService" check="false" interface="com.xx.XxService"
timeout="3000" mock="return null" />
<dubbo:reference id="xxService2" check="false" interface="com.xx.XxService2"
timeout="3000" mock="return 1234" />
如果是标注 则使用@Reference(mock="return null") @Reference(mock="return 简单值") 也支持 @Reference(mock="force:return null")
3.使用java代码 动态写入配置中心
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension() ;
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://IP:端 口"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?
category=configurators&dynamic=false&application=foo&mock=force:return+null"));