Версия для печати

Поднимаем XML веб-сервис на Пайтоне
1. Python:

В системе должен быть установлен python, версии не ниже 2.3

2. Устанавливаем apache ( httpd-2.2.8 ):

$ ./configure  --prefix=/usr/local/apache2-soap \
--with-mpm=prefork \
--enable-so \
--enable-mods-shared="actions alias asis auth rewrite ssl" \
--disable-userdir \
--disable-cgi \
--disable-include \
--disable-autoindex
$ make
$ make install

Здесь, в общем-то, обычная установка apache. И если у вас уже есть apache, можно использовать его. Здесь я попытался собрать «лёгкий» апач, с минимумом модулей. Использовать можно как apache 1.3, так и apache 2.0 или 2.2

3. Устанавливаем mod_python ( mod_python-3.3.1 )

$ ./configure \
--with-apxs=/usr/local/apache2-soap/bin/apxs \
--without-flex
$ make
$ make install

Обратите внимание, что mod_python 3.3.1 только для apache версии 2.0 и 2.2. Если у вас apache 1.3, используйте mod_python 2.7.11

4. Устанавливаем ZSI ( The Zolera Soap Infrastructure ):

Берём ZSI не ниже версии ZSI-2.1-a1, ZSI-2.0 имеет баги. Установка либо традиционным для python образом:

$ python ./setup.py install

Или можно использовать уже собранный пакет ZSI для python нужной версии, например так:

$ easy_install http://heanet.dl.sourceforge.net/sourceforge/pywebsvcs/ZSI-2.1_a1-py2.4.egg

5. Конфигурируем Apache:

Конфигурируем Apache и виртуальный хост по вкусу.

Если предположить, что скрипты для обработки запросов будут расположены тут: /usr/local/apache2-soap/soapagent, то добавляем Location для обработки веб-сервиса:

<Location /soapagent>
  SetHandler mod_python
  PythonPath "['/usr/local/apache2-soap/soapagent']+sys.path"
  PythonHandler soapagent_handler
# PythonAutoReload On
# PythonDebug On
</Location>

Oпиции PythonAutoReload On и PythonDebug On удобно использовать при отладке, но для продакшин использования лучше выключить.

Если у вас apache 1.3 с mod_python 2.7.11, то конфигурация Location будет немного иной:

<Location /soapagent>
  SetHandler python-program
  PythonPath "['/usr/local/apache2-soap/soapagent']+sys.path"
  PythonHandler soapagent_handler
# PythonAutoReload On
# PythonDebug On
</Location>

6. Конвертируем WSDL в python методы:

При установке ZSI в системе должен появиться скрипт, который умеет конвертировать WSDL файлы в python методы. Это делается так:

$ wsdl2py http://kocmuk.ru/files/2008/05/soapagent.wsdl

Конвертация возможна как с удалённого wsdl, так и с локального:

$ wget http://kocmuk.ru/files/2008/05/soapagent.wsdl
$ wsdl2py ./soapagent.wsdl

В результате получаем три файла: soapagent_client.py, soapagent_server.py, soapagent_types.py. На основе soapagent_client.py можно сделать клиента, но для сервера достаточно soapagent_server.py, soapagent_types.py. Эти два скрипта нужно положить туда же, где, как мы договорились, будут расположены скрипты для обработки запросов, т.е. в /usr/local/apache2-soap/soapagent

Для данного примера WSDL файл можно взять тут:http://kocmuk.ru/files/2008/05/soapagent.wsdl

Обратите внимание, что URL, на котором будет жить веб-сервис указан внутри wsdl. В данном случает он такой: location="http://localhost:8080/soapagent". Потому веб-сервис надо поднимать именно на этом URL или изменить location в wsdl на требуемый. А так же, хорошо бы сам WSDL выложить в общий доступ, что бы клиенты могли его свободно получать и использовать.

7. Скрипты для обработки XML запросов:

Исходя из договорённости выше, выкладываем их в: /usr/local/apache2-soap/soapagent

Набор скриптов:
soapagent_handler.py — это mod_python handler, который выполняет начальную подготовку и начинает диспетчеризацию XML сервисов.

###### soapagent_handler.py ##########

