.NET Core微服务之路:让我们对上一个Demo通讯进行修改,完成RPC通讯
- 当服务端启动后,绑定并监听(READ)设定的端口,比如1889。
- 当客户端启动后,绑定指定端口,等待用户输入。
- 当用户输入任意字符串数据后,客户端将这组数据进行转码为byte格式进行传输到服务端。
- 当服务端收到客户端传来的数据,进行转码后输出控制台,并将这组数据再次回传到客户端。
- 客户端收到数据,也打印出来。

服务端
增加两个静态方法SayHello和SayByebye,用于提供远程调用,超级简单,不解释。
public static class Say
{
public static string SayHello(string content)
{
return $"hello {content}";
} public static string SayByebye(string content)
{
return $"byebye {content}";
}
}
在我们原来的ChannelRead函数中,将原有的Echo回路传输,直接替换成如下内容。
public override void ChannelRead(IChannelHandlerContext context, object message)
{
if (message is IByteBuffer buffer)
{
Console.WriteLine($"message length is {buffer.Capacity}");
var obj = JsonConvert.DeserializeObject<Dictionary<string, string>>(buffer.ToString(Encoding.UTF8).Replace(")", "")); // (1) byte[] msg = null;
if (obj["func"].Contains("sayHello")) // (2)
{
msg = Encoding.UTF8.GetBytes(Say.SayHello(json["username"]));
} if (obj["func"].Contains("sayByebye")) // (2)
{
msg = Encoding.UTF8.GetBytes(Say.SayByebye(json["username"]));
} if (msg == null) return;
// 设置Buffer大小
var b = Unpooled.Buffer(msg.Length, msg.Length); // (3)
IByteBuffer byteBuffer = b.WriteBytes(msg); // (4)
context.WriteAsync(byteBuffer); // (5)
}
}
客户端
我们将上一个demo中的EchoClientHandler做如下修改,以完成一个简单的请求
public EchoClientHandler()
{
var hello = new Dictionary<string, string> // (1)
{
{"func", "sayHello"},
{"username", "stevelee"}
};
SendMessage(ToStream(JsonConvert.SerializeObject(hello)));
} private byte[] ToStream(string msg)
{
Console.WriteLine($"string length is {msg.Length}");
using (var stream = new MemoryStream()) // (2)
{
Serializer.Serialize(stream, msg);
return stream.ToArray();
}
} private void SendMessage(byte[] msg)
{
Console.WriteLine($"byte length is {msg.Length}");
_initialMessage = Unpooled.Buffer(msg.Length, msg.Length);
_initialMessage.WriteBytes(msg); // (3)
}
启动一下
由于在客户端明文标注了使用sayHello这个方法,客户端会收到服务端返回的"hello stevelee"。

这样一个最简单的RPC远程调用就完成了(其实上一篇就也属于RPC,只是这里用方法和过滤来指定调用)。
问题
- 服务端不可能都通过这样笨拙的过滤方式来调用方法吧?是的,这只是DEMO,为了演示和理解基础概念而已,而是要动过动态代理来实现方法Invoke。
- 这个DEMO只是一个点对点的远程调用,不会涉及到任何服务路由和转发等高级特性。
- 有新的接口的时候时候,需要重新编译和暴露,如果有上万个新的接口,这样的重复工作岂不是疯了。
- ...etc
Esay.Rpc

