企业级工作流解决方案(八)--微服务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提供了几种方式,根据需要选择,每一个用户身份认证与权限验证都需要完全的隔离 这里设计的权限数据全部存储在缓存中,每个租户单 ...
随机推荐
- Chimm.Excel —— 使用Java 操作 excel 模板文件生成 excel 文档
Chimm.Excel -- 设置模板,填充数据,就完事儿了~ _____ _ _ _____ _ / __ \ | (_) | ___| | | | / \/ |__ _ _ __ ___ _ __ ...
- C++ Win 32 使用原始套接字获取所有ip数据包并分析(包括ping包)
/*页面编码:GBK 开发环境 VS2019 */ #define _WINSOCK_DEPRECATED_NO_WARNINGS#include <iostream>#include&l ...
- git 报错 error: failed to push some refs to .....
git push 代码的时候报错,报错如下: 这种报错是因为远程仓库的代码和本地仓库的代码不同步,对本地的代码进行一次拉取,再 git push 就可以解决了 通过如下命令进行代码合并 git pul ...
- Python ( 学习基础篇 第二部 )
目录 运算符 算数运算符 比较运算符 赋值运算符 位运算符 逻辑运算符 成员运算符 身份运算符 Python 中运算符的优先级 运算符总结基础语法 判断类型 isinstence 代码块 流程控制 w ...
- jq animate 的第二写法
俩个参数的写法 例子: $('#div1').animate({num:'auto'},{ duration : 1000, //运动时间 easing : 'linear', //运动形式 ...
- java数据结构-05双向链表
一.双向链式存储: ①简述:要是节点中包含两个指针部分,一个指向前驱元,一个指向后继元,Java中LinkedList集合类的实现就是双向链表 (以下图片为网络收集,侵删) ②特点:数据是非连续的,链 ...
- 关于机器翻译评价指标BLEU(bilingual evaluation understudy)的直觉以及个人理解
最近我在做Natural Language Generating的项目,接触到了BLEU这个指标,虽然知道它衡量的是机器翻译的效果,也在一些文献的experiment的部分看到过该指标,但我实际上经常 ...
- mysql分组函数与查询
Ⅰ.分组函数的分类: max():最大值 min():最小值 sum():和 avg():平均值 count():计算非空的个数 这些都是通用的,sqlserver.oracle.mysql都是一样的 ...
- Reverse for ‘password_reset_complete‘ not found. ‘password_reset_complete‘ is not a valid view funct
关注公众号"轻松学编程"了解更多 原因 在使用xadmin与django 2版本以上修改密码时会报这个错,这是由于django修改密码成功后使用的是success_url参数,而x ...
- python使用pip安装第三方模块遇到的问题及解决方法
python使用pip安装第三方模块遇到的问题及解决方法 关注公众号"轻松学编程"了解更多. 使用国内源: 清华:https://pypi.tuna.tsinghua.edu.cn ...