Desenvolvimento - ASP. NET
Criando uma aplicação .Net para realizar recycle remoto no IIS
O artigo mostra o desenvolvimento de uma aplicação .net que realiza remotamente recycles no servidor.
por Vinicius TutuyAlgo muito comum é gerarmos uma dll com correções ou atualizações para uma aplicação web que já está em produção. E a parte mais demorada desse processo é na hora de realizar recycle em todos os servidores, pois existe a necessidade de conectar remotamente em cada um deles. Então por que não criar uma aplicação que faça o recycle nos servidores remotamente?
Mostrarei como fazer isso utilizando um serviço com .Net Remoting para disponibilizar as funcionalidades de recycle remotamente.
Desenvolvimento da aplicação server.
Crie um projeto Class Library para desenvolvermos o nosso serviço. Iniciaremos pelas entidades:
ApplicationPool.cs
[Serializable] public class ApplicationPool { private int _id; public int Id { get { return _id; } }
private string _nome; public string Nome { get { return _nome; } }
private Servidor _servidor; public Servidor Servidor { get { return _servidor; } set { _servidor = value; } }
public ApplicationPool() { }
public ApplicationPool(int id, Servidor servidor) { _id = id; _servidor = servidor; }
public ApplicationPool(int id, string nome) { _id = id; _nome = nome; }
public ApplicationPool(int id, string nome, Servidor servidor) { _id = id; _nome = nome; _servidor = servidor; } } |
Servidor.cs
[Serializable] public class Servidor { private int _codigo; public int Codigo { get { return _codigo; } }
private string _host; public string Host { get { return _host; } }
private int _porta; public int Porta { get { return _porta; } }
private string _servico; public string Servico { get { return _servico; } }
private ApplicationPool[] _applicationPools; public ApplicationPool[] ApplicationPools { get { return _applicationPools; } set { // Adiciona uma referência da entidade Servidor na entidade ApplicationPool foreach (ApplicationPool pool in value) pool.Servidor = this;
_applicationPools = value; } }
public Servidor(){ }
public Servidor(int codigo) { _codigo = codigo; }
public Servidor(int codigo, string host, int porta, string servico) { _codigo = codigo; _host = host; _porta = porta; _servico = servico; } } |
Chegamos à parte interessante, que é a classe Comandos. Esta classe fica
encarregada de realizar as solicitações para buscar as Application Pools do
servidor e realizar o Recycle das mesmas. Todas as solicitações ao iis são
feitas através de comandos enviados ao cmd (Prompt de Comando), pois ele possui
diversos recursos que podem ser adicionados à classe visando melhorar sua
aplicação.
Comandos.cs
public class Comandos : MarshalByRefObject { public ApplicationPool[] BuscarApplicationPools() { List<string> resultadoComando = ExecutarComando("iisapp"); List<ApplicationPool> pools = new List<ApplicationPool>(); foreach (string r in resultadoComando) { int indexId = r.IndexOf("PID:"); int indexNome = r.IndexOf("AppPoolId:");
if (indexId == -1 || indexNome == -1) continue;
indexId += 4; string Id = r.Substring(indexId, indexNome - indexId);
indexNome += 10; string Nome = r.Substring(indexNome, r.Length - indexNome);
ApplicationPool pool = new ApplicationPool(Int32.Parse(Id), Nome); pools.Add(pool); } return pools.ToArray(); }
public string RecycleApplicationPool(int PID) { List<string> resultadoComando = ExecutarComando(String.Format("iisapp /p {0} /r", PID)); return string.Join("\\n\\r", resultadoComando.ToArray()); }
private List<string> ExecutarComando(string comando) { ProcessStartInfo psi = new ProcessStartInfo("cmd", String.Format("/c {0}", comando)); psi.CreateNoWindow = true; psi.RedirectStandardOutput = true; psi.UseShellExecute = false;
List<string> resultado = new List<string>(); Process processo = null; try { processo = Process.Start(psi); StreamReader sr = processo.StandardOutput;
string linha; do { linha = sr.ReadLine(); if (!String.IsNullOrEmpty(linha)) resultado.Add(linha); } while (linha != null); } catch (Exception ex) { throw ex; } finally { if (processo != null) processo.Close(); }
return resultado; } } |
Feito nossa classe de negócio, devemos criar um Windows Service para iniciar o nosso serviço do .Net Remoting no servidor. Para isso crie um novo projeto Windows Service:
MonitorIIS.cs
public partial class MonitorIIS : ServiceBase { private HttpServerChannel _channel;
public MonitorIIS() { InitializeComponent(); // Inicializa o canal de comunicação buscando a porta utilizada pelo serviço no App.Config. _channel = new HttpServerChannel(Int32.Parse(ConfigurationManager.AppSettings["Porta_Http"])); }
protected override void OnStart(string[] args) { // Registra o serviço. RemotingConfiguration.RegisterWellKnownServiceType(typeof(Comandos), "MonitorIIS", WellKnownObjectMode.SingleCall); // Habilita a emissão de erros para o clinte (util durante a depuração). RemotingConfiguration.CustomErrorsMode = CustomErrorsModes.Off; // Registra o canal de comunicação. ChannelServices.RegisterChannel(_channel, false); }
protected override void OnStop() { // Desregistra a canal de comunicação. ChannelServices.UnregisterChannel(_channel); } } |
Agora devemos criar um Install do Windows Service e instalar o mesmo no servidor, mas não farei a demonstração para não fugir do foco da matéria.
Desenvolvimento da aplicação cliente.
Agora que já temos um serviço de .Net Remoting rodando em nosso servidor, criaremos então o nosso cliente. Neste exemplo eu criei uma aplicação web, mas poderia ser qualquer outro tipo aplicação. Para resumir a matéria vou criar uma classe que interage com um único servidor, mas o correto seria realizar uma busca de servidores em um banco de dados:
Monitor.cs
public class Monitor { private Servidor _servidor;
public Monitor() { // Cria uma instância da entidade servidor. _servidor = new Servidor(1, "MyHost", Int32.Parse(ConfigurationManager.AppSettings["Porta_Http"]), "MonitorIIS"); }
private Comandos MontarComandos(Servidor servidor) { // Cria uma instância da entidade Comandos já apontando para o serviço .Net Remoting Hospedado no servidor. Comandos comando = (Comandos)Activator.GetObject(typeof(Comandos), String.Format("http://{0}:{1}/{2}", servidor.Host, servidor.Porta, servidor.Servico)); return comando; }
public Servidor BuscarServidor() { Comandos comando = MontarComandos(_servidor); _servidor.ApplicationPools = comando.BuscarApplicationPools(); return _servidor; }
public string RecycleApplicationPool(ApplicationPool app) { Comandos comando = MontarComandos(_servidor); return comando.RecycleApplicationPool(app.Id); } } |
Todas as regras de negócio já estão prontas, agora falta somente criarmos a nossa interface com o usuário.
Default.aspx
<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>MonitorIIS</title> <style> body { font-family:Arial; font-size:small} .pagHeader {background-color: #d8e9ec; width: 100%; border-right: black thin solid; border-top: black thin solid; border-left: black thin solid; border-bottom: black thin solid;} .srv { border-right: black thin solid; border-top: black thin solid; border-left: black thin solid; border-bottom: black thin solid; width: 250px; float: left; text-align: left;} .srvHeader { background-color: #d8e9ec; border-bottom: black thin solid; width: 100%; text-align: left;} </style> </head> <body> <form id="form1" runat="server"> <div> <div class="pagHeader"> <div style="float: left;"><img src="Imgs/Monitor.png" /></div> <div><b>Monitor > Internet Information Services</b></div> <div> <asp:ImageButton ID="ibtProcurar" runat="server" ImageUrl="Imgs/Find.png" Height="25" Width="25" ToolTip="Buscar Servidores" OnClick="ibtProcurar_Click" /></div> </div> <br /><br /><br /> <div class="srv"> <div class="srvHeader"> <img src="Imgs/web-server.png" /> <asp:Label ID="lblServidor" runat="server" Text=""></asp:Label> </div> <div style="height:15px;"></div> <asp:Repeater ID="rptAppPools" runat="server" OnItemCommand="rptAppPools_ItemCommand" OnItemDataBound="rptAppPools_ItemDataBound"> <ItemTemplate> <div> <asp:ImageButton ID="ibtRecycle" runat="server" ImageUrl="Imgs/atualizar.gif" /> <%# DataBinder.Eval(Container.DataItem, "Nome")%> </div> </ItemTemplate> </asp:Repeater> </div> </div> </form> </body> </html> |
Default.aspx.cs
public partial class _Default : System.Web.UI.Page { private MonitorIIS.Client.Monitor _monitor;
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) PopularServidores(); }
private void PopularServidores() { if (_monitor == null) _monitor = new MonitorIIS.Client.Monitor();
Servidor servidor = _monitor.BuscarServidor();
lblServidor.Text = servidor.Host; rptAppPools.DataSource = servidor.ApplicationPools; rptAppPools.DataBind(); }
protected void rptAppPools_ItemCommand(object source, RepeaterCommandEventArgs e) { if (e.CommandName.ToLower().Equals("recycle")) { try { string[] args = e.CommandArgument.ToString().Split("|"); ApplicationPool app = new ApplicationPool(Int16.Parse(args[1]), new Servidor(Int32.Parse(args[0])));
if (_monitor == null) _monitor = new MonitorIIS.Client.Monitor();
string mensagem = _monitor.RecycleApplicationPool(app); Page.ClientScript.RegisterStartupScript(this.GetType(), "Recycle.Mensagem", String.Format("alert(\"{0}\")", mensagem), true); PopularServidores(); } catch (Exception ex) { Page.ClientScript.RegisterStartupScript(this.GetType(), "Recycle.MensagemErro", String.Format("alert(\"{0}\")", ex.Message), true); } } }
protected void rptAppPools_ItemDataBound(object sender, RepeaterItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { ImageButton ibtRecycle = (ImageButton)e.Item.FindControl("ibtRecycle"); ApplicationPool pool = (ApplicationPool) e.Item.DataItem;
ibtRecycle.CommandName = "Recycle"; ibtRecycle.CommandArgument = pool.Servidor.Codigo.ToString() + "|" + pool.Id.ToString(); } } protected void ibtProcurar_Click(object sender, ImageClickEventArgs e) { PopularServidores(); } } |
Demonstração da aplicação
|
Obrigado e até a próxima!
Vinicius Tutuy