PublishWebHelper.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. using Microsoft.Web.Administration;
  2. using Newtonsoft.Json;
  3. using Newtonsoft.Json.Linq;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Configuration;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Security.Cryptography.X509Certificates;
  10. using System.ServiceProcess;
  11. using System.Text;
  12. using System.Threading.Tasks;
  13. using System.Xml.Linq;
  14. namespace OneClickPublishWeb
  15. {
  16. /// <summary>
  17. /// 网站发布帮助类
  18. /// </summary>
  19. public class PublishWebHelper
  20. {
  21. /// <summary>
  22. /// 执行一键部署(单个网站部署)
  23. /// </summary>
  24. /// <returns></returns>
  25. public static void Execute()
  26. {
  27. //IIS 配置信息
  28. var configs = GetIISConfigs();
  29. foreach (var config in configs.MainApplications)
  30. {
  31. var vDir = config.VDir;
  32. var app = config.Applications;
  33. //程序池配置信息
  34. var poolsConfig = config.ApplicationPools;
  35. var iismanager = CreateServerManager(config);
  36. //创建虚拟目录
  37. if (vDir != null && vDir.Count() > 0)
  38. {
  39. foreach (var dir in vDir)
  40. {
  41. CreateVDir(config.WebName, dir.DirName, dir.PhysicalPath, iismanager);
  42. }
  43. }
  44. if (app!=null)
  45. {
  46. //创建子程序虚拟目录
  47. foreach (var item in app)
  48. {
  49. if (item.VDir!=null)
  50. {
  51. foreach (var dir in item.VDir)
  52. {
  53. CreateSubitemVDir(config.WebName, item.Path, dir.DirName, dir.PhysicalPath, iismanager);
  54. }
  55. }
  56. }
  57. }
  58. Console.WriteLine("---------------- 程序池 Start ----------------");
  59. //创建程序池
  60. foreach (var item in poolsConfig)
  61. {
  62. CreateApplicationPool(item, iismanager);
  63. }
  64. Console.WriteLine("---------------- 程序池 End ----------------");
  65. //提交保存
  66. CommitChanges(iismanager);
  67. }
  68. }
  69. /// <summary>
  70. /// 创建应用程序
  71. /// </summary>
  72. /// <param name="config"></param>
  73. /// <returns></returns>
  74. private static ServerManager CreateServerManager(MainApplication config)
  75. {
  76. var ApplicationsConfig = config;
  77. ServiceController service = ServiceController.GetServices("127.0.0.1").FirstOrDefault(x => x.ServiceName == "W3SVC");
  78. if (service is null)
  79. {
  80. Console.WriteLine("服务器尚未安装 IIS 服务模块!");
  81. return null;
  82. }
  83. if (!System.IO.Directory.Exists(config.WebsiteDirectory))
  84. {
  85. Console.WriteLine("指定目录不存在!");
  86. return null;
  87. }
  88. ServerManager iismanager = new ServerManager();
  89. //判断web应用程序是否存在
  90. if (iismanager.Sites[config.WebName] != null)
  91. {
  92. ///移除应用程序
  93. iismanager.Sites.Remove(iismanager.Sites[config.WebName]);
  94. }
  95. //建立web应用程序(第二个参数为安装文件的地址)
  96. Site site = default;
  97. if (config.IsCertificate)
  98. {
  99. site = AddHostHeaderSSL(iismanager, config.WebName, config.Port, "", config.PhysicalPath);
  100. }
  101. else
  102. {
  103. site = iismanager.Sites.Add(config.WebName, config.WebsiteDirectory, config.Port);
  104. }
  105. Console.WriteLine("---------------- 主应用程序 Start ----------------");
  106. Console.WriteLine($"网站名称:{config.ServerDomainName}");
  107. Console.WriteLine($"端口:{config.Port}");
  108. Console.WriteLine($"服务器域名:{config.ServerDomainName}");
  109. Console.WriteLine($"网站目录:{config.WebsiteDirectory}");
  110. Console.WriteLine($"程序池名称:{config.ApplicationPoolName}");
  111. Console.WriteLine("---------------- 主应用程序 End ----------------");
  112. Console.WriteLine("---------------- 子程序 Start ----------------");
  113. //设置子程序 - 应用程序池
  114. if (ApplicationsConfig.Applications!=null)
  115. {
  116. foreach (var item in ApplicationsConfig.Applications)
  117. {
  118. var application = site.Applications.Add("/" + item.Path, item.PhysicalPath);
  119. application.ApplicationPoolName = item.ApplicationPoolName;
  120. Console.WriteLine("****************************** ↓");
  121. Console.WriteLine($"子程序路径名称:/{item.Path}");
  122. Console.WriteLine($"物理路径:{item.PhysicalPath}");
  123. }
  124. Console.WriteLine("---------------- 子程序 End ----------------");
  125. }
  126. //设置web网站的应用程序池
  127. var website = iismanager.Sites[config.WebName];
  128. website.Applications["/"].ApplicationPoolName = config.ApplicationPoolName;
  129. if (!string.IsNullOrEmpty(config.ServerDomainName))
  130. {
  131. string str = website.Bindings[0].Host.Split(new char[] { '.' })[0];
  132. string bindingInformation = $"*:{config.Port}:{str}{config.ServerDomainName}";
  133. website.Bindings.Add(bindingInformation, "http");
  134. }
  135. return iismanager;
  136. }
  137. /// <summary>
  138. /// 提交更改
  139. /// </summary>
  140. /// <param name="iismanager"></param>
  141. private static void CommitChanges(ServerManager iismanager)
  142. {
  143. //提交更改
  144. iismanager.CommitChanges();
  145. }
  146. /// <summary>
  147. /// 创建程序池
  148. /// </summary>
  149. /// <param name="pool"></param>
  150. private static void CreateApplicationPool(ApplicationPool poolConfig, ServerManager iismanager)
  151. {
  152. //判断应用程序池是否存在
  153. if (iismanager.ApplicationPools[poolConfig.Name] != null)
  154. {
  155. //移除应用程序池
  156. iismanager.ApplicationPools.Remove(iismanager.ApplicationPools[poolConfig.Name]);
  157. }
  158. //cpu
  159. var cpuConfig = poolConfig.Cpu;
  160. //回收
  161. var recyclingConfig = poolConfig.Recycling;
  162. //定期重启
  163. var periodicRestartConfig = poolConfig.Recycling.PeriodicRestart;
  164. //进程孤立
  165. var failureConfig = poolConfig.Failure;
  166. //进程模型
  167. var processModelConfig = poolConfig.ProcessModel;
  168. Microsoft.Web.Administration.ApplicationPool pool = iismanager.ApplicationPools.Add(poolConfig.Name);
  169. pool.Name = poolConfig.Name; // 程序池名字
  170. pool.StartMode = poolConfig.StartMode;//启动模式
  171. pool.QueueLength = poolConfig.QueueLength;//队列长度
  172. pool.ManagedRuntimeVersion = poolConfig.ManagedRuntimeVersion;
  173. //pool.Enable32BitAppOnWin64 = pool.Enable32BitAppOnWin64;
  174. pool.ManagedPipelineMode = ManagedPipelineMode.Integrated; //托管管道模式
  175. pool.Cpu.Limit = cpuConfig.Limit;//限制最大CPU 50%
  176. pool.Cpu.Action = cpuConfig.Action;//竞争cpu时限制使用最大cpu 百分比
  177. pool.Cpu.ResetInterval = new TimeSpan(00, cpuConfig.ResetInterval, 00); //时隔5分钟
  178. pool.Cpu.SmpAffinitized = cpuConfig.SmpAffinitized ?? false;
  179. //回收
  180. pool.Recycling.DisallowRotationOnConfigChange = recyclingConfig.DisallowRotationOnConfigChange ?? true; //发生配置更改时禁止回收
  181. pool.Recycling.DisallowOverlappingRotation = recyclingConfig.DisallowOverlappingRotation ?? true;//禁用重叠回收
  182. RecyclingLogEventOnRecycle logEventOnRecycle = RecyclingLogEventOnRecycle.None;
  183. foreach (var item in recyclingConfig.LogEventOnRecycle)
  184. {
  185. logEventOnRecycle = logEventOnRecycle | item;
  186. }
  187. if (recyclingConfig.LogEventOnRecycle != null && recyclingConfig.LogEventOnRecycle.Count() > 0)
  188. pool.Recycling.LogEventOnRecycle = logEventOnRecycle;
  189. foreach (var item in periodicRestartConfig.Schedule)
  190. {
  191. pool.Recycling.PeriodicRestart.Schedule.Add(item);//定时回收资源
  192. }
  193. pool.Recycling.PeriodicRestart.PrivateMemory = periodicRestartConfig.PrivateMemory;
  194. pool.Recycling.PeriodicRestart.Time = new TimeSpan(00, periodicRestartConfig.Time, 00);
  195. pool.Recycling.PeriodicRestart.Requests = periodicRestartConfig.Requests;
  196. pool.Recycling.PeriodicRestart.Memory = periodicRestartConfig.Memory;
  197. //进程孤立
  198. pool.Failure.OrphanActionParams = failureConfig.OrphanActionParams;
  199. pool.Failure.OrphanActionExe = failureConfig.OrphanActionExe;
  200. pool.Failure.OrphanWorkerProcess = failureConfig.OrphanWorkerProcess ?? false;
  201. //模型
  202. pool.ProcessModel.PingInterval = new TimeSpan(00, 00, processModelConfig.PingInterval);
  203. pool.ProcessModel.PingResponseTime = new TimeSpan(00, 00, processModelConfig.PingResponseTime);
  204. pool.ProcessModel.IdentityType = processModelConfig.IdentityType;
  205. pool.ProcessModel.UserName = processModelConfig.UserName;
  206. pool.ProcessModel.Password = processModelConfig.Password;
  207. pool.ProcessModel.ShutdownTimeLimit = new TimeSpan(00, 00, processModelConfig.ShutdownTimeLimit);
  208. pool.ProcessModel.LoadUserProfile = processModelConfig.LoadUserProfile ?? false;
  209. pool.ProcessModel.IdleTimeoutAction = IdleTimeoutAction.Terminate;
  210. pool.ProcessModel.StartupTimeLimit = new TimeSpan(00, 00, processModelConfig.StartupTimeLimit);
  211. pool.ProcessModel.PingingEnabled = processModelConfig.PingingEnabled ?? false;
  212. pool.ProcessModel.LogEventOnProcessModel = processModelConfig.LogEventOnProcessModel;
  213. pool.ProcessModel.IdleTimeout = new TimeSpan(00, processModelConfig.IdleTimeout, 00);
  214. pool.ProcessModel.MaxProcesses = processModelConfig.MaxProcesses;
  215. Console.WriteLine("****************************** ↓");
  216. Console.WriteLine($"程序池名称:{poolConfig.Name}");
  217. Console.WriteLine($"队列长度:{poolConfig.QueueLength}");
  218. Console.WriteLine($"启动模式:{poolConfig.StartMode}");
  219. //Console.WriteLine($"启用32位应用程序:{poolConfig.Enable32BitAppOnWin64}");
  220. Console.WriteLine($"托管管道模式:{poolConfig.ManagedPipelineMode}");
  221. }
  222. /// <summary>
  223. /// 获取IIS配置
  224. /// </summary>
  225. /// <returns></returns>
  226. private static IISConfig GetIISConfigs()
  227. {
  228. var path = Path.GetFullPath("./cfg/config.json");
  229. using (System.IO.StreamReader file = System.IO.File.OpenText(path))
  230. {
  231. using (JsonTextReader reader = new JsonTextReader(file))
  232. {
  233. var o = (JObject)JToken.ReadFrom(reader);
  234. var iis = o.ToObject<IISConfig>();
  235. return iis;
  236. }
  237. }
  238. }
  239. /// <summary>
  240. /// 添加虚拟目录
  241. /// </summary>
  242. /// <param name="siteName">网站名</param>
  243. /// <param name="vDirName">目录名</param>
  244. /// <param name="physicalPath">对应的文件夹路径</param>
  245. /// <param name="iismanager"></param>
  246. private static void CreateVDir(string siteName, string vDirName, string physicalPath, ServerManager iismanager)
  247. {
  248. Site site = iismanager.Sites[siteName];
  249. if (site == null)
  250. {
  251. return;
  252. }
  253. site.Applications["/"].VirtualDirectories.Add("/" + vDirName, physicalPath);
  254. }
  255. /// <summary>
  256. /// 添加虚拟目录
  257. /// </summary>
  258. /// <param name="siteName">网站名</param>
  259. /// <param name="vDirName">目录名</param>
  260. /// <param name="physicalPath">对应的文件夹路径</param>
  261. /// <param name="iismanager"></param>
  262. private static void CreateSubitemVDir(string siteName, string subitemSiteName, string vDirName, string physicalPath, ServerManager iismanager)
  263. {
  264. var app = iismanager.Sites[siteName].Applications["/" + subitemSiteName];
  265. if (app == null)
  266. {
  267. return;
  268. }
  269. app.VirtualDirectories.Add("/" + vDirName, physicalPath);
  270. }
  271. // <summary>
  272. /// 增加绑定域名(ssl)
  273. /// </summary>
  274. /// <param name="webName">站点名称</param>
  275. /// <param name="port">端口</param>
  276. /// <param name="bindingDomainName">绑定域名</param>
  277. public static Site AddHostHeaderSSL(ServerManager iismanager, string webName, int port, string bindingDomainName, string physicalPath)
  278. {
  279. //**pfxPath**是指提供给你的.pfx文件的路径,购买ssl之后他会提供给你相关的文件,里面有多个文件
  280. //一般包含apache文件夹,iis文件夹,nginx文件夹,我使用的是iis部署,所以.pfx文件在iis文件夹里面
  281. //**certPwd**是指购买ssl之后提供给你的秘钥
  282. string pfx = "./cfg/isas.pfx";
  283. var store = new X509Store(StoreName.AuthRoot, StoreLocation.LocalMachine);
  284. store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
  285. var certificate = new X509Certificate2(pfx, "123456", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
  286. store.Add(certificate);
  287. store.Close();
  288. var certificateStoreName = store.Name; //绑定的证书名称
  289. var certificateHash = certificate.GetCertHash(); //证书内容
  290. string bindingInformation = "*:" + port + ":" + bindingDomainName;
  291. Site site = iismanager.Sites.Add(webName,bindingInformation, physicalPath, certificateHash, certificateStoreName);
  292. return site;
  293. }
  294. }
  295. }