On Github devill / soa-with-thrift
Created by Rafael Ordog a.k.a. DeVill / @devillsroom
https://github.com/devill/poker-croupier
Single database server
Several million lines of code
Monolitikus alkalmazas
Egy adatbazis
MLOC
It's fine as long as there are really bad parts
Leaking logic to SQL to get better performance is a huge red flag
Preformancia optimalizalas
Logika az SQLben
Running the same process on several threads is hard
Concurrence issues
The amount of communication can explode
Threading nehez
Lockok
Sok kommunikacio
Master/Slave and sharding?
Lot of concurrent queries
Hard to tell which one caused a performance issue
Egy adatbazis
Master/Slave
Sharding
Sok query
Ki okozza a bajt?
A single deployment can break everything
A single query can paralyze the entire DB
Elszabadult fuggosegek
Deployment
Lancreakcio
Csapat felelosseg?
is the idea of breaking up your application into smaller independent applications
Failures and performance issues are localized
Dependent services can fail gracefully
It's easier to monitor performance issues per service
If there is a database issue, it's immediately clear which team should take care of it
Stronger encapsulation
DevOps - Development and operations is taken care of by the same team
It's easier to remove past mistakes
It's less risky to experiment
With a service oriented architecture your hard decisions are also localized
Rewrites almost never work out well
Your hammer ain't gonna make everything a nail
Just be aware of the shiny nickel
Especially if it's similar to something you already have
Each new technology needs to be maintained
Try using different languages
Try using different types of databases
etc...
Each language has it's virtues
Choose the best tool
Use it for server side UI to reduce duplication
Use it for IO heavy application with little logic in it
Use them when fast time to market is important
Use them if business logic is complex but CPU and RAM is unlikely to be a problem
Great for computation heavy applications where scaling up is the only viable option
Although this situation is getting pretty rare
Each database technology has it's virtues
That DOES NOT mean that you should have both Oracle SQL, MySQL and Microsoft SQL Server
It's kind of good at handling unexpected requirements for a wide variety of aggregates
BUT it promotes leaking logic to the database
It's not the best choice for most operational purposes
Really good for storing session related data
Non persistent versions can be used as in-memory cache
Preferable when a data set is presented in the same way many times
Moves the schema to the code, where it belongs
Similar to SQL in that it let's you slice and dice the data freely
Gives a lot more freedom than SQL, and it's a lot better at recursive queries
Horrible at performing similar queries repeatedly
It's perfect for storing a large corpus of un-indexed text
REST APIs work well between different organizations
It's a viable option for in-house communication too
A remote procedure call framework by
Single interface definition can be compiled to many languages
Both server and client works out of the box
enum BinaryOperation { ADDITION = 1, SUBTRACTION = 2, MULTIPLICATION = 3, DIVISION = 4, MODULUS = 5 } struct ArithmeticOperation { 1:BinaryOperation op, 2:double lh_term, 3:double rh_term, } service Calculator { double calc(1:ArithmeticOperation op), }
Note the numbers before each field
They help with versioning
thrift -gen rb calculator.thrift
class CalculatorHandler def calc(val) lh_term = val.lh_term rh_term = val.rh_term case val.op when 1 lh_term+rh_term when 2 lh_term-rh_term when 3 lh_term*rh_term when 4 lh_term/rh_term when 5 lh_term%rh_term end end end
create_server(CalculatorHandler.new).serve
def create_server(handler) processor = Calculator::Processor.new(handler) transport = Thrift::ServerSocket.new(9090) transportFactory = Thrift::BufferedTransportFactory.new() Thrift::ThreadPoolServer.new(processor, transport, transportFactory) end create_server(CalculatorHandler.new).serve
ruby calculator.rb
$client= new CalculatorClient(ThriftProtocol::get()); $aritmectiOperation = new ArithmeticOperation(); $aritmectiOperation->op = BinaryOperation::ADDITION; $aritmectiOperation->lh_term = 25; $aritmectiOperation->rh_term = 10; echo $client->calc($aritmectiOperation) . "\n"; ThriftProtocol::close();
class ThriftProtocol { private static $transport = NULL; private static $protocol = NULL; static function get() { if(self::$protocol == NULL) { self::$transport = new TSocket('localhost', 9090); self::$transport->open(); self::$protocol = new TBinaryProtocol(self::$transport); } return self::$protocol; } static function close() { self::$transport->close(); } }
service AsyncService { oneway void (1:string message), }
You can call it just as you call other functions, but execution will be asynchronous
Thrift has static types versus REST promotes dynamic types
Thrift has versioning support
REST is not a strict standard
The documentation of Thrift is horrible
Server type
Transport
Protocol
Let's suppose we already have tests
It may change later, but you need a starting point
Try to isolate something small and simple first
Until there are shared tables the service would not be independent
In terms of performance this is the most important step anyway
Decide which service will own it
Replicate the data for other service candidates as needed
Once all necessary data is replicated read from the new sources
Dependencies should be pointing in one direction
Each service candidate should only read and write it's own DB
All other DB access should go through the owning module
Also the databases can be on separate machines
This class will become your service gateway
Make sure that only plane old data structures are passed between the two classes
Note that up until now all our tests were kept green
Run the compiler
With the new factory you have an independent service
Your tests will still run with the original factory
There is no out of the box solution to inject a dependency through a remote call
Design your services with that in mind