Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Comment développer un serveur métier en python/C++

763 views

Published on

Quelles sont les problématiques d'interfaçage, les avantages/inconvénients des langages, la stratégie de code, etc. dans le développement d'un serveur métier ?

Published in: Software
  • Be the first to comment

  • Be the first to like this

Comment développer un serveur métier en python/C++

  1. 1. Un serveur métier en Python / C++ Meet-up C++ 02/10/2014 Pierre Marquis & Alexandre Bonnasseau
  2. 2. Plan • Retour d’expérience • En pratique, quelques lignes de code
  3. 3. L'équipe dev carto Info trafic Itinéraire Plan Moteur de recherche géolocalisée Moteur de suggestion Géolocalisation Itinéraire transports en commun
  4. 4. Python WTF ??!
  5. 5. Python WTF ??! - Simplicité de code - Framework web léger - Richesse des modules - Facilité d'industrialisation
  6. 6. Notre philosophie - Python quand on peut - C++ quand c’est nécessaire
  7. 7. Chemin de l’équipe ● Fonctions plus atomiques ● Test U ● Lint ● Dojo ● Pair programming ● Code review
  8. 8. Schéma d’archi du plan
  9. 9. Chaîne d’industrialisation
  10. 10. Aujourd’hui + 80% des serveurs migrés + Langage très intéressant + Framework mieux maîtrisé + Dev plus rapide + Plus d’agilité - Moins de maîtrise C++ - Multithread - Débogage binding
  11. 11. En pratique : appeler du C++ depuis Python
  12. 12. Cadre – Existence d’une base de code C++ fiable et testée – On souhaite prototyper rapidement (besoin mouvant) – Besoin d’exécution rapide pour des fonctions critiques
  13. 13. L'existant #define _USE_MATH_DEFINES #include <cmath> #include "geo.hpp" #include <sstream> #include <stdexcept> string geocoord2string(double angle) { if ( (angle > 180) || (angle < -180) ) throw invalid_argument("Invalid argument : angle must be beetween -180° and 180°"); int deg = int(floor(angle)); double rest = angle - deg; int minute = int(floor( rest * 60)); rest = rest * 60 - minute; int second = int(floor( rest * 60)); ostringstream result; result << deg << "° " << minute << "' " << second <<"''"; return result.str(); } double deg2rad(double deg) { return (deg * M_PI / 180); } GeoPoint::GeoPoint(double lat, double lng) : lat(lat), lng(lng) {} double GeoPoint::distance(const GeoPoint &other) { double nDLat = deg2rad(other.lat - this->lat); double nDLon = deg2rad(other.lng - this->lng); double thisLatRad = deg2rad(this->lat); double otherLatRad = deg2rad(other.lat); double nA = pow ( sin(nDLat/2), 2 ) + cos(thisLatRad) * cos(otherLatRad) * pow ( sin(nDLon/2), 2 ); double nC = 2 * atan2( sqrt(nA), sqrt( 1 - nA )); double nD = EARTH_RADIUS * nC; return nD; // Return our calculated distance }
  14. 14. Objectif Utiliser les fonctions C++ en python : $ python Python 2.7.3 >>> import geo >>> print geo.geocoord2string(5.36) 5° 21’ 36‘’ Projet complet disponible sur github : https://github.com/Mappy/un-serveur-metier-en-python-cpp
  15. 15. Un module python écrit en C++ D'après : https://docs.python.org/2/extending/extending.html geomodule.cpp #include "Python.h" #include "geo.hpp" static PyObject * geocoord2string_py(PyObject *self, PyObject *args) { double angle = 0; if (!PyArg_ParseTuple(args, "d", &angle)) return NULL; string res = geocoord2string(angle); return Py_BuildValue("s", res.c_str()); } // La liste des fonctions qu'on expose en Python static PyMethodDef geoMethods[] = { { "geocoord2string", geocoord2string_py, METH_VARARGS, "Convert a latitude or a longitude as an angle in a string in the form : d° m' s''." }, { NULL, NULL, 0, NULL } /* Sentinel */ }; // La fonction initnomdumodule est appelée par l'interpréteur Python // à l'import du module PyMODINIT_FUNC initgeo(void) { (void) Py_InitModule("geo", geoMethods); }
  16. 16. Avec Boost.Python ? geomudule.cpp #include <boost/python.hpp> #include <boost/python/module.hpp> #include "geo.hpp" using namespace boost::python; BOOST_PYTHON_MODULE(geo) { def("geocoord2string", geocoord2string); class_<GeoPoint>("GeoPoint", init<double, double>()) .def("distance", &GeoPoint::distance) … C’est tout ! ; }
  17. 17. Avec Boost.Python, on peut – Exposer des fonctions C++ en Python – Exposer des classes C++ en Python – Choisir quelles méthodes exposer pour une classe – Utiliser Boost::optional pour gérer les paramètres optionnels
  18. 18. Packager une extension Python Le fichier setup.py s’occupe de la compilation : from distutils.core import setup, Extension geo_module = Extension('geo', include_dirs = ['/usr/local/include',], library_dirs = ['/usr/local/lib',], extra_compile_args=['-std=c++11'], sources = ['geomodule.cpp', 'geo.cpp']) setup (name = 'Geo', version = '1.0', description = 'A geo package from Mappy', author = 'LBS team', author_email = 'lbs@mappy.com', url = 'http://github.com/mappy', long_description = ‘A demo package with geo functions', ext_modules = [geo_module]) Installer avec : python setup.py install Ou packager : python setup.py bdist
  19. 19. Focus : Gestion de la mémoire – Un code C++ qui conserve une référence sur un objet Python doit appeler la macro Py_INCREF() pour s'assurer que l'objet ne sera pas détruit – Il faut appeler Py_DECREF() pour libérer l'objet
  20. 20. Focus : Gestion des erreurs Le code suivant lève une exception C++ : import geo print geo.geocoord2string(181) Sans Boost.Python : terminate called after throwing an instance of 'std::invalid_argument' what(): Invalid argument : angle must be beetween -180° and 180° Avec Boost.Python : Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Invalid argument : angle must be beetween -180° and 180° Boost.Python permet également d'affiner la gestion des erreurs en associant des erreurs spécifiques Python aux exceptions C++
  21. 21. Un serveur de distance géographiques geoserveur.py from bottle import route, run, template from geo import GeoPoint @route('/distance/<lat1:float>,<lng1:float>/<lat2:float>,<lng2:float>') def index(lat1, lng1, lat2, lng2): dep = GeoPoint(lat1, lng1) arr = GeoPoint(lat2, lng2) return template("Distance = {{distance}}", distance=dep.distance(arr)) run(host='localhost', port=8888) A lancer par : python geoserver.py Tester l’url : http://localhost:8888/distance/48.85,2.35/43.30,5.38
  22. 22. Une autre approche : Producteur / Consommateur – Le code python peut appeler des méthodes C++ à distance – Par exemple via un bus RabbitMQ ou bien des appels distants ZMQ – Au final, le code Python délègue l‘exécution des sections critiques à des workers C++
  23. 23. Les difficultés en Python quand on vient du C++
  24. 24. Typage dynamique – En C++ le typage statique offre une première validation du code – En Python, on ne détecte les erreurs de type qu'à l'exécution !  il faut exécuter le code pour le tester  les tests unitaires sont indispensables !
  25. 25. Threads – Le Global Interpreter Lock (GIL) assure que les traitements concurrents ne se marchent pas dessus : https://docs.python.org/2/glossary.html#term-global-interpreter-lock http://dabeaz.blogspot.fr/2010/01/python-gil-visualized.html – Les modules thread / threading – encapsulent les primitives système de thread – fournissent des primitives de synchronisation : verrous, sémaphores, etc. – ... Mais à aucun moment deux instructions peuvent être exécutées en même temps à cause du GIL !  Mieux vaut utiliser multiprocessing
  26. 26. Mémoire partagée – Le module mmap (Memory-mapped file) permet de partager de la mémoire entre plusieurs process – On peut accéder à cette mémoire comme un fichier en lecture / écriture – On accède à cette mémoire comme un tableau de char – Mais on ne peut pas utiliser de types complexes (listes, dictionnaires, objets définis sur mesures)  Pas aussi souple qu’en C++
  27. 27. Conclusion
  28. 28. Où va Mappy ? – Amélioration continue de l’outillage – Nouveaux services asynchrones en Python – … Et bientôt un nouveau serveur d'itinéraire en Python / C++ ?
  29. 29. Merci Enjoy : http://fr.mappy.com/ Pierre Marquis : pierre.marquis@mappy.com Alexandre Bonnasseau : https://github.com/abonnasseau

×