服务端启动

  服务端启动主要做几件事情,1. 从配置文件读取服务配置(主要是服务监听端口和编解码配置),2. 注册编解码器工厂,3. 启动dotnetty监听端口,4. 读取配置文件,解析全局消息处理模型5. 注册服务端处理对象到容器。

  JsonRpcServerModule代码如下,见备注说明

[DependsOn(typeof(AbpKernelModule))]
public class JsonRpcServerModule : AbpModule
{
public override void PreInitialize()
{
// 注册客户端配置,固定从Xml文件读取
SocketServiceConfiguration socketServiceConfiguration = XmlConfigProvider.GetConfig<SocketServiceConfiguration>("SocketServiceConfiguration.xml");
IocManager.IocContainer.Register(
Component
.For<ISocketServiceConfiguration>()
.Instance(socketServiceConfiguration)
);
IocManager.Register<IServiceExecutor, DefaultServiceExecutor>(Dependency.DependencyLifeStyle.Singleton);
} public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(JsonRpcServerModule).GetAssembly());
var socketServiceConfiguration = Configuration.Modules.RpcServiceConfig();
switch (socketServiceConfiguration.MessageCode) // 根据配置文件,编解码配置选择
{
case EMessageCode.Json:
IocManager.RegisterIfNot<ITransportMessageCodecFactory, JsonTransportMessageCodecFactory>(Dependency.DependencyLifeStyle.Singleton);
break;
case EMessageCode.MessagePack:
IocManager.RegisterIfNot<ITransportMessageCodecFactory, MessagePackTransportMessageCodecFactory>(Dependency.DependencyLifeStyle.Singleton);
break;
case EMessageCode.ProtoBuffer:
IocManager.RegisterIfNot<ITransportMessageCodecFactory, ProtoBufferTransportMessageCodecFactory>(Dependency.DependencyLifeStyle.Singleton);
break;
default:
break;
} RegisterDefaultProtocol();
} public override void PostInitialize()
{
var socketServiceConfiguration = IocManager.Resolve<ISocketServiceConfiguration>();
// 方法里面调用ServiceHost构造函数传入的委托,启动dotnetty监听
IocManager.Resolve<IServiceHost>().StartAsync(new IpAddressModel("0.0.0.0", socketServiceConfiguration.Port).CreateEndPoint()); // 从配置文件读取json-rpc服务配置,解析消息处理模型
JsonRpcRegister.LoadFromConfig(IocManager);
} private void RegisterDefaultProtocol()
{
var dotNettyServerMessageListener = new DotNettyServerMessageListener(Logger,
IocManager.Resolve<ITransportMessageCodecFactory>(), IocManager.Resolve<ISocketServiceConfiguration>()); IocManager.IocContainer.Register(
Component
.For<IMessageListener>()
.Instance(dotNettyServerMessageListener)
); var serviceExecutor = IocManager.Resolve<IServiceExecutor>(); // 新建一个ServiceHost对象,放入容器,这个时候dotnetty还未启动,只是定义了执行方法。
var serverHost = new DefaultServiceHost(async endPoint =>
{
await dotNettyServerMessageListener.StartAsync(endPoint); // 启动dotnetty监听
return dotNettyServerMessageListener;
}, serviceExecutor); IocManager.IocContainer.Register(
Component
.For<IServiceHost>()
.Instance(serverHost)
);
}
}

  Dotnetty启动监听代码,参考dotnetty提供的实例代码,ServerHandler为自定义消息处理Chanel

