dubbo服务管控

我们已经介绍了Dubbo在服务治理方面提供的特性,今天我们一起来看看Dubbo在其它方面提供的特性。同服务治理篇一样,本文的目的在于学会使用Dubbo在服务管控方面提供的特性,依旧不涉及任何实现原理。

工程结构

嗯~~

是这样的,因为电脑过于拉胯,而且IDEA着实有些吃内存了,所有我将测试工程按照子项目合并到一起了,目前我使用的工程结构是这样的:

子模块名由两部分组成:配置方式+功能,如:XMLProvider,表示以XML配置方式为主的服务提供方。

Tips:IDEA快要追上“内存雄狮”CLionl了。

本地存根(Stub)

使用Dubbo时,服务使用方只集成了接口,所有的实现全都在服务提供方,但部分场景中,我们希望服务使用方完成一些逻辑的处理,以此来减少RPC交互带来的性能消耗,例如:将参数校验放在服务使用方去做,减少一次与服务调用方的网络交互。

这种场景中,我们可以使用Dubbo提供的本地存根特性。我们有如下的服务提供方的工程结构:

xml-provider-api模块中定义了对外提供服务的接口XMLProviderService,代码如下:

public interface XMLProviderService {String say(String message);
}

以及接口存根XMLProviderServiceStub,代码如下:

public class XMLProviderServiceStub implements XMLProviderService {private final XMLProviderService xmlProviderService;public XMLProviderServiceStub(XMLProviderService xmlProviderService) {this.xmlProviderService = xmlProviderService;}@Overridepublic String say(String message) {if (StringUtils.isBlank(message)) {return "message不能为空!";}try {return this.xmlProviderService.say(message);} catch (Exception e) {return "远程调用失败:" + e.getMessage();}}
}

接着我们在服务使用方的工程中配置接口存根:

<dubbo:reference id="xmlProviderService" interface="com.wyz.api.XMLProviderService" stub="com.wyz.api.stub.XMLProviderServiceStub"/>

Tips:使用本地存根,要求存根的实现类必须有传入Proxy实例(服务使用方提生成的Proxy实例)的构造函数。

本地伪装(Mock)

本地伪装即我们在《Dubbo的高级特性:服务治理篇》中提到的服务降级,我们今天再稍微做一个补充。本地伪装是本地存根的一个子集,本地存根可以处理RPC调用环节中各种各样的错误和异常,而本地伪装则专注于处理RpcException(如网络失败,响应超时等)这种需要容错处理的异常

我们为XMLProviderService添加一个本地伪装服务XMLProviderServiceMock,工程结构如下:

XMLProviderServiceMock的代码如下:

public class XMLProviderServiceMock implements XMLProviderService {@Overridepublic String say(String message) {return "服务出错了!";}
}

配置文件可以按如下方式配置:

<dubbo:reference id="xmlProviderService" interface="com.wyz.api.XMLProviderService" mock="true"/>

这种配置中,要求Mock的实现必须按照“接口名+Mock后缀”的方式进行命名;如果不想使用这种命名方式,可以使用全限名:

<dubbo:reference id="xmlProviderService" interface="com.wyz.api.XMLProviderService" mock="com.wyz.api.mock.XMLProviderServiceMock"/>

Tips:再“重复”一遍Mock的原因是,上一篇中出了一点错误,本应在<dubbo:reference>标签中做的配置,我写到了<dubbo:service>标签中,产生错误的原因还是没有动手在项目中写一写,哎,真应了那句“纸上得来终觉浅,绝知此事要躬行”。

参数回调

Dubbo支持参数回调功能,使服务提供方可以“反向”调用服务使用方,该功能是基于长链接生成的反向代理实现的,效果类似于异步调用。我们举个支付的例子:

XMLProvider工程的xml-provider-api模块中添加PaymentService接口,同时添加PaymentNotifyService用于通知PaymentService的结果:

