Compare commits

...
5 Commits
Author SHA1 Message Date
xrain d2b5f14e78 fix some 2025-10-25 17:03:43 +08:00
xrain 116cbcdc7f update to 9 2025-10-14 02:55:29 +08:00
xrain 4e1b7d4845 update to 9 2025-10-14 02:20:37 +08:00
xrain 71acb35df9 cancle json refrence 2025-08-13 17:28:24 +08:00
xrain 67cc7346dc use concurrentDic 2025-05-17 21:17:43 +08:00
7 changed files with 64 additions and 71 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: "8.0.200"
dotnet-version: "9.0.305"
- name: Publish
run: |
version=`git describe --tags`
+6 -8
View File
@@ -1,13 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace SimApi.Communications;
namespace SimApi.Communications;
/// <summary>
/// 只有ID的请求
/// </summary>
public class SimApiIdOnlyRequest
{
[Required] public int Id { get; set; }
public required int Id { get; set; }
}
/// <summary>
@@ -15,7 +13,7 @@ public class SimApiIdOnlyRequest
/// </summary>
public class SimApiStringIdOnlyRequest
{
[Required] public string? Id { get; set; }
public required string Id { get; set; }
}
/// <summary>
@@ -24,7 +22,7 @@ public class SimApiStringIdOnlyRequest
/// <typeparam name="T"></typeparam>
public class SimApiOneFieldRequest<T>
{
[Required] public T? Data { get; set; }
public required T Data { get; set; }
}
/// <summary>
@@ -32,6 +30,6 @@ public class SimApiOneFieldRequest<T>
/// </summary>
public class SimApiBasePageRequest
{
[Required] public int Page { get; set; }
[Required] public int Count { get; set; }
public required int Page { get; set; }
public required int Count { get; set; }
}
+1 -1
View File
@@ -9,6 +9,6 @@ public class SimApiLoginItem
{
public string Id { get; set; } = null!;
public string[] Type { get; set; } = ["user"];
public Dictionary<string, string> Meta { get; set; } = new();
public Dictionary<string, string> Meta { get; set; } = [];
public object? Extra { get; set; }
};
+2 -2
View File
@@ -24,10 +24,10 @@ public static class SimApiUtil
/// </summary>
public static JsonSerializerOptions JsonOption => new()
{
ReferenceHandler = ReferenceHandler.Preserve,
// ReferenceHandler = ReferenceHandler.Preserve,
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
// DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
/// <summary>
+2 -1
View File
@@ -14,8 +14,8 @@
<SynchReleaseVersion>false</SynchReleaseVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageVersion>5.0.2</PackageVersion>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
@@ -31,6 +31,7 @@
<PackageReference Include="MQTTnet" Version="5.0.1.1416" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="8.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.1" />
<PackageReference Include="System.Text.Json" Version="9.0.10" />
</ItemGroup>
<ProjectExtensions>
<MonoDevelop>
-2
View File
@@ -3,7 +3,6 @@ using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Hangfire;
using Hangfire.Console;
using Hangfire.Redis.StackExchange;
@@ -251,7 +250,6 @@ public static class SimApiExtensions
.AddJsonOptions(opt =>
{
opt.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
opt.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});
}
+52 -56
View File
@@ -1,20 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Packets;
using SimApi.Communications;
using SimApi.Helpers;
using System.Collections.Concurrent;
namespace SimApi;
public partial class Synapse
{
private Dictionary<string, TaskCompletionSource<string>> ResponseCompletionSources { get; } = new();
private ConcurrentDictionary<string, TaskCompletionSource<string>> ResponseCompletionSources { get; } = new();
private string EventClientTopicPrefix => $"{Options.SysName}/{Options.AppName}/rpc/client/{Options.AppId}/";
@@ -27,7 +26,7 @@ public partial class Synapse
var messageId = e.ApplicationMessage.Topic.Replace(EventClientTopicPrefix, string.Empty);
if (!ResponseCompletionSources.TryGetValue(messageId, out var tcs)) return Task.CompletedTask;
tcs.SetResult(reqBody);
ResponseCompletionSources.Remove(messageId);
ResponseCompletionSources.Remove(messageId, out _);
return Task.CompletedTask;
};
SubRpcClientTopic();
@@ -43,64 +42,61 @@ public partial class Synapse
private string? FireRpc(string app, string action, object? param, Dictionary<string, string>? headers = null,
int? timeout = null)
{
// 检查并发情况下的线程安全问题,使用锁保证字典操作的原子性
lock (ResponseCompletionSources)
// 移除锁语句
string paramJson;
if (param is string strParam)
{
string paramJson;
if (param is string strParam)
paramJson = strParam;
}
else
{
paramJson = JsonSerializer.Serialize(param, SimApiUtil.JsonOption);
}
var topic = $"{Options.SysName}/{app}/rpc/server/{action}";
var messageId = Guid.NewGuid().ToString();
var tcs = new TaskCompletionSource<string>();
ResponseCompletionSources.TryAdd(messageId, tcs);
var messageBuilder = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(paramJson)
.WithResponseTopic($"{Options.AppName},{Options.AppId},{messageId}")
.WithContentType("application/json")
.WithRetainFlag(false);
foreach (var h in headers ?? new Dictionary<string, string>())
{
messageBuilder.WithUserProperty(h.Key, h.Value);
}
var message = messageBuilder.Build();
if (!Client!.IsConnected) return null;
Client.PublishAsync(message, CancellationToken.None).Wait();
logger.LogDebug(
"Synapse RPC Client Request: ({PropsMessageId}) {OptionsAppName} -> {Action}@{App}\n{ParamJson}\nHeaders: {Headers}",
messageId, Options.AppName, action, app, paramJson, JsonSerializer.Serialize(headers));
string response;
try
{
timeout ??= Options.RpcTimeout;
if (tcs.Task.Wait(timeout.Value * 1000))
{
paramJson = strParam;
response = tcs.Task.Result;
logger.LogDebug(
"Synapse RPC Client Response: ({BasicPropertiesCorrelationId}) {BasicPropertiesType}@{BasicPropertiesReplyTo} -> {OptionsAppName}\n{S}",
messageId, action, app, Options.AppName, response);
}
else
{
paramJson = JsonSerializer.Serialize(param, SimApiUtil.JsonOption);
response = JsonSerializer.Serialize(new SimApiBaseResponse(502, "timeout"), SimApiUtil.JsonOption);
}
var topic = $"{Options.SysName}/{app}/rpc/server/{action}";
var messageId = Guid.NewGuid().ToString();
var tcs = new TaskCompletionSource<string>();
ResponseCompletionSources.Add(messageId, tcs);
var messageBuilder = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(paramJson)
.WithResponseTopic($"{Options.AppName},{Options.AppId},{messageId}")
.WithContentType("application/json")
.WithRetainFlag(false);
foreach (var h in headers ?? new Dictionary<string, string>())
{
messageBuilder.WithUserProperty(h.Key, h.Value);
}
var message = messageBuilder.Build();
if (!Client!.IsConnected) return null;
Client.PublishAsync(message, CancellationToken.None).Wait();
logger.LogDebug(
"Synapse RPC Client Request: ({PropsMessageId}) {OptionsAppName} -> {Action}@{App}\n{ParamJson}\nHeaders: {Headers}",
messageId, Options.AppName, action, app, paramJson, JsonSerializer.Serialize(headers));
string response;
try
{
timeout ??= Options.RpcTimeout;
if (tcs.Task.Wait(timeout.Value * 1000))
{
response = tcs.Task.Result;
logger.LogDebug(
"Synapse RPC Client Response: ({BasicPropertiesCorrelationId}) {BasicPropertiesType}@{BasicPropertiesReplyTo} -> {OptionsAppName}\n{S}",
messageId, action, app, Options.AppName, response);
}
else
{
response = JsonSerializer.Serialize(new SimApiBaseResponse(502, "timeout"), SimApiUtil.JsonOption);
}
}
catch
{
response = JsonSerializer.Serialize(new SimApiBaseResponse(500, "Synapse RPC Client Error"),
SimApiUtil.JsonOption);
}
return response;
}
catch
{
response = JsonSerializer.Serialize(new SimApiBaseResponse(500, "Synapse RPC Client Error"),
SimApiUtil.JsonOption);
}
return response;
}
}