Go To My HomePage

dubbo应用开发

一、基础知识

分布式系统

​ 分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统。

架构发展演变

单一应用架构

一个war包将所有功能都部署在一起搞定,减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

sing-arch

优点:适用与小型网站、简单易用

缺点:性能扩展难、协同开发问题、不利于升级维护

垂直应用架构

当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

v-arch

优点:团队各司其职更易管理、性能扩展也更方便、更有针对性

缺点:公用模块无法重复利用,开发性的浪费

分布式服务架构

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)[Remote Procedure Call]是关键。

distru-arch

优点:增大系统容量、模块化高、开发及团队协作效率高

缺点:系统设计、管理和运维难度增加

流动计算架构

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)[Service Oriented Architecture]是关键

soa-arch

优点:增大系统容量、模块化高、动态协调资源、开发及团队协作效率高

缺点:系统设计、管理和运维难度增加

RPC

RPC定义

​ RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。

RPC基本原理

rpc-base

RPC最核心两个模块:通讯、序列化

二、Dubbo核心概念

简介

​ dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

基本概念

dubbo-architecture

服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。

服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者

监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

调用关系说明

  • 服务容器负责启动,加载,运行服务提供者
  • 服务提供者在启动时,向注册中心注册自己提供的服务
  • 服务消费者在启动时,向注册中心订阅自己所需的服务
  • 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  • 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
  • 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

连通性说明

  • 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
  • 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
  • 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
  • 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
  • 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
  • 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
  • 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
  • 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者

三、Dubbbo入门

前提条件:配置zookeeper环境、incubator-dubbo-ops切换到master分支【dubbo-admin和dubbo-monitor】

incubator-dubbo-ops下载地址

搭建Dubbo Admin、搭建Dubbo Monitor方法详见GitHub参考文档

创建Dubbo应用

  • 创建maven项目公共项目抽取公共部分【bean、service接口等等】

  • 创建provider和consumer

    • 引入dubbo依赖,并引入curator依赖,并适配配置日志环境
    <!-- 连接zookeeper使用的curator -->
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>2.13.0</version>
        <exclusions>
            <exclusion>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- 日志采用slfj+logback -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.6</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.2.3</version>
    </dependency>
    
    • provider实现service并配置dubbo
    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="user-service-provider" />
      
    <!-- 使用zookeeper广播注册中心暴露服务地址 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.155:2181,192.168.1.155:2182,192.168.1.155:2183"/>
      
    <!-- 用dubbo协议在20800端口暴露服务 -->	
    <dubbo:protocol name="dubbo" port="20800"/>
      
    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="com.kun.service.UserService" ref="userService"/>
      
    <!-- 和本地bean一样实现服务 -->
    <bean id="userService" class="com.kun.service.impl.UserServiceImpl"/>
    
    • consumer调用service并配置dubbo
    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
    <dubbo:application name="user-service-consumer" />
      
    <!-- 使用zookeeper注册中心暴露发现服务地址 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.155:2181,192.168.1.155:2182,192.168.1.155:2183" />
      
    <!-- 生成远程服务代理,可以像本地bean一样使用userService -->
    <dubbo:reference id="userService" interface="com.kun.service.UserService" ></dubbo:reference>
    
  • 启动Provider在Dubbo Admin可看相关信息,启动Consumer进行消费在Dubbo Admin也可见相关信息

四、Dubbo常用详解

Dubbo配置文件加载顺序

覆盖关系的优先级:System Properties【启动时的环境变量】> Externlized Configuration【使用控制台调节】 > Spring/Api【硬编码】 > Local File【本地配置文件】

Dubbo配置的覆盖关系

  • 方法级优先,接口级次之,全局配置再次之【精确优先】
  • 如果级别一样,则消费方优先,提供方次之【消费者优先】

Dubbo启动检查

dubbo:reference&dubbo:consumer设定了启动时是否检查,默认取dubbo:consumer的true

Dubbo超时设置

timeout为dubbo:methoddubbo:referencedubbo:servicedubbo:consumerdubbo:provider 属性 默认设置为1000。timeout为服务超时时间

Dubbo重试

retries为dubbo:methoddubbo:referencedubbo:servicedubbo:consumerdubbo:provider 属性默认设置为2。retries为当服务未返回结果【由于网络、超时等原因】重试次数,不包含第一次请求

幂等操作【查询、删除、修改】应当设置重试次数提高服务的可用性、非幂等操作【新增】为保证数据的正确性不应当设置重试次数

Dubbo多版本

version为dubbo:methoddubbo:referencedubbo:servicedubbo:consumerdubbo:providerdubbo:application属性。version用于实现灰度发布、测试版本区分等

Dubbo本地存根

stub为dubbo:referencedubbo:service属性。一般为dubbo:reference使用,为调用服务之前处理相关业务逻辑,一般情况下Stub类存放在抽取的接口工程中

<dubbo:reference id="userService" interface="com.kun.service.UserService" stub="com.kun.service.impl.UserServiceStub"></dubbo:reference>
public class UserServiceStub implements UserService {

  private final UserService userService;

