dubbo入门

DUBBO入门

Zookeeper准备

本文默认使用Zookeeper作为注册中心。
1)到Zookeeper官网下载,安装到本机。我这里安装在Windows上。
2)找到conf下的zoo_sample.cfg,复制一份修改为zoo.cfg,并在里面增加:
dataDir=D:\dev\zookeeper-3.4.12\data
dataLogDir=D:\dev\zookeeper-3.4.12\log
3)进入bin目录,启动Zookeeper。windows是运行zkServer.cmd,linux是运行zkServer.sh。注意不要关闭zkServer.cmd启动弹出的控制台窗口。

一、RPC原理

1.RPC原理简介


单体应用:本地服务调用

RMI:远程方法调用(Remote Method Invocation),让客户端Java虚拟机的对象像调用本地对象一样调用服务端Java虚拟机的对象的方法。

分布式应用:远程调用(Remote Procedure Call),HTTP,RPC

>RPC:远程过程调用,调用远程机器的服务,就像调用本地服务一样。WebService:基于HTTP的RPC,跨平台;dubbo:基于TCP的RPC框架,性能比基于HTTP的高。分布式应用的基础。RPC和RMI有相识之处。

2.调用过程


如图


(1)服务消费方client艺本地调用方式调用服务;

(2) client stub接收调用后将方法、参数编码;

(3)client stub 取到远程接口的请求地址,组建协议报文并发送;

(4)server stub 收到消息,并解码;

(5)server stub反射调用本地服务;

(6)本地服务执行,结果放回给server stub;

(7)server stub将消息编码发送给client;

(8)client stub接收消息,解码;

(9)服务消费方client获得结果。

stub:存根,简单理解为代理对象;应用透明,是指2-8被封装,对client而言调用远程方法就像调用本地方法,对本地client实际调用的是代理对象。dubbo,代理是Jdk动态代理;AOP,也有动态代理。服务,简单的理解为一个接口。

3.实现原理

1).服务端


服务接口interface:service。dubbo把interface打包发布,调用方、提供方公共引用。

接口实现interfaceImpl:serviceImpl。

服务注册与暴露export:Rpc.export(ip,service,port)。

2).客户端


服务接口interface:service。与server端公用。接口interface含义:通讯协议

服务引用:localService=Rpc.reference(service,ip,port);localService.callMethod();这里的service对象在本地而言是一个代理对象。

3).RPC原型


服务端:注册并暴露服务。客户端:取得调用地址,调用服务。

实现:

服务端:

export(service,ip,port){

//1.创建Socket服务器

server=ServerSocket(ip,port)

//2.监听Socket请求

server.accept()

//3.接收请求流,解码,反序列化为请求对象

input=decodeAndDeserialize(inputStream)

//4.反射调用本地服务

method=service.getClass().getMethod(methodName,paramTypes)

localResult=method.invoke(service,args)

//5.编码处理,回传client消息

output=encodeAndSerialize(localResult)

}


客户端:reference(clazz,ip,port){

//1.生成JDK动态代理

proxy=newProxyInstanc(clazz's ClassLoader,Class[]{clazz},InvocationHandler)

//2.invoke方法,发送请求和接收响应

InvocationHandler.invoke(proxy,method,args){

//3.创建Socket Client

client=Socekt(ip,port);

//4.编码,序列化,发送请求

output=encodeAndSerialize(methodName,paramTypes,args)

//5.读取服务端响应,解码,反序列化为结果对象

localResult=decodeAndDeserialize(remoteResult)
}
}

上面RPC原型可能存在的问题

服务发布与地址管理?增加注册中心,采用发布订阅模式,如Zookeeper。

序列化选择与效率?dubbo默认使用Hessian。

通信框架?简化Socket操作,dubbo默认使用netty。

负载均衡,分布式配置,容错机制??

二、dubbo服务暴露与调用过程

1.服务暴露

对每个<dubbo:service>创建一个ServiceBean(父类是ServiceConfig)。
如图:


1)获取注册中心url列表

2)创建协议url

3)远程暴露

实现:

class DubboExport{
	private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();		
<br>	/**
      *1.获取注册中心url列表
       * registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&pid=2956&registry=zookeeper&timestamp=1507004600231
       */
<br>    List<URL> registryURLs = loadRegistries();

    	/**
         * 2.创建协议url
         * dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2956&side=provider&timestamp=1507004625957
         */
      URL url = new URL(name, host, port, path, map);

      /**3.远程暴露*/
      for (URL registryURL : registryURLs) {
                Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                Exporter<?> exporter = protocol.export(invoker);
                exporters.add(exporter);
            }}


远程暴露过程:

将ref封装为invoker

将invoker转换为exporter

>注册服务到zookeeper

订阅

返回新的exporter实例
查看方法:RegistryProtocol.export(final Invoker originInvoker)

2.服务引用

引用过程:


ReferenConfig:reference.xml。

远程引用服务配置:<dubbo:reference id="service" checke="false" interfacer="com.xxx.yyy.Service">

本地使用service会得到对应的远程服务代理对象

Service service = (Service) context.getBean("service"); // 获取远程服务代理

Object result = demoService.method(args); // 执行远程方法


获取代理简化流程:查看InvokerInvocationHandler.invoke方法
proxy0.xxxMethod()
-->InvokerInvocationHandler.invoke
-->MockClusterInvoker.invoke(Invocation invocation)
-->FailoverClusterInvoker.invoke(final Invocation invocation)