public interface PaymentService {void payment(String cardNo, PaymentNotifyService paymentNotifyService);
}public interface PaymentNotifyService {void paymentNotify(String message);
}

XMLProvider工程的xml-provider-service模块中实现PaymentService接口:

public class PaymentServiceImpl implements PaymentService {@Overridepublic void payment(String cardNo, PaymentNotifyService paymentNotifyService) {System.out.println("向卡号[" + cardNo + "]付钱!");// 业务逻辑paymentNotifyService.paymentNotify("付款成功");}
}

执行PaymentService#payment方法,并调用PaymentNotifyService#paymentNotify方法通知服务调用方执行结果。

XMLConsumer工程中实现PaymentNotifyService接口:

public class PaymentNotifyServiceImpl implements PaymentNotifyService {@Overridepublic void paymentNotify(String message) {System.out.println("支付结果:" + message);}
}

来看一下此时的工程结构:

接下来是XML的配置,参数回调中,我们需要关注的是服务提供方XMLProvider工程的xml-provider-service模块的配置:

<bean id="paymentServiceImpl" class="com.wyz.service.impl.PaymentServiceImpl"/>
<dubbo:service interface="com.wyz.api.PaymentService" ref="paymentServiceImpl" callbacks="10"><dubbo:method name="payment"><dubbo:argument index="1" callback="true"/></dubbo:method>
</dubbo:service>

配置通过第4行的<dubbo:argument index="1" callback="true"/>来确定PaymentService#payment方法中第2个(index从0开始)参数是回调参数;callbacks限制了同一个长链接下回调的次数,而不是总共回调的次数。

Tips:在实际的支付业务场景中,更倾向于异步处理,比如服务提供方在接收到支付请求是,启动新线程处理支付业务并调用通知接口,主线程返回成功接收支付请求。

异步调用

异步调用允许服务提供方立即返回响应,同时后台继续执行请求处理,当服务使用方请求响应结果时,服务提供方将结果返回。

DUbbo支持两种异步调用方式:

  • 使用CompletableFuture接口
  • 使用RpcContext

DUbbo 2.7之后,DUbbo以CompletableFuture接口为异步编程的基础。

使用CompletableFuture实现异步调用

我们先来看如何使用CompletableFuture实现异步调用,声明CompletableFutureAsyncService接口:

public interface CompletableFutureAsyncService {CompletableFuture<String> async(String message);
}

接着是接口实现:

public class CompletableFutureAsyncServiceImpl implements CompletableFutureAsyncService {@Overridepublic CompletableFuture<String> async(String message) {return CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + " say : " + message);try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}return "异步调用成功!";});}
}

XML配置与普通的DubboRPC接口配置相同,xml-provider-service模块的配置:

<bean id="completableFutureAsyncServiceImpl" class="com.wyz.service.impl.CompletableFutureAsyncServiceImpl" />
<dubbo:service interface="com.wyz.api.CompletableFutureAsyncService" ref="completableFutureAsyncServiceImpl" />

XMLConsumer模块的配置:

<dubbo:reference id="completableFutureAsyncService" interface="com.wyz.api.CompletableFutureAsyncService"/>

使用方式也非常简单:

CompletableFuture<String> completableFuture = completableFutureAsyncService.async("Hello");
System.out.println(completableFuture.get());

Tips

  • Dubbo中使用CompletableFuture与单独使用CompletableFuture并无什么差异~~
  • CompletableFutureAsyncServiceImpl的实现中打印接口名称的目的是为了清晰的展示出异步调用的效果;
  • CompletableFuture#supplyAsync(Supplier<U> supplier)默认使用ForkJoinPool#commonPool()
  • 重载方法CompletableFuture#supplyAsync(Supplier<U> supplier, Executor executor)允许使用自定义线程池。

使用AsyncContext实现异步调用

除了使用CompletableFuture外,还可以通过Dubbo定义的AsyncContext实现异步调用。先来编写接口和接口实现:

