Dubbo第一讲:从RPC到Dubbo框架详解

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Dubbo第一讲:从RPC到Dubbo框架详解

1、什么是RPC?

RPC是指远程过程调用,是一种进程间通信方式。它允许程序调用另一台机器的程序,而不必关注调用的细节


2、RPC作用

1、将核心业务抽取出来,作为独立的服务,形成稳定的服务中心,通过远程调用的方式对外提供服务

2、整合业务,提高复用,使前端应用能更快速的响应多变的市场需求

3、远程通讯:基于长连接的NIO框架抽象封装(netty),包括多种线程模型,序列化方式;

4、集群容错:提供基于接口方法的透明远程过程调用

5、动态提供服务

动态查找服务地址:基于注册中心目录服务,使服务消费方能动态的查找服务提供方;

动态平滑的增减机器

业务场景

1)要搭建一个新服务,免不了需要依赖他人的服务,而现在他人的服务都在远端,怎么调用?

2)其它团队要使用我们的服务,我们的服务该怎么发布以便他人调用?


3、RPC框架架构:(rpc的核心是通讯和序列化)

1、基于Netty实现NIO通讯 //介绍NIO,看上一篇文章

2、Spring实现基于接口方法的透明远程调用

AOP,IOC,DI

3、zk实现动态查找服务地址,动态增减机器

ZAB协议,高可靠的一致性服务


4、RPC调用的流程/原理?**

调用原理如下图所示

1、服务消费方(client)调用以本地调用方式调用服务

2、client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(序列化);

3、client stub找到服务地址,并将消息发送到服务端;

4、server stub收到消息后进行解码(反序列化);

5、server stub根据解码结果调用本地的服务

6、本地服务执行并将结果返回给server stub;

7、server stub将返回结果打包成消息并发送至消费方

8、client stub接收到消息,并进行解码

9、服务消费方得到最终结果

RPC的目标是将2-8这些步骤封装起来,让用户对这些细节透明底层使用 jdk 动态代理

RPC的两大核心:

  • 通讯,序列化

5、说说RPC的实现原理?重点

1、服务端具体实现(即dubbo中的服务层)

解析表现层的request对象,通过接口在Map查找实现类,并调用结果封装成result对象(requestID,状态code,返回object),result对象通过NIO返回给客户端,客户端解析后作为方法调用的结果

1)、自定义注解

服务端写一个自定义注解RPCService,在spring.xml中配置RPCService、MyServer(或使用@Component注解扫描)

@Target({ElementType.TYPE}) //注解用在接口上
@Rentention(RententionPolicy.RUNTIME) //VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息
@Component 
  public @interface RpcService{
    String value();  //使注解可以添加参数  一个字符串 
  }

2)、服务器端写实现类HelloServiceImpl,实现业务逻辑,并用RpcService注解

@RpcService("HelloService")
public class HelloServiceImpl implements HelloService{
    public String hello(String name){
      return "Hello:"+name;}
}

3)、存入map

spring在初始化MyServer实例时,会调用setApplicationContext,即设置上下文环境的方法:实现在Spring启动后初始化的功能。在该方法中,通过getBeansWithAnnotation 获取自定义注解的所有bean,存入serviceBeanMap<注解参数,实例bean>(接口名-实现类) Map:<HelloService,HelloServiceImpl@3427b02d>。遍历serviceBeanMap,将所有实现类实例,在存入一个handlerMap<接口名,实现类>,相当于复制内存数据作为待处理map

public void setApplicationContext(ApplicationContext ctx)throws BeansException{
  //通过spring上下文环境获取被自定义注解的所有bean,存入map
  Map<String,Object> serviceBeanMap = ctx.getBeansWithAnnotation(RPCService.class);
    //遍历serviceBeanMap,获取注解的参数,将所有实现类实例,再存入一个handlerMap<接口名,实现类>
    if(MapUtils.isNotEmpty(serviceBeanMap)){
      for(Object serviceBean:serviceBeanMap.values()){
        String interfaceName = serviceBean.getClass().getAnnotation(RpcService.class).value().getName();
        handlerMap.put(interfaceName,serviceBean);
      }
    }
}

4)、handle解析 (通过反射技术

1、从表现层的request中解析出类名、方法名、参数类型、参数值

2、从handlerMap里,通过类名,查询实现类serviceBean

3、反射方式调用实现类方法

a Class.forName根据类名className加载接口名

b getMethod获得接口类的指定方法和参数类型

c invoke(serviceBean,parameters) 反射传入实现类对象和参数,并返回方法的执行结果

private Object handle(RpcRequest request)throws Throwable{
  //1、从request中解析出类名、方法名、参数类型、参数值;
  String className = request.getClassName();
  String methodName = request.getMethodName();
  Class<?>[] parameterTypes = request.getParameterTypes();
  Object[] parameters = request.getParameters();
  //2.根据类名,从handleMap里查询实现类对象;
  Object serviceBean = handlerMap.get(className);
  //3.根据类名加载接口类
  Class<?> forName = Class.forName(className);
  //3.获得接口类的指定方法和参数类型;
  Method method = forName.getMethod(methodName, parameterTypes);
  //4.invoke反射调用方法并返回(实现类对象,参数)
  return   method.invoke(serviceBean, parameters);
}

5)、nettyServer.start