/// <summary>
/// 触发接收到消息事件。
/// </summary>
/// <param name="sender">消息发送者。</param>
/// <param name="message">接收到的消息。</param>
/// <returns>一个任务。</returns>
public async Task OnReceived(IMessageSender sender, TransportMessage message)
{
if (Received == null)
return;
await Received(sender, message);
}
public async Task StartAsync(EndPoint endPoint)
{
_logger.Debug($"准备启动服务主机,监听地址:{endPoint}。"); IEventLoopGroup bossGroup = new MultithreadEventLoopGroup(1);
IEventLoopGroup workerGroup = new MultithreadEventLoopGroup();//Default eventLoopCount is Environment.ProcessorCount * 2
var bootstrap = new ServerBootstrap(); bossGroup = new MultithreadEventLoopGroup(1);
workerGroup = new MultithreadEventLoopGroup();
bootstrap.Channel<TcpServerSocketChannel>();
bootstrap
.Option(ChannelOption.SoBacklog, _socketServiceConfiguration.Backlog)
.ChildOption(ChannelOption.Allocator, PooledByteBufferAllocator.Default)
.Group(bossGroup, workerGroup)
.ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
{
var pipeline = channel.Pipeline;
pipeline.AddLast(new LengthFieldPrepender(4));
pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 4, 0, 4));
pipeline.AddLast(new TransportMessageChannelHandlerAdapter(_transportMessageDecoder));
pipeline.AddLast(new ServerHandler(async (contenxt, message) =>
{
var sender = new DotNettyServerMessageSender(_transportMessageEncoder, contenxt);
await OnReceived(sender, message);
}, _logger));
}));
try
{
_channel = await bootstrap.BindAsync(endPoint);
_logger.Debug($"服务主机启动成功,监听地址:{endPoint}。");
}
catch
{
_logger.Error($"服务主机启动失败,监听地址:{endPoint}。 ");
}
}

  消息最终经过解码处理之后,会落到DefaultServiceExecutor类进行处理,在这里调用JsonRpcProcessor静态类的Process方法,处理Json-Rpc请求,并构造答复消息,答复客户端。

public class DefaultServiceExecutor : IServiceExecutor
{
private readonly ILogger _logger;
public DefaultServiceExecutor(ILogger logger)
{
_logger = logger;
}
public async Task ExecuteAsync(IMessageSender sender, TransportMessage message)
{
_logger.Debug("服务提供者接收到消息"); if (!message.IsInvokeMessage())
return; JsonRequest jsonRequest;
try
{
jsonRequest = message.GetContent<JsonRequest>();
}
catch (Exception exception)
{
_logger.Error("将接收到的消息反序列化成 TransportMessage<JsonRequest> 时发送了错误。", exception);
return;
} _logger.Debug("准备执行本地逻辑。"); var resultMessage = await LocalExecuteAsync(jsonRequest, message.Headers); //向客户端发送调用结果。
await SendRemoteInvokeResult(sender, message.Id, JsonConvert.DeserializeObject<JsonResponse>(resultMessage));
} private async Task<string> LocalExecuteAsync(JsonRequest jsonRequest,object headers)
{
return await JsonRpcProcessor.Process(JsonConvert.SerializeObject(jsonRequest), headers);
} private async Task SendRemoteInvokeResult(IMessageSender sender, string messageId, JsonResponse resultMessage)
{
try
{ _logger.Debug("准备发送响应消息。"); await sender.SendAndFlushAsync(TransportMessage.CreateInvokeResultMessage(messageId, resultMessage, new NameValueCollection()));
_logger.Debug("响应消息发送成功。");
}
catch (Exception exception)
{
_logger.Error("发送响应消息时候发生了异常。", exception);
}
}
}

这部分内容没有太多的说明,参见surging