public interface RpcContextAsyncService {String async(String message);
}public class RpcContextAsyncServiceImpl implements RpcContextAsyncService {@Overridepublic String async(String message) {final AsyncContext asyncContext = RpcContext.startAsync();new Thread(() -> {asyncContext.signalContextSwitch();asyncContext.write(Thread.currentThread().getName() + " say : " + message);}).start();// 异步调用中,这个返回值完全没有意义return null;}
}

服务提供方的配置与其它Dubbo接口的配置并无不同:

<bean id="rpcContextAsyncServiceImpl" class="com.wyz.service.impl.RpcContextAsyncServiceImpl"/>
<dubbo:service interface="com.wyz.api.RpcContextAsyncService" ref="rpcContextAsyncServiceImpl"/>

接着是服务使用方的配置,需要添加async参数:

<dubbo:reference id="rpcContextAsyncService" interface="com.wyz.api.RpcContextAsyncService" async="true"/>

最后是在服务使用方中调用RPC接口:

rpcContextAsyncService.async("Thanks");Future<String> future = RpcContext.getServiceContext().getFuture();
System.out.println(future.get());

泛化调用

Dubbo的泛化调用提供了一种不依赖服务提供方API(SDK)的而调用服务的实现方式。主要场景在于网关平台的实现,通常网关的实现不应该依赖于其他服务的API(SDK)。

Dubbo官方提供了3种泛化调用的方式:

  • 通过API使用泛化调用
  • 通过Spring使用泛化调用(XML形式)
  • Protobuf对象泛化调用

这里我们介绍以XML的形式配置泛化调用的方式。

准备工作

首先我们再准备一个服务提供的工程GenericProvider,工程结构如下:

工程中定义了接口即实现类GenericProviderService和GenericProviderServiceImpl,代码如下:

public interface GenericProviderService {String say(String message);
}public class GenericProviderServiceImpl implements GenericProviderService {@Overridepublic String say(String message) {return "GenericProvider say:" + message;}
}

generic-dubbo-provider.xml中只需要正常配置GenericProvider提供的服务即可:

'<bean id="genericProviderServiceImpl" class="com.wyz.service.impl.GenericProviderServiceImpl"/>
<dubbo:service interface="com.wyz.service.api.GenericProviderService" ref="genericProviderServiceImpl" generic="true"/>

application.yml文件的配置我们就不多赘述了。

服务使用方的配置

回到XMLConsumer工程中,先配置Dubbo服务引用,xml-dubbo-consumer.xml中添加如下内容:

<dubbo:reference id="genericProviderService" generic="true" interface="com.wyz.service.api.GenericProviderService"/>

参数generic声明这是一个泛化调用的服务。此时IDEA会将interface="com.wyz.service.api.GenericProviderService"标红,提示“Cannot resolve class 'GenericProviderService'”,这个我们不需要关注,因为com.wyz.service.api包下确实不存在GenericProviderService接口。

接着我们来使用GenericProviderService接口:

ApplicationContext context = SpringContextUtils.getApplicationContext();
// genericProviderService是XML中定义的服务id
GenericService genericService = (GenericService) context.getBean("genericProviderService");// $invoke的3个参数分别为:方法名,参数类型,参数
Object result = genericService.$invoke("say", new String[]{"java.lang.String"}, new Object[]{"wyz"});
System.out.println(result);

这样,我们就可以通过ApplicationContext获取到GenericProviderService接口提供的服务了。

Tips:SpringContextUtils用于获取ApplicationContext,代码如下:

@Component
public class SpringContextUtils implements ApplicationContextAware {private static ApplicationContext applicationContext = null;public static ApplicationContext getApplicationContext() {return SpringContextUtils.applicationContext;}@Overridepublic void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException {SpringContextUtils.applicationContext = applicationContext;}
}

结语

好了,到目前为止,我们已经一起认识并学习了Dubbo中常用特性的配置与使用,当然了,经历了多年的发展,Dubbo的提供的特性远不止于此,如果想要了解更多内容,可以查看阿里巴巴提供的文档《Apache Dubbo微服务框架从入门到精通》。

下一篇,我们从服务注册部分正式开启对Dubbo实现原理的探索。


如果本文对你有帮助的话,还请多多点赞支持。如果文章中出现任何错误,还请批评指正。最后欢迎大家关注分享硬核Java技术的金融摸鱼侠王有志,我们下次再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/66825.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

登录校验的相关知识点

登录校验的相关知识点 【1】会话技术1)会话:2)会话跟踪:3)常见的几种会话跟踪&#xff1a; 【2】JWT令牌1)定义解释2&#xff09;测试生成Jwt令牌并解析3&#xff09;注意事项 【3】过滤器Filter1)过滤器工作原理如下&#xff1a;2)简单使用示例3)自定义拦截路径4)疑问5)过滤器…