-->RegistryDirectory.list(Invocation invocation) //根据RpcInvocation中的methodName获取Invoker

-->router过滤

-->loadBalancer选取一个Invoker

-->执行filter链

-->DubboInvoker.invoke(Invocation inv)

3.Dubbo协议


Dubbo协议:是一种单连接、长连接、TCP、NIO、Hessian序列化的协议。

报文格式:

具体的编码解码实现可以参看:

消息头编码:ExchangeCodec.encodeRequest(Channel channel, ChannelBuffer buffer, Request req)

消息头解码:ExchangeCodec.decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header)


request编码:DubboCodec.encodeRequestData(Channel channel, ObjectOutput out, Object data)

request解码:DecodeableRpcInvocation.decode(Channel channel, InputStream input)

response编码:DubboCodec.encodeResponseData(Channel channel, ObjectOutput out, Object data)

response解码:DecodeableRpcResult.decode(Channel channel, InputStream input)

4.泛化调用

含义:以GenericService代替所有远程接口引用

使用:

GenericService service = (GenericService) context.getBean("testGenericService");

//POJO参数需要转map

Object result = service.$invoke("doService", new String[]{String.class.getName()}, new Object[]{"aaa"});

String strResult = (String) result;

三、dubbo异步调用

默认情况dubbo的client端和server端请求和响应是同步的,除非指定异步。
流程:


使用:
applicationContext.xml

<dubbo:reference id="asyncService" interface="com.zafkiel.demo.api.itf.IAsyncService"

group="demo-server" version="1.0.0" check="false" async="true" timeout="60000"/>
或者

<dubbo:reference id="demoService" interface="com.zafkiel.demo.DemoService">

<dubbo:method name="sayHello" async="true" />

</dubbo:reference>

具体可以查看下面的实例

四、Demo

这里会简单介绍一个实例。

1.首先定义消费者和提供者共同使用的api

简单的接口IAsyncService.java

/**

  • @Author: Zafkiel
  • @Date: 2019/02/10
    */
    public interface IAsyncService {
    String sayHello(String name);
    }

2.在提供者(服务端)提供api的实现

1).api的实现AsyncServiceImpl.java

/**

  • @Author: Zafkiel
  • @Date: 2019/02/10
    */
    @Service(value = "asyncService")
    public class AsyncServiceImpl implements IAsyncService {
    @Override
    public String sayHello(String s) {
    System.out.println("远程服务asyncService处理启动时间" + System.currentTimeMillis());
    System.out.println("IAsyncService接口实现");
    System.out.println("等待6s");
    try {
    Thread.sleep(6000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("远程服务asyncService处理结束时间" + System.currentTimeMillis());
    return "hello," + s + "!";
    }
    }

2).server端配置

demo-context.xml:

<context:component-scan base-package="com.zafkiel.demo.server"/>

<!--Dubbo应用配置-->
<dubbo:application name="demo-server" owner="zafkiel" organization="com.zafkiel"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:registry protocol="zookeeper" address="zookeeper://127.0.0.1:2181"/>
<dubbo:annotation package="com.zafkiel.demo.server"/>

<!--服务暴露-->
<dubbo:service ref="asyncService" interface="com.zafkiel.demo.api.itf.IAsyncService"
               group="demo-server" version="1.0.0" />

3).在resources下面增加dubbo.properties:

dubbo.spring.config=classpath:demo-context.xml
指定applicationContext的配置文件。

4).启动Server:写一个Main执行dubbo的Main方法。

3.在消费者(客户端)调用提供者的服务

1).写一个简单的调用方法:

public class AsyncStarter {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:demo-context.xml");
context.start();

    IAsyncService asyncService = (IAsyncService) context.getBean("asyncService");
    Long start = System.currentTimeMillis();
    System.out.println("业务线程启动时间:" + start);
    //sayHello需要消耗6s
    //这个结果并没有什么卵用
    String initResult = asyncService.sayHello("Nero");
    //活动Future
    Future<String> stringFuture = RpcContext.getContext().getFuture();
    //业务线程,当前线程睡眠5s
    Thread.sleep(5000);
    String finalResult = stringFuture.get();
    System.out.println(finalResult);
    Long end = System.currentTimeMillis();
    System.out.println("业务线程结束时间:" + end);
    System.out.println(end - start);
    context.close();

}

}

2).client配置

demo-context.xml:

<context:component-scan base-package="com.zafkiel.demo.client"/>

<!--Dubbo应用配置-->
<dubbo:application name="demo-client" owner="zafkiel" organization="com.zafkiel"/>
<dubbo:protocol name="dubbo" port="20990"/>
<dubbo:registry protocol="zookeeper" address="zookeeper://127.0.0.1:2181"/>
<dubbo:annotation package="com.zafkiel.demo.client"/>

<!--服务引用-->
<!--对方法级别使用<dubbo:method>-->
<dubbo:reference id="asyncService" interface="com.zafkiel.demo.api.itf.IAsyncService"
                 group="demo-server" version="1.0.0" check="false" async="true" timeout="60000"/>

3).同样增加dubbo.properties

dubbo.spring.config=classpath:demo-context.xml

4).运行AsyncStarter

完整代码可以从我的云盘分享下载:
链接: https://pan.baidu.com/s/1n8BM8QXfaibPIJiFCVec2Q 提取码: 5nnc。

引用


1.dubbo源码解析

2.dubbo官网

3.Zookeeper官网

posted @ 2019-03-13 15:14  Warspite  阅读(324)  评论(1编辑  收藏  举报