先看看接口定义了些什么:
/// <summary>
/// 接口UserService的定义
/// </summary>
[RpcTagBundle]
public interface IUserService
{
Task<string> GetUserName(int id); Task<int> GetUserId(string userName); Task<DateTime> GetUserLastSignInTime(int id); Task<UserModel> GetUser(int id); Task<bool> Update(int id, UserModel model); Task<IDictionary<string, string>> GetDictionary(); Task Try(); Task TryThrowException();
}
[ProtoContract]
public class UserModel
{
[ProtoMember()] public string Name { get; set; } [ProtoMember()] public int Age { get; set; }
}
上面有两个不一样的标记,也是protobuf-net中独有的特性。
protobuf-net的坑
- 默认例子中该类没有任何继承,因此不会存在一个妖孽问题,但如果UserModel是一个子类,他继承于一个父类,而这个父类也同样拥有多个子类,直接ProtoContract参与序列化将会报错,需要在特性上增加DataMemberOffset = x,此处的x不是字母,而是这个子类的一个序列化顺序。比如有3个子类继承同一个父类,前面两个子类的偏移量分别是1和2,那么这个类的偏移量将设置为3,以此类推。
- 默认的数据类型中,系统定义的标准类型没问题,但有个妖孽的int[]这样的数组类型,那也将是个噩梦,官网团队没有解释为何不支持数组的序列化,我猜测估计是因为数组的不规则性(比如多维数组、甚至不规则的多维数组)而放弃了这个类型的序列化,毕竟序列化是不能影响性能的。
接下来继续服务端的代码
static void Main()
{
var bTime = DateTime.Now; // 实现自动装配
var serviceCollection = new ServiceCollection();
{
serviceCollection
.AddLogging()
.AddRpcCore()
.AddService()
.UseSharedFileRouteManager("d:\\routes.txt")
.UseDotNettyTransport(); // ** 注入本地测试类
serviceCollection.AddSingleton<IUserService, UserServiceImpl>();
} // 构建当前容器
var buildServiceProvider = serviceCollection.BuildServiceProvider(); // 获取服务管理实体类
var serviceEntryManager = buildServiceProvider.GetRequiredService<IServiceEntryManager>();
var addressDescriptors = serviceEntryManager.GetEntries().Select(i => new ServiceRoute
{
Address = new[]
{
new IpAddressModel {Ip = "127.0.0.1", Port = }
},
ServiceDescriptor = i.Descriptor
});
var serviceRouteManager = buildServiceProvider.GetRequiredService<IServiceRouteManager>();
serviceRouteManager.SetRoutesAsync(addressDescriptors).Wait(); // 构建内部日志处理
buildServiceProvider.GetRequiredService<ILoggerFactory>().AddConsole((console, logLevel) => (int) logLevel >= ); // 获取服务宿主
var serviceHost = buildServiceProvider.GetRequiredService<IServiceHost>(); Task.Factory.StartNew(async () =>
{
//启动主机
await serviceHost.StartAsync(new IPEndPoint(IPAddress.Parse("127.0.0.1"), ));
}); Console.ReadLine();
}
static void Main()
{
var serviceCollection = new ServiceCollection();
{
serviceCollection
.AddLogging() // 添加日志
.AddClient() // 添加客户端
.UseSharedFileRouteManager(@"d:\routes.txt") // 添加共享路由
.UseDotNettyTransport(); // 添加DotNetty通信传输
} var serviceProvider = serviceCollection.BuildServiceProvider(); serviceProvider.GetRequiredService<ILoggerFactory>().AddConsole((console, logLevel) => (int) logLevel >= ); var services = serviceProvider.GetRequiredService<IServiceProxyGenerater>()
.GenerateProxys(new[] {typeof(IUserService)}).ToArray(); var userService = serviceProvider.GetRequiredService<IServiceProxyFactory>().CreateProxy<IUserService>(
services.Single(typeof(IUserService).GetTypeInfo().IsAssignableFrom)
); while (true)
{
Task.Run(async () =>
{
Console.WriteLine($"userService.GetUserName:{await userService.GetUserName(1)}");
Console.WriteLine($"userService.GetUserId:{await userService.GetUserId("rabbit")}");
Console.WriteLine($"userService.GetUserLastSignInTime:{await userService.GetUserLastSignInTime(1)}");
var user = await userService.GetUser();
Console.WriteLine($"userService.GetUser:name={user.Name},age={user.Age}");
Console.WriteLine($"userService.Update:{await userService.Update(1, user)}");
Console.WriteLine($"userService.GetDictionary:{(await userService.GetDictionary())["key"]}");
await userService.Try();
Console.WriteLine("client function completed!");
}).Wait();
Console.ReadKey();
}
}
.NET Core微服务之路:让我们对上一个Demo通讯进行修改,完成RPC通讯的更多相关文章
- .NET Core微服务之路:文章系列和内容索引汇总 (v0.52)
微服务架构,对于从事JAVA架构的童鞋来说,早已不是什么新鲜的事儿,他们有鼎鼎大名的Spring Cloud这样的全家桶框架支撑,包含微服务核心组件如 1. Eureka:实现服务注册与发现. 2. ...
- NET Core微服务之路:实战SkyWalking+Exceptionless体验生产环境下的追踪系统
前言 当一个APM或一个日志中心实际部署在生产环境中时,是有点力不从心的. 比如如下场景分析的问题: 从APM上说,知道某个节点出现异常,或延迟过过高,却不能及时知道日志反馈情况,总不可能去相应的节点 ...
- NET Core微服务之路:实战SkyWalking+Exceptionless体验生产下追踪系统
原文:NET Core微服务之路:实战SkyWalking+Exceptionless体验生产下追踪系统 前言 当一个APM或一个日志中心实际部署在生产环境中时,是有点力不从心的. 比如如下场景分析的 ...
- .NET Core微服务之路:不断更新中的目录 (v0.43)
原文:.NET Core微服务之路:不断更新中的目录 (v0.43) 微服务架构,对于从事JAVA架构的童鞋来说,早已不是什么新鲜的事儿,他们有鼎鼎大名的Spring Cloud这样的全家桶框架支撑, ...
- NET Core微服务之路:自己动手实现Rpc服务框架,基于DotEasy.Rpc服务框架的介绍和集成
本篇内容属于非实用性(拿来即用)介绍,如对框架设计没兴趣的朋友,请略过. 快一个月没有写博文了,最近忙着两件事; 一:阅读刘墉先生的<说话的魅力>,以一种微妙的,你我大家都会经常遇见 ...
- NET Core微服务之路:简单谈谈对ELK,Splunk,Exceptionless统一日志收集中心的心得体会
前言 日志,一直以来都是开发人员和运维人员最关心的问题.开发人员可通过日志记录来协助问题定位,运维人员可通过日志发现系统隐患,故障等定位问题.如果你的系统中没有日志,就像一个断了线的风筝,你永远不知道 ...
- .NET Core微服务之路:基于gRPC服务发现与服务治理的方案
重温最少化集群搭建,我相信很多朋友都已经搭建出来,基于Watch机制也实现了出来,相信也有很多朋友有了自己的实现思路,但是,很多朋友有个疑问,我API和服务分离好了,怎么通过服务中心进行发现呢,这个过 ...
- .NET Core微服务之路:基于Consul最少集群实现服务的注册与发现(二)
重温Consul最少化集群的搭建
- NET Core微服务之路:基于Ocelot的API网关Relay实现--RPC篇
前言 我们都知道,API网关是工作在应用层上网关程序,为何要这样设计呢,而不是将网关程序直接工作在传输层.或者网络层等等更底层的环境呢?让我们先来简单的了解一下TCP/IP的五层模型. (图片 ...
随机推荐
- leetcode207
拓扑排序问题. class Solution { public: bool canFinish(int numCourses, vector<pair<int, int>>&a ...
- Node Sass does not yet support your current environment解决办法
在React项目中,使用了sass.之前运行的好好的,今天突然报错,提示当前环境不支持sass模块,然后就百度了下,发现有相同问题的.原来问题是之前开发时node是6.x的版本,几天前更新到最新10. ...
- 如何创建.gitignore文件,忽略不必要提交的文件
1.gitignore 在工程实现过程中,会生成一些中间文件,或者在项目中的部分文件是不需要进行版本管理的.对于这些文件应该对于Github来讲是透明的.Github提供这种功能,可以自己指定哪些文件 ...
- df命令详解
1.简介: df命令作用是列出文件系统的整体磁盘空间使用情况.可以用来查看磁盘已被使用多少空间和还剩余多少空间. df命令显示系统中包含每个文件名参数的磁盘使用情况,如果没有文件名参数,则显示所有当前 ...
- JAVA数据库连接池C3p0 以及阿里Druid提供的连接池
一:连接池的定义 本质上就是个容器(集合) 存放数据库连接的容器,当系统初始化后,容器被创建,容器中就会申请一些连接对象,当用户来访问数据库的时候,从容器中取连接对象,用户用完之后,归还. 二:常用的 ...
- C++ is_same
is_same template< class T, class U > struct is_same; 如果T与U具有同一const-volatile限定的相同类型,则is_same&l ...
- LENGTH()和CHAR_LENGTH()区别
LENGTH()返回以字节为单位的字符串的长度.CHAR_LENGTH()返回以字符为单位的字符串的长度. 分别对应于java中的str.getBytes().length和str.length( ...
- 求树的重心 poj 1655
题目链接:https://vjudge.net/problem/POJ-1655 这个就是找树的重心,树的重心就是树里面找一个点,使得以这个点为树根的所有的子树中最大的子树节点数最小.题目应该讲的比较 ...
- 记录一次Centos磁盘空间占满的解决办法(转)
原文地址:https://blog.csdn.net/everything1209/article/details/70209157 解决前 磁盘使用情况: 第二块磁盘使用率达到97% [root@f ...
- 【Android】异步加载布局探索
最近在做的项目页面复杂导致布局嵌套多层,而且又使用了百分比布局(可能主要是这个原因)导致页面加载的时候主线程会被阻塞, 那要想减少主线程阻塞,一来就是简化布局,减轻LayoutInflater的负担, ...