前言

    一直以来对内部服务间使用RPC的方式调用都比较赞同,因为内部间没有这么多限制,最简单明了的方式就是最合适的方式。个人比较喜欢类似Dubbo的那种使用方式,把接口层单独出来,作为服务的契约,服务端以这套契约提供服务,客户端使用这套契约调用服务,和使用本地方法的方式是一样的。.Net平台上类似Dubbo这种相对比较完善的RPC框架还是比较少的,GRPC确实是一款非常优秀的RPC框架,能夸语言调用,但是从个人的角度上来说每次还得编写proto文件感觉还是比较麻烦的。如今服务拆分,微服务架构比较盛行的潮流下,一个简单实用的RPC框架确实可以提升很多开发效率。

简介

    随着.Net Core逐渐成熟稳定,为我一直以来想实现的这个目标提供了便利的方式。于是利用闲暇时间本人手写了一套基于Asp.Net Core的RPC框架,算是实现了一个自己的小目标。大致的实现方式,Server端依赖Asp.Net Core,采用的是中间件的方式拦截处理请求比较方便。Client端可以是任何可承载.Net Core的宿主程序。通信方式是HTTP协议,使用的是HttpClientFactory。至于为什么使用HttpClientFactory,因为HttpClientFactory可以更轻松的实现服务发现,而且可以很好的集成Polly,很方便的实现,超时重试,熔断降级这些,给开发过程中提供了很多便利。由于本人能力有限,基于这些便利,站在巨人的肩膀上,简单的实现了一个RPC框架,项目托管在GitHub上https://github.com/softlgl/DotNetCoreRpc有兴趣的可以自行查阅。

开发环境

  • Visual Studio 2019
  • .Net Standard 2.1
  • Asp.Net Core 3.1.x

使用方式

    打开Visual Studio先新建一个RPC契约接口层,这里我起的名字叫IRpcService。然后新建一个Client层(可以是任何可承载.Net Core的宿主程序)叫ClientDemo,然后建立一个Server层(必须是Asp.Net Core项目)叫WebDemo,文末附本文Demo连接,建完这些之后项目结构如下:

Client端配置

Client端引入DotNetCoreRpc.Client包,并引入自定义的契约接口层

<PackageReference Include="DotNetCoreRpc.Client" Version="1.0.2" />

然后可以愉快的编码了,大致编码如下

class Program
{
static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
//*注册DotNetCoreRpcClient核心服务
services.AddDotNetCoreRpcClient()
//*通信是基于HTTP的,内部使用的HttpClientFactory,自行注册即可
.AddHttpClient("WebDemo", client => { client.BaseAddress = new Uri("http://localhost:13285/"); }); IServiceProvider serviceProvider = services.BuildServiceProvider();
//*获取RpcClient使用这个类创建具体服务代理对象
RpcClient rpcClient = serviceProvider.GetRequiredService<RpcClient>(); //IPersonService是我引入的服务包interface,需要提供ServiceName,即AddHttpClient的名称
IPersonService personService = rpcClient.CreateClient<IPersonService>("WebDemo"); PersonDto personDto = new PersonDto
{
Id = 1,
Name = "yi念之间",
Address = "中国",
BirthDay = new DateTime(2000,12,12),
IsMarried = true,
Tel = 88888888888
}; bool addFlag = personService.Add(personDto);
Console.WriteLine($"添加结果=[{addFlag}]"); var person = personService.Get(personDto.Id);
Console.WriteLine($"获取person结果=[{person.ToJson()}]"); var persons = personService.GetAll();
Console.WriteLine($"获取persons结果=[{persons.ToList().ToJson()}]"); personService.Delete(person.Id);
Console.WriteLine($"删除完成"); Console.ReadLine();
}
}

到这里Client端的代码就编写完成了

Server端配置

Client端引入DotNetCoreRpc.Client包,并引入自定义的契约接口层

<PackageReference Include="DotNetCoreRpc.Server" Version="1.0.2" />

然后编写契约接口实现类,比如我的叫PersonService

