基于AppDomain的插件开发-WCF+HttpServer分布式部署(六)
前面实现了单机程序中使用的插件框架,但如果插件以服务形式存在,并且分布在多个服务器上, 更新一次插件需要到每个服务器上进行更新,这样很不方便。这次我们在此原框架基础上,利用WCF在进程中Host一个WebService,然后管理端引用该服务后,就可以指的对被管理运算服务器进行插件更新了。
插件管理服务:
/// <summary>
///插件管理接口
/// </summary>
[ServiceContract]
public interface IPluginServiceContract
{
[OperationContract]
PluginItem[] List();
[OperationContract]
void Add(string assemblyName, string pluginUri);
[OperationContract]
void Remove(string assemblyName);
}
/// <summary>
///数据类
/// </summary>
[DataContract]
public class PluginItem
{
/// <summary>
///程序集名称
/// </summary>
[DataMember]
public string AssemblyName { get; set; }
/// <summary>
///更新日期
/// </summary>
[DataMember]
public DateTime UpdateTime { get; set; }
}
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = false)]
public class PluginServiceImpl : IPluginServiceContract
{
private DirectoryInfo pluginPath;
public PluginServiceImpl(string pluginPath)
{
this.pluginPath = new DirectoryInfo(pluginPath);
}
/// <summary>
///枚举当前所有插件
/// </summary>
/// <returns></returns>
public PluginItem[] List()
{
var ret = this.pluginPath.GetFiles("*.dll").ToList().ConvertAll(d =>
new PluginItem() { AssemblyName = Path.GetFileNameWithoutExtension(d.Name), UpdateTime = d.LastWriteTime });
return ret.ToArray();
}
/// <summary>
///添加或者更新一个插件
/// </summary>
/// <param name="assemblyName">程序集名称</param>
/// <param name="pluginUri">下载最新插件程序集的URI地址 eg:http://remote.com/plugins/plugins.hello.dll </param>
public void Add(string assemblyName, string pluginUri)
{
byte[] pluginContent = new System.Net.WebClient().DownloadData(pluginUri);
if(!assemblyName.ToLower().EndsWith(".dll"))
{
assemblyName = string.Concat(assemblyName, ".dll");
}
File.WriteAllBytes(Path.Combine(pluginPath.FullName, assemblyName), pluginContent);
}
/// <summary>
///依程序集删除一个插件
/// </summary>
/// <param name="assemblyName">程序集名称</param>
public void Remove(string assemblyName)
{
if (!assemblyName.ToLower().EndsWith(".dll"))
{
assemblyName = string.Concat(assemblyName, ".dll");
}
File.Delete(Path.Combine(pluginPath.FullName, assemblyName));
}
}
/// <summary>
///插件WCF启动、停止类
/// </summary>
public class PluginService
{
private ServiceHost host;
/// <summary>
///开始服务
/// </summary>
/// <param name="hostName">主机名</param>
/// <param name="port">端口</param>
/// <param name="pluginPath">插件目录,一定要和插件加载类配置成一样的目录</param>
public void Start(string hostName, int port, string pluginPath = "plugins")
{
IPluginServiceContract serviceContract = new PluginServiceImpl(pluginPath);
this.host = new ServiceHost(serviceContract, new UriBuilder("http", hostName, port, "pluginservice").Uri);
var smb = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior();
host.Description.Behaviors.Add(smb);
}
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy12;
host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding()
, "mex");
host.AddServiceEndpoint(typeof(IPluginServiceContract), new BasicHttpBinding()
, "");
host.Open();
}
/// <summary>
///停止服务
/// </summary>
public void Stop()
{
this.host.Close();
}
}
最后是管理客户端调用:
static void Main(string[] args)
{
using (WebReferences.PluginServiceImpl ps = new WebReferences.PluginServiceImpl())
{
var list = ps.List();
foreach (var i in list)
{
Console.Write("{0}, {1}", i.AssemblyName, i.UpdateTime);
}
Console.WriteLine("开始删除");
Console.ReadLine();
if (list.Length > 0)
{
ps.Remove(list[0].AssemblyName);
list = ps.List();
foreach (var i in list)
{
Console.Write("{0}, {1}", i.AssemblyName, i.UpdateTime);
}
}
Console.WriteLine("开始部署");
Console.ReadLine();
System.IO.Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(),"plugins"), "Plugins.*.dll").ToList().ForEach(d =>
{
//http://localhost/plugins/Plugins.HelloWorld.dll
ps.Add(Path.GetFileNameWithoutExtension(d), "http://localhost/plugins/" + Path.GetFileName(d));
});
list = ps.List();
foreach (var i in list)
{
Console.Write("{0}, {1}", i.AssemblyName, i.UpdateTime);
}
}
Console.ReadLine();
}