WebApiRoot.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using Aliyun.OSS;
  2. using LiteDB;
  3. using NTMiner.Controllers;
  4. using NTMiner.Core;
  5. using NTMiner.Core.Impl;
  6. using NTMiner.Core.Mq.MqMessagePaths;
  7. using NTMiner.Core.Mq.Senders.Impl;
  8. using NTMiner.Core.Redis.Impl;
  9. using NTMiner.Impl;
  10. using NTMiner.ServerNode;
  11. using System;
  12. using System.Linq;
  13. using System.Threading;
  14. using System.Web.Http;
  15. namespace NTMiner {
  16. public static class WebApiRoot {
  17. private static readonly EventWaitHandle WaitHandle = new AutoResetEvent(false);
  18. private static OssClient _ossClient = null;
  19. public static OssClient OssClient {
  20. get {
  21. return _ossClient;
  22. }
  23. }
  24. private static IServerContext _serverContext;
  25. private static Mutex _sMutexApp;
  26. // 该程序编译为控制台程序,如果不启用内网支持则默认设置为开机自动启动
  27. [STAThread]
  28. static void Main() {
  29. NTMinerConsole.DisbleQuickEditMode();
  30. HomePath.SetHomeDirFullName(AppDomain.CurrentDomain.BaseDirectory);
  31. try {
  32. bool mutexCreated;
  33. try {
  34. // 锁名称上带上本节点的端口号,从而允许一个服务器上运行多个WebApiServer节点,这在软升级服务端程序时有用。
  35. // 升级WebApiServer程序的时候步骤是:
  36. // 1,在另一个端口启动新版本的程序;
  37. // 2,让Widnows将来自旧端口的所有tcp请求转发到新端口;
  38. // 3,退出旧版本的程序并更新到新版本;
  39. // 4,删除第2步添加的Windows的端口转发;
  40. // 5,退出第1步运行的节点;
  41. // TODO:实现软升级策略
  42. _sMutexApp = new Mutex(true, $"NTMinerServicesMutex{ServerRoot.HostConfig.GetServerPort().ToString()}", out mutexCreated);
  43. }
  44. catch {
  45. mutexCreated = false;
  46. }
  47. if (mutexCreated) {
  48. try {
  49. // 用本节点的地址作为队列名,消费消息时根据路由键区分消息类型
  50. string queue = $"{ServerAppType.WebApiServer.GetName()}.{ServerRoot.HostConfig.ThisServerAddress}";
  51. string durableQueue = queue + MqKeyword.DurableQueueEndsWith;
  52. AbstractMqMessagePath[] mqMessagePaths = new AbstractMqMessagePath[] {
  53. new UserMqMessagePath(durableQueue),
  54. new MinerClientMqMessagePath(queue)
  55. };
  56. _serverContext = ServerContext.Create(mqClientTypeName: ServerAppType.WebApiServer.GetName(), mqMessagePaths);
  57. if (_serverContext == null) {
  58. Write.UserError("启动失败,无法继续,因为服务器上下文创建失败");
  59. return;
  60. }
  61. Console.Title = $"{ServerAppType.WebApiServer.GetName()}_{ServerRoot.HostConfig.ThisServerAddress}";
  62. _ossClient = new OssClient(ServerRoot.HostConfig.OssEndpoint, ServerRoot.HostConfig.OssAccessKeyId, ServerRoot.HostConfig.OssAccessKeySecret);
  63. var minerClientMqSender = new MinerClientMqSender(_serverContext.Channel);
  64. var userMqSender = new UserMqSender(_serverContext.Channel);
  65. var wsServerNodeMqSender = new WsServerNodeMqSender(_serverContext.Channel);
  66. var minerRedis = new MinerRedis(_serverContext.RedisConn);
  67. var userRedis = new UserRedis(_serverContext.RedisConn);
  68. var captchaRedis = new CaptchaRedis(_serverContext.RedisConn);
  69. WsServerNodeSet = new WsServerNodeSet(wsServerNodeMqSender);
  70. UserSet = new UserSet(userRedis, userMqSender);
  71. UserAppSettingSet = new UserAppSettingSet();
  72. CaptchaSet = new CaptchaSet(captchaRedis);
  73. CalcConfigSet = new CalcConfigSet();
  74. NTMinerWalletSet = new NTMinerWalletSet();
  75. ClientDataSet clientDataSet = new ClientDataSet(minerRedis, minerClientMqSender);
  76. ClientDataSet = clientDataSet;
  77. CoinSnapshotSet = new CoinSnapshotSet(clientDataSet);
  78. MineWorkSet = new UserMineWorkSet();
  79. MinerGroupSet = new UserMinerGroupSet();
  80. NTMinerFileSet = new NTMinerFileSet();
  81. OverClockDataSet = new OverClockDataSet();
  82. KernelOutputKeywordSet = new KernelOutputKeywordSet(SpecialPath.LocalDbFileFullName, isServer: true);
  83. ServerMessageSet = new ServerMessageSet(SpecialPath.LocalDbFileFullName, isServer: true);
  84. UpdateServerMessageTimestamp();
  85. if (VirtualRoot.LocalAppSettingSet.TryGetAppSetting(nameof(KernelOutputKeywordTimestamp), out IAppSetting appSetting) && appSetting.Value is DateTime value) {
  86. KernelOutputKeywordTimestamp = value;
  87. }
  88. else {
  89. KernelOutputKeywordTimestamp = Timestamp.UnixBaseTime;
  90. }
  91. }
  92. catch (Exception e) {
  93. Write.UserError(e.Message);
  94. Write.UserError(e.StackTrace);
  95. Write.UserInfo("按任意键退出");
  96. Console.ReadKey();
  97. return;
  98. }
  99. VirtualRoot.StartTimer();
  100. NTMinerRegistry.SetAutoBoot("NTMinerServices", true);
  101. Type thisType = typeof(WebApiRoot);
  102. Run();
  103. }
  104. }
  105. catch (Exception e) {
  106. Logger.ErrorDebugLine(e);
  107. }
  108. }
  109. private static bool _isClosed = false;
  110. private static void Close() {
  111. if (!_isClosed) {
  112. _isClosed = true;
  113. _sMutexApp?.Dispose();
  114. }
  115. }
  116. public static void Exit() {
  117. Close();
  118. Environment.Exit(0);
  119. }
  120. private static void Run() {
  121. try {
  122. string baseAddress = $"http://{NTKeyword.Localhost}:{ServerRoot.HostConfig.GetServerPort().ToString()}";
  123. HttpServer.Start(baseAddress, doConfig: config => {
  124. // 向后兼容
  125. config.Routes.MapHttpRoute("CalcConfigs", "api/ControlCenter/CalcConfigs", new {
  126. controller = RpcRoot.GetControllerName<ICalcConfigController>(),
  127. action = nameof(ICalcConfigController.CalcConfigs)
  128. });
  129. });
  130. Windows.ConsoleHandler.Register(Close);
  131. WaitHandle.WaitOne();
  132. Close();
  133. }
  134. catch (Exception e) {
  135. Logger.ErrorDebugLine(e);
  136. }
  137. finally {
  138. Close();
  139. }
  140. }
  141. static WebApiRoot() {
  142. }
  143. public static DateTime StartedOn { get; private set; } = DateTime.Now;
  144. public static IUserAppSettingSet UserAppSettingSet { get; private set; }
  145. public static IUserSet UserSet { get; private set; }
  146. public static IWsServerNodeSet WsServerNodeSet { get; private set; }
  147. public static ICaptchaSet CaptchaSet { get; private set; }
  148. public static ICalcConfigSet CalcConfigSet { get; private set; }
  149. public static INTMinerWalletSet NTMinerWalletSet { get; private set; }
  150. public static IClientDataSet ClientDataSet { get; private set; }
  151. public static ICoinSnapshotSet CoinSnapshotSet { get; private set; }
  152. public static IUserMineWorkSet MineWorkSet { get; private set; }
  153. public static IUserMinerGroupSet MinerGroupSet { get; private set; }
  154. public static INTMinerFileSet NTMinerFileSet { get; private set; }
  155. public static IOverClockDataSet OverClockDataSet { get; private set; }
  156. public static IKernelOutputKeywordSet KernelOutputKeywordSet { get; private set; }
  157. public static IServerMessageSet ServerMessageSet { get; private set; }
  158. public static DateTime ServerMessageTimestamp { get; set; }
  159. public static DateTime KernelOutputKeywordTimestamp { get; private set; }
  160. public static LiteDatabase CreateLocalDb() {
  161. return new LiteDatabase($"filename={SpecialPath.LocalDbFileFullName}");
  162. }
  163. /// <summary>
  164. /// 因为客户端具有不同的ClientVersion,考虑到兼容性问题将来可能不同版本的客户端需要获取不同版本的server.json,所以
  165. /// 服务端用返回什么版本的server.json应由客户端自己决定,所以该方法有个jsonVersionKey入参,这个入参就是来自客户端。
  166. /// </summary>
  167. /// <param name="jsonVersionKey">server.json的版本</param>
  168. /// <returns></returns>
  169. public static ServerStateResponse GetServerStateResponse(string jsonVersionKey) {
  170. string jsonVersion = string.Empty;
  171. string minerClientVersion = string.Empty;
  172. try {
  173. var fileData = NTMinerFileSet.LatestMinerClientFile;
  174. minerClientVersion = fileData != null ? fileData.Version : string.Empty;
  175. if (!VirtualRoot.LocalAppSettingSet.TryGetAppSetting(jsonVersionKey, out IAppSetting data) || data.Value == null) {
  176. jsonVersion = string.Empty;
  177. }
  178. else {
  179. jsonVersion = data.Value.ToString();
  180. }
  181. }
  182. catch (Exception e) {
  183. Logger.ErrorDebugLine(e);
  184. }
  185. return new ServerStateResponse {
  186. JsonFileVersion = jsonVersion,
  187. MinerClientVersion = minerClientVersion,
  188. Time = Timestamp.GetTimestamp(),
  189. MessageTimestamp = Timestamp.GetTimestamp(ServerMessageTimestamp),
  190. OutputKeywordTimestamp = Timestamp.GetTimestamp(KernelOutputKeywordTimestamp),
  191. WsStatus = WsServerNodeSet.WsStatus
  192. };
  193. }
  194. public static WebApiServerState GetServerState() {
  195. var ram = Windows.Ram.Instance;
  196. var cpu = Windows.Cpu.Instance;
  197. cpu.GetSensorValue(out double performance, out float temperature, out double _);
  198. return new WebApiServerState {
  199. WsServerNodes = WsServerNodeSet.AsEnumerable().ToList(),
  200. Address = ServerRoot.HostConfig.ThisServerAddress,
  201. Description = string.Empty,
  202. AvailablePhysicalMemory = ram.AvailablePhysicalMemory,
  203. TotalPhysicalMemory = ram.TotalPhysicalMemory,
  204. Cpu = cpu.ToData(),
  205. OSInfo = Windows.OS.Instance.OsInfo,
  206. CpuPerformance = performance,
  207. CpuTemperature = temperature
  208. };
  209. }
  210. public static void UpdateServerMessageTimestamp() {
  211. var first = ServerMessageSet.AsEnumerable().OrderByDescending(a => a.Timestamp).FirstOrDefault();
  212. if (first == null) {
  213. ServerMessageTimestamp = DateTime.MinValue;
  214. }
  215. else {
  216. ServerMessageTimestamp = first.Timestamp;
  217. }
  218. }
  219. public static void UpdateKernelOutputKeywordTimestamp(DateTime timestamp) {
  220. KernelOutputKeywordTimestamp = timestamp;
  221. VirtualRoot.Execute(new SetLocalAppSettingCommand(new AppSettingData {
  222. Key = nameof(KernelOutputKeywordTimestamp),
  223. Value = timestamp
  224. }));
  225. }
  226. }
  227. }