【Linux】简单的小程序:进度条

在学习进度条之前&#xff0c;需要学一点预备知识。 1. 预备知识 回车换行 现在的换行符&#xff08;\n&#xff09;其实就是回车式换行符&#xff0c;另起一行&#xff0c;光标指向最新一行的开头。回车符&#xff08;\r&#xff09;是光标指向这一行的开头。 缓冲区 &a…

VR全景对行业发展有什么帮助?VR全景制作需要注意什么?

引言&#xff1a; 虚拟现实&#xff08;Virtual Reality&#xff0c;简称VR&#xff09;早已不再是科幻电影的概念&#xff0c;而是在以惊人的速度改变着我们的世界。VR全景&#xff0c;作为其中的重要组成部分&#xff0c;正为多个行业带来了全新的机遇。 一、VR全景的应用领…

WebGL Varing变量的作用和内插过程,及执行Varing时涉及的图形装配、光栅化、颜色插值、片元着色器执行机制等详解

目录 前言 在 WebGL 或 OpenGL 中&#xff0c;“varying” 是一种用于在顶点着色器和片元着色器之间传递数据的特殊类型的变量。它允许在顶点着色器对数据进行处理后&#xff0c;在片元着色器中使用该处理后的数据进行进一步计算。 彩色三个点 ​编辑 彩色三个点示例代码…

大数据可视化大屏实战项目(4)物流数据云看台(包括动态登陆页面)—HTML+CSS+JS【源码在文末】(可用于比赛项目或者作业参考中)

大数据可视化大屏实战项目&#xff08;4&#xff09;物流数据云看台&#xff08;包括动态登陆页面&#xff09;—HTMLCSSJS【源码在文末】&#xff08;可用于比赛项目或者作业参考中&#x1f415;&#x1f415;&#x1f415;&#xff09; 一&#xff0c;项目概览 ☞☞☞☞☞☞…

【STM32】学习笔记(TIM定时器)

TIM&#xff08;Timer&#xff09;定时器 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能&#xff0c;而且…

Javase | IO流

目录&#xff1a; 1.输入 (Intput/Read)2.输出 (Output/Write)3.IO4.IO流5.IO流的分类&#xff1a;5.1 分类总述5.2 按照 “流的方向” 进行分类5.3 按照 “读取数据的方式” 进行分类 6.IO包下要重点掌握的流&#xff1a;6.1 文件专属 (流)6.2 转换流 ( 将字节流转换为字符流 …

2023.9 - java - 浅拷贝

与 js的浅拷贝不同&#xff1a; 在 JavaScript 中&#xff0c; Object.assign() 或 spread 运算符等方法可以实现浅拷贝&#xff0c;但只针对对象的第一层属性进行复制。如果一个对象只包含基本数据类型的属性&#xff0c;那么对浅拷贝出来的对象进行修改不会影响原始对象&…

C#安装“Windows 窗体应用(.NET Framework)”

目录 背景: 第一步: 第二步: 第三步&#xff1a; 总结: 背景: 如下图所示:在Visual Studio Installer创建新项目的时候&#xff0c;想要添加windows窗体应用程序&#xff0c;发现里面并没有找到Windows窗体应用(.NET Framework)模板&#xff0c;快捷搜索也没有发现&#…

