在客户端,通过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 }
其中,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 }
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 }
获取地址后,将地址和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; } } }
通过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 }
编码默认使用了MessagePack,可通过扩展IBinarySerializer,扩展为其他编码方式。
以上是整个客户端关键代码部分的分析。后续会继续服务端的代码分析。