企业级工作流解决方案(八)--微服务Tcp消息传输模型之服务端处理
服务端启动
服务端启动主要做几件事情,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消息传输模型之服务端处理的更多相关文章
- 企业级工作流解决方案(七)--微服务Tcp消息传输模型之消息编解码
Tcp消息传输主要参照surging来做的,做了部分裁剪和改动,详细参见:https://github.com/dotnetcore/surging Json-rpc没有定义消息如何传输,因此,Jso ...
- 企业级工作流解决方案(九)--微服务Tcp消息传输模型之客户端处理
客户端启动 客户端启动主要做三件事情,1. 从配置文件读取服务调用配置,存储到全局对象中.2. 指定客户端编解码器工厂.3. 预连接,即预先建立与服务端的通信Chanel. [DependsOn(ty ...
- 企业级工作流解决方案(十四)--集成Abp和ng-alain--自动化脚本
对于.net方向,做过自动化的,应该没有人不熟悉msbuild吧,非常强大的代码编译工具,.net平台的编译工作都是交给他来完成的,包括.net core的命令,本质上都是调用msbuild来执行的 ...
- 企业级工作流解决方案(六)--微服务消息处理模型之与Abp集成
身份认证传递 对于Abp比较熟悉的朋友应该对他里面的用户身份认证比较熟悉,他是通过实现微软提供的权限认证方式实现的,用户登录身份信息存储在System.Security.Claims.ClaimsPr ...
- 企业级工作流解决方案(十五)--集成Abp和ng-alain--Abp其他改造
配置功能增强 Abp定义了各种配置接口,但是没有定义这些配置数据从哪里来,但是管理配置数据对于一个应用程序来说,是必不可少的一件事情. .net的配置数据管理,一般放在Web.config文件或者Ap ...
- 企业级工作流解决方案(十一)--集成Abp和ng-alain--权限系统服务
权限系统主要定义为管理员增删改查权限数据,直接读取数据库,权限系统服务主要定义为供其他系统调用的权限验证接口,定义为两个不同的微服务. 权限系统有一个特点,数据变动比较小,数据量本身并不是很大,访问量 ...
- 企业级工作流解决方案(十)--集成Abp和ng-alain--权限系统
权限系统 应用系统离不开权限控制,权限中心不一定能抽象出所有的业务场景,这里定义的权限系统不一定能够满足所有的场景,但应该可以满足多数的业务需求. Abp的zero项目也定义了权限相关的表,但里面很多 ...
- 企业级工作流解决方案(十三)--集成Abp和ng-alain--数据库读写分离
说到程序里面数据库管理,无非就是两件事情,一是数据库操作,对于数据库的操作,各种程序语言都有封装,也就是所谓的ORM框架,.net 方向一般用得比较多和就是.net framework和dapper, ...
- 企业级工作流解决方案(十二)--集成Abp和ng-alain--用户身份认证与权限验证
多租户 如果系统需要支持多租户,那么最好事先定义好多租户的存储部署方式,Abp提供了几种方式,根据需要选择,每一个用户身份认证与权限验证都需要完全的隔离 这里设计的权限数据全部存储在缓存中,每个租户单 ...
随机推荐
- c# 误区系列(二)
前言 继续整理误区系列,可能会对刚入门的新手有些帮助,然后希望有错误的地方可以指出. 正文 关于泛型方法的确定 class Person<T> { public void add(T a) ...
- Java进阶--Java动态代理
JDK version: 1.8 动态代理中所说的"动态", 是针对使用Java代码实际编写了代理类的"静态"代理而言的, 它的优势不在于省去了编写代理类那一点 ...
- 类型“DbContext”在未引用的程序集中定义。必须添加对程序及“EntityFramework,Version=6.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089”的引用。using语句中使用的类型必须可隐式转换为”System.IDisposable
其他层引用Model层的ef模型时会发生这个错误 解决方法: 在你要使用EF模型的层下点击添加引用 然后点击浏览 找到Model层文件下的bin>debug文件 引用这两个dll文件 如 ...
- 500G Python从入门到进阶的视频资料
第一部分:Python资源500G百度网盘学习视频300+本电子书需要的小伙伴可以叫小编的Q群867067945 点击展开,查看完整图片 回复关键字:学习第二部分:Python就业指导 更多更详细的就 ...
- 5年Android程序员面试字节跳动两轮后被完虐,请查收给你的面试指南
大家应该看过很多分享面试成功的经验,但根据幸存者偏差的理论,也许多看看别人面试失败在哪里,对自己才更有帮助. 最近跟一个朋友聊天,他准备了几个月,刚刚参加完字节跳动面试,第二面结束后,嗯,挂了- 所以 ...
- eclipse配置打开选中文件存储的目录快捷配置
方便同时复制多个包的文件 https://jingyan.baidu.com/article/adc8151353a896f723bf73cd.html
- onedrive同步其他任意文件夹
经过多次尝试,成功同步其他文件夹内容到onedrive,最后那次尝试是成功的,前面是可能犯的错误.注意3点:1. 用管理员身份运行cmd:2. 路径有空格时用双引号括起来:3. /d后面接的第一个路径 ...
- vue-cli3生产环境和开发环境路径的替换
在根目录下创建两个文件,这样的好处在于不用手动去书写判断环境替换路径代码 .env.development(开发) .env.production(生产) 内容: 必须是VUE_APP前缀开头,这样w ...
- 4G DTU在使用时有哪些注意事项?
4G DTU是用来帮助工业设备快速连接4G网络的设备.众山物联网研发.生产的LTE660正是这样一款功能强大的4G联网"利器". DTU是英文Data Transfer unit的 ...
- mysql处理数据库事务
数据库事务 关注公众号"轻松学编程"了解更多. 1.概念 执行批量操作时,这些操作作为一个整体,要么全部成功,要么全部失败.如银行转账,己方扣钱.对方加钱,这两个操作是一个整体 ...