22.3D等距社交媒体菜单的悬停特效

效果 源码 <!doctype html> <html><head><meta charset="utf-8"><title>CSS Isometric Social Media Menu</title><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.…

springboot web开发登录拦截器

在SpringBoot中我们可以使用HandlerInterceptorAdapter这个适配器来实现自己的拦截器。这样就可以拦截所有的请求并做相应的处理。 应用场景 日志记录&#xff0c;可以记录请求信息的日志&#xff0c;以便进行信息监控、信息统计等。权限检查&#xff1a;如登陆检测&#xff…

SQL sever中用户管理

目录 一、用户管理常见方法 二、用户管理方法示例 2.1. 创建登录账户&#xff1a; 2.1.1 检查是否创建账户成功&#xff1a; 2.2. 创建数据库用户&#xff1a; 2.2.1检查用户是否创建成功&#xff1a; 2.3. 授予权限&#xff1a; 2.3.1授予 SELECT、INSERT 和 U…

什么是BEM命名规范(Block-Element-Modifier Notation)?它有什么优势?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ BEM命名规范&#xff08;Block-Element-Modifier Notation&#xff09;⭐ BEM命名结构⭐ 优势⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎…

如何解决分库分表主键问题?

分析&回答 从问题角度出发&#xff1a;我们需要一个全局唯一的 id 来支持&#xff0c;排序问题等。这都是你实际生产环境中必须考虑的问题。可以先看下我们之前的文章分布式系统唯一ID如何生成&#xff1f; 雪花算法和雪花算法的变种是大家常用的 喵呜面试助手&#xff1…

春秋云镜 CVE-2018-1273

春秋云镜 CVE-2018-1273 Spring-data-commons 远程命令执行漏洞 靶标介绍 Spring Data是一个用于简化数据库访问&#xff0c;并支持云服务的开源框架&#xff0c;Spring Data Commons是Spring Data下所有子项目共享的基础框架。Spring Data Commons 在2.0.5及以前版本中&…

【算法】滑动窗口

滑动窗口应用场景 关键词&#xff1a; 满足xxx条件&#xff08;计算结果&#xff0c;出现次数&#xff0c;同时包含&#xff09; 最长/最短 子串/子数组/子序列 例如&#xff1a;长度最小的子数组 滑动窗使用思路&#xff08;寻找最长&#xff09; 核心&#xff1a;左右双指…

git大文件推送报错

报错信息 不多掰扯&#xff0c;直接上报错信息和截图 Delta compression using up to 8 threadsRPC failde; HTTP 413 curl 22 The requested URL returned error: 413 Request Entity Too Large从以上的报错信息不难看出推送仓库的时候&#xff0c;请求体过大&#xff0c;为…

第五讲Java面向对象——类及类的成员

前言 前面几讲,我们讲解了java的基础知识,也写了很多代码,有没有发现,每次编写代码前都会新建一个类,并且开头是public class修饰。可能有些同学不知道什么是类,以及前面我们提到方法。那么本讲,就要开始深入了解他们。 在开始讲解,我们将先了解一下编程的思想,面向对…

8.Redis-set

Set 常用命令saddsmemberssismemberscardspopsmovesrem集合间操作sinter 交集sinterstoresunion 并集sunionstoresdiff 差集sdiffstore 命令总结 内部编码应用场景使用 set来保存用户的“标签” set(集合)就是把一些有关联的数据放刀一起。 它与list的区别如下&#xff1a; 集合…

zookeeper 集群

zookeeper 集群 1、zookeeper 集群说明 initLimit 是Zookeeper用它来限定集群中的Zookeeper服务器连接到Leader的时限 syncLimit 限制了follower服务器与leader服务器之间请求和应答之间的时限 服务器名称与地址&#xff1a;集群信息&#xff08;服务器编号&#xff0c;服务器…