Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >RPC编程

RPC编程

作者头像
小蜜蜂
修改于 2019-07-16 09:22:47
修改于 2019-07-16 09:22:47
1.4K00
代码可运行
举报
文章被收录于专栏:明丰随笔明丰随笔
运行总次数:0
代码可运行

什么是RPC?

RPC全称Remote Procedure Call,即远程方法调用。它的表现形式是:

一个RPC请求从客户端初始化,并发送到远程服务器,执行远程服务器上的方法,远程服务器处理完成之后会把响应返回给客户端,然后客户端的应用程序继续执行。RPC的意义是实现一种调用执行远程方法的协议,让你在本地调用远程的方法,而对你来说这个调用是透明的,就向调用本地方法一样。通过RPC能解耦服务,当server需要对方法的实现进行修改时,client完全感知不到,不用做任何变更。

RPC的使用场景探讨

假设我们有两个数据中心,一个是US,一个是CN。我们的需求是产生的数据需要在US和CN之间进行同步。我们最初的方案是在US环境搭建服务,并在US数据库生成数据。然后再从US和CN的数据库层面进行数据的同步,US和CN背后通过V**的方式连接。这个方案是可行的,但是突然有一天V**坏了,不能再继续使用了,US和CN的数据不能同步了。我们怎么办?从对现有代码修改的成本角度来考虑,我们希望代码改动尽可能小,业务逻辑也能更多确保其正确性。我们的解决方案就是使用RPC,现在的问题是CN的数据没有办法同步,如果我们在CN环境使用相同的代码搭建CN服务,并直接连接CN的数据库。US的服务调用data层方法时候,能够同时调用US本地方法并RPC调用CN服务的data层方法,这样US和CN就会同时产生数据了,数据也就一致了,我们的需求也就解决了。

使用WebService实现RPC

创建一个web服务:RpcWebService.asmx,并部署在远程服务器上。这个服务会对来自客户端的调用,使用反射技术进行还原并调用本地的方法,并把本地的执行结果返回给客户端。在客户端里面需要做一个AOP代理层,从原来的只有调用本地方法变成调用远程方法。我们实现依赖了这些package:

Castle.Core: AOP类库

Newtonsoft.Json: 序列化类库

客户端和服务器之间的关系,请看下图:

具体的代码改动:

1. 服务端添加WebService,处理客户端的请求。

2. 客户端添加AOP代理层。

3. 客户端代码修改成RPC的调用形式。

1. 服务端添加WebService,用来处理来自客户端的请求:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>
/// Summary description for RpcWebService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
// [System.Web.Script.Services.ScriptService]
public class RpcWebService : System.Web.Services.WebService
{
    [WebMethod]
    public RpcServiceResult CallMethod(RpcServiceContext callContext)
    {
        RpcServiceResult rpcResult = new RpcServiceResult();

        try
        {
            // validate interface/class type
            Type type = Type.GetType(callContext.MethodInstanceTypeName);
            if (type == null)
            {
                throw new InvalidOperationException("Failed to locate type:" + callContext.MethodInstanceTypeName);
            }

            // extract arguments information
            var arguments = callContext.Arguments
                .Select(args =>
                    new
                    {
                        TypeName = args.TypeName,
                        Type = Type.GetType(args.TypeName),
                        Value = RpcJsonSerializer.Deserialize(args.Value, Type.GetType(args.TypeName))
                    });

            // validate arguments
            if (arguments.Any())
            {
                var badTypes = arguments.Where(t => t.Type == null).Select(t => t.TypeName);
                if (badTypes.Any())
                {
                    var msg = string.Join(",", badTypes.ToArray());
                    throw new InvalidOperationException("Failed to locate parameter types:" + badTypes);
                }
            }

            // validate method
            Type[] argsTypes = arguments.Select(t => t.Type).ToArray();
            MethodInfo method = type.GetMethod(callContext.MethodName, argsTypes);
            if (method == null)
            {
                throw new InvalidOperationException(string.Format("Failed to find method named:{0}, with parameters:{1}", callContext.MethodName, string.Join(",", argsTypes.Select(t => t.FullName).ToArray())));
            }

            // invoke method on local instance
            var instance = Activator.CreateInstance(type);
            if (instance == null)
            {
                throw new InvalidOperationException("Failed to create instance for type:" + type.AssemblyQualifiedName);
            }

            var methodResult = method.Invoke(instance, arguments.Select(args => args.Value).ToArray());
            rpcResult.Result = RpcJsonSerializer.Serialize(methodResult);
            rpcResult.ResultTypeName = method.ReturnType.FullName;
            rpcResult.ResultFormat = RpcResultFormat.Json;
            rpcResult.IsSuccess = true;
        }
        catch (Exception ex)
        {
            rpcResult.IsSuccess = false;
            rpcResult.ErrorMessage = ex.Message;
        }

        return rpcResult;
    }
}