  //必须有Service作为参数的构造函数
  public UserServiceStub(UserService userService) {
    this.userService = userService;
  }

  @Override
  public List<UserAddress> getUserAddressList(String userId) {
    System.out.println("调用本地存根进行参数验证。。。");
    if(userId != null && !userId.trim().isEmpty()) {
      return userService.getUserAddressList(userId);
    }
    return null;
  }
}

线程模型

  • 如果事件处理的逻辑能迅速完成,并且不会发起新的 IO 请求,比如只是在内存中记个标识,则直接在 IO 线程上处理更快,因为减少了线程池调度。

  • 但如果事件处理逻辑较慢,或者需要发起新的 IO 请求,比如需要查询数据库,则必须派发到线程池,否则 IO 线程阻塞,将导致不能接收其它请求。

  • 如果用 IO 线程处理事件,又在事件处理过程中发起新的 IO 请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常,但不会真死锁。

因此,需要通过不同的派发策略和不同的线程池配置的组合来应对不同的场景,缺省设置如下

<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="200" />

Dispatcher

  • all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。
  • direct 所有消息都不派发到线程池,全部在 IO 线程上直接执行。
  • message 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
  • execution 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
  • connection 在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。

ThreadPool

  • fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
  • cached 缓存线程池,空闲一分钟自动删除,需要时重建。
  • limited 可伸缩线程池,但池中的线程数只会增长不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题。
  • eager 优先创建Worker线程池。在任务数量大于corePoolSize但是小于maximumPoolSize时,优先创建Worker来处理任务。当任务数量大于maximumPoolSize时,将任务放入阻塞队列中。阻塞队列充满时抛出RejectedExecutionException。(相比于cached:cached在任务数量超过maximumPoolSize时直接抛出异常而不是将任务放入阻塞队列)
属性 描述
threadpool 线程池类型,默认fixed
threads 服务线程池大小(固定大小),默认200
iothreads IO线程池,接收网络读写中断,以及序列化和反序列化,不处理业务,此线程池和CPU相关,不建议配置
accepts 服务提供者最大可接受连接数,默认0
dispatcher 协议的消息派发方式,用于指定线程模型,默认all
queues 线程池队列大小,当线程池满时,排队等待执行的队列大小,建议不要设置,当线程程池时应立即失败,重试其它服务提供机器,而不是排队,除非有特殊需求

如果服务器抛出java.lang.OutOfMemoryError: unable to create new native thread unable to create new native thread 异常可能是启动用户线程数限制导致的,需修改/etc/security/limits.d目录下的文件,最大限制数使用ulimit -u查看用户可创建线程的最大数,必须要小于这个数值否则线程占用完无法登陆主机导致死机现象

直连提供者

在开发及测试环境,绕过注册中心直连提供者,方便开发调试,线上勿用

<dubbo:reference id="xxxService" interface="com.kun.xxx.XxxService" url="dubbo://localhost:2090" />

只订阅

为方便开发测试,服务可能需要连接注册中心以依赖其他服务,而自己并不想注册在注册中心,可使用dubbo:registry中的registry属性控制是否只订阅

subscribe-only

只注册

当有多个注册中心,需要只注册到一部分注册中心【依赖这部分注册中心的服务】,另一部分注册中心只提供服务【不依赖提供的服务】,使用dubbo:registry中的subscribe属性控制是否只注册

五、服务启动与优雅停机

服务启动

使用com.alibaba.dubbo.container.Main启动可实现服务启动和优雅停机

<!-- 打包jar文件时,配置manifest文件,加入lib包的jar依赖 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <classesDirectory>target/classes/</classesDirectory>
        <archive>
            <manifest>
                <!-- 启动类 -->
                <mainClass>com.alibaba.dubbo.container.Main</mainClass>
                <!-- 打包时MANIFEST.MF 文件不记录时间戳-->
                <useUniqueVersions>false</useUniqueVersions>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
            </manifest>
        </archive>
    </configuration>
</plugin>