企业级工作流解决方案(八)--微服务Tcp消息传输模型之服务端处理的更多相关文章

  1. 企业级工作流解决方案(七)--微服务Tcp消息传输模型之消息编解码

    Tcp消息传输主要参照surging来做的,做了部分裁剪和改动,详细参见:https://github.com/dotnetcore/surging Json-rpc没有定义消息如何传输,因此,Jso ...

  2. 企业级工作流解决方案(九)--微服务Tcp消息传输模型之客户端处理

    客户端启动 客户端启动主要做三件事情,1. 从配置文件读取服务调用配置,存储到全局对象中.2. 指定客户端编解码器工厂.3. 预连接,即预先建立与服务端的通信Chanel. [DependsOn(ty ...

  3. 企业级工作流解决方案(十四)--集成Abp和ng-alain--自动化脚本

    对于.net方向,做过自动化的,应该没有人不熟悉msbuild吧,非常强大的代码编译工具,.net平台的编译工作都是交给他来完成的,包括.net core的命令,本质上都是调用msbuild来执行的 ...

  4. 企业级工作流解决方案(六)--微服务消息处理模型之与Abp集成

    身份认证传递 对于Abp比较熟悉的朋友应该对他里面的用户身份认证比较熟悉,他是通过实现微软提供的权限认证方式实现的,用户登录身份信息存储在System.Security.Claims.ClaimsPr ...

  5. 企业级工作流解决方案(十五)--集成Abp和ng-alain--Abp其他改造

    配置功能增强 Abp定义了各种配置接口,但是没有定义这些配置数据从哪里来,但是管理配置数据对于一个应用程序来说,是必不可少的一件事情. .net的配置数据管理,一般放在Web.config文件或者Ap ...

  6. 企业级工作流解决方案(十一)--集成Abp和ng-alain--权限系统服务

    权限系统主要定义为管理员增删改查权限数据,直接读取数据库,权限系统服务主要定义为供其他系统调用的权限验证接口,定义为两个不同的微服务. 权限系统有一个特点,数据变动比较小,数据量本身并不是很大,访问量 ...

  7. 企业级工作流解决方案(十)--集成Abp和ng-alain--权限系统

    权限系统 应用系统离不开权限控制,权限中心不一定能抽象出所有的业务场景,这里定义的权限系统不一定能够满足所有的场景,但应该可以满足多数的业务需求. Abp的zero项目也定义了权限相关的表,但里面很多 ...

  8. 企业级工作流解决方案(十三)--集成Abp和ng-alain--数据库读写分离

    说到程序里面数据库管理,无非就是两件事情,一是数据库操作,对于数据库的操作,各种程序语言都有封装,也就是所谓的ORM框架,.net 方向一般用得比较多和就是.net framework和dapper, ...

  9. 企业级工作流解决方案(十二)--集成Abp和ng-alain--用户身份认证与权限验证

    多租户 如果系统需要支持多租户,那么最好事先定义好多租户的存储部署方式,Abp提供了几种方式,根据需要选择,每一个用户身份认证与权限验证都需要完全的隔离 这里设计的权限数据全部存储在缓存中,每个租户单 ...

随机推荐

  1. ERP仓库管理的操作与设计--开源软件诞生20

    赤龙ERP库房管理讲解--第20篇 用日志记录"开源软件"的诞生 [点亮星标]----祈盼着一个鼓励 博主开源地址: 码云:https://gitee.com/redragon/r ...

  2. 基于.Net Core开发的物联网平台 IoTSharp V1.5 发布

    很高兴的宣布新版本的发布, 这次更新我们带来了大量新特性, 最值得关注的是, 我们逐步开始支持分布式, 这意味着你可以通过多台服务器共同处理数据, 而不是原来的单机处理, 我们也将遥测数据进行分开存储 ...

  3. G1摘要

    G1 启动参数示例 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+PrintTenuringDistribution -XX:+UseG1 ...

  4. EXPECT交互式批量上传公钥

    EXPECT交互式批量上传公钥 # vim key.sh #/bin/bash Public_Key() { [ ! -f /usr/bin/expect ] && yum insta ...

  5. array_walk_recursive 地址引用报错的问题

    今天看十八哥的视频,学习array_walk_recursive的用法,发现一直报错: PHP版本:5.6.19 代码界面: 报错界面: 查了很长时间,不知道什么问题,后来在网上终于找到原因所在: + ...

  6. Java学习的第五十三天

    1.例9.5引用静态数据成员 public class Cjava { public static void main(String[]args) { Box b[] = {new Box(12,15 ...

  7. MONGODB01 - Prematurely reached end of stream 错误定位及修复

    最近项目在运行过程,当一段时间没有操作mongo,再次访问报错,如下 org.springframework.data.mongodb.UncategorizedMongoDbException: P ...

  8. 【17】进大厂必须掌握的面试题-50个Angular面试

    我们整理了一份主要的Angular面试问题清单,分为三部分: 角度面试问题–初学者水平 角度面试问题–中级 角度面试问题–高级 初学者水平–面试问题 1.区分Angular和AngularJS. 特征 ...

  9. 【SpringBoot】06.SpringBoot访问静态资源

    SpringBoot访问静态资源 1.SpringBoot从classpath/static的目录 目录名称必须是static 启动项目,访问http://localhost:8080/0101.jp ...

  10. Redis学习(一)——初识Redis

    1.Redis是什么 1)REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统. 2)Redis的特点 Red ...