随着谷歌浏览器不断的改变https调用websocket和非https资源的策略,从谷歌大概70以后不允许https调用非https资源和ws的websocket。后面实现了wss解决谷歌这一策略的影响。随着谷歌到90后版本限制了私有证书的调用,导致最新浏览器又不能调用wss的websocket了。一方面是安保要求https传输,一方面是谷歌不断加严格https策略和证书策略,有好一段时间竟然有点难两全了。
最近想起在linux试nginx的反向代理可以代理url,然后它还有负载均衡的功能。可不可以给客户端运行个nginx把https代理为http。顺带也实现了负载均衡呢?
有几个问题需要验证: 1.windows的nginx部署是否麻烦? 2.windows的nginx包是否太大?
如果需要安装或者包太大那么不可行。前几天用nginx正好解决了http调用https润乾页面老是要信任证书的问题。整个windows的nginx包就几兆。而且不需要安装,运行也是后台的,所以符合需求。
那么就可以自己用C#写个负载均衡启动程序,程序设计流程如下。
实现控制台代码如下:
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Diagnostics;
namespace LISLoadBalance
{
class Program
{
static void Main(string[] args)
{
string openUrl = TryStartNginx();
if (openUrl != "")
{
OpenChromeUtil.OpenUrl(openUrl);
}
Environment.Exit(0);
}
public static string TryStartNginx()
{
try
{
string nginxConfPath = "C:\\TRAK\\nginx\\conf\\nginx.conf";
if (!File.Exists(nginxConfPath))
{
MessageBox.Show(nginxConfPath + "不存在!", "提示", MessageBoxButtons.OK);
return "";
}
string confStr = TxtUtil.ReadTxt(nginxConfPath);
string updateUrl = confStr.Split((char)13)[0];
updateUrl = updateUrl.Replace("#", "");
string openUrl = confStr.Split((char)13)[1];
openUrl = openUrl.Replace("#", "").Replace((char)10 + "", "");
string confStrServer = GetServerConf(updateUrl);
if (confStrServer != "" && confStrServer != confStr)
{
foreach (Process p in System.Diagnostics.Process.GetProcessesByName("nginx"))
{
try
{
p.Kill();
p.WaitForExit();
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
System.Diagnostics.EventLog.WriteEntry("AlchemySearch:KillProcess", exp.Message, System.Diagnostics.EventLogEntryType.Error);
}
}
TxtUtil.WriteTxt(nginxConfPath, confStrServer, true);
}
bool hasStart = false;
foreach (Process p in System.Diagnostics.Process.GetProcessesByName("nginx"))
{
hasStart = true;
}
if (hasStart == false)
{
string nginxPath = "C:\\TRAK\\nginx\\nginx.exe";
if (File.Exists(nginxPath))
{
Process.Start(nginxPath);
System.Threading.Thread.Sleep(1000);
}
else
{
MessageBox.Show(nginxPath + "不存在!", "提示", MessageBoxButtons.OK);
}
}
return openUrl;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK);
return "";
}
}
private static string GetServerConf(string updateUrl)
{
HttpWebRequest request = null;
try
{
if (updateUrl.StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
request = WebRequest.Create(updateUrl) as HttpWebRequest;
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
request.ProtocolVersion = HttpVersion.Version11;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
request.KeepAlive = false;
ServicePointManager.CheckCertificateRevocationList = true;
ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.Expect100Continue = false;
}
else
{
request = (HttpWebRequest)WebRequest.Create(updateUrl);
}
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
Stream responseStream = response.GetResponseStream();
StreamReader myread = new StreamReader(responseStream);
string serverStr = myread.ReadToEnd();
responseStream.Close();
return serverStr;
}
catch (Exception ex)
{
MessageBox.Show("从主站点获取nginx配置失败:" + ex.Message, "提示", MessageBoxButtons.OK);
return "";
}
}
private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
}
}
}
OpenChromeUtil.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace LISLoadBalance
{
public class OpenChromeUtil
{
public static void OpenUrl(string loginPath)
{
string chromePath = "";
bool IsChrome = TryGetSoftwarePath("chrome", out chromePath);
if (IsChrome && chromePath.Length > 0)
{
System.Diagnostics.Process.Start(chromePath, "--app="+loginPath);
}
else
{
Process pro = new Process();
pro.StartInfo.FileName = loginPath;
pro.StartInfo.CreateNoWindow = true;
pro.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
pro.StartInfo.Verb = "Open";
pro.Start();
}
}
public static bool TryGetSoftwarePath(string softName, out string path)
{
string strPathResult = string.Empty;
string strKeyName = "";
object objResult = null;
Microsoft.Win32.RegistryValueKind regValueKind;
Microsoft.Win32.RegistryKey regKey = null;
Microsoft.Win32.RegistryKey regSubKey = null;
try
{
regKey = Microsoft.Win32.Registry.LocalMachine;
regSubKey = regKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\" + softName.ToString() + ".exe", false);
if (regSubKey == null)
{
regKey = Microsoft.Win32.Registry.CurrentUser;
regSubKey = regKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\" + softName.ToString() + ".exe", false);
}
objResult = regSubKey.GetValue(strKeyName);
regValueKind = regSubKey.GetValueKind(strKeyName);
if (regValueKind == Microsoft.Win32.RegistryValueKind.String)
{
strPathResult = objResult.ToString();
}
}
catch (System.Security.SecurityException ex)
{
path = "";
MessageBox.Show("你没有读取注册表的权限! " + ex.Message, "提示", MessageBoxButtons.OK);
}
catch (Exception ex)
{
path = "";
MessageBox.Show("读取注册表错误! " + ex.Message, "提示", MessageBoxButtons.OK);
}
finally
{
if (regKey != null)
{
regKey.Close();
regKey = null;
}
if (regSubKey != null)
{
regSubKey.Close();
regSubKey = null;
}
}
if (strPathResult != string.Empty)
{
path = strPathResult;
return true;
}
else
{
path = "";
return false;
}
}
}
}
TxtUtil.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
namespace LISLoadBalance
{
public static class TxtUtil
{
public static string ReadTxt(string path)
{
if (!File.Exists(path))
{
return "";
}
FileStream fs = null;
try
{
System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(false);
fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader sr = new StreamReader(fs, utf8);
string str = sr.ReadToEnd();
return str;
}
catch (Exception ex)
{
MessageBox.Show("读文件异常:" + ex.Message, "提示", MessageBoxButtons.OK);
return "";
}
finally
{
fs.Close();
}
}
public static bool WriteTxt(string path, string str, bool isReplace = true, Encoding ecod = null)
{
if (ecod == null)
{
System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(false);
ecod = utf8;
}
FileStream fs = null;
StreamWriter sw1 = null;
try
{
if (!File.Exists(path))
{
fs = new FileStream(path, FileMode.Create, FileAccess.Write);
sw1 = new StreamWriter(fs, ecod);
sw1.WriteLine(str);
}
else
{
if (isReplace)
{
using (StreamWriter sw = new StreamWriter(path, false, ecod))
{
sw.Write("");
sw.Close();
}
}
fs = new FileStream(path, FileMode.Append, FileAccess.Write);
sw1 = new StreamWriter(fs, ecod);
sw1.WriteLine(str);
}
return true;
}
catch (Exception ex)
{
MessageBox.Show("读文件异常:" + ex.Message, "提示", MessageBoxButtons.OK);
return false;
}
finally
{
if (sw1 != null)
{
sw1.Close();
}
if (fs != null)
{
fs.Close();
}
}
}
}
}
对nginx配置约定加上前两行注释,以实现自动从服务器更新配置。达到在服务器维护负载信息的目的。 维护时候就只要把主web的nginx配置这里维护可用的负载服务器地址就行。客户端启动器就能及时得的负载列表,驱动nginx负载调用。
搞完了之后把编译的启动器和nginx以前打包放入网站服务器,让初始化程序初始化到本地桌面即可
然后就可以通过负载启动程序驱动登录了。
哈哈,说干就干。此模式不需要依赖别的组提供负载均衡。直接自己内部就实现负载,何其美哉。
|