public class RpcServiceResult
{
    public bool IsSuccess { get; set; }

    /// <summary>
    /// Serialized result of the method call.
    /// </summary>
    public string Result { get; set; }

    /// <summary>
    /// Name of the type of method return value.
    /// </summary>
    public string ResultTypeName { get; set; }

    /// <summary>
    /// In which the result is formatted.
    /// </summary>
    public RpcResultFormat ResultFormat { get; set; }

    public string ErrorMessage { get; set; }
}

public class RpcServiceContext
{
    /// <summary>
    /// Name of the type where the method is obtained.
    /// </summary>
    public string MethodInstanceTypeName { get; set; }

    /// <summary>
    /// Name of the method
    /// </summary>
    public string MethodName { get; set; }

    /// <summary>
    /// List of method argument
    /// </summary>
    public List<RpcServiceArgument> Arguments { get; set; }

    public RpcServiceContext()
    {
        this.Arguments = new List<RpcServiceArgument>();
    }
}

public class RpcServiceArgument
{
    /// <summary>
    /// Type of the argument
    /// </summary>
    public string TypeName { get; set; }

    /// <summary>
    /// Serialized value of argument
    /// </summary>
    public string Value { get; set; }
}

public enum RpcResultFormat
{
    /// <summary>
    /// Default & preferred
    /// </summary>
    Json
}

2. 客户端AOP代理层代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static class ProxyFactory
{
    private static readonly ProxyGenerator _proxyGenerator = new ProxyGenerator();
    private static ConcurrentDictionary<string, object> _objectCache = new ConcurrentDictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);

    public static T CreateProxy<T>(T target) where T : class
    {
        if (target == null)
        {
            throw new ArgumentNullException("target");
        }

        var proxy = _objectCache.GetOrAdd(typeof(T).AssemblyQualifiedName, (type) =>
        {
            var intercepter = new RemoteInvokeIntercepter();
            return _proxyGenerator.CreateInterfaceProxyWithTarget<T>(target, intercepter);
        }) as T;

        if (proxy == null)
        {
            throw new InvalidOperationException(string.Format("Failed to create proxy for Type: {0}", typeof(T).AssemblyQualifiedName));
        }

        return proxy;
    }
}

internal class RemoteInvokeIntercepter : IInterceptor
{
    private const int MAX_SERVICE_RETRY = 3;

    public RemoteInvokeIntercepter()
    {
    }

    public void Intercept(IInvocation invocation)
    {
        RpcServiceContext callContext = new RpcServiceContext()
        {
            MethodInstanceTypeName = invocation.InvocationTarget.GetType().AssemblyQualifiedName,
            MethodName = invocation.Method.Name,
        };

        callContext.Arguments = invocation.MethodInvocationTarget
            .GetParameters()
            .OrderBy(p => p.Position)
            .Select(p => new RpcServiceArgument()
            {
                TypeName = invocation.Arguments[p.Position] != null
                            ? invocation.Arguments[p.Position].GetType().AssemblyQualifiedName
                            : p.ParameterType.AssemblyQualifiedName,
                Value = RpcJsonSerializer.Serialize(invocation.Arguments[p.Position])
            })
            .ToArray();

        RpcWebServiceSoapClient proxy = new RpcWebServiceSoapClient();

        var returnValueResponse = CallWebServiceWithRetry(proxy.CallMethod, callContext);
        if (!returnValueResponse.IsSuccess)
        {
            throw new InvalidOperationException(string.Format("Failed to call service:{0}, remote error:{1}. Check the error log in rpc service for more details.", invocation.Method.Name, returnValueResponse.ErrorMessage));
        }

        if (invocation.Method.ReturnType.FullName.Equals("System.Void"))
        {
            return;
        }

        if (returnValueResponse.ResultFormat == RpcResultFormat.Json)
        {
            invocation.ReturnValue = RpcJsonSerializer.Deserialize(returnValueResponse.Result, invocation.Method.ReturnType);
        }
        else
        {
            throw new NotSupportedException(string.Format("RpcResultFormat '{0}' is not supported", returnValueResponse.ResultFormat));
        }
    }

    private RpcServiceResult CallWebServiceWithRetry(Func<RpcServiceContext, RpcServiceResult> service, RpcServiceContext args, int retryCountdown = MAX_SERVICE_RETRY)
    {
        try
        {
            return service.Invoke(args);
        }
        catch (Exception exception)
        {
            if (retryCountdown > 0)
            {
                return CallWebServiceWithRetry(service, args, --retryCountdown);
            }
            else
            {
                throw new Exception(string.Format("Failed to call service:{0}, with {1} retries.", service.Method.Name, MAX_SERVICE_RETRY - retryCountdown), exception);
            }
        }
    }
}

