目录
RPC框架
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务的协议。
基本原理:
- 通过网络连接进行服务间的通信;
- 序列化与反序列化机制。
RPC调用过程:
- 客户端(client)以接口的方式调用服务;
- 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);
- 客户端通过sockets将消息发送到服务端;
- 服务端存根( server stub)收到消息后进行解码(将消息对象反序列化);
- 服务端存根( server stub)根据解码结果调用本地的服务;
- 本地服务执行并将结果返回给服务端存根( server stub);
- 服务端存根( server stub)将返回结果打包成消息(将结果消息对象序列化);
- 服务端(server)通过sockets将消息发送到客户端;
- 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);
- 客户端(client)得到最终结果。
Dubbo架构
Dubbo快速入门
- 服务提供者
1. 定义服务接口
public interface DemoService {
String sayHello(String name);
}
2. 实现服务接口
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name +
", request from consumer: " + RpcContext.getContext().getRemoteAddress());
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
}
}
3. 用spring配置声明暴露服务
<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" 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://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">
<context:property-placeholder/>
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="demo-provider"/>
<dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<bean id="demoService" class="org.apache.dubbo.samples.basic.impl.DemoServiceImpl"/>
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService"/>
</beans>
4. 加载spring配置
public class BasicProvider {
public static void main(String[] args) throws Exception {
new EmbeddedZooKeeper(2181, false).start();
// wait for embedded zookeeper start completely.
Thread.sleep(1000);
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-demo-provider.xml");
context.start();
System.out.println("dubbo service started");
new CountDownLatch(1).await();
}
}
- 服务消费者
1. 通过 Spring 配置引用远程服务
<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" 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://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">
<context:property-placeholder/>
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="demo-consumer"/>
<dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="demoService" check="true" interface="org.apache.dubbo.samples.basic.api.DemoService"/>
</beans>
2. 加载Spring配置,并调用远程服务
public class BasicConsumer {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-demo-consumer.xml");
context.start();
DemoService demoService = (DemoService) context.getBean("demoService");
String hello = demoService.sayHello("world");
System.out.println(hello);
}
}
Dubbo中的异步调用
异步通讯对于服务端响应时间较长的方法是必须的,能够有效地利用客户端的资源,在dubbo中,消费端<dubbp:method>通过 async="true"标识。
<dubbo:reference id="xxx" ....>
<dubbo:method name="method1" async="true" />
</dubbo:reference>
- 方式一:NIO future主动获取结果,返回结果放在RpcContext中。
AsyncService asyncService = context.getBean("asyncService", AsyncService.class);
asyncService.sayHello("world");
CompletableFuture<String> helloFuture = RpcContext.getContext().getCompletableFuture();
helloFuture.whenComplete((retValue, exception) -> {
if (exception == null) {
System.out.println("return value: " + retValue);
} else {
exception.printStackTrace();
}
});
CompletableFuture<String> f = RpcContext.getContext().asyncCall(() -> asyncService.sayHello("async call request"));
System.out.println("async call returned: " + f.get());
- 方式二:通过回调(Callback)参数
(1)服务提供者需在方法中声明Callback参数,其后在Service实现中显示地调用Callback的方法;
<dubbo:service interface="org.apache.dubbo.samples.callback.api.CallbackService" ref="callbackService"
connections="1" callbacks="1000"> # 标识第二个参数是callback类型
<dubbo:method name="addListener">
<dubbo:argument index="1" callback="true"/>
</dubbo:method>
</dubbo:service>
(2)Callback接口的实现类在消费端,当方法发生调用时,消费端会自动export一个Callback服务,在RPC调用完成后,不能立即结束线程。
- 方式三:事件通知(推荐)
这种方式更简单,对服务提供方来讲是透明的,在配置和代码上无需做任何改动。只要在消费端定义一个“通知者”的Spring Bean,指定方法的onreturn和onthrow事件即可。
<bean id="demoCallback" class="org.apache.dubbo.samples.notify.impl.NotifyImpl"/>
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.samples.notify.api.DemoService"
version="1.0.0" group="cn">
<dubbo:method name="sayHello" async="false" onreturn="demoCallback.onReturn" onthrow="demoCallback.onThrow"/>
</dubbo:reference>