Skip to content

Commit

Permalink
新增RPC服务器例程
Browse files Browse the repository at this point in the history
  • Loading branch information
nnhy committed Apr 28, 2024
1 parent 6519e05 commit b7f128e
Show file tree
Hide file tree
Showing 12 changed files with 364 additions and 8 deletions.
13 changes: 12 additions & 1 deletion NewLife.Remoting.sln
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewLife.Remoting", "NewLife.Remoting\NewLife.Remoting.csproj", "{D07654F5-5A89-4781-9094-070D026858CE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{685205DD-0754-4877-9A96-40CD18F8D84B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo", "Demo\Demo.csproj", "{685205DD-0754-4877-9A96-40CD18F8D84B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{8DB8495C-1EB6-41AE-83DD-55DBBB0E6FA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Zero.RpcServer", "Samples\Zero.RpcServer\Zero.RpcServer.csproj", "{8351AC88-1955-48AD-B6F4-26959E1A0C4C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -42,10 +46,17 @@ Global
{685205DD-0754-4877-9A96-40CD18F8D84B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{685205DD-0754-4877-9A96-40CD18F8D84B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{685205DD-0754-4877-9A96-40CD18F8D84B}.Release|Any CPU.Build.0 = Release|Any CPU
{8351AC88-1955-48AD-B6F4-26959E1A0C4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8351AC88-1955-48AD-B6F4-26959E1A0C4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8351AC88-1955-48AD-B6F4-26959E1A0C4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8351AC88-1955-48AD-B6F4-26959E1A0C4C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8351AC88-1955-48AD-B6F4-26959E1A0C4C} = {8DB8495C-1EB6-41AE-83DD-55DBBB0E6FA2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {323831A1-A95B-40AB-B9AD-36A0BC10C2CB}
EndGlobalSection
Expand Down
6 changes: 4 additions & 2 deletions NewLife.Remoting/ApiServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using NewLife.Messaging;
using NewLife.Model;
using NewLife.Net;
using NewLife.Reflection;
using NewLife.Threading;

namespace NewLife.Remoting;
Expand Down Expand Up @@ -146,6 +145,9 @@ public IApiServer EnsureCreate()
{
Name = Name,
Host = this,

Log = Log,
SessionLog = Log,
Tracer = Tracer,
};
server.Init(new NetUri(NetType.Unknown, "*", Port), this);
Expand All @@ -167,7 +169,7 @@ public virtual void Start()

Encoder.Log = EncoderLog;

Log.Info("启动{0},服务器 {1}", GetType().Name, Server);
Log.Info("启动[{0}]", Name);
Log.Info("编码:{0}", Encoder);
Log.Info("处理:{0}", Handler);

Expand Down
2 changes: 1 addition & 1 deletion NewLife.Remoting/NewLife.Remoting.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="NewLife.Core" Version="10.9.2024.402" />
<PackageReference Include="NewLife.Core" Version="10.10.2024.427-beta2349" />
</ItemGroup>

<ItemGroup>
Expand Down
61 changes: 61 additions & 0 deletions Samples/Zero.RpcServer/AreaController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using NewLife.Log;
using NewLife.Remoting;
using XCode.Membership;

namespace Zero.RpcServer;

/// <summary>地区控制器。会话获取,请求过滤</summary>
[Api("Area")]
internal class AreaController : IApi, IActionFilter
{
/// <summary>会话。同一Tcp/Udp会话多次请求共用,执行服务方法前赋值</summary>
public IApiSession Session { get; set; }

[Api(nameof(FindByID))]
public Area FindByID(Int32 id)
{
// Session 用法同Web
var times = Session["Times"].ToInt();
times++;
Session["Times"] = times;

// 故意制造异常
if (times >= 2)
{
// 取得当前上下文
var ctx = ControllerContext.Current;

throw new ApiException(507, $"[{ctx.ActionName}]调用次数过多!Times={times}");
}

return Area.FindByID(id);
}

/// <summary>本控制器执行前</summary>
/// <param name="filterContext"></param>
public void OnActionExecuting(ControllerContext filterContext)
{
// 请求参数
var ps = filterContext.Parameters;

// 服务参数
var cs = filterContext.ActionParameters;

foreach (var item in ps)
{
if (cs != null && !cs.ContainsKey(item.Key))
XTrace.WriteLine("服务[{0}]未能找到匹配参数 {1}={2}", filterContext.ActionName, item.Key, item.Value);
}
}

/// <summary>本控制器执行后,包括异常发生</summary>
/// <param name="filterContext"></param>
public void OnActionExecuted(ControllerContext filterContext)
{
var ex = filterContext.Exception;
if (ex != null && !filterContext.ExceptionHandled)
{
XTrace.WriteLine("控制器拦截到异常:{0}", ex.Message);
}
}
}
76 changes: 76 additions & 0 deletions Samples/Zero.RpcServer/ClientTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Net.Sockets;
using System.Text;
using NewLife;
using NewLife.Log;
using NewLife.Remoting;
using NewLife.Security;
using NewLife.Serialization;

namespace Zero.RpcServer;

static class ClientTest
{
/// <summary>Tcp连接ApiServer</summary>
public static async void TcpTest(Int32 port)
{
await Task.Delay(1_000);
XTrace.WriteLine("");
XTrace.WriteLine("Tcp开始连接!");

// 连接服务端
var client = new ApiClient("tcp://127.0.0.2:12346");
client.Open();

await Process(client);

// 关闭连接
client.Close("测试完成");
}

/// <summary>Udp连接ApiServer</summary>
public static async void UdpTest(Int32 port)
{
await Task.Delay(2_000);
XTrace.WriteLine("");
XTrace.WriteLine("Udp开始连接!");

// 连接服务端
var client = new ApiClient("udp://127.0.0.2:12346");
client.Open();

await Process(client);

// 关闭连接
client.Close("测试完成");
}

/// <summary>Tcp连接ApiServer</summary>
public static async void WebSocketTest(Int32 port)
{
await Task.Delay(3_000);
XTrace.WriteLine("");
XTrace.WriteLine("WebSocket开始连接!");

// 连接服务端
var client = new ApiClient("ws://127.0.0.2:12346");
client.Open();

await Process(client);

// 关闭连接
client.Close("测试完成");
}

static async Task Process(ApiClient client)
{
// 获取所有接口
var apis = await client.InvokeAsync<String[]>("api/all");
client.WriteLog("共有接口数:{0}", apis.Length);

// 获取服务端信息
var state = Rand.NextString(8);
var state2 = Rand.NextString(8);
var infs = await client.InvokeAsync<IDictionary<String, Object>>("api/info", new { state, state2 });
client.WriteLog("服务端信息:{0}", infs.ToJson(true));
}
}
32 changes: 32 additions & 0 deletions Samples/Zero.RpcServer/MyController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using NewLife;
using NewLife.Data;

namespace Zero.RpcServer;

/// <summary>自定义控制器。包含多个服务</summary>
/// <remarks>
/// 控制器规范:
/// 1,控制器类不要求公开可见,也无需继承任何基类,需要明码注册到服务端
/// 2,控制器类中的方法不要求公开可见,但方法名和参数名必须固定,跟客户端一致
/// 3,第一段路径名为控制器名(去掉Controller),第二段路径名为方法名,如/Area/FindByID
/// 4,通过[Api]特性指定控制器名和方法名
/// </remarks>
internal class MyController
{
/// <summary>添加,标准业务服务,走Json序列化</summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public Int32 Add(Int32 x, Int32 y) => x + y;

/// <summary>RC4加解密,高速业务服务,二进制收发不经序列化</summary>
/// <param name="pk"></param>
/// <returns></returns>
public Packet RC4(Packet pk)
{
var data = pk.ToArray();
var pass = "NewLife".GetBytes();

return data.RC4(pass);
}
}
60 changes: 60 additions & 0 deletions Samples/Zero.RpcServer/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using NewLife.Caching;
using NewLife.Caching.Services;
using NewLife.Log;
using NewLife.Model;
using NewLife.Remoting;
using Stardust;
using Zero.RpcServer;

// 启用控制台日志,拦截所有异常
XTrace.UseConsole();

var services = ObjectContainer.Current;

// 配置星尘。自动读取配置文件 config/star.config 中的服务器地址、应用标识、密钥
var star = services.AddStardust();

// 默认内存缓存,如有配置RedisCache可使用Redis缓存
services.AddSingleton<ICacheProvider, RedisCacheProvider>();

// 引入Redis,用于消息队列和缓存,单例,带性能跟踪。一般使用上面的ICacheProvider替代
//services.AddRedis("127.0.0.1:6379", "123456", 3, 5000);

// 实例化RPC服务端,指定端口,同时在Tcp/Udp/IPv4/IPv6上监听
var server = new ApiServer(12346)
{
Name = "银河服务端",

// 指定编码器
Encoder = new JsonEncoder(),

//EncoderLog = XTrace.Log,
Log = XTrace.Log,
Tracer = star.Tracer,
};

// 注册服务控制器
server.Register<MyController>();
server.Register<UserController>();
server.Register<AreaController>();

#if DEBUG
// 打开编码日志
server.EncoderLog = XTrace.Log;
#endif

// 启动网络服务,监听端口,所有逻辑将在 xxxController 中处理
server.Start();
XTrace.WriteLine("服务端启动完成!");

// 注册到星尘,非必须
star?.Service?.Register("MyRpcServer", () => $"tcp://*:{server.Port},udp://*:{server.Port}");

// 客户端测试,非服务端代码,正式使用时请注释掉
_ = Task.Run(() => ClientTest.TcpTest(server.Port));
_ = Task.Run(() => ClientTest.UdpTest(server.Port));
_ = Task.Run(() => ClientTest.WebSocketTest(server.Port));

// 阻塞,等待友好退出
var host = services.BuildHost();
await host.RunAsync();
61 changes: 61 additions & 0 deletions Samples/Zero.RpcServer/UserController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using NewLife.Log;
using NewLife.Remoting;
using XCode.Membership;

namespace Zero.RpcServer;

/// <summary>产品控制器。会话获取,请求过滤</summary>
[Api("User")]
internal class UserController : IApi, IActionFilter
{
/// <summary>会话。同一Tcp/Udp会话多次请求共用,执行服务方法前赋值</summary>
public IApiSession Session { get; set; }

[Api(nameof(FindByID))]
public User FindByID(Int32 id)
{
// Session 用法同Web
var times = Session["Times"].ToInt();
times++;
Session["Times"] = times;

// 故意制造异常
if (times >= 2)
{
// 取得当前上下文
var ctx = ControllerContext.Current;

throw new ApiException(507, $"[{ctx.ActionName}]调用次数过多!Times={times}");
}

return User.FindByID(id);
}

/// <summary>本控制器执行前</summary>
/// <param name="filterContext"></param>
public void OnActionExecuting(ControllerContext filterContext)
{
// 请求参数
var ps = filterContext.Parameters;

// 服务参数
var cs = filterContext.ActionParameters;

foreach (var item in ps)
{
if (cs != null && !cs.ContainsKey(item.Key))
XTrace.WriteLine("服务[{0}]未能找到匹配参数 {1}={2}", filterContext.ActionName, item.Key, item.Value);
}
}

/// <summary>本控制器执行后,包括异常发生</summary>
/// <param name="filterContext"></param>
public void OnActionExecuted(ControllerContext filterContext)
{
var ex = filterContext.Exception;
if (ex != null && !filterContext.ExceptionHandled)
{
XTrace.WriteLine("控制器拦截到异常:{0}", ex.Message);
}
}
}
32 changes: 32 additions & 0 deletions Samples/Zero.RpcServer/Zero.RpcServer.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<AssemblyTitle>RPC服务端</AssemblyTitle>
<Description>高性能,长连接,数据接口</Description>
<Company>新生命开发团队</Company>
<Copyright>©2002-2024 NewLife</Copyright>
<VersionPrefix>1.0</VersionPrefix>
<VersionSuffix>$([System.DateTime]::Now.ToString(`yyyy.MMdd`))</VersionSuffix>
<Version>$(VersionPrefix).$(VersionSuffix)</Version>
<FileVersion>$(Version)</FileVersion>
<AssemblyVersion>$(VersionPrefix).*</AssemblyVersion>
<Deterministic>false</Deterministic>
<OutputPath>..\..\Bin\RpcServer</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NewLife.Redis" Version="5.6.2024.101" />
<PackageReference Include="NewLife.Stardust" Version="2.9.2024.101" />
<PackageReference Include="NewLife.XCode" Version="11.11.2024.402" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\NewLife.Remoting\NewLife.Remoting.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit b7f128e

Please sign in to comment.