public static class RpcJsonSerializer
{
    /// <summary>
    /// Specificly for circular references.
    /// </summary>
    private static readonly JsonSerializer _jsonSerializer = JsonSerializer.Create(
        new JsonSerializerSettings()
        {
            PreserveReferencesHandling = PreserveReferencesHandling.Objects
        }
    );
    public static string Serialize(object obj)
    {
        StringBuilder sb = new StringBuilder();
        using (StringWriter sw = new StringWriter(sb))
        {
            _jsonSerializer.Serialize(sw, obj);
        }
        return sb.ToString();
    }
    public static object Deserialize(string jsonText, Type objectType)
    {
        var reader = new JsonTextReader(new StringReader(jsonText));
        return _jsonSerializer.Deserialize(reader, objectType);
    }
}  

3. 客户端代码修改成RPC的调用形式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MySvc : IMySvc
{
    private static readonly IMySvc instance = new MySvc();
    public static IMySvc RPCInstance
    {
        get
        {
            return ProxyFactory.CreateProxy(instance);
        }
    }
    public string GetString(int num)
    {
        return num.ToString();
    }
}

public class HomeController : Controller
{
    private IMySvc _mySvc;
    public HomeController()
    {
        _mySvc = MySvc.RPCInstance;
    }
    public ActionResult Index()
    {
        ViewBag.Hello = _mySvc.GetString(123);
        return View();
    }
}  

进过这一系列的操作,我们的代码就具备了调用远程服务的能力,而且调用起来就像调用本地代码一样简单!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-05-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 明丰随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
手写dubbo 10-基于netty实现RPC
博客中代码地址:https://github.com/farliu/farpc.git
并发笔记
2020/11/09
1.2K0
Dubbo源码学习--服务发布(ProxyFactory、Invoker)
本文介绍了Dubbo服务发布的Invoker生成过程,主要包括了Dubbo服务引用的生成、服务发布时生成Invoker、服务调用时生成Invoker以及动态拓展点的实现。此外,还介绍了如何使用Nutz.Dao对服务进行编排,包括服务引用的获取、服务发布和调用等。
YGingko
2017/12/28
2.1K0
Dubbo源码学习--服务发布(ProxyFactory、Invoker)
dubbo源码学习笔记----RPC
RpcContext 整个RpcContext通过ThreadLocal维持。 public class RpcContext { private static final ThreadLocal<RpcContext> LOCAL = new ThreadLocal<RpcContext>() { @Override protected RpcContext initialValue() { return new RpcContext();
春哥大魔王
2018/04/17
6500
【RPC 专栏】简单了解RPC实现原理
中文详细注释的开源项目 Java 并发源码合集 RocketMQ 源码合集 Sharding-JDBC 源码解析合集 Spring MVC 和 Security 源码合集 MyCAT 源码解析合集 时下很多企业应用更新换代到分布式,一篇文章了解什么是RPC。 原作者梁飞,在此记录下他非常简洁的rpc实现思路。 核心框架类 /* * Copyright 2011 Alibaba.com All right reserved. This software is the * confidential a
芋道源码
2018/06/12
1.7K0
Netty在Dubbo中的使用过程源码分析
最近项目中使用了netty服务,空余时间差了下dubbo中是如何使用netty做底层服务的,找了相关资料记录一下:
小勇DW3
2020/04/26
8020
rpc系列5-添加拦截器链,实现rpc层面的AOP
首先,拦截器的作用很强大,它提供RPC层面的AOP功能,比如进行日志记录,rpc调用次数统计,service提供方添加白名单等等。在平时的学习过程中,很多框架都会实现这样的功能,比如:Servlet中的Filter,Struts中的Interceptor,Netty中的PipelineChannel和ChannelHandler,Tomcat中的Realm等等。这其实是软件可扩展性的一种体现,那么我们的rpc框架必须也要紧跟时代潮流!
topgunviper
2022/05/12
4750
rpc系列5-添加拦截器链,实现rpc层面的AOP
Dubbo 技术详解,我非常喜欢Dubbo的设计
Dubbo 是阿里巴巴开源的一款高性能、轻量级分布式服务框架,基于 Java 的 RPC 协议,支持多种协议和多种注册中心。其官方网站为 https://dubbo.apache.org/zh/。
疯狂的KK
2023/04/01
5550
RPC框架原理与实现
RPC,全称 Remote Procedure Call(远程过程调用),即调用远程计算机上的服务,就像调用本地服务一样。那么RPC的原理是什么呢?了解一个技术最好的思路就是寻找一个该类型麻雀虽小五脏俱全的开源项目,不负所期,找到一个轻量级分布式 RPC 框架,本文从这个项目入手来解读RPC的原理及其实现。 其实说到RPC,大家应该不会陌生才是,以往流行的Web Service就是一种RPC,一般来说RPC 可基于 HTTP 或 TCP 协议,因为Web Service 基于HTTP,所以具有良好的跨平台性
JadePeng
2018/03/12
2.3K0
RPC框架原理与实现
Dubbo服务提供者发布过程
首先ServiceConfig类拿到对外提供服务的实际类ref(如:HelloServiceImpl),然后通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转化。接下来就是Invoker转换到Exporter的过程。
搜云库技术团队
2019/10/18
5140
万万没想到一个xxl-job源码分析,竟然能引发这么多血案!(上)
executor端通过上文中xxlRpcProviderFactory.invokeService(xxlRpcRequest);函数执行本地暴露的服务方法
用户2032165
2020/03/27
4.3K0
万万没想到一个xxl-job源码分析,竟然能引发这么多血案!(上)
Dubbo RPC在Provider端是如何跑起来的
dubbo RCP请求到达provider后,首先经过数据接收、解码(NettyWokerThread/NioEventLoop),然后传递到RPC后续流程(DubboServerHandler),即filter、service invoke过程,service invoke过程也就是执行真正服务的逻辑,执行完毕后再经过编码作为响应返回给RPC调用者。
luoxn28
2020/07/14
4840
手写RPC框架,一小时足矣!
RPC即远程过程调用,也叫远程方法调用,RPC框架可以实现调用方可以像调用本地方法一样调用远程服务的方法。要了解微服务和分布式,RPC必不可少,话不多说,下面直接开整。
程序员小义
2024/04/10
5700
手写RPC框架,一小时足矣!
RPC(简单实现)
RPC(Remote Procedure Call)远程过程调用,即通过网络通信来调用远程计算机程序上的服务,而这个调用过程就像调用本地方法一样简单透明,并且不需要了解底层的网络技术协议。RPC采用C/S架构,发出请求的程序是Client,提供服务的则是Server,类似于Http请求与响应。简单总结就是:调用的方法实际在远程,而要像调用本地方法一样简单。
晚上没宵夜
2020/03/10
9400
Dubbo如何处理业务异常
https://blog.csdn.net/m0_57077948/article/details/117524555?utm_medium=distribute.pc_relevant.none-t
用户9131103
2023/07/17
6480
Dubbo如何处理业务异常
rpc系列1-10 minute Tutorial
最近在网上看到阿里巴巴2015年的中间件性能挑战赛的一个题目,实现一个简单的RPC框架,于是乎有一种冲动实现一个简单的rpc,要求基本按照竞赛题目的要求,具体如下:
topgunviper
2022/05/12
2370
rpc系列1-10 minute Tutorial
Dubbo 泛化调用在vivo统一配置系统的应用
Dubbo泛化调用特性可以在不依赖服务接口API包的场景中发起远程调用, 这种特性特别适合框架集成和网关类应用开发。
2020labs小助手
2022/09/27
1.1K0
为了带你搞懂RPC,我们手写了一个RPC框架
远程服务调用(Remote procedure call)的概念历史已久,1981年就已经被提出,最初的目的就是为了调用远程方法像调用本地方法一样简单,经历了四十多年的更新与迭代,RPC 的大体思路已经趋于稳定,如今百家争鸣的 RPC 协议和框架,诸如 Dubbo (阿里)、Thrift(FaceBook)、gRpc(Google)、brpc (百度)等都在不同侧重点去解决最初的目的,有的想极致完美,有的追求极致性能,有的偏向极致简单。
Rude3Knife的公众号
2022/04/28
4010
为了带你搞懂RPC,我们手写了一个RPC框架
从源码中分析 Hadoop 的 RPC 机制
RPC是Remote Procedure Call(远程过程调用)的简称,这一机制都要面对两个问题 对象调用方式; 序列/反序列化机制 在此之前,我们有必要了解什么是架构层次的协议。通俗一点说,就是我
Star先生
2017/07/28
2.5K0
从源码中分析 Hadoop 的 RPC 机制
JAVA实现一个简单的RPC+项目源码
论坛中说到聊一聊RPC远程过程调用协议 http://www.52itstyle.com/thread-22564-1-1.html RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 无意中浏览到了du
小柒2012
2018/04/13
9710
JAVA实现一个简单的RPC+项目源码
【原创】自己动手实现RPC服务调用框架
引言 本文利用java自带的socket编程实现了一个简单的rpc调用框架,由两个工程组成分别名为battercake-provider(服务提供者)、battercake-consumer(服务调用者)。 设计思路如下: 1、在battercake-provider中,写一个服务叫BatterCakeService 2、在battercake-provider中,启动RpcProvider,发布该服务 3、在battercake-consumer中,启动测试类RpcTest 4、在battercake-c
Java高级架构
2018/07/20
3230
相关推荐
手写dubbo 10-基于netty实现RPC
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验