同时启动一个nettyServer.start():向zk注册服务器地址sericeRegis;等待客户端连接并处理Handler类用户请求,业务调用,封装返回结果

Spring.xml的配置

<context:component-scan base-package="com.zjut.rpc.sample.server"/>  //注解扫描,扫描路径下的类
<context:property-placeholder location="classpath:rpc.properties"/> //加载属性文件,存储zk地址
<bean id="serviceRegistry" class="com.zjut.rpc.registry.ServiceRegistry"> //管理 服务注册的对象
  <constructor-arg name="registryAddress" value="${registry.address}"/>
</bean>
<bean id="rpcServer" class="com.zjut.rpc.server.RpcServer">    //提供服务的对象,依赖注入服务注册对象
  <constructor-arg name="serverAddress" value="${server.address}"/>
  <constructor-arg name="serviceRegistry" ref="serviceRegistry"/>
</bean>

2、客户端具体实现(即dubbo中的表现层)

spring动态代理,封装成request对象,在zk中通过接口名查询服务器地址,构造NIO连接与服务器通信 , 客户端实现代理类,通过代理类让客户端“感觉”在操作本地方法

1)、tomcat启动Spring

tomcat启动时,有springListener,加载spring.xml;(xml里配置RpcProxy bean)

@autowired  
RpcProxy rpcProxy;//自动装配(在spring.xml按类型查找bean,注入到rpcProxy、默认按类型匹配)

2)、客户端调用业务方法时,创建动态代理类RpcProxy,接管和扩展该方法

代理该业务接口HelloService create(HelloService.class) 调用的方法不用继承HelloService接口,两者无关

@Autowired
private RpcProxy rpcProxy;
@Test 
public void helloTest1(){
  //调用代理对象的create方法,代理HelloService接口;因为肯定是实现该接口的对象,所以直接传对象;create名字自己定,可以是getProxy
  HelloService helloService = rpcProxy.create(HelloService.class);
  //调用代理的方法,内部执行invoke
  String result = helloService.hello("World");
  SYSo("服务器返回结果");
  SYSO(result);
}

3)、代理类中

  1. 通过invoke反射获取(UUID(标识唯一请求id)、类名、方法名、参数类型,参数,超时时间),封装成request对象
  2. 代理类内部代码【向zk查询服务器地址、netty连接服务器、将request对象序列化并发送,等待返回结果response.getResult()】
  3. Handler ( 封装,解码,提取result返回给业务函数 )*****
public<T> T create(Class<?> interfaceClass){
   return(T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class<?>[]{interfaceClass},new InvocationHandler(){
      public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
        //创建RpcRequest,封装被代理类的属性
        RPCRequest request = new RpcRequest();
        request.setRequestId(UUID.randomUUID().toString());
        request.setClassName(method.getDeclaringClass().getName());
        request.setMethodName(method.getName());
        request.setParameterTypes(method.getParameterTypes());
        request.setParameters(args);
        //向zk查询服务器地址  host  port
        serverAddress = serviceDiscovery.discover();
        //创建RpcClient,netty连接服务器
        RpcClient client = new RpcClient(host, port);
        //request对象序列化并发送
        RpcResponse response = client.send(request);
        //等待返回结果response.getResult()
        if(response.isError()){
          throw response.getError();
        }else{
          return response.getResult();
        }
      }
    };
   )
}

3、zk存储服务层地址

客户端从zk查询获取服务器地址(接口,服务器地址)

服务器要向zk注册服务

对于服务提供者,zookeeper提供心跳检测功能,定时向各个服务提供者发送请求(socket长连接),若是没有响应,则认为服务提供者已经挂了,会将其剔除

对于服务消费者,一旦路径数据变化(增或减),zookeeper会通知地址列表发生改变,从而更新(watcher机制,后期的博客会讲到)


6、消息里为什么要带有requestID? 重点

如果使用netty的话,一般会用channel.writeAndFlush()方法来发送消息二进制串,这个方法调用后对于整个远程调用(从发出请求到接收到结果)来说是一个异步的, 至于服务端的结果,是服务端处理完成后,再以消息的形式发送给客户端的。

出现以下两个问题:

1)怎么让当前线程“暂停”,等结果回来后,再向后执行?****