//实现契约接口IPersonService
public class PersonService:IPersonService
{
private readonly ConcurrentDictionary<int, PersonDto> persons = new ConcurrentDictionary<int, PersonDto>();
public bool Add(PersonDto person)
{
return persons.TryAdd(person.Id, person);
} public void Delete(int id)
{
persons.Remove(id,out PersonDto person);
} //自定义Filter
[CacheFilter(CacheTime = 500)]
public PersonDto Get(int id)
{
return persons.GetValueOrDefault(id);
} //自定义Filter
[CacheFilter(CacheTime = 300)]
public IEnumerable<PersonDto> GetAll()
{
foreach (var item in persons)
{
yield return item.Value;
}
}
}

通过上面的代码可以看出,我自定义了Filter,这里的Filter并非Asp.Net Core框架定义的Filter,而是DotNetCoreRpc框架定义的Filter,自定义Filter的方式如下

//*要继承自抽象类RpcFilterAttribute
public class CacheFilterAttribute: RpcFilterAttribute
{
public int CacheTime { get; set; } //*支持属性注入,可以是public或者private
//*这里的FromServices并非Asp.Net Core命名空间下的,而是来自DotNetCoreRpc.Core命名空间
[FromServices]
private RedisConfigOptions RedisConfig { get; set; } [FromServices]
public ILogger<CacheFilterAttribute> Logger { get; set; } public override async Task InvokeAsync(RpcContext context, RpcRequestDelegate next)
{
Logger.LogInformation($"CacheFilterAttribute Begin,CacheTime=[{CacheTime}],Class=[{context.TargetType.FullName}],Method=[{context.Method.Name}],Params=[{JsonConvert.SerializeObject(context.Parameters)}]");
await next(context);
Logger.LogInformation($"CacheFilterAttribute End,ReturnValue=[{JsonConvert.SerializeObject(context.ReturnValue)}]");
}
}

以上代码基本上完成了对服务端业务代码的操作,接下来我们来看如何在Asp.Net Core中配置使用DotNetCoreRpc。打开Startup,配置如下代码既可

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IPersonService, PersonService>()
.AddSingleton(new RedisConfigOptions { Address = "127.0.0.1:6379", Db = 10 })
//*注册DotNetCoreRpcServer
.AddDotNetCoreRpcServer(options => {
//*确保添加的契约服务接口事先已经被注册到DI容器中 //添加契约接口
//options.AddService<IPersonService>(); //或添加契约接口名称以xxx为结尾的
//options.AddService("*Service"); //或添加具体名称为xxx的契约接口
//options.AddService("IPersonService"); //或扫描具体命名空间下的契约接口
options.AddNameSpace("IRpcService"); //可以添加全局过滤器,实现方式和CacheFilterAttribute一致
options.AddFilter<LoggerFilterAttribute>();
});
} public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//这一堆可以不要+1
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} //添加DotNetCoreRpc中间件既可
app.UseDotNetCoreRpc(); //这一堆可以不要+2
app.UseRouting(); //这一堆可以不要+3
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Server Start!");
});
});
}
}

以上就是Server端简单的使用和配置,是不是感觉非常的Easy。附上可运行的Demo地址,具体编码可查看Demo.

总结

    能自己实现一套RPC框架是我近期以来的一个愿望,现在可以说实现了。虽然看起来没这么高大上,但是整体还是符合RPC的思想。主要还是想自身实地的实践一下,顺便也希望能给大家提供一些简单的思路。不是说我说得一定是对的,我讲得可能很多是不对的,但是我说的东西都是我自身的体验和思考,也许能给你带来一秒钟、半秒钟的思考,亦或是哪怕你觉得我哪一句话说的有点道理,能引发你内心的感触,这就是我做这件事的意义。最后,欢迎大家评论区或本项目GitHub下批评指导。