from ZSI import dispatch
from mod_python import apache              

import soapagent_services  as ss

mod = __import__('encodings.utf_8', globals(), locals(), '*')
mod = __import__('encodings.utf_16_be', globals(), locals(), '*')              
def handler(req):
    # Опция "PythonAutoReload On" из конфига mod_python не перегружает вложенные модули.
    # В стадии разработки можно добавить принудительню перезагрузку модуля сервисов при каждом запросе.
    # ВНИМАНИЕ! Это будет происходить при КАЖДОМ запросе! Для продакшин использования это надо выключить!
    #reload(ss)
    dispatch.AsHandler(modules=(ss,), request=req)
    return apache.OK

soapagent_services.py — непосредственно скрипт с реализованными обработчиками.

###### soapagent_services.py ##########
from soapagent_server import *

# svc: SUM
#
def SUM(ps):
    aVal = ps['aVal']
    bVal = ps['bVal']              

#   Здесь мы выполняем какие-то действия
    cVal = aVal + bVal              
#   А здесь мы возвращаем результат
    response = SUMResponse()
    response._result = cVal
    return response

8. Запускаем Apache:

$ /usr/local/apache2-soap/bin/apachectl start

Конфигурация сервера получилась такая:

Server: Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8d mod_python/3.3.1 Python/2.4.3

9. Для теста можно выполнить клиенский XML запрос:

$ python
>>> from ZSI import ServiceProxy
>>> service = ServiceProxy.ServiceProxy('http://localhost:8080/wsdl/soapagent.wsdl')
>>> service.SUM(aVal=1, bVal=2)
{'result': 3}

Предполагается, что WSDL файл доступен по адресу: http://localhost:8080/wsdl/soapagent.wsdl

В качестве frontend web-сервера можно использовать и сам python. В библиотеке ZSI кроме mod_python диспетчера «AsHandler» есть еще: AsServer, AsCGI, AsJonPy. Так что можно выбрать по вкусу.

Я выбрал apache. Причины таковы:

  • возможность гибко управлять доступом к сервисам (ACL, парольный доступ, SSL и прочее)
  • многопоточность получившейся конструкции
  • проверенный и надёжный web-сервер, который удобен и знаком обслуживающим его администраторам
  • и всё это при достаточно простой схеме установки, настройки и обслуживании ПО