2)多个线程同时进行远程方法调用,这时建立在client server之间的socket连接上会有很多双方发送的消息传递,前后顺序也可能是随机的,怎么知道哪个消息结果是原先哪个线程调用的。

怎么解决呢?

1、client线程每次通过socket调用一次远程接口前,生成一个唯一的ID,即requestID

2、将处理结果的回调对象callback存放到全局ConcurrentHashMap里面put(requestID, callback)

3、当线程调用channel.writeAndFlush()发送消息后,紧接着执行**callback的get()**方法试图获取远程返回的结果

4、服务端接收到请求并处理后,将response结果发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到requestID,再从前面的ConcurrentHashMap里面get(requestID),从而找到callback对象


千万不要轻易喜欢一个人,因为遇见一个人只需要一秒,爱上一个人可能需要一天,而忘记一个人需要一辈子 。 --黄渤对张艺兴说《极限挑战》

相关文章
|
4月前
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
7天前
|
负载均衡 Dubbo NoSQL
Dubbo框架的1个核心设计点
Java领域要说让我最服气的RPC框架当属Dubbo,原因有许多,但是最吸引我的还是它把远程调用这个事情设计得很有艺术。
Dubbo框架的1个核心设计点
|
18天前
|
Dubbo Java 应用服务中间件
微服务框架Dubbo环境部署实战
微服务框架Dubbo环境部署的实战指南,涵盖了Dubbo的概述、服务部署、以及Dubbo web管理页面的部署,旨在指导读者如何搭建和使用Dubbo框架。
68 17
微服务框架Dubbo环境部署实战
|
1月前
|
Dubbo 网络协议 Java
RPC框架:一文带你搞懂RPC
这篇文章全面介绍了RPC(远程过程调用)的概念、原理和应用场景,解释了RPC如何工作以及为什么在分布式系统中广泛使用,并探讨了几种常用的RPC框架如Thrift、gRPC、Dubbo和Spring Cloud,同时详细阐述了RPC调用流程和实现透明化远程服务调用的关键技术,包括动态代理和消息的编码解码过程。
RPC框架:一文带你搞懂RPC
|
1月前
|
开发框架 Dubbo 应用服务中间件
微服务开发框架-----Apache Dubbo
这篇文章介绍了Apache Dubbo微服务开发框架,它提供RPC通信和微服务治理能力,支持服务发现、负载均衡和流量治理等功能,并强调了Dubbo在微服务规模化实践和企业级治理方面的优势。
微服务开发框架-----Apache Dubbo
|
1月前
|
缓存 负载均衡 监控
Dubbo框架整体认知
该文章主要介绍了Dubbo框架的整体认知,包括Dubbo的概念、产生的背景、解决的问题、架构以及功能特性等。
Dubbo框架整体认知
|
1月前
|
负载均衡 Dubbo 应用服务中间件
框架巨擘:Dubbo如何一统异构微服务江湖,成为开发者的超级武器!
【8月更文挑战第8天】在软件开发中,微服务架构因灵活性和可扩展性备受欢迎。面对异构微服务的挑战,Apache Dubbo作为高性能Java RPC框架脱颖而出。它具备服务注册与发现、负载均衡及容错机制等核心特性,支持多种通信协议和序列化方式,能有效连接不同技术栈的微服务。Dubbo的插件化设计保证了面向未来的扩展性,使其成为构建稳定高效分布式系统的理想选择。
35 5
|
1月前
|
XML 存储 JSON
(十二)探索高性能通信与RPC框架基石:Json、ProtoBuf、Hessian序列化详解
如今这个分布式风靡的时代,网络通信技术,是每位技术人员必须掌握的技能,因为无论是哪种分布式技术,都离不开心跳、选举、节点感知、数据同步……等机制,而究其根本,这些技术的本质都是网络间的数据交互。正因如此,想要构建一个高性能的分布式组件/系统,不得不思考一个问题:怎么才能让数据传输的速度更快?
|
2月前
|
分布式计算 负载均衡 数据安全/隐私保护
什么是RPC?有哪些RPC框架?
RPC(Remote Procedure Call,远程过程调用)是一种允许运行在一台计算机上的程序调用另一台计算机上子程序的技术。这种技术屏蔽了底层的网络通信细节,使得程序间的远程通信如同本地调用一样简单。RPC机制使得开发者能够构建分布式计算系统,其中不同的组件可以分布在不同的计算机上,但它们之间可以像在同一台机器上一样相互调用。
117 8
|
2月前
|
网络协议 Dubbo Java
什么是RPC?RPC和HTTP对比?RPC有什么缺点?市面上常用的RPC框架?
选择合适的RPC框架和通信协议,对于构建高效、稳定的分布式系统至关重要。开发者需要根据自己的业务需求和系统架构,综合考虑各种因素,做出适宜的技术选型。
126 1