Tema II. Ambientes Distribuidos. 1. Ambientes distribuidos Un sistema distribuido es un conjunto de componentes de hardware y software heterogeneos y localizados que colaboran entre si para dar la imagen de que es un módulo unico, de una manera transparente, confiable y tolerante a fallas. Existen varios modelos de procesamiento en ambientes distribuidos. Cliente/Servidor, que implica un módulo que procesa peticiones (el servidor) y un módulo cliente que genera peticiones. Procesamiento paralelo y masivo. Existe un modulo maestro que dicta ordenes a muchos modulos esclavos Peer-to-Peer (P2P). La manera de procesar es que cualquier modulo de software puede fungir como cliente o servidor, sin tener un papel unico, sino que puede tomar cualquier rol. 2. Cliente/Servidor y modelo de N capas Un sistema cliente/servidor son entidades lógicas independientes y heterogeneas que operan en conjunto a través de una red para realizar una tarea. Un sistema cliente/servidor debe tener las siguientes características : Servicio. Un servidor contiene un conjunto de operaciones para proveer un conjunto de funcionalidades. Recursos Compartidos. Un servidor atiende a muchos clientes al mismo tiempo Protocolos asimétricos. Los clientes siempre inician el diálogo y los servidores aguardan pasivamente las solicitudes. Transparencia de ubicación. El servidor puede residir en cualquier nodo de procesamiento, y los clientes no necesitan conocer la ubicación del servidor Independencia del hardware o plataforma de software o sistema opertaivo. Mensajes. El intercambio entre cliente y servidor se realiza por medio de solicitudes y respuestas del servicio, en forma de mensajes Escalable. Consiste en agregar de manera transparente hardware y software, sin afectar a los clientes y servidores, y que de hecho sean capaces de detectar los cambios Para que un cliente y servidor se comuniquen, necesitan de un conjunto de protocolos. Tipicamente son protocolos que se encuentran en la capa de aplicación, apoyandose en pilas como TCP/IP. Dichos protocolos, la implantación de los mismos y mecanismos intermedios se denominan en conjunto como middleware. La tecnología cliente servidor comenzo con la construcción de programas que utilizaban sockets. En algunos casos, surgian protocolos estandarizados, como por ejemplo ODBC para acceso a base de datos y en otros casos eran protocolos hechos a la medida. El desarrollo basado en sockets es complejo y propenso a errores; dado que implica una programación de bajo nivel. De ahí se ingenió una tecnología en la cual se enunciaban procedimientos o funciones que podian transformarse en servicios remotos. Esto origino al modelo de "llamadas a procedimientos remotos" o RPC. Esta tecnología, principalmente propuesta por el modelo distribuido de DCE (Distributed Computer Environment) y ONC (Open Network Connectivity) fue utilizada por muchos desarrolladores que comunicaban sistemas UNIX, o sistemas UNIX/Win32. Se basaba en la siguiente filosofía: ======= ========== Interfaz Compilador ------ Procedimiento cliente Remota --------- IDL --------| escrita ------ Procedimiento Servidor en IDL ======== =========== IDL. Interface Definition Language o Lenguaje de definición de interfases permite enunciar procedimientos, indicando el nombre del procedimiento, parámetros de la rutina y valor de retorno. Este lenguaje es completamente declarativo, y no indica COMO se implanta, solo declara QUE es lo que hace el servicio Compilador IDL se encarga de tomar un interfaz IDL y convertirla a un lenguaje como C, C++, COBOL. Procedimiento Servidor. Implantación de la lógica del servicio, el COMO de la interfaz. En dicho procedimiento se incluye código fuente para manejar los servicios de red. Procedimiento Cliente. Implantación de la lógica de peticiones, para invocar operaciones del servicio. La interacción con los servicios de red se incluye como código fuente generado. Con el código generado se construyen los programas ejecutables y se siguen los siguientes pasos: 1. Anuncio del servidor y puesta en marcha del servidor. Tipicamente se asocia el servicio con un nombre o alias, en un servicio de directorios 2. Puesta en marcha del cliente y Busqueda del servicio. 3. Alimentación de entrada utilizando la aplicación cliente. 4. Invocación del cliente al servicio. 5. Al invocar, los parámetros de la invocación se empaquetan en un formato estándar y se aplica una petición al servidor 6. El servidor recibe la petición y desempaqueta los argumentos 7. Se entrega al código responsable para procesar la petición y entregar el resultado. Este esquema evolucionó de tal manera que en lugar de utilizar lenguajes estructurados, empezó a utilizar tecnología de objetos, donde se enunciaban interfases orientadas a objetos. Esto llevo al surgimiento de objetos distribuídos, que consiste en ejecutar métodos de objetos en distintos nodos de la red. La tecnología de objetos distribuídos empezó con la tecnología CORBA (Common Object Request Broker Architecture) que es el resultado de tratar de comunicar objetos distribuidos con un protocolo estandarizado como IIOP (Internet InterObject Protocol). Con el surgimiento de Java, los sistemas cliente/servidor evolucionaron de una manera acelerada y surgieron tecnologías que juntaron las ideas de CORBA con las nuevas capacidades de Java, principalmente la de generar código en el vuelo. Al diseñar un sistema cliente/servidor, el arquitecto siempre se tiene que topar con la decisión relacionada en donde ubicar los distintos componentes. Esto lleva a identificiar los modelos de capas. El modelo C/S surgió con un modelo de 2 capas, donde se tenía un FrontEnd y un servidor (tipicamente una base de datos). Esto llevaba a que la lógica de procesamiento se tenía que cargar en cualquiera de las dos capas. El manejo de transacciones se volvió algo que lograba saturar a las bases de datos, por lo que se establecio un modelo de una capa intermedia, los objetos del negocio, encargados de llevar toda la lógica del sistema, un frontend y una base de datos. Cuando surgió la tecnología de WWW, se integró una nueva capa, donde el navegador de Internet podía fungir como un nuevo frontend, mas portable y ubicuo, y una capa que procesaba peticiones de WWW. Hoy en día han surgido nuevas capas, y de hecho, la creación del concepto de Servicios de Web o Web Services ha permitido liberar aplicaciones del navegador de WWW, pero bastante fáciles de contactar, gracias a estándares como XML. 3. Servicios de un ambiente distribuido Un sistema ambiente distribuido siempre debe tener un conjunto de servicios para lograr sus objetivos. Servicios de comunicación. Pila de protocolos TCP/IP Sistemas de mensajes. Llamadas a procedimientos remotos Servicio de directorios. Permite asociar a recursos de cómputo heterogeneos, un nombre homologado y estándar Servicios de procesamiento de transacciones. Ayuda a manejar la consistencia, concurrencia y atomicidad de una operación realizada por un servidor. Servicios de manejadores de bases de datos Servicios de manipulación de objetos distribuidos Tecnologías ERP como flujos de trabajo, procesos, planeación, etc 4. Modelos de comunicación 4.1 Sockets La manera como se comunican los módulos cliente/servidor estan basados en su mayoria en Sockets. Un socket es el modelo de comunicación de información, en el cual se transmiten datos utilizando llamadas al sistema operativo. Por tanto, su manipulación es de muy bajo nivel, de hecho se tiene que transmitir la información como flujos de bytes (http://www.geocities.com/gusdelact/cib9112002/codigo/ServidorSocket.java) El siguiente código muestra un programa servidor Java con sockets: /************************************************************************************/ import java.net.*; import java.io.*; /** * * @author gustavo */ public class ServidorSocket { /** Creates a new instance of ServidorSocket */ public ServidorSocket() { } /** * @param args the command line arguments */ public static void main(String[] args) { ServerSocket serv =null; try { serv = new ServerSocket(9080); Socket sock; while ((sock=serv.accept()) != null) { OutputStream out=sock.getOutputStream(); out.write((byte)'a'); out.close(); sock.close(); } } catch (Exception ex) { ex.printStackTrace(); } finally { try { serv.close(); } catch (Exception ex) { ex.printStackTrace(); } } } } /************************************************************************************/ El programa crear un servidor de sockets utilizando una clase predefinida de Java, llamada java.net.ServerSocket, aceptando peticiones en el puerto 9080. Proced a quedar en un ciclo infinito, esperando conexiones, y por cada conexión que reciba, escribe una letra 'a'. El programa cliente, se conecta al servidor en el puerto 9080: /************************************************************************************/ import java.net.*; import java.io.*; public class ClientSocket { /** Creates a new instance of ClientSocket */ public ClientSocket() { } /** * @param args the command line arguments */ public static void main(String[] args) { try { Socket sock = new Socket("localhost",9080); InputStream in=sock.getInputStream(); System.out.println((char)in.read()); in.close(); sock.close(); } catch (Exception ex) { ex.printStackTrace(); } } } /************************************************************************************/ En ambos programas, se tienen que manipular flujos o streams de bytes, usando las clases java.net.InputStream y java.net.OutputStream 4.2 Invocaciones a métodos remotos. El modelo de sockets lleva a una programación de bajo nivel, dado que se tiene que considerar el intercambio de información como flujo de bytes. Un modelo cliente/servidor debe orientarse a resolver problemas propios de ambientes distribuidos. Una manera de abstraer la invocación a servicios, es pensar a manera de invocaciones a objetos distribuidos. Un objeto distribuido es un módulo de software que tiene propiedades y métodos expuestos o disponibles a un protocolo de comunicación. Cuando el objeto distribuido expone sus métodos, los clientes lo invocan via llamadas a metodos remotos. En un modelo de invocaciones remotas se tiene un proceso o servicio que centraliza o es el mediador entre una aplicación cliente y servidor. La función de dicho proceso es otorgar un nombre a cada objeto distribuido y atender peticiones TCP que contienen el protocolo de invocaciones remotas 4.3 Remote Method Invocation (RMI) En el caso de Java, su modelo de invocaciones remotas es definidio por el protocolo RMI. Remote Method Invocation permite construir objetos distribuidos Java Para codificar un servicio basado en RMI, se crea primero una interfaz, que es la definicion de los métodos que expone un servicio: /************************************************************************************/ import java.rmi.*; /** Remote interface. */ public interface Servicio extends java.rmi.Remote { public int directiva() throws RemoteException; } /************************************************************************************/ En este caso, existe una interfaz Java que indica que existe un método denominado directiva y el cual retorna un entero Se debe crear un programa Java que implante la interfaz, es decir, de el codigo para el método servicio /************************************************************************************/ import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java.net.MalformedURLException; /** Unicast remote object implementing remote interface. */ public class ServicioImpl extends java.rmi.server.UnicastRemoteObject implements cibernetica.Servicio { /** Register ServicioImpl object with the RMI registry. * @param name - name identifying the service in the RMI registry * @param create - create local registry if necessary * @throw RemoteException if cannot be exported or bound to RMI registry * @throw MalformedURLException if name cannot be used to construct a valid URL * @throw IllegalArgumentException if null passed as name */ public static void registerToRegistry(String name, Remote obj, boolean create) throws RemoteException, MalformedURLException{ if (name == null) throw new IllegalArgumentException("registration name can not be null"); try { Naming.rebind(name, obj); } catch (RemoteException ex){ if (create) { Registry r = LocateRegistry.createRegistry(Registry.REGISTRY_PORT); r.rebind(name, obj); } else throw ex; } } /** Main method. */ public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); try { ServicioImpl obj = new ServicioImpl(); registerToRegistry("ServicioImpl", obj, true); } catch (RemoteException ex) { ex.printStackTrace(); } catch (MalformedURLException ex) { ex.printStackTrace(); } } public int directiva() throws RemoteException { System.out.println("Sirviendo directiva"); return 1; } } /************************************************************************************/ Lo importante a notar de este codigo es que el servicio al crearse, se registra con un nombre en el servidor de RMI o rmiregistry, con el nombre de "ServicioImpl". El método directiva retorna el valor de 1 Este programa al compilarlo, se debe dar un paso adicional, que generar una clase ServicioImpl_Stub, que es código Java que maneja de manera automática el protocolo RMI (y por ende Socketes) y procesa todas las peticiones del cliente Este programa depende de otro programa, denominado rmiregistry, el cual queda activo en un puerto TCP (tipicamente 1090) y que escucha peticiones de objetos cliente y servidor. A un servidor le da la posibilidad de registrarlo con un nombre y lo invoca cuando un cliente necesita obtener un servicio de él. A un cliente le provee todas las actividades necesarias para contactarlo con el objeto distribuido. El cliente es el siguiente: /************************************************************************************/ import java.rmi.*; public class Cliente extends Object { /** * @param args the command line arguments */ public static void main(String args[]) { try { System.setSecurityManager(new RMISecurityManager()); Servicio serv=(Servicio)Naming.lookup("//180.50.50.115:1099/ServicioImpl"); System.out.println(serv.directiva()); } catch (Exception ex) { ex.printStackTrace(); } } } /************************************************************************************/ Se puede observar que el clietne tiene que indicar la dirección IP del nodo donde reside el servidor rmiregistry, y un URI, que indica el nombre del objeto distribuido, como fue registrado ante el servicio de RMI. Al obtener una referencia (o handle) al objeto distribuido, puede invocar los metodos como si fueran LOCALES. En conclusion, RMI es un conjunto de clases, protocolos y programas Java que permiten que una aplicacion Java se convierta en un objeto distribuido. 5. Java 2 Enterprise Edition (J2EE) JavaSoft propone un modelo cliente/servidor en el cual los servicios anteriores son implantados con lenguaje Java 4.1 Arquitectura J2EE. Ver http://java.sun.com/j2ee/sdk_1.2.1/techdocs/guides/j2ee-overview/OverviewTOC.fm.html mientras se completan estos apuntes 5. Servlets & JSP Del modelo de N capas de un sistema cliente/servidor, una de las capas que permite establecer una liga entre un frontend basado en un navegador de Internet y el servidor, es conocido como la capa de WWW. Dicho servicio está compuesto por programas que se integran con el servidor de WWW. Una aplicación de este tipo, se conoce que es un CGI (Common Gateway Interface). Un CGI es un programa que recibe datos de alimentación via protocolo HTTP, ejecuta procesos o lógica, y a partir de eso, genera resultados, que retorna con protocolo HTTP, ya sea que los datos esten en formato HTML u otro. Existen varias soluciones para crear CGIs. Algunas son librerias o APIs que permiten construir CGIs en lenguaje C,C++ o Perl. Tipicamente el modelo de estas librerias es lanzar un proceso al sistema operativo (via la llamada fork() si se habla de UNIX) Otras soluciones mas ligeras, integran librerias dinamicas al propio servidor de WWW. Esas librerias dan la oportunidad de incorporar lenguajes hechos en lenguajes de tercera generación o combinar HTML con lenguajes orientados a generar HTML Dinámico. En el caso de Java, la solución propuesta es una tecnología denominada Servlets. Un Servlet es una clase Java, que se ejecuta en un marco de trabajo o framework y cuya función es atender, procesar y generar resultados usando el protocolo HTTP. Un ejemplo de un servlet es la siguiente clase: /***************************************************************************************** * Hola.java * * Created on August 30, 2002, 12:41 PM */ import javax.servlet.*; import javax.servlet.http.*; /** * * @author gustavo * @version */ public class Hola extends HttpServlet { /** Initializes the servlet. */ public void init(ServletConfig config) throws ServletException { super.init(config); } /** Destroys the servlet. */ public void destroy() { } /** Processes requests for both HTTP GET and POST methods. * @param request servlet request * @param response servlet response */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { response.setContentType("text/html"); java.io.PrintWriter out = response.getWriter(); /* output your page here*/ out.println(""); out.println(""); out.println("Servlet"); out.println(""); out.println("hola mundo servlets
"); out.println(""); out.println(""); out.close(); } /** Handles the HTTP GET method. * @param request servlet request * @param response servlet response */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Handles the HTTP POST method. * @param request servlet request * @param response servlet response */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } /** Returns a short description of the servlet. */ public String getServletInfo() { return "Short description"; } } /*****************************************************************************/ Este programa, correctamente cargado a un servidor que soporta Servlets, debe imprimir en el navegador de WWW, la palabra "hola mundo servlets". Por ejemplo, es posible procesar todos los componentes de una forma HTML: Forma HTML para procesamiento basado en Servlets
Texto
Password
Seleccion
Lista
Area Texto
Radio
Linux
NT
MacOS
CheckBox
campo 1
campo 2
campo 3
Boton
Y el código Java correspondiente, es un servlet /*********************************************************************/ import javax.servlet.*; import javax.servlet.http.*; public class ProcesaForma extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { response.setContentType("text/html"); java.io.PrintWriter out = response.getWriter(); out.println(""); out.println(""); out.println("Servlet"); out.println(""); out.println(""); java.util.Enumeration enum= request.getParameterNames(); while (enum.hasMoreElements()) { String name = enum.nextElement().toString(); out.println(name); String [] multiples = request.getParameterValues(name); if ( multiples != null ) { for (int i=0;i"); out.println(""); out.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { processRequest(request, response); } } /*********************************************************************/ Lo importante del programa es entender que via herencia toma la propiedad de ser un servlet (al ser descendiente de la clase javax.servlet.http.HttpServlet) y la lógica consiste en recibir la petición HTTP, la cual procesa los datos de la forma, via las variables CGI que se reciben. De hecho, el nombre de cada campo de la forma y los parámetros o variables que recibe el servlet coinciden. El programa extrae el valor que envia en cada campo de la forma HTML y en caso de que sea un campo multiple, como un checkbox, lo trata como una lista o arreglo de parámetros. Es importante aclarar que todo el codigo Java es ejecutado en un servidor de aplicaciones Java. El mas conocido y facil de obtener es el creado por la comunidad de Apache, denominado TomCat; que es un servidor de servlets y JSP, y permite ejecutar, administrar y configurar dichas aplicaciones. Con un servlet es posible generar cualquier tipo de respuesta,desde archivos ASCII, paginas HTML, formatos gráficos, etc. Un problema es que para el caso de una pagina HTML, el diseño de la misma queda incluida como parte del código, lo cual hace muy dificil de administrar y separar el trabajo de programadores de aplicaciones WWW y diseñadores de páginas Para resolver este problema, JavaSoft propuso una solución denominada JSP, que permite incluir código HTML con código Java, lo que lleva a un módulo de generación de páginas dinámicas HTML (DHTML). Se nombra a esto como Java Server Pages Un ejemplo de un JSP es el siguiente: <%@page contentType="text/html"%> <%! int i = 0 ; int limite=0; %> JSP Page <% if (request.getParameter("limite") == null ) { %> <%@ include file="alimentacion.html" %> <% } else { try { limite = Integer.parseInt(request.getParameter("limite")); while ( i < limite) { %> <% } } catch (Exception ex) { out.println("Introducir un numero"); } } %>
<% i++; %> <%= i %>
Lo que se debe observar de este ejemplo, es la combinación de código HTML con Java. Un JSP se incluye en un servidor de aplicaciones Java (como TomCat). El proceso que sigue es: ======= =============== ============== Copiar Servidor El JSP queda disponible en JSP compila el JSP el WWW server y es invocado a servidor =====> a Java, generando =====> via un navegador WWW Web un servlet ========== ================== ================== 6. Java Database Connectivity (JDBC) Las bases de datos relacionales son el medio en el cual se almacena la mayoria de la informacion de un sistema. El lenguaje que se utiliza es SQL. Para lograr un nivel de automatización, siempre se ha tratado de integrar SQL con un lenguaje de programación. Las soluciones han sido desde integrar SQL con un lenguaje (SQL Incrustado o Embedded SQL). El caso mas comun es proveer comunicación con un servicio de Sockets o IPC que permita interactuar con la base de datos a manera de un cliente/servidor. El detalle es que cada fabricante de software tiene un protocolo (por ejemplo Oracle, Oracle Callable Interface, Sybase con su TDS) ODBC fue el primer esfuerzo de integrar un API en lenguaje C para tener un acceso uniforme a cualquier base de datos. JavaSoft propuso la idea de integrar Java con ODBC. Surgio el primer esfuerzo, que fue un conjunto de clases, denominadas Java Database Connectivity. El diseño consistio en dar una manera uniforme de programacion para crear objetos que accesaran a la base de datos. La unica variante entre cada base de datos es lo que se conoce como un manipulador o driver de conexion a la base de datos o JDBC Driver. La arquitectura de JDBC es de la siguiente manera: ======= ====== ============ Clases Driver Manejador de JDBC ------- JDBC ----- Base de Datos ======= ====== ============= El Driver JDBC es provisto por terceros desarrolladores y puede tener el siguiente tipo de arquitectura: + JDBC-ODBC driver. Es un puente que traduce todas las invocaciones JDBC a ODBC. Depende de la plataforma de HW/SO donde se ejecuta + JDBC-Java-Nativo. El driver utiliza librerias propietarias del RDBMS, quedando tambien ligado al HW/SO donde se ejecuta + JDBC intermedio. El driver utiliza Java para comunicarse con un servidor central que tiene todos los metodos necesarios para interactuar con la base de datos, pero utilizando librerias del RDBMS. Permite que el cliente sea independiente de la arquitectura de HW/SO + JDBC Java Puro. El driver habla de manera directa con el RDBMS, sin necesitar una libreria nativa. Este modelo es completamente independiente de la arquitectura. La base de datos que se utilizara para este ejemplo se llama HyperSonicSQL, es un manejador de base de datos relacional completamente escrito en Java, que maneja archivos del sistema operativo http://hsql.sourceforge.net Provee un conjunto de clases de Java para administrar el esquema de base de datos, ademas de otorgar el driver JDBC. Los siguientes ejemplos aplican: + Creacion de un esquema de base de datos e Insercion de datos /****************************************************************************/ import java.sql.*; //import all the JDBC classes public class CreateJoltData { static String[] SQL = { "create table JoltData ("+ "programmer varchar (32),"+ "day varchar (3),"+ "cups integer);", "insert into JoltData values ('Gilbert', 'Mon', 1);", "insert into JoltData values ('Wally', 'Mon', 2);", "insert into JoltData values ('Edgar', 'Tue', 8);", "insert into JoltData values ('Wally', 'Tue', 2);", "insert into JoltData values ('Eugene', 'Tue', 3);", "insert into JoltData values ('Josephine', 'Wed', 2);", "insert into JoltData values ('Eugene', 'Thu', 3);", "insert into JoltData values ('Gilbert', 'Thu', 1);", "insert into JoltData values ('Clarence', 'Fri', 9);", "insert into JoltData values ('Edgar', 'Fri', 3);", "insert into JoltData values ('Josephine', 'Fri', 4);", }; public static void main(String[] args) { String URL = "jdbc:hsqldb:/tmp/basedatos/gusdb"; String username = "sa"; String password = ""; try { Class.forName("org.hsqldb.jdbcDriver"); } catch (Exception e) { System.out.println("Failed to load JDBC/ODBC driver."); return; } Statement stmt = null; Connection con=null; try { con = DriverManager.getConnection ( URL, username, password); stmt = con.createStatement(); } catch (Exception e) { System.err.println("problems connecting to "+URL); } try { // execute SQL commands to create table, insert data for (int i=0; i