Álvaro Rodríguez
@alvrod
PayTrue
Características generales de C#
Ejemplos comparados
Observaciones
var zero = 0;var numbers = new[] {1, 2, 3, 4};numbers.Select(x => x / zero).ToList();
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.at Test.Application.<>c__DisplayClass3.<Main>b__2(Int32 x) in F:\Visual Studio
Projects\ScalaVsCs\Program.cs:line 21at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)at Test.Application.Main() in F:\Visual Studio Projects\ScalaVsCs\Program.cs:line 21
wat
Scala
C#
def upperCase(args: Array[String]) { val res = for (a <- args) yield a.toUpperCaseprintln("Arguments: " + res.mkString)
}…upperCase(Array ("a", "b", "c"))
public static void UpperCase(string[] args) {var res = from arg in args select arg.ToUpperInvariant();Console.Out.WriteLine("Arguments: {0}", String.Join("", res));
}…UpperCase(new []{"a", "b", "c"});
Scala
C#
def upperCase(args: Array[String]) { val res = for (a <- args) yield a.toUpperCaseprintln("Arguments: " + res.mkString)
}…upperCase(Array ("a", "b", "c"))
public static IEnumerable<string> UpperCaseYield(string[] args) {foreach (var arg in args) {yield return arg.ToUpperInvariant();
}}
object implicits {implicit def arrayWrapper[A : ClassTag](x: Array[A]) = new { def sort(p: (A, A) => Boolean) = {
util.Sorting.stableSort(x, p); x }
} }
import implicits._ …
Scala
val numbers = Array(2, 3, 1, 4) numbers.sort((x: Int, y: Int) => x < y))
C#public static class implicits {
public static IList<T> MySort<T>(this IList<T> list, Func<T, T, int> func) where T : class {
var temp = list.ToArray();Array.Sort(temp, (x, y) => func(x, y));return temp;
}}
var objects = new[] {"a", "b", "c"}; objects.MySort(String.CompareOrdinal);
Pattern matching
Inmutabilidad
Inferencia de tipos
Locuacidad (DSL)
Expressions everywhere
Option > null, Nullable<T>
trait >>>> interface
case class > auto-properties
macros / compiler-as-a-service > roslyn
Automatic Resource Management Scala, a través de librerías
C#, magia en el compilador
Debatible Madurez
dynamic
async / await
Código nativo
Xamarin
JVM
Scala
CLR
C# tiene bastantes cosas, aunque a veces la sintaxis no es la mejor
F#
“Scala feels closer to Java than F# to C#”
Java-----C#-------Scala-------------------F#
let zero = 0let numbers = [| 1; 2; 3 |]Seq.map(fun x -> x / zero) numbers |> Seq.length
System.DivideByZeroException: Attempted to divide by zero.
at [email protected](Int32 x) <- watat [email protected](b& )at Microsoft.FSharp.Collections.IEnumerator.MapEnumerator`1.System-Collections-IEnumerator-
MoveNext()at Microsoft.FSharp.Collections.SeqModule.Length[T](IEnumerable`1 source)at <StartupCode$FSI_0002>.$FSI_0002.main@()at main@dm()
Stopped due to error
Una breve introducción a la industria de tarjetas de crédito
Por qué el proyecto vale la pena
Visión funcional de la aplicación
MasterCard, VISA, American Express
Esquema descentralizado de cobertura mundial
Iniciativas para nuevos negocios, control de fraude Chip & PIN (EMV), “Verified By Visa”,
etc.
PCI Council PCI DSS
Data Security Standard
Aplicable a ambientes productivos
PA DSS
Aplicable a aplicaciones de pagos
Estándares de seguridad para proteger información sensible de tarjetas y prevenir fraude
Obligatorios para proveedores de servicio, comercios, etc.
Apuntan a impedir el acceso pero también a desvalorizar la información
Nº de tarjeta, tracks, PIN, en claro, “por todos lados”CRM, contabilidad, facturación, etc. etc.
Sistemas no preparados para cumplir con la norma PCI DSS
Enorme costo de adaptación
La norma en sí es cara y difícil de cumplir
El estándar tiene más de 260 requisitos individuales, afectando desde políticas de RRHH hasta el control de qué servicios se ejecutan en cada computadora dentro del “ambiente de tarjetas PCI”
Análisis GAP: 2 meses
Proyecto PCI: 7 meses
Certificación / ajustes: 2 meses
~2 millones U$S
+
200.000 – 500.000+ U$S / añoen auditorías
Cada módulo de software que deba certificarse tiene un sobrecosto enorme por los requisitos técnicos.
Documentación
Detalles de logging, seguridad, estándares
Evidencia de procesos de desarrollo seguro, etc.
Criptografía -> gestión de claves
PCI aplica sólo a “sistemas que almacenan, procesan o transmiten el nº de tarjeta”
La forma más fácil de “certificar PCI” entonces es evitar almacenar, procesar o transmitir el nº de tarjeta
SecureTX es PA-DSS, el resto queda fuera de la norma
Estrategia de reducción del alcance a través de tokenización
def tokenize(card:String): String
5588 3201 2345 6789
->
1000 0000 4365 6789
Desarrollo de middleware tokenizador “Secure TX”
Diseño con Akka
Implementación
Observaciones. Discusión.
“Tokenizer” es un servicio web aparte
“SecureTX switch”es la aplicación que estamos discutiendo
“Authorizer / switch”podría ser un procesador de pagos cualquiera
El esquema de la derecha es minimal, se dan escenarios más complejos
Transaction
storage
Authorizer /
switch
SecureTX
switch
SecureTX
tokenizer
Encrypted card
storage
TX
1
2
34
5
Producto caja, cerrado
Distintos canales
TCP
JMS
Distintos formatos
ISO 8583 en distintos sabores
ASCII largo fijo
Distintos flujos
Camino crítico transaccional
Estabilidad
Performance
Akka
Librería y runtime para desarrollar aplicaciones para la JVM
Altamente concurrentes
Distribuidas
Con tolerancia a fallas
Orientadas a eventos asincrónicos
Modelo de actores
Cada actor tiene un mailbox de eventos: mensajes que procesa secuencialmente
Modelo de ejecución simplificado, fácil de entender y evitar problemas típicos de concurrencia
Modelo de supervisión para tolerancia a fallas
Meta-actores (p.e. para distribución de carga)
Librería para integración de mensajes
“Todos con todos” de canales x formatos
Canales: JMS, Archivos, HTTP, … …
Formatos: Largo fijo, CSV, XML, JSON, etc.
Muy configurable mediante Spring
Rutas, endpoints, etc.
EAI patterns en código o XML
Akka – Camel integra ambas librerías
SecureTX "Switch"
Switch
Tokenizer
PaymentProcessorSender
PaymentProcessorReceiver
ManagementReceiver
ManagementSender
TcpServerA
TcpServerB
TcpClientD
HSM
Tokenizer Service
Authorizer
HSM Server
POS Network
E Gateway
VISA
MasterCard
TcpClientE
TCP, HSM – Akka.IO 2.2 en Scala
Precisamos full-duplex asincrónico, y Camel es one-way o request-reply
Binario + ISO8583 o “ASCII largo fijo” configurable
Tokenizer, PaymentProcessor, Management
Camel
En principio:
Tokenizer por HTTP4
PaymentProcessor, Management por JMS
Pero parte de la gracia de Camel es que esto es muy configurable
class Tokenizer extends Actor with Producer with akka.actor.ActorLogging {
val config = SwApp.appConfigContext.getBean(self.path.name).asInstanceOf[TokenizerBean]
val contextHeader = "TOKENIZER_CONTEXT"
val cardIdResource = "cardid"
val cardNumberResource = "cardnumber"
def endpointUri: String = s"http4://${config.host}:${config.port}"
override def transformOutgoingMessage(msg: Any) = msg match {
case TokenizeRequest(cardNumber, senderContext) => CreateCamelMessage(cardIdResource, cardNumber, senderContext)
case OpenCardRequest(token, senderContext) => CreateCamelMessage(cardNumberResource, token, senderContext)
}
private def CreateCamelMessage(resource:String, data:String, senderContext:Any) = {
val headers = Map(
Exchange.HTTP_URI -> s"$endpointUri/$resource/$data",
Exchange.HTTP_METHOD -> "GET",
contextHeader -> senderContext
)
CamelMessage("", headers)
}
override def transformResponse(msg: Any) = msg match {
case msg: CamelMessage => msg.headerAs[String](Exchange.HTTP_URI) match {
case Success(cardId) if cardId.contains(cardIdResource) => TokenizeResponse(msg.bodyAs[String], msg.headers(contextHeader))
case Success(cardNumber) if cardNumber.contains(cardNumberResource) => OpenCardResponse(msg.bodyAs[String], msg.headers(contextHeader))
case Success(other) => log.error("Unexpected response {} from Tokenizer", other)
case Failure(e) => log.error(e, "Error received from Tokenizer")
}
case akka.actor.Status.Failure(e) => log.error(e, "Error received from Tokenizer")
}
}
2200 LOC Scala
promedio 100 por archivo
1000 LOC Java (HSM)
promedio 145 por archivo
.jar 300 kb
lib 22 mb
“If I have seen further it is by standing on the shoulders of giants”
akka-testkit
ScalaTest
TeamCity no sabe ejecutar tests de Scala
http://youtrack.jetbrains.com/issue/TW-29678
JMeter
En condiciones preliminares e imperfectas
Ambiente local + Oracle de desarrollo
Flujo simplificado
“Enviando” las transacciones a log por Camel (sin JMS)
Todo con la misma tarjeta
~100 tps
A través del “ManagementReceiver” podemos dinámicamente configurar, bajar, reiniciar canales.
Trivial de hacer con Akka
Metrics de @coda
Además enviamos status a través del "ManagementSender"