可使用dubbo包下的/META-INF/assembly/bin/*.sh脚本管理程序的启动、停止、dump等

注:dubbo.spring.config默认为META-INF/spring,dubbo启动时会加载此路径下的文件为Spring配置文件

优雅停机

Dubbo是通过JDK的ShutdownHook完成优雅停机的,所以使用 kill -9 PID 等强制关闭指令只会强制关闭不会优雅停机,只有通过 kill PID 时,才会优雅停机

六、高可用

zookeeper宕机

现象:zookeeper注册中心宕机,还可以消费dubbo暴露的服务。

  • 监控中心宕掉不影响使用,只是丢失部分采样数据

  • 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务

  • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台

  • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯

  • 服务提供者无状态,任意一台宕掉后,不影响使用

  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

dubbo直连

url为dubbo:reference的属性。提供了dubbo直连方式【只需提供IP地址和端口号】,不经过注册中心

七、负载均衡

Random LoadBalance

  • 随机,按权重设置随机概率。

Random LoadBalance

RoundRobin LoadBalance

  • 轮询,按公约后的权重设置轮询比率。

RoundRobin LoadBalance

LeastActive LoadBalance

  • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。

    LeastActive LoadBalance

ConsistentHash LoadBalance

  • 一致性 Hash,相同参数的请求总是发到同一提供者。

    ConsistentHash LoadBalance

默认Dubbo采用Random LoadBalance负载均衡策略,可在dubbo:servicedubbo:referencedubbo:methodloadbalance修改负载均衡策略

八、服务降级

场景:当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作

处理方式:可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略

  • mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。【admin界面中点击【屏蔽】】
  • 还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响【admin界面中点击【容错】】

九、集群容错

Dubbo集群容错策略

在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试

  • Failover Cluster

    失败自动切换,当出现失败,重试其它服务器 。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)

  • Failfast Cluster

    快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录

  • Failsafe Cluster

    失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作

  • Failback Cluster

    失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作

  • Forking Cluster

    并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数

  • Broadcast Cluster

    广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息

可在dubbo:servicedubbo:reference中配置集群容错策略

整合Hystrix

配置spring-cloud-starter-netflix-hystrix

1、导入依赖

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  <version>1.4.4.RELEASE</version>
</dependency>

2、在主配置类上添加@EnableHystrix

@SpringBootApplication
@EnableHystrix
public class ProviderApplication {
  //......
}

3、配置Provider端

在Dubbo的Provider上增加@HystrixCommand配置,这样子调用就会经过Hystrix代理

@Service(version = "1.0.0")
public class HelloServiceImpl implements HelloService {
  @HystrixCommand(commandProperties = {
    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
  @Override
  public String sayHello(String name) {
    throw new RuntimeException("Exception to show hystrix enabled.");
  }
}

4、配置Consumer端

对于Consumer端,则可以增加一层method调用,并在method上配置@HystrixCommand。当调用出错时,会走到fallbackMethod = “reliable”的调用里

@Reference(version = "1.0.0")
private HelloService helloService;

@HystrixCommand(fallbackMethod = "reliable")
public String doSayHello(String name) {
  return helloService.sayHello(name);
}
public String reliable(String name) {
  return "hystrix fallback value";
}

十、Dubbo原理

框架设计

dubbo-framework

  • config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
  • proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy为中心,扩展接口为 ProxyFactory
  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
  • protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool

启动解析、加载配置信息

Dubbo扩展了Java 原生的 SPI 机制会加载META-INF/dubbo下的配置文件,加载到Spring容器中

DubboNamespaceHandler集成了Spring下的NamespaceHandlerSupportj,用于解析dubbo名称空间标签

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

  //用于初始化各种解析器
  @Override
  public void init() {
    registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
    registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
    registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
    registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
    registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
    registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
    registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
    registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
    registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
    registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
    registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
    registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
  }
}

DubboBeanDefinitionParser实现了Spring下的BeanDefinitionParser,提供了标签解析功能

服务暴露

dubbo-export

服务导出的入口方法是ServiceBean的onApplicationEvent,在Spring容器上下文刷新后回调,执行暴露服务

public void onApplicationEvent(ContextRefreshedEvent event) {
  // 是否有延迟导出 && 是否已导出 && 是不是已被取消导出
  if (isDelay() && !isExported() && !isUnexported()) {
    // 导出服务
    export();
  }
}

暴露过程中发现注册中心时zookeeper会将服务信息注册在zookeeper上

dubbo-export-zookeeper

暴露完成后会启动Netty服务,持续提供服务

服务引用

dubbo-reference

服务引用的入口方法为ReferenceBean的实现FactoryBean中的getObject方法

public Object getObject() throws Exception {
  return get();
}

public synchronized T get() {
  if (destroyed) {
    throw new IllegalStateException("Already destroyed!");
  }
  // 检测 ref 是否为空,为空则通过 init 方法创建
  if (ref == null) {
    // init 方法主要用于处理配置,以及调用 createProxy 生成代理类
    init();
  }
  return ref;
}

init()方法中使用createProxy()方法实现创建代理对象

服务调用流程

send-request-process

突破口是InvokerInvocationHandler实现InvocationHandler接口的invoke方法,得到调用结果,最后将结果转型并返回给调用方

public class InvokerInvocationHandler implements InvocationHandler {

  private final Invoker<?> invoker;

  public InvokerInvocationHandler(Invoker<?> handler) {
    this.invoker = handler;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    Class<?>[] parameterTypes = method.getParameterTypes();

    // 拦截定义在 Object 类中的方法(未被子类重写),比如 wait/notify
    if (method.getDeclaringClass() == Object.class) {
      return method.invoke(invoker, args);
    }

    // 如果 toString、hashCode 和 equals 等方法被子类重写了,这里也直接调用
    if ("toString".equals(methodName) && parameterTypes.length == 0) {
      return invoker.toString();
    }
    if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
      return invoker.hashCode();
    }
    if ("equals".equals(methodName) && parameterTypes.length == 1) {
      return invoker.equals(args[0]);
    }

    // 根据不同的invoker使用不同的集群容错策略,调用提供者的方法
    return invoker.invoke(new RpcInvocation(method, args)).recreate();
  }
}