背景:
windows系统下生成的jar包通过FTP上传到linux服务器,然后通过XShell进行jar包的发布,这样反复了几个月后,开发阶段需要频繁更新包的部署。个人觉得很繁琐,想一键式把这个工作做了,不想经常花时间发布。前几天去了解了下Docker+Jenkins的方式,但是过于麻烦,对我个人学习时间和成本比较高。从而衍生出想用自己比较拿手的C#编写一个小工具解决我这个问题
需求:
windows环境jar包部署到linux服务器,一键操作
下载:
https://files.cnblogs.com/files/shexunyu/Linux%E9%83%A8%E7%BD%B2%E5%B0%8F%E5%B7%A5%E5%85%B7V2.zip
https://pan.baidu.com/s/15HpNjKDBiuLf7mNgSlbDFw 提取码:pk5n
小工具:
界面
程序构建方式
1.通过Neget安装SSH.NET
2.后端代码
1.设计器代码
1 namespace Deploy 2 { 3 partial class DeployFrm 4 { 5 /// <summary> 6 /// Required designer variable. 7 /// </summary> 8 private System.ComponentModel.IContainer components = null; 9 10 /// <summary> 11 /// Clean up any resources being used. 12 /// </summary> 13 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> 14 protected override void Dispose(bool disposing) 15 { 16 if (disposing && (components != null)) 17 { 18 components.Dispose(); 19 } 20 base.Dispose(disposing); 21 } 22 23 #region Windows Form Designer generated code 24 25 /// <summary> 26 /// Required method for Designer support - do not modify 27 /// the contents of this method with the code editor. 28 /// </summary> 29 private void InitializeComponent() 30 { 31 this.label1 = new System.Windows.Forms.Label(); 32 this.txtLocalFilePath = new System.Windows.Forms.TextBox(); 33 this.label2 = new System.Windows.Forms.Label(); 34 this.txtServerFilePath = new System.Windows.Forms.TextBox(); 35 this.txtPort = new System.Windows.Forms.TextBox(); 36 this.label8 = new System.Windows.Forms.Label(); 37 this.IP = new System.Windows.Forms.Label(); 38 this.label3 = new System.Windows.Forms.Label(); 39 this.label4 = new System.Windows.Forms.Label(); 40 this.txtPassword = new System.Windows.Forms.TextBox(); 41 this.label5 = new System.Windows.Forms.Label(); 42 this.txtUserName = new System.Windows.Forms.TextBox(); 43 this.txtIP = new System.Windows.Forms.TextBox(); 44 this.txtServerDirectory = new System.Windows.Forms.TextBox(); 45 this.btnConnection = new System.Windows.Forms.Button(); 46 this.btnUpload = new System.Windows.Forms.Button(); 47 this.btnRun = new System.Windows.Forms.Button(); 48 this.label7 = new System.Windows.Forms.Label(); 49 this.txtLog = new System.Windows.Forms.TextBox(); 50 this.btnSaveInfo = new System.Windows.Forms.Button(); 51 this.btnStop = new System.Windows.Forms.Button(); 52 this.SuspendLayout(); 53 // 54 // label1 55 // 56 this.label1.AutoSize = true; 57 this.label1.Location = new System.Drawing.Point(23, 164); 58 this.label1.Name = "label1"; 59 this.label1.Size = new System.Drawing.Size(89, 12); 60 this.label1.TabIndex = 0; 61 this.label1.Text = "LocalFilePath:"; 62 // 63 // txtLocalFilePath 64 // 65 this.txtLocalFilePath.Location = new System.Drawing.Point(127, 157); 66 this.txtLocalFilePath.Name = "txtLocalFilePath"; 67 this.txtLocalFilePath.Size = new System.Drawing.Size(125, 21); 68 this.txtLocalFilePath.TabIndex = 1; 69 // 70 // label2 71 // 72 this.label2.AutoSize = true; 73 this.label2.Location = new System.Drawing.Point(23, 200); 74 this.label2.Name = "label2"; 75 this.label2.Size = new System.Drawing.Size(95, 12); 76 this.label2.TabIndex = 2; 77 this.label2.Text = "ServerFilePath:"; 78 // 79 // txtServerFilePath 80 // 81 this.txtServerFilePath.Location = new System.Drawing.Point(127, 197); 82 this.txtServerFilePath.Name = "txtServerFilePath"; 83 this.txtServerFilePath.Size = new System.Drawing.Size(125, 21); 84 this.txtServerFilePath.TabIndex = 3; 85 // 86 // txtPort 87 // 88 this.txtPort.Location = new System.Drawing.Point(127, 42); 89 this.txtPort.Name = "txtPort"; 90 this.txtPort.Size = new System.Drawing.Size(125, 21); 91 this.txtPort.TabIndex = 28; 92 // 93 // label8 94 // 95 this.label8.AutoSize = true; 96 this.label8.Location = new System.Drawing.Point(23, 45); 97 this.label8.Name = "label8"; 98 this.label8.Size = new System.Drawing.Size(35, 12); 99 this.label8.TabIndex = 27; 100 this.label8.Text = "Port:"; 101 // 102 // IP 103 // 104 this.IP.AutoSize = true; 105 this.IP.Location = new System.Drawing.Point(23, 24); 106 this.IP.Name = "IP"; 107 this.IP.Size = new System.Drawing.Size(23, 12); 108 this.IP.TabIndex = 19; 109 this.IP.Text = "IP:"; 110 // 111 // label3 112 // 113 this.label3.AutoSize = true; 114 this.label3.Location = new System.Drawing.Point(23, 72); 115 this.label3.Name = "label3"; 116 this.label3.Size = new System.Drawing.Size(101, 12); 117 this.label3.TabIndex = 20; 118 this.label3.Text = "ServerDirectory:"; 119 // 120 // label4 121 // 122 this.label4.AutoSize = true; 123 this.label4.Location = new System.Drawing.Point(23, 97); 124 this.label4.Name = "label4"; 125 this.label4.Size = new System.Drawing.Size(59, 12); 126 this.label4.TabIndex = 21; 127 this.label4.Text = "UserName:"; 128 // 129 // txtPassword 130 // 131 this.txtPassword.Location = new System.Drawing.Point(127, 124); 132 this.txtPassword.Name = "txtPassword"; 133 this.txtPassword.Size = new System.Drawing.Size(125, 21); 134 this.txtPassword.TabIndex = 26; 135 // 136 // label5 137 // 138 this.label5.AutoSize = true; 139 this.label5.Location = new System.Drawing.Point(23, 127); 140 this.label5.Name = "label5"; 141 this.label5.Size = new System.Drawing.Size(59, 12); 142 this.label5.TabIndex = 22; 143 this.label5.Text = "Password:"; 144 // 145 // txtUserName 146 // 147 this.txtUserName.Location = new System.Drawing.Point(127, 94); 148 this.txtUserName.Name = "txtUserName"; 149 this.txtUserName.Size = new System.Drawing.Size(125, 21); 150 this.txtUserName.TabIndex = 25; 151 // 152 // txtIP 153 // 154 this.txtIP.Location = new System.Drawing.Point(127, 16); 155 this.txtIP.Name = "txtIP"; 156 this.txtIP.Size = new System.Drawing.Size(125, 21); 157 this.txtIP.TabIndex = 23; 158 // 159 // txtServerDirectory 160 // 161 this.txtServerDirectory.Location = new System.Drawing.Point(127, 67); 162 this.txtServerDirectory.Name = "txtServerDirectory"; 163 this.txtServerDirectory.Size = new System.Drawing.Size(125, 21); 164 this.txtServerDirectory.TabIndex = 24; 165 // 166 // btnConnection 167 // 168 this.btnConnection.Location = new System.Drawing.Point(286, 16); 169 this.btnConnection.Name = "btnConnection"; 170 this.btnConnection.Size = new System.Drawing.Size(111, 57); 171 this.btnConnection.TabIndex = 29; 172 this.btnConnection.Text = "Connection"; 173 this.btnConnection.UseVisualStyleBackColor = true; 174 this.btnConnection.Click += new System.EventHandler(this.btnConnection_Click); 175 // 176 // btnUpload 177 // 178 this.btnUpload.Location = new System.Drawing.Point(434, 16); 179 this.btnUpload.Name = "btnUpload"; 180 this.btnUpload.Size = new System.Drawing.Size(117, 57); 181 this.btnUpload.TabIndex = 30; 182 this.btnUpload.Text = "Upload"; 183 this.btnUpload.UseVisualStyleBackColor = true; 184 this.btnUpload.Click += new System.EventHandler(this.btnUpload_Click); 185 // 186 // btnRun 187 // 188 this.btnRun.Location = new System.Drawing.Point(440, 97); 189 this.btnRun.Name = "btnRun"; 190 this.btnRun.Size = new System.Drawing.Size(111, 60); 191 this.btnRun.TabIndex = 33; 192 this.btnRun.Text = "Run"; 193 this.btnRun.UseVisualStyleBackColor = true; 194 this.btnRun.Click += new System.EventHandler(this.btnRun_Click); 195 // 196 // label7 197 // 198 this.label7.AutoSize = true; 199 this.label7.Location = new System.Drawing.Point(29, 272); 200 this.label7.Name = "label7"; 201 this.label7.Size = new System.Drawing.Size(29, 12); 202 this.label7.TabIndex = 34; 203 this.label7.Text = "Log:"; 204 // 205 // txtLog 206 // 207 this.txtLog.Location = new System.Drawing.Point(127, 240); 208 this.txtLog.Multiline = true; 209 this.txtLog.Name = "txtLog"; 210 this.txtLog.Size = new System.Drawing.Size(636, 171); 211 this.txtLog.TabIndex = 35; 212 // 213 // btnSaveInfo 214 // 215 this.btnSaveInfo.Location = new System.Drawing.Point(584, 16); 216 this.btnSaveInfo.Name = "btnSaveInfo"; 217 this.btnSaveInfo.Size = new System.Drawing.Size(111, 57); 218 this.btnSaveInfo.TabIndex = 36; 219 this.btnSaveInfo.Text = "SaveInfo"; 220 this.btnSaveInfo.UseVisualStyleBackColor = true; 221 this.btnSaveInfo.Click += new System.EventHandler(this.btnSaveInfo_Click); 222 // 223 // btnStop 224 // 225 this.btnStop.Location = new System.Drawing.Point(280, 97); 226 this.btnStop.Name = "btnStop"; 227 this.btnStop.Size = new System.Drawing.Size(117, 57); 228 this.btnStop.TabIndex = 37; 229 this.btnStop.Text = "Stop"; 230 this.btnStop.UseVisualStyleBackColor = true; 231 this.btnStop.Click += new System.EventHandler(this.btnStop_Click); 232 // 233 // DeployFrm 234 // 235 this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 236 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 237 this.ClientSize = new System.Drawing.Size(790, 450); 238 this.Controls.Add(this.btnStop); 239 this.Controls.Add(this.btnSaveInfo); 240 this.Controls.Add(this.txtLog); 241 this.Controls.Add(this.label7); 242 this.Controls.Add(this.btnRun); 243 this.Controls.Add(this.btnUpload); 244 this.Controls.Add(this.btnConnection); 245 this.Controls.Add(this.txtPort); 246 this.Controls.Add(this.label8); 247 this.Controls.Add(this.IP); 248 this.Controls.Add(this.label3); 249 this.Controls.Add(this.label4); 250 this.Controls.Add(this.txtPassword); 251 this.Controls.Add(this.label5); 252 this.Controls.Add(this.txtUserName); 253 this.Controls.Add(this.txtIP); 254 this.Controls.Add(this.txtServerDirectory); 255 this.Controls.Add(this.txtServerFilePath); 256 this.Controls.Add(this.label2); 257 this.Controls.Add(this.txtLocalFilePath); 258 this.Controls.Add(this.label1); 259 this.Name = "DeployFrm"; 260 this.Text = "DeployFrm"; 261 this.Load += new System.EventHandler(this.DeployFrm_Load); 262 this.ResumeLayout(false); 263 this.PerformLayout(); 264 265 } 266 267 #endregion 268 269 private System.Windows.Forms.Label label1; 270 private System.Windows.Forms.TextBox txtLocalFilePath; 271 private System.Windows.Forms.Label label2; 272 private System.Windows.Forms.TextBox txtServerFilePath; 273 private System.Windows.Forms.TextBox txtPort; 274 private System.Windows.Forms.Label label8; 275 private System.Windows.Forms.Label IP; 276 private System.Windows.Forms.Label label3; 277 private System.Windows.Forms.Label label4; 278 private System.Windows.Forms.TextBox txtPassword; 279 private System.Windows.Forms.Label label5; 280 private System.Windows.Forms.TextBox txtUserName; 281 private System.Windows.Forms.TextBox txtIP; 282 private System.Windows.Forms.TextBox txtServerDirectory; 283 private System.Windows.Forms.Button btnConnection; 284 private System.Windows.Forms.Button btnUpload; 285 private System.Windows.Forms.Button btnRun; 286 private System.Windows.Forms.Label label7; 287 private System.Windows.Forms.TextBox txtLog; 288 private System.Windows.Forms.Button btnSaveInfo; 289 private System.Windows.Forms.Button btnStop; 290 } 291 }
2.业务代码
using Renci.SshNet; using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Windows.Forms; namespace Deploy { public partial class DeployFrm : Form { public DeployFrm() { InitializeComponent(); deployBus.TxtLog = this.txtLog; } public static DeployBusness deployBus = new DeployBusness(); public Deploy GetDeploy() { DateTime dt = DateTime.Parse("2019-09-30"); if (DateTime.Now > dt) { throw new Exception("请联系shexun进行版本更新"); } var deploy = new Deploy(this.txtIP.Text, this.txtPort.Text, this.txtServerDirectory.Text, this.txtUserName.Text, this.txtPassword.Text, this.txtLocalFilePath.Text, this.txtServerFilePath.Text, ""); return deploy; } public void LoadInfo() { var deploy = deployBus.ReadInfo(); if (deploy != null) { this.txtIP.Text = deploy.Host; this.txtPort.Text = deploy.Port; this.txtServerDirectory.Text = deploy.ServerDirectory; this.txtUserName.Text = deploy.UserName; this.txtPassword.Text = deploy.Password; this.txtLocalFilePath.Text = deploy.LocalFilePath; this.txtServerFilePath.Text = deploy.ServerFilePath; } } private void btnUpload_Click(object sender, EventArgs e) { try { deployBus.Upload(GetDeploy()); } catch (Exception ex) { deployBus.ShowLog(ex.Message); } } private void btnRun_Click(object sender, EventArgs e) { try { deployBus.Run(GetDeploy()); } catch (Exception ex) { deployBus.ShowLog(ex.Message); } } private void btnSaveInfo_Click(object sender, EventArgs e) { try { deployBus.SaveInfo(GetDeploy()); } catch (Exception ex) { deployBus.ShowLog(ex.Message); } } private void DeployFrm_Load(object sender, EventArgs e) { try { LoadInfo(); } catch (Exception ex) { deployBus.ShowLog(ex.Message); } } private void btnStop_Click(object sender, EventArgs e) { try { deployBus.Stop(GetDeploy()); } catch (Exception ex) { deployBus.ShowLog(ex.Message); } } private void btnBackUp_Click(object sender, EventArgs e) { } private void btnConnection_Click(object sender, EventArgs e) { try { deployBus.Connection(GetDeploy()); } catch (Exception ex) { deployBus.ShowLog(ex.Message); } } /// <summary> /// 部署类 /// </summary> [DataContract] public class Deploy { public Deploy(string Host, string Port, string ServerDirectory, string UserName, string Password, string LocalFilePath, string ServerFilePath, string Log) { this.Host = Host != null ? Host.Trim() : null; this.Port = Port != null ? Port.Trim() : null; this.ServerDirectory = ServerDirectory != null ? ServerDirectory.Trim() : null; this.UserName = UserName != null ? UserName.Trim() : null; this.Password = Password != null ? Password.Trim() : null; this.LocalFilePath = LocalFilePath != null ? LocalFilePath.Trim() : null; this.ServerFilePath = ServerFilePath != null ? ServerFilePath.Trim() : null; this.Log = Log != null ? Log.Trim() : null; } [DataMember] public string Host { get; set; } [DataMember] public string Port { get; set; } [DataMember] public string ServerDirectory { get; set; } [DataMember] public string UserName { get; set; } [DataMember] public string Password { get; set; } [DataMember] public string LocalFilePath { get; set; } [DataMember] public string ServerFilePath { get; set; } [DataMember] public string Log { get; set; } } public class DeployBusness { public TextBox TxtLog { get; set; } public emAction EmAction { get; set; } public enum emAction { ___, Connection, Upload, BackUp, Run, Stop, SaveInfo, ReadInfo, Download } public void ShowLog(string log) { string logStr = ""; logStr = EmAction.ToString(); var defaultNull = 10 - logStr.Length; if (defaultNull > 0) { string temp = ""; for (int i = 0; i < defaultNull; i++) { temp += " "; } logStr += temp; } logStr += " => [ " + DateTime.Now.ToString() + " ] "; logStr += log + "。 "; if (this.TxtLog != null) { TxtLog.Text += logStr; } EmAction = emAction.___; } public bool Connection(Deploy deploy) { this.EmAction = emAction.Connection; using (var client = new SftpClient(deploy.Host, int.Parse(deploy.Port), deploy.UserName, deploy.Password)) //创建连接对象 { client.Connect(); //连接 this.ShowLog("连接服务器成功"); } return true; } public bool BackUp(Deploy deploy) { //对应的服务未停止不能进行上传 this.EmAction = emAction.BackUp; using (var client = new SftpClient(deploy.Host, int.Parse(deploy.Port), deploy.UserName, deploy.Password)) //创建连接对象 { client.Connect(); //连接 var fileName = Path.GetFileName(deploy.ServerFilePath); var changeDir = deploy.ServerFilePath.Replace(fileName, ""); client.ChangeDirectory(changeDir); //切换目录 //复制文件到新的文件目录 using (var fileStream = new FileStream(deploy.LocalFilePath, FileMode.Open)) { this.ShowLog("正在上传文件,请稍等......"); client.BufferSize = 4 * 1024; // bypass Payload error large client.UploadFile(fileStream, fileName); //上传文件 this.ShowLog("上传文件成功"); } } return true; } public bool Upload(Deploy deploy) { //对应的服务未停止不能进行上传 this.EmAction = emAction.Upload; using (var client = new SftpClient(deploy.Host, int.Parse(deploy.Port), deploy.UserName, deploy.Password)) //创建连接对象 { client.Connect(); //连接 var fileName = Path.GetFileName(deploy.ServerFilePath); var changeDir = deploy.ServerFilePath.Replace(fileName, ""); client.ChangeDirectory(changeDir); //切换目录 using (var fileStream = new FileStream(deploy.LocalFilePath, FileMode.Open)) { this.ShowLog("正在上传文件,请稍等......"); client.BufferSize = 4 * 1024; // bypass Payload error large client.UploadFile(fileStream, fileName); //上传文件 this.ShowLog("上传文件成功"); } } return true; } public void Run(Deploy deploy) { this.EmAction = emAction.Run; var con = new ConnectionInfo(deploy.Host, int.Parse(deploy.Port), deploy.UserName, new AuthenticationMethod[]{ // Pasword based Authentication new PasswordAuthenticationMethod(deploy.UserName,deploy.Password) }); // Execute (SHELL) Commands using (var sshclient = new SshClient(con)) { sshclient.Connect(); this.ShowLog("连接服务器成功"); var psResevice = sshclient.CreateCommand("ps -ef").Execute(); var runList = new List<string>(); string[] lines = psResevice.Split(' '); //用 表示换行符 注意是char类型 分割行 var fileName = Path.GetFileName(deploy.ServerFilePath); foreach (var item in lines) { if (item.Contains(fileName)) { runList.Add(item); this.ShowLog("找到服务:" + item); } } foreach (var item in runList) { var temp = item.Substring(0, 15); var pid = temp.Substring(4, 15 - 4).Trim(); //获取到PID var msg = sshclient.CreateCommand("kill -9 " + pid).Execute(); this.ShowLog("关闭服务PID:" + pid); } //更新文件 成功后执行命令 var changeDir = deploy.ServerFilePath.Replace(fileName, "").TrimEnd('/'); var command = "nohup java -jar " + fileName + ">>m.out.log 2>&1 &"; var msg2 = sshclient.CreateCommand("cd " + changeDir + ";" + command).Execute(); this.ShowLog("启动服务成功"); } } public void Stop(Deploy deploy) { this.EmAction = emAction.Stop; var con = new ConnectionInfo(deploy.Host, int.Parse(deploy.Port), deploy.UserName, new AuthenticationMethod[]{ // Pasword based Authentication new PasswordAuthenticationMethod(deploy.UserName,deploy.Password) }); // Execute (SHELL) Commands using (var sshclient = new SshClient(con)) { sshclient.Connect(); var psResevice = sshclient.CreateCommand("ps -ef").Execute(); string[] lines = psResevice.Split(' '); //用 表示换行符 注意是char类型 分割行 var fileName = Path.GetFileName(deploy.ServerFilePath); if (lines != null && lines.Length > 0) { this.ShowLog("存在" + lines.Length.ToString() + "个服务"); } foreach (var item in lines) { if (item.Contains(fileName)) { this.ShowLog("找到服务:" + item); var temp = item.Substring(0, 15); var pid = temp.Substring(4, 15 - 4).Trim(); //获取到PID var msg = sshclient.CreateCommand("kill -9 " + pid).Execute(); } } psResevice = sshclient.CreateCommand("ps -ef").Execute(); lines = psResevice.Split(' '); //用 表示换行符 注意是char类型 分割行 var stopStatus = false; foreach (var item in lines) { if (item.Contains(fileName)) { stopStatus = true; } } if (!stopStatus) { this.ShowLog("停止服务成功"); } } } /// <summary> /// 读取信息 /// </summary> public Deploy ReadInfo() { this.EmAction = emAction.ReadInfo; string path = AppDomain.CurrentDomain.BaseDirectory + @"TempRecord"; var defaultFileName = "Info.txt"; string fileFullPath = path + defaultFileName; //加载对应的文件目录的所有文件 if (File.Exists(fileFullPath)) { string info = File.ReadAllText(fileFullPath); this.ShowLog("读取配置成功"); //反序列化 var deploy = MySerializer.JsonToObject<Deploy>(info); return deploy; } return null; } /// <summary> /// 保存信息 /// </summary> public void SaveInfo(Deploy deploy) { this.EmAction = emAction.SaveInfo; string path = AppDomain.CurrentDomain.BaseDirectory + @"TempRecord"; var fileFullPath = path + "Info.txt"; StreamWriter sw; if (File.Exists(fileFullPath)) { File.Delete(fileFullPath); } var info = MySerializer.ObjectToJson(deploy); sw = File.CreateText(fileFullPath); sw.WriteLine(info); sw.Close(); this.ShowLog("保存配置成功"); } } public class MySerializer { /// <summary> /// 将对象序列化为json字符串 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="t">实例</param> /// <returns>json字符串</returns> public static string ObjectToJson<T>(T t) where T : class { DataContractJsonSerializer formatter = new DataContractJsonSerializer(typeof(T)); using (MemoryStream stream = new MemoryStream()) { formatter.WriteObject(stream, t); string result = System.Text.Encoding.UTF8.GetString(stream.ToArray()); return result; } } /// <summary> /// json字符串转成对象 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="json">json格式字符串</param> /// <returns>对象</returns> public static T JsonToObject<T>(string json) where T : class { DataContractJsonSerializer formatter = new DataContractJsonSerializer(typeof(T)); using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json))) { T result = formatter.ReadObject(stream) as T; return result; } } } } }
总结:此工具我已分享给小组的开发同事一起使用,目前也有考虑在此基础上增加新的小功能,有兴趣的朋友请评论留言。