RPC: Remote Procedure Calls LSUB GSYC 13 de abril de 2016 (cc) 2015 Laboratorio de Sistemas, Algunos derechos reservados. Este trabajo se entrega bajo la licencia Creative Commons Reconocimiento NoComercial - SinObraDerivada (by-nc-nd). Para obtener la licencia completa, véase http://creativecommons.org/licenses/. También puede solicitarse a Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. Las imágenes de terceros conservan su licencia original. RPC I Llamada a un procedimiento que ejecuta en otro espacio de direcciones. I Objetivo: enmascarar un llamada remota como una llamada a un procedimiento local. I Sigue el modelo cliente/servidor, petición-respuesta. I Pueden ser sı́ncronas o ası́ncronas. I Middleware: capa de software que permite realizar RPCs ocultando los detalles de la comunicación y de la distribución: transparencia de acceso y de distribución. ecución RPC de una RPC PC involucra una serie de etapas que se ejecutan tanto en la máquina que realiza omo en la máquina que ejecuta el procedimiento remoto (servidor). Figura 2: Pasos en la ejecución de una RPC RPC: fallos ¿Y si el servidor falla mientras se está haciendo? Hay distintas semánticas: I At most once: la operación se ejecuta como mucho una vez, pero puede que ninguna. Si se retorna error, no se puede saber si se ha realizado la operación o no. I At least once: la operacion se ejecuta al menos una vez, puede que varias. Si la operación es idempotente no hay problema. I Exactly once: la operación se realiza exactamente una vez. En general, es muy complicado (mecanismos de tolerancia a fallos). RPC: Nombrado I Necesitamos nombrar los recursos para poder acceder a ellos (p. ej. hacer una RPC sobre un recurso concreto). I Transparencia de localización: permite encontrar los recursos con independencia de su localización fı́sica. I El servicio puede ser centralizado o distribuido. I El esquema de nombrado puede ser plano o estructurado. Ejemplo de resolución de nombres: DHT I Una tabla hash distribuida es un servicio de nombres planos. Como en una tabla hash, a una clave le pertenece un valor asociado. I La tabla hash puede estar distribuida en una red P2P (lo veremos más adelante). Nombres estructurados I Los nombres estructurados forman un espacio de nombres (name space) I Generalmente se representan como un grafo dirigido con nodos contenedores y nodos hoja, I Los objetos se pueden nombrar con nombres absolutos o relativos. I La resolución puede ser iterativa o recursiva. Ejemplo de resolución de nombres: DNS I El DNS es un servicio distribuido de nombres estructurados para traducir nombres a direcciones IP. I Ya habéis estudiado en profundidad DNS. (cc) George Shuklin Resolución de nombres iterativa (c) A. Tanenbaum Resolución de nombres recursiva I Reduce los mensajes en el cliente (puede afectar a la latencia en gran medida) y facilita el caching. (c) A. Tanenbaum RPC: Serialización Serialización o aplanado: I Necesitamos transmitir los datos por la red con un formato dado. I Datos binarios (enteros, floats, etc.), texto, colecciones, referencias a otros objetos... I ¿Interoperabilidad? Serialización en Java I Cliente y servidor (ambos en Java) se pueden despreocupar del formato de los datos. Problema: ambos deben ser programas en Java1 . I Una clase que cumpla con la interfaz Serializable se puede serializar. Esa interfaz no tiene ningún método. I Aplana el objeto y todos los objetos referenciados (web of objects). I Todas las clases involucradas tienen que implementar Serializable. 1 o el componente ejecutando en otra plataforma debe usar una biblioteca de compatibilidad con Java. Serialización en Java I Hay que poner una constante serialVersionUID a la clase. Esta constante sirve para verificar en el desaplanado que las clases que tiene el receptor son compatibles con las clases que tiene el emisor. Debemos cambiarla si modificamos la estructura de la clase. I Si un miembro es transient no forma parte del estado persistente del objeto y no se serializa. I Tampoco se serializan los miembros de clase (static). I Si un objeto no se puede serializar, se levanta la excepción NotSerializableException. Código F i g u r e s = new S q u a r e ( p1 , p2 ) ; ObjectOutputStream os = new O b j e c t O u t p u t S t r e a m ( s o c k . g e t O u t p u t S t r e a m ( ) ) ; os . w r i t e O b j e c t ( s ) ; Serialización en Java Las clases que necesitan personalizar la serialización deben redefinir ciertos métodos como: p r i v a t e void w r i t e O b j e c t ( j a v a . i o . ObjectOutputStream out ) throws IOException private void readObject ( java . io . ObjectInputStream in ) throws IOException , ClassNotFoundException ; Serialización I Pero las máquinas del sistema distribuido pueden ser heterogéneas (hardware y software). I En este caso, tiene que haber una representación canónica para los datos. I Cada extremo debe convertir los datos de su formato local al formato canónico y viceversa: serialización (marshalling o aplanado). I Podemos definir nuestra propia serialización o usar uno de los múltiples estándars: XDR (Sun rpc), JSON, ASN.1, XML-RPC, etc. Ejemplo de serialización: XML I Es un formato de texto. I Bastante enrevesado. <? xml v e r s i o n=” 1 . 0 ” e n c o d i n g=”UTF−8” s t a n d a l o n e=” no ” ?> <SOAP−E N V: En ve lo pe SOAP−E N V : e n c o d i n g S t y l e=” h t t p : // schemas . x m l s o a p . o r g / s o a p / e n c o d i n g / ” xmlns:SOAP−ENV=” h t t p : // schemas . x m l s o a p . o r g / s o a p / e n v e l o p e / ” xmlns:SOAP−ENC=” h t t p : // schemas . x m l s o a p . o r g / s o a p / e n c o d i n g / ” x m l n s : x s i=” h t t p : //www . w3 . o r g /1999/XMLSchema−i n s t a n c e ” x m l n s : x s d=” h t t p : //www . w3 . o r g /1999/XMLSchema”> <SOAP−ENV:Body> <n s 1 : d o u b l e A n I n t e g e r x m l n s : n s 1=” u r n : M y S o a p S e r v i c e s ”> <param1 x s i : t y p e =” x s d : i n t ”>123</ param1> </ n s 1 : d o u b l e A n I n t e g e r> </SOAP−ENV:Body> </SOAP−EN V :En ve lo pe> Ejemplo de serialización: JSON I Es un formato de texto. I En general, más sencillo que XML. Permite aplanar: I I I Una colección de pares atributo/valor con el que se pueden serializar objetos, records, diccionarios, etc. Lista de valores con el que se pueden aplanar arrays, listas, conjuntos, etc. Ejemplo de serialización: JSON Objeto: { ” rectangulo ” : { ” v i s i b l e ” : true , ” color ” : ” verde ” , ” supIzq ” : { ”x” : 34 , ” y ” : 30 }, ” infDer ” : { ”x” : 43 , ” y ” : 10 } } } Ejemplo de serialización: JSON Lista: { ”listaCoor”: [ {”x” : 0 , ”y” : 0} , {”x” : 2 , ”y” : 1} , {”x” : 4 , ”y” : 5} , {”x” : 0 , ”y” : 1} , { ” x ” : −1 , ” y ” : 23} ] } Serialización Para realizar nuestra propia serialización tenemos dos alternativas: I Texto. Factores a tener en cuenta: I I I Codificación (p. ej. UTF-8). Terminación de las cadenas. Binario. Factores a tener en cuenta: I I I Longitud de los datos. Endianess. Formato (p. ej: coma flotante IEEE 754, IEEE 754-2008, etc.). Serialización: endianess c imagen OOO Program Verification Systems Serialización: texto vs binario Ejemplo: serializar un array de 3 enteros : 1, -2, 3. I Texto (UTF-8): "[[1], [-2], [3]]" I Binario (little endian, 4 bytes por entero, complemento a 2): 0x03 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0xfe 0xff 0xff 0xff 0x03 0x00 0x00 0x00 Ejemplo de serialización: aplanar un Integer positivo Formato de red: little-endian, 32-bit. private final static int INTSIZE = 4 ; p u b l i c s t a t i c byte [ ] m a r s h a l l ( i n t b y t e r [ ] = new b y t e [ INTSIZE ] ; r [ 3 ] = ( b y t e ) ( i >>24 & 0 x f f ) ; r [ 2 ] = ( b y t e ) ( i >>16 & 0 x f f ) ; r [ 1 ] = ( b y t e ) ( i >>8 & 0 x f f ) ; r [ 0 ] = ( byte ) ( i & 0 x f f ) ; return r ; } i ) throws Exception { Ejemplo de serialización: desaplanar un Integer Formato de red: little-endian, 32-bit. private final static int INTSIZE = 4 ; p u b l i c s t a t i c i n t g e t I n t ( B y t e B u f f e r b ) throws Exception { int n; i f ( b . c a p a c i t y ( ) < INTSIZE ) throw new E x c e p t i o n ( ”Bad a r r a y l e n g t h : must ” + ” be g r e a t e r o r e q u a l t o ” + INTSIZE ) ; b . o r d e r ( B y t e O r d e r . LITTLE ENDIAN ) ; n = b. getInt (); return n ; } Ejemplo de serialización: aplanar un String Formato de red: UTF-8, tamaño + null-terminated. public byte int byte static [] b = size = [] r = byte [ ] m a r s h a l l ( S t r i n g s ) throws Exception { s . g e t B y t e s ( ”UTF−8” ) ; b . length + 1; new b y t e [ s i z e+INTSIZE ] ; // a p l a n a m o s e l tamano como a r r a y de b y t e s y // l o c o p i a m o s a l a r r a y de b y t e s de d e s t i n o . System . a r r a y c o p y ( m a r s h a l l ( s i z e ) , 0 , r , 0 , INTSIZE ) ; // c o p i a m o s l o s b y t e s de l a s t r i n g a l a r r a y de d e s t i n o // a p a r t i r de donde n o s quedamos ( INTSIZE ) . System . a r r a y c o p y ( b , 0 , r , INTSIZE , s i z e −1); r [ INTSIZE+s i z e −1] = ( b y t e ) 0 ; return r ; } Ejemplo de serialización: desaplanar un String Formato de red: UTF-8, tamaño + null-terminated. p u b l i c s t a t i c S t r i n g g e t S t r ( B y t e B u f f e r b ) throws Exception { int sz = b . getInt ( ) ; b y t e b u f [ ] = new b y t e [ s z − 1 ] ; /∗ i g n o r e t h e n u l l c h a r ∗/ b . get ( buf ) ; b . g e t ( ) ; /∗ s k i p t h e n u l l c h a r ∗/ r e t u r n new S t r i n g ( buf , ”UTF−8” ) ; } Ejemplo de serialización: aplanar un array de Integer p u b l i c s t a t i c byte [ ] m a r s h a l l ( i n t [ ] a r r ) throws Exception { b y t e r [ ] = new b y t e [ INTSIZE + INTSIZE ∗ a r r . l e n g t h ] ; i n t pos = 0 ; // s i m i l a r a l a p l a n a d o de l a s t r i n g a n t e r i o r System . a r r a y c o p y ( m a r s h a l l ( a r r . l e n g t h ) , 0 , r , pos , INTSIZE ) ; p o s += INTSIZE ; f o r ( i n t i : a r r ){ System . a r r a y c o p y ( m a r s h a l l ( i ) , 0 , r , pos , INTSIZE ) ; p o s += INTSIZE ; } return r ; } Ejemplo de serialización: desaplanar un array de Integer public s t a t i c Integer [ ] getIntArr ( ByteBuffer b) throws Exception { I n t e g e r n el em = g e t I n t ( b ) ; i f ( ne le m < 0 ) throw new E x c e p t i o n ( ”Bad a r r a y l e n , n e g a t i v e ” ) ; I n t e g e r [ ] a r r = new I n t e g e r [ n el em ] ; f o r ( i n t i = 0 ; i < n el em ; i ++) arr [ i ] = getInt (b ); return arr ; }