当前位置: 首页 > 文档资料 > Hprose 中文文档 >

1.12 单向调用

优质
小牛编辑
131浏览
2023-12-01

单向调用

这是最简单的一个插件。可以作为 Hprose 插件的入门实例。

该插件的用途是当客户端发起调用后直接返回,不需要等待结果。

TypeScript 版本

该插件代码非常简单,这里先以 TypeScript 版本为例来进行讲解:

import { Context, NextInvokeHandler } from '@hprose/rpc-core';

export class Oneway {
    public static async handler(name: string, args: any[], context: Context, next: NextInvokeHandler): Promise<any> {
        const result = next(name, args, context);
        if (context.oneway) {
            result.catch(() => { });
            return undefined;
        }
        return result;
    }
}

上面是该插件的全部代码。下面我们先来分析一下这段代码。

该插件实现了一个调用处理器(InvokeHandler),使用 async 函数来定义是为了让代码看上去更简洁。

该函数首先调用 next 方法,并将返回值保存在常量 result 中,在这里 next 的返回值跟 handler 方法的返回值一样,是一个 Promise<any> 类型,也就是说,在执行完这一句后,实际上调用还没有开始,只是把调用放入了请求队列中。

接下来,判断 context.oneway 是否为 true,如果为 true,则对 result 做错误忽略的处理,然后不需要等待远程调用执行完毕,直接返回 undefined 的结果。否则,返回 result

那么在使用时,该如何将该插件应用于客户端,又该如何设置 context.oneway 的值呢?我们来看下面的使用代码:

import { Client, ClientContext } from '@hprose/rpc-core';
import { Oneway } from '@hprose/rpc-plugin-oneway';
import '@hprose/rpc-node';

async function main() {
    const client = new Client('http://127.0.0.1/');
    client.use(Oneway.handler);
    const context = new ClientContext({ oneway: true });
    await client.invoke('restart', [], context);
}

main();

在上面这段代码中,服务地址为 http://127.0.0.1/,该服务上有一个远程方法 restart,该方法用来重启服务器,不会有返回值,所以我们不需要等待它。

客户端使用 use 方法来设置 Oneway.handler 插件。

context.oneway 的值是通过直接创建一个 context 对象,并在调用 invoke 方法时,通过第三个参数传入该上下文对象即可。

在本例中,远程方法调用之后,context 中的值是否改变我们并不关心,在这种情况下,在 TypeScript 代码可以简化为:

import { Client } from '@hprose/rpc-core';
import { Oneway } from '@hprose/rpc-plugin-oneway';
import '@hprose/rpc-node';

async function main() {
    const client = new Client('http://127.0.0.1/');
    client.use(Oneway.handler);
    await client.invoke('restart', [], { oneway: true });
}

main();

不过这种简化写法,在其它语言中不一定都适用。

使用该插件后,如果在远程调用时,没有设置 context.onewaytrue,那么远程调用跟没有设置该插件时行为相同,只有将 context.oneway 设为 true 时,远程调用才会变为单向调用,单向调用不但忽略远程调用的结果,连异常也会一起忽略。

C# 版本

接下来我们来看看 C# 版本如何实现:

using System;
using System.Threading.Tasks;

namespace Hprose.RPC.Plugins.Oneway {
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class OnewayAttribute : ContextAttribute {
        public OnewayAttribute() : base("Oneway", true) { }
    }

    public static class Oneway {
        public static async Task<object> Handler(string name, object[] args, Context context, NextInvokeHandler next) {
            var result = next(name, args, context);
            if (context.Contains("Oneway")) {
                return null;
            }
            return await result.ConfigureAwait(false);
        }
    }
}

这段代码跟 TypeScript 版本相比,多了一个 OnewayAttribute,该属性用于在客户端定义调用接口时,标注接口方法为单向调用模式。该属性并不是必须的,OnewayAttribute 属性继承自 ContextAttribute,它的效果跟直接使用 [Context("Oneway", true)] 对接口方法进行标注的效果是一样的,但是直接用 [Oneway] 来做标注,会让代码更简洁清晰。

C# 版本的 Oneway 静态类跟 TypeScript 版本 Oneway 类代码几乎是一样的。只不过 C# 版本不需要对 result 做错误忽略的处理,因为只要不对该结果执行 Waitawait 操作,错误就会自动被忽略。这跟 .NET 的 Task 和 JavaScript/TypeScript 的 Promise 的内部处理机制有关,这里就不做详细讨论了。

下面我们来看一下在 .NET 中如何使用:

using Hprose.RPC;
using Hprose.RPC.Plugins.Oneway;

class Program {
    static void Main(string[] args) {
        var client = new Client("http://127.0.0.1/");
        client.Use(Oneway.Handler);
        client.Invoke("restart", null, new ClientContext() {
            ["Oneway"] = true
        });
    }
}

这段代码跟上面的 TypeScript 版本的代码几乎一模一样,只是这里用的 Invoke 方法是使用的同步版本,使用异步版本效果也是一样的。在这里创建 ClientContext 对象时,利用了 C# 新版本的对象属性初始化的功能,这让代码变的更加简洁。

下面我们来看一下使用代理对象如何使用该插件:

using System.Threading.Tasks;
using Hprose.RPC;
using Hprose.RPC.Plugins.Oneway;

public interface IRestart {
    [Oneway]
    Task Restart();
}
class Program {
    static async Task Example() {
        var client = new Client("http://127.0.0.1/");
        client.Use(Oneway.Handler);
        var proxy = client.UseService<IRestart>();
        await proxy.Restart();
    }
    static void Main(string[] args) {
        Example().Wait();
    }
}

虽然代码上,比直接使用 Invoke 要多一些,但是代码结构还是很清晰的。IRestart 接口中,定义了一个 Restart 方法,返回值为 Task 类型,表示该方法时异步的,该方法上面加了 [Oneway] 属性标注,表示该方法是单向调用。后面的 Example 方法中,使用 UseService 生成代理对象,然后通过代理对象就可以直接调用 Restart 方法了,该方法在使用时,形式上跟使用本地方法没有什么区别。因为控制台程序的入口函数 Main 不能定义为 async 方法,所以单独定义了一个 async 方法 Example 并在 Main 入口函数中同步调用它。

上面的代码,不论服务器有没有打开,代码执行都不会出错。这是因为 Oneway 模式把异常忽略了。如果去掉 [Oneway] 标注,再执行上面的代码,就会看到一串异常信息了。

其它语言的实现和使用方式跟这两个都是类似的,这里就不再举例说明了。

在下面介绍其它插件的实现和用法时,我们仍然使用这两种语言来举例说明,但是在代码较长的情况下,我们可能会省略掉一些无关紧要代码,只保留核心算法的代码。