手动造轮子——基于.NetCore的RPC框架DotNetCoreRpc的更多相关文章

  1. 这样基于Netty重构RPC框架你不可能知道

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365天原创计划”第5天. 今天呢!灯塔君跟大家讲: 基于Netty重构RPC框架 一.CyclicBarrier方法说明 1. ...

  2. NetCore版RPC框架NewLife.ApiServer

    微服务和消息队列的基础都是RPC框架,比较有名的有WCF.gRPC.Dubbo等,我们的NewLife.ApiServer建立在网络库NewLife.Net之上,支持.Net Core,追求轻量级和高 ...

  3. 基于Netty重构RPC框架

    下面的这张图,大概很多小伙伴都见到过,这是Dubbo 官网中的一张图描述了项目架构的演进过程.随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在 ...

  4. 基于netty实现rpc框架-spring boot服务端

    demo地址 https://gitee.com/syher/grave-netty RPC介绍 首先了解一下RPC:远程过程调用.简单点说就是本地应用可以调用远程服务器的接口.那么通过什么方式调用远 ...

  5. 一个简单的基于BIO的RPC框架

    github地址:https://github.com/Luyu05/BioRpcExample PART1:先来整体看下项目的构成 其中bio-rpc-core就是所谓的rpc框架 bio-rpc- ...

  6. 基于 SOA 概念 RPC 框架 的 消息中心 云部署 设计 漫谈

    一.背景 假设有一个系统的最大并发量有2000TPS左右.同时该系统有闲时和忙时,希望可以随时进行拓展和削减服务能力,以节省服务器费用开销. 该系统能提供站内消息.短信.app消息.邮箱的一个消息系统 ...

  7. 基于RabbitMQ的Rpc框架

    参考文档:https://www.cnblogs.com/ericli-ericli/p/5917018.html 参考文档:RabbitMQ 实现RPC MQ的使用场景大概包括解耦,提高峰值处理能力 ...

  8. 基于netty实现rpc框架-spring boot客户端

    上篇讲了RPC服务端的实现.原理就是解析netty通道数据拿到类.方法及入参等信息,然后通过java反射机制调用本地接口返回结果.没有用到很复杂的技术. 这篇我们将客户端的实现.说白了客户端的任务很简 ...

  9. .NetCore下构建自己的服务配置中心-手动造轮子

    本人主要利用IdentityServer4以及SignalR来实现,IdentityServer4作为认证,SignalR来交互配置,这里一些代码可能就是部分提出来,主要介绍实现原理及方法 实现配置中 ...

随机推荐

  1. 14.5 Go 爬虫

    14.5 Go 爬虫 爬虫库/框架 henrylee2cn/pholcus 幽灵蛛 星5200 gocrawl 星1700 colly 星7169 hu17889/go_spider 星1400 go ...

  2. mysql设置文档快捷写

    select distinct column_name,column_comment,column_type from information_schema.columns where table_n ...

  3. jquery.autocomplete的使用-----------------------摘抄别人的

    作者:lxhwss | 2011/10/11 9:46:38 | 阅读43次 document.write(”<script language=javascript src=’/js/2.js’ ...

  4. BitArray虽好,但请不要滥用,又一次线上内存暴增排查

    一:背景 1. 讲故事 前天写了一篇大内存排查在园子里挺火,这是做自媒体最开心的事拉,干脆再来一篇满足大家胃口,上个月我写了一篇博客提到过使用bitmap对原来的List<CustomerID& ...

  5. Hystrix入门

    hystrix对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制,这与hystrix本身的功能不谋而合,因此Netflix团队将该框架命名为Hystrix,并使用 ...

  6. ShoneSharp语言(S#)的设计和使用介绍系列(7)— 布尔Bool及对象Object

    ShoneSharp语言(S#)的设计和使用介绍 系列(7)— 布尔Bool及对象Object 作者:Shone 声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ ...

  7. Pyqt5_QlineEdit

    QlineEdit 方法 setAlignment() 按固定值方式对齐文本 Qt.AlignLeft:水平方向靠左对齐 Qt.AlignRight:水平方向靠右对齐 Qt.AlignCenter:水 ...

  8. JavaScript的基础语法及DOM元素和事件

    一,JavaScript是什么? 1,JavaScript简称:js,是一种浏览器解释型语言,嵌套在HTML文件中交给浏览器解释执行.主要用来实现网页的动态效果,用户交互及前后端的数据传输等. 2,J ...

  9. C#中操作JSON

    引入支持JSON操作的库,比如LitJSON: 引入之后就可以解析JSON了. 写一个JSON文本 读取这个JSON文本: 解析JSON数据最佳实践--使用泛型,将泛型类型指定成自己定义的类型,直接获 ...

  10. Spring基础之AOP

    一.AOP能解决什么问题 业务层每个service都要管理事务,在每个service中单独写事务,就会产生很多重复性的代码,而且修改事务时,需要修改源码,不利于维护.为此,把横向重复的代码,纵向抽取形 ...