Комментарии (18) на запись “Building a Web Services in Python”

  1. const пишет:

    В примере Apache использует MPM prefork. Опыт показывает, что даже не очень навороченное питоновское приложение легко может занять 20M памяти. И так на каждый апачевский процесс. Если использовать MPM worker, то память здорово экономится. Но надо учесть, что питоновский интерпретатор сможет обрабатывать два запроса параллельно только тогда, когда один из них занят операцией ввода-вывода (например, ждёт ответа от СУБД). Золотую середину можно поискать в конфигурации типа M worker'ов по N потоков. И в таком случае надо быть осторожным с глобальными объектами: они общие на все потоки в одном worker'е. Либо нужно от них отказаться, либо заворачивать их в threading.local.

  2. kocmuk.ru пишет:

    Всё именно так! И это надо учитывать. Спасибо за комментарий, const.

    Исходя из моего железа и предполагаемой нагрузки на это приложение — MPM prefork мой выбор. Apache в режиме MPM worker и multithreading-safe приложение — отдельный разговор.

  3. Slach пишет:

    а вы случайно не делали SOAP клиентов на Python с использованием WS Security?

  4. kocmuk.ru пишет:

    Привет.

    WS Security, когда безопасность на уровне самих сервисов — к сожалению, не делал. А вот безопасность на уровне транспорта (SSL, https) + Basic-аутентификация или аутентификация по SSL сертификатам — делал. ZSI отлично справляется с этим как клиент, а apache — как сервер. Вот так оно примерно выглядит.

  5. Valerikk пишет:

    Что-то не получается с сервером Апач :(

    Пишет:

    Traceback (most recent call last):

    File "", line 1, in

    File «build/bdist.linux-i686/egg/ZSI/ServiceProxy.py», line 324, in __call__

    File «build/bdist.linux-i686/egg/ZSI/ServiceProxy.py», line 300, in call_closure

    File «build/bdist.linux-i686/egg/ZSI/client.py», line 452, in Receive

    File «build/bdist.linux-i686/egg/ZSI/client.py», line 416, in ReceiveSOAP

    TypeError: Response is «text/html», not «text/xml»

    Где рыть?

  6. kocmuk.ru пишет:

    Я бы помотрел для начала вот это: Response is «text/html», not «text/xml»

    Это значит, что пришёл не XML ответ, а значит что-то не так. Можно включить трейс запросов и ответов через tracefile=sys.stdout. Или через tcpdump посмотреть запросы и ответы. Думаю, что там (в ответе) будет более понятная диагностика.

  7. Bayasaa пишет:

    У меня такая же ошибка

    Я добавил print client.py а там действительно хтмл:

    >>> service = ServiceProxy.ServiceProxy ('http://localhost/wsdl/soapagent.wsdl')

    >>> service.SUM (aVal=1, bVal=2)

    **************************

    None

    **************************

    False

    **************************

    301 Moved Permanently

    Moved Permanently

    The document has moved here.

    Apache/2.2.8 (Ubuntu) mod_python/3.3.1 Python/2.5.2 Server at localhost Port 80

    **************************

    Traceback (most recent call last):

    File "", line 1, in

    File «build/bdist.linux-x86_64/egg/ZSI/ServiceProxy.py», line 324, in __call__

    File «build/bdist.linux-x86_64/egg/ZSI/ServiceProxy.py», line 300, in call_closure

    File «build/bdist.linux-x86_64/egg/ZSI/client.py», line 459, in Receive

    File «build/bdist.linux-x86_64/egg/ZSI/client.py», line 423, in ReceiveSOAP

    TypeError: Response is «text/html», not «text/xml»

    >>>

    Я так и не понял откуда это

  8. kocmuk.ru пишет:

    Это от того, что вместо ответа Апач прислал вам редирект «301 Moved Permanently». Смотрите на конфигурацию Апача, что-то там сконфигурено не так, как надо.

  9. Bayasaa пишет:

    http.conf:

    SetHandler mod_python

    PythonPath ['/var/www/soapagent']«+sys.path»

    PythonHandler soapagent_handler

    PythonAutoReload On

    PythonDebug On

    soapagent_handler.py находится в /var/www/soapagent/ здесь же находиться

    soapagent_client.py

    soapagent_handler.py

    soapagent_server.py

    soapagent_services.py

    soapagent_types.py

    soapagent.wsdl находиться в /var/www/wsdl/ и в нём адрес

    У меня Убунту 8.04 Апаче 2.2 Python 2.5

    Я протестировал mod_python он работает отлично

  10. Bayasaa пишет:

    Буду очень признателен если поможете разобраться

  11. kocmuk.ru пишет:

    Сначала стоит посмотреть в ваш localhost/wsdl/soapagent.wsdl. А именно в секцию вида:

    <soap :address location="http://xxxxxxxxxxxxxxxxxxxxx">

    </soap>

    Именно этот URL вызывается при обращении в сервису. Далее я бы сделал: wget xxxxxxxxxxxxxxxxxxxxx и посмотрел ответ Апача. Скорее всего он должен ответить вам тем же, что вы привели выше: «301 Moved Permanently» А далее, смотря на этот URL и на ваш конфиг Апача, понять что не так.

    Выше вы привели часть вашего http.conf. Но из неё, к сожалению, много не понятно. То что вы привели находится прямо в основном конфиге или принадлежит какому-то вирт.хосту? Этот soapagent_handler принадлежит всему хосту или какому-то locaton? Соответствует ли этот location URL-у, указанному в вашем wsdl?

  12. Bayasaa пишет:

    с помошью wget localhost/wsdl/soapagent.wsdl получаеться скачать файл

    В http.conf-е находиться

    SetHandler mod_python

    PythonHandler mod_python.testhandler

    SetHandler mod_python

    PythonPath ['/var/www/soapagent']«+sys.path»

    PythonHandler soapagent_handler

    PythonAutoReload On

    PythonDebug On

    mpinfo работает так работает если его заменить другим питоновским файлом с «Hello World»-ом

    VirtalHost находиться в другом файле

    /etc/apache2/sites-available/default

    И если я правильно понял soap_handler принадлежить основному хосту

  13. Bayasaa пишет:

    не получаеться писать с тегами

    --Location /mpinfo

    SetHandler mod_python

    PythonHandler mod_python.testhandler

    --Location

    --Location /soapagent

    SetHandler mod_python

    PythonPath ['/var/www/soapagent']«+sys.path»

    PythonHandler soapagent_handler

    PythonAutoReload On

    PythonDebug On

    --Location

    service name="soapagent"

    port name="soapagentPort" binding="tns:soapagentPort"

    soap:address location="http://localhost/soapagent"

    /soap:address

    /port

    /service

  14. kocmuk.ru пишет:

    Что происходит при: wget localhost/soapagent Особенно обратите внимание происходит ли редирект на: localhost/soapagent/ (слэш в конце).

  15. Bayasaa пишет:

    Получаеться вот что:

    bayasaa@bayasaa-desktop:~$ wget localhost/soapagent/

    --23:55:37-- localhost/soapagent/

    => `index.html'

    Resolving localhost... 127.0.0.1

    Connecting to localhost|127.0.0.1|:80... connected.

    HTTP request sent, awaiting response... 500 Internal Server Error

    23:55:39 ERROR 500: Internal Server Error.

    А на браузере:

    Traceback (most recent call last):

    File «/usr/lib/python2.5/site-packages/mod_python/importer.py», line 1537, in HandlerDispatch

    default=default_handler, arg=req, silent=hlist.silent)

    File «/usr/lib/python2.5/site-packages/mod_python/importer.py», line 1202, in _process_target

    module = import_module (module_name, path=path)

    File «/usr/lib/python2.5/site-packages/mod_python/importer.py», line 304, in import_module

    return __import__ (module_name, {}, {}, ['*'])

    File «/var/www/soapagent/soapagent_handler.py», line 6, in

    import soapagent_services as ss

    File «/var/www/soapagent/soapagent_services.py», line 2, in

    from soapagent_server import *

    File «/var/www/soapagent/soapagent_server.py», line 38, in

    class soapagent (ServiceSOAPBinding):

    File «/var/www/soapagent/soapagent_server.py», line 50, in soapagent

    root[(SUM.typecode.nspname,SUM.typecode.pname)] = 'soap_SUM'

    AttributeError: class SUM has no attribute 'typecode'

  16. Bayasaa пишет:

    На браузере вот это:

    File «/usr/lib/python2.5/site-packages/mod_python/importer.py», line 1537, in HandlerDispatch

    default=default_handler, arg=req, silent=hlist.silent)

    File «/usr/lib/python2.5/site-packages/mod_python/importer.py», line 1229, in _process_target

    result = _execute_target (config, req, object, arg)

    File «/usr/lib/python2.5/site-packages/mod_python/importer.py», line 1128, in _execute_target

    result = object (arg)

    File «/var/www/soapagent/soapagent_handler.py», line 12, in handler

    dispatch.AsHandler (modules=(ss,), request=req)

    File «build/bdist.linux-x86_64/egg/ZSI/dispatch.py», line 271, in AsHandler

    ps = ParsedSoap (request)

    File «build/bdist.linux-x86_64/egg/ZSI/parse.py», line 64, in __init__

    self.dom = self.reader.fromStream (input)

    File «/usr/lib/python2.5/xml/dom/expatbuilder.py», line 928, in parse

    result = builder.parseFile (file)

    File «/usr/lib/python2.5/xml/dom/expatbuilder.py», line 211, in parseFile

    parser.Parse ("", True)

    ExpatError: no element found: line 1, column 0

  17. kocmuk.ru пишет:

    Замените в вашем WSDL вот это:

    soap:address location="http://localhost/soapagent"

    на это:

    soap:address location="http://localhost/soapagent/"

    (слэш в конце добавить)

    И попробовать вызвать сервис клиентом.

  18. Bayasaa пишет:

    Ураа. Получился

    Спасибо

    Я первый раз имею дело с SOAP-ом, так что не обесудьте за ламерство

    Теперь перейпу на 2ую часть

Оставить комментарий


Антиспам-картинка