.Net Rpc服务调用记录2

在客户端,通过IProxy作为调用的总入口,方法签名如下:

Task<TResult> InvokeAsync<TResult>(string serviceType,string serviceMethod,params object[] parameters);

(此处没有生成服务的代理,通过字符串指定要调用的类型,方法);具体实现如下:

 1 public class Proxy : IProxy
 2 {
 3 private IServiceFinder _finder;
 4 private IRemoteServiceInvoker _invoker;
 5 public ITracer Tracer { get; set; }
 6 
 7 public Proxy(
 8 IServiceFinder finder,
 9 IRemoteServiceInvoker invoker)
10 {
11 _finder = finder;
12 _invoker = invoker;
13 Tracer = EmptyTracer.Instance;
14 }
15 
16 public async Task<TResult> InvokeAsync<TResult>(string serviceType, string serviceMethod, params object[] parameters)
17 {
18 ContractUtility.CheckEmptyOrNull(serviceType, nameof(serviceType));
19 ContractUtility.CheckEmptyOrNull(serviceMethod, nameof(serviceMethod));
20 
21 Tracer.Trace($"Begin to finds server address by type:{serviceType},method:{serviceMethod}");
22 var address = await _finder.FindAysnc(serviceType,serviceMethod);
23 ContractUtility.CheckNull(address, nameof(address));
24 
25 var request = new RequestMessage {
26 Id = Guid.NewGuid().ToString("N"),
27 ServiceType = serviceType,
28 ServiceMethod = serviceMethod,
29 Parameters = parameters
30 };
31 
32 var instance = await _invoker.InvokeAsync(address, request);
33 return ConvertTo<TResult>(instance);
34 }
35 
36 private TResult ConvertTo<TResult>(object instance) {
37 if (instance == null) {
38 return default(TResult);
39 }
40 return (TResult)instance;
41 }
42 }
View Code

其中,IServiceFinder 内部从IServiceRouteManager中得到可用的服务地址;IServiceRouteManager 提供了两种实现 1.文件 2.Consul;获得地址后,调用IServiceRouteSelector进行地址筛选,提供了两种简单筛选1.随机 2.轮询。具体实现如下:

 1  public class DefaultServiceFinder : IServiceFinder
 2     {
 3         private IServiceRouteManager _serviceRouteManager;
 4         private IServiceRouteSelector _serviceRouteSelector;
 5         public DefaultServiceFinder(
 6             IServiceRouteManager serviceRouteManager,
 7             IServiceRouteSelector serviceRouteSelector) {
 8             _serviceRouteManager = serviceRouteManager;
 9             _serviceRouteSelector = serviceRouteSelector;
10         }
11         public async Task<IPAddressModel> FindAysnc(string serviceType, string serviceMethod)
12         {
13             var route = await _serviceRouteManager.GetRouteAsync(serviceType,serviceMethod);      
14             return await _serviceRouteSelector.SelectAsync(route);            
15         }
16     }
View Code
 1 public class ConsulServiceRouteManager : ServiceRouteManagerBase
 2     {
 3         private ConsulClient _client;
 4 
 5         public ConsulServiceRouteManager(Action<ConsulRuntimeConfiguration> config) {
 6 
 7             var runtimeConfig = new ConsulRuntimeConfiguration();
 8             if (config != null){
 9                 config(runtimeConfig);
10             }
11             _client = new ConsulClient(c=> {
12                 c.Address = new Uri(runtimeConfig.Address);
13                 c.Token = runtimeConfig.Token;
14                 c.Datacenter = runtimeConfig.DataCenter;
15             } );
16         }
17 
18         public override Task ClearAsync()
19         {
20             return Task.FromResult(0);
21         }
22 
23         public override async Task<IEnumerable<ServiceRoute>> GetRouteAsync()
24         {
25             var response = await _client.Agent.Services();
26             if (response.StatusCode == System.Net.HttpStatusCode.OK) {
27                 var serviceRoutes = response.Response.ToServiceRoutes();
28                 return serviceRoutes.MergeAddress();
29             }
30             throw new Exception($"Fail to get services from consul server {_client.Config.Address.ToString()}");
31         }
32 
33         public override async Task SetRouteAsync(IEnumerable<ServiceRoute> serviceRoutes)
34         {
35             ContractUtility.CheckNullOrLength0(serviceRoutes, nameof(serviceRoutes));
36 
37             var consulServices = serviceRoutes.SelectMany(sr => sr.ToConsulServiceDefinition());
38 
39             foreach (var serviceDefinition in consulServices) {
40                 await _client.Agent.ServiceRegister(serviceDefinition);
41             }
42         }
43     }
View Code

获取地址后,将地址和RequestMessage交给IRemoteServiceInvoker,进行发送,实现如下;这里面关键是在发送前,通过TaskCompletionSource设置回调钩子:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using GP.RPC.Logging.Trace;
using GP.RPC.Message;
using GP.RPC.Route.Model;
using GP.RPC.Transport.Receiver;
using GP.RPC.Transport.Sender;
using GP.RPC.Utilities;

namespace GP.RPC.Proxy.ServiceInvoker
{
    public class RemoteServiceInvoker : IRemoteServiceInvoker
    {
        private IMessageSender _sender;
        private IMessageReceiver _receiver;
       
        private ConcurrentDictionary<string, TaskCompletionSource<object>> _innerCachedTaskHook;
        public RemoteServiceInvoker(IMessageSender sender,
            IMessageReceiver receiver)
        {
            _innerCachedTaskHook = new ConcurrentDictionary<string, TaskCompletionSource<object>>();
            _sender = sender;
            _receiver = receiver;
            _receiver.Received += Message_Received;
            _sender.SetReceiverHook(_receiver);
            Tracer = EmptyTracer.Instance;
        }
        public ITracer Tracer { get; set; }
        private Task Message_Received(object message)
        {
            var response = message as ResponseMessage;
            if (message!=null) {

                TaskCompletionSource<object> temp;
                if (_innerCachedTaskHook.TryGetValue(response.RequestId,out temp)) {
                    if (response.Success)
                    {
                        temp.SetResult(response.Result);
                    }
                    else {
                        temp.SetException(new Exception(response.Error));
                    }
                }
            }
            return Task.FromResult(0);
        }

        public async Task<object> InvokeAsync(IPAddressModel address, RequestMessage request)
        {
            var result = RegisterTaskHook(request.Id);
            try
            {
                Tracer.Trace($"Prapre to send to request,host is {address.ToString()}");
                Tracer.Trace($"request message is {request.ToJson()}");
                await _sender.SendAsync(address, request);             
                return await result;
            }            
            finally
            {
                Tracer.Trace($"Remove hook for{request.Id} ");
                TaskCompletionSource<object> temp;
                if (_innerCachedTaskHook.TryRemove(request.Id, out temp))
                {
                    if (!temp.Task.IsCompleted)
                    {
                        Tracer.Trace($"Invoke timeout error for {request.Id} manually");
                        temp.SetException(new TimeoutException("Timeout to receive message from server"));
                    }
                }
            }           
        }

        private Task<object> RegisterTaskHook(string requestId) {

            Tracer.Trace($"Register hook for {requestId}");
            var taskHook = new TaskCompletionSource<object>();                
            _innerCachedTaskHook.TryAdd(requestId, taskHook);
            return taskHook.Task;
        }

    }
}
View Code

通过IMessageSender发送消息,底层的通信使用DotNetty实现,因此通信Channel的实现如下:

 1 public class NettyClientMessageSender : IMessageSender
 2     {
 3         private Bootstrap bootstrap;
 4         private MultithreadEventLoopGroup worker;
 5       
 6         private ConcurrentDictionary<string, Lazy<IChannel>> _innerCachedChannel = new ConcurrentDictionary<string, Lazy<IChannel>>(); 
 7        
 8         public NettyClientMessageSender() {
 9             Tracer = EmptyTracer.Instance;
10             Initialize();
11         }
12 
13         public ITracer Tracer {
14             get;set;
15         }
16         public void Initialize() {
17             Tracer.Trace("Begin to initialize the client sender");
18             bootstrap = new Bootstrap();
19             worker = new MultithreadEventLoopGroup();
20             bootstrap
21                   .Group(worker)
22                   .Channel<TcpSocketChannel>()
23                   .Option(ChannelOption.TcpNodelay, true)
24                   .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
25                   {
26                       IChannelPipeline pipeline = channel.Pipeline;
27                       var handler = new NettyClientHandler();
28                       handler.AsyncClientReceiverHook = this.afterSendingHook;
29                       pipeline.AddLast(new LengthFieldPrepender(4));
30                       pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 4, 0, 4));
31                       pipeline.AddLast(IocManager.Instance.Resolve<NettyMessageEncoderAdapter>(),
32                                        IocManager.Instance.Resolve<NettyMessageDecoderAdapter>(), handler);                     
33                   }));
34         }
35 
36         public async Task SendAsync(IPAddressModel iPAddress, object message)
37         {
38             Tracer.Trace("Begin to get the client channel");
39             IChannel channel = null; //await bootstrap.ConnectAsync(iPAddress.ToEndPoint()); //
40             channel =  _innerCachedChannel.GetOrAdd(iPAddress.ToString(), new Lazy<IChannel>(() => bootstrap.ConnectAsync(iPAddress.ToEndPoint()).Result)).Value;
41             if (!channel.Open) {
42                 Tracer.Trace("Channel is not open");
43             }
44             await channel.WriteAndFlushAsync(message);
45         }
46 
47         private Func<object, Task> afterSendingHook;
48 
49         public void SetReceiverHook(IMessageReceiver receiver)
50         {
51             ContractUtility.CheckNull(receiver,nameof(receiver));
52             this.afterSendingHook += receiver.OnReceived;
53         }
54     }
View Code

编码默认使用了MessagePack,可通过扩展IBinarySerializer,扩展为其他编码方式。

 

以上是整个客户端关键代码部分的分析。后续会继续服务端的代码分析。

 

posted @ 2018-11-15 19:30  于洋.TECH  阅读(223)  评论(0编辑  收藏  举报