RPC中Callback Function与CountDownLatch的用法

目录

 

●What & Why

●回调函数Callback Function与倒数计数器CountDownLatch


●What & Why

RPC(Remote Procedure Call),通俗地说,就是在一台计算机上调用另一台计算机提供的服务。这里的服务对应RPC中的P(Procedure),表现形式通常是API接口,或者说好比一个本地代码工程中的一个函数。那为什么要用RPC呢?最主要的原因有两点:1、符合低耦合、职责分离、可复用的开发原则,将不同的服务(模块、功能……什么名字都好,理解其本质即可)放在不同的代码工程,甚至不同的计算机(服务器)上,避免所有代码杂糅在一个工程中,难以开发与维护;2、缓解负载压力,不同计算机(服务器)提供不同的服务,各司其职,不用做所有事,降低资源耗尽的风险。

 

RPC其实没有那么高深,如果不考虑底层实现原理,则对于程序员来说几乎完全透明,就是一套固定步骤的开发流程,和调用本地工程代码中的函数没有差别。以笔者目前所做的项目为例,步骤大体为:准备对应的stub(笔者将其理解为对方所能提供函数,在我方的一个说明)、准备远程调用的通道、控制器对象;开启子线程,利用回调函数Callback Function,等待处理响应,并用线程倒数控制器CountDownLatch让主线程阻塞;开启远程调用。

●回调函数Callback Function与倒数计数器CountDownLatch

笔者之前看过一些关于回调函数的文章,概念上已经理解,即A类的函数a1调用B类的函数b1,b1中需要调用A类的函数a2,这个a2就是回调函数。但是对于其存在的意义,或者说使用回调函数的场景到底是什么,还不太想得到。直到项目中接触到了RPC,才真的体会到回调函数的作用。

 

我们做如下安排,服务器B是一台提供许多实用功能/服务的机器,它对外提供的接口包括字符串处理(strDeal)等,我们在服务器A的代码中,去调用服务器B所提供的函数。

服务器A——

public class Client{
	public boolean checkBackMessage(BackMessage backMessage){
		return backMessage.equals("Done") ? true : false;
	}
	
	public BackMessage dealStr(String str) {
		//接收RPC响应消息的对象
		final BackMessage backMessage = new BackMessage();
		//构造一个RPC通道,具体代码未贴出,根据不同框架、项目而异
		RpcChannelImpl channel = RpcChannelImpl.builderChannel();
		if (channel != null) {
			//构造一个RPC控制器
			RpcController controller = channel.newRpcController();
			//对方所能提供的服务/函数都通过stub进行描述
			Service.Stub stub = Service.newStub(channel);
			//构造RPC的请求
			DealStrRequest.Builder request = DealStrRequest.newBuilder();
			//设置请求参数
			request.setCmd(CmdUtil.newRequestCmd(Hpp.CmdId.SERVER_RESTART_REQ));
			request.setStr(str);
			//倒数计数器
			final CountDownLatch latch = new CountDownLatch(1);
			//回调函数处理响应
			RpcCallback<DealStrResponse> done = new RpcCallback<DealStrResponse>() {	
				@Override
				public void run(DealStrResponse response) {
					try {
						//响应处理
						backMessage.setBackCode(response.getCmd().getResultCode());
						backMessage.setBackMessage(response.getCmd().getResultString());
						System.out.println("完成RPC调用,接收到响应,已进行设置")
					} catch (Exception e) {
						latch.countDown();
					}
					latch.countDown();
				}
			};
			//执行RPC调用
			stub.dealStr(controller, request.build(), done);
			//主线程阻塞等待响应处理完成
			try {
				latch.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			backMessage.setBackCode(-1);
		}
		return backMessage;
	}
}

在服务器A中,我们需要准备一些对象,他们包括接收响应对象backMessage、RPC通道channel、PRC控制器controller、对方服务存根stub、RPC请求request。通过调用存根里的函数接口就和调用本地的函数一样。

我们对于响应的处理采用了比较精妙的方式:开启一个子线程,定义回调函数run(),并且用CountDownLatch去阻塞主线程,让其等待子线程中回调函数完成处理再继续进行。

我们继续看看服务器B所提供的RPC的服务——

public class ServiceImpl extends Service{
	@HppMethod(commonId = 10001)
	public void dealStr(RpcController controller, DealStrRequest request, RpcCallback<DealStrResponse> done) {
		//对请求中的字符串进行大写转换并存入数据库
		request.getStr.toUpperCase().save();
		int ret=ErrorCode.CMS_SUCCESSED;
		DealStrResponse.Builder response = DealStrResponse.newBuilder().setCmd(CmdUtil.newResponseCmd(request.getCmd(), ResultUtil.getResultCode(ret)));
		//回调函数!!
		done.run(response.build());
	}
	
	//提供的其他RPC服务/函数
	@HppMethod(commonId = 10002)
	public void method1(RpcController controller, method1tRequest request, RpcCallback<method1tResponse> done) {
		……
	}
	
	@HppMethod(commonId = 10003)
	public void method2(RpcController controller, method2tRequest request, RpcCallback<method1tResponse> done) {
		……
	}
	
	……
}

在其处理完字符串后,回调了服务器A的函数,即done.run(response.build()),利用回调函数,把响应作为参数从服务器B传给服务器A中,并在服务器A的回调函数中对响应进行处理(设置响应码、响应消息、打印日志等操作)。

整个过程的数据流向就非常明晰了:服务器A的字符串通过请求发给服务器B(走RPC调用),服务器B处理完请求,生成响应对象,利用回调函数将其交给服务器A处理。怎么样,是不是似曾相识,好像在哪儿听过?Bingo!没错!Ajax里面也用到了回调函数!页面前端发送请求(例如Post)给后端处理,后端生成响应回传给页面,Ajax异步处理响应生成页面内容。

function testAjax(){
	$.ajax({
		dataType:"text",
		type:"POST",
		cache:false,
		url: path+"/testAjax.action",
		data:{
		 	"textDetail" : str
		},
		success:function(response){			
			if(response != null){
				consolo.log(response);
		  	}						
		},
		error:function(response){
			tLayer.warning("Error!");
		}
	 });		
}

回到正题,我们再看看服务器A中的回调函数里面用到的CountDownLatch,这也是一个非常精妙的设计,它的作用是通过倒数来控制线程的阻塞。我们设置倒数计数为1,当回调函数所在线程完成了响应处理,则将其减1,而主线程则在latch.await()的地方等待CountDownLatch倒数变为0才继续进行。可见,CountDownLatch适用于主线程中等待多个线程均执行完成后才继续进行的场景。

笔者目前接触的项目自己搭建的RPC架构,实际开发中,大家也可以选择Thrift、Dubbo等第三方的框架进行使用。目前很火的微服务架构,就用到了RPC的相关思想,这一部分的知识还是值得掌握的。最后,给大家推荐两篇文章:

https://blog.csdn.net/mindfloating/article/details/39473807

https://blog.csdn.net/mindfloating/article/details/39474123

今天,你学会了吗?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值