MySQLdb & client encoding

Opublikowane przez Jarosław Zabiełło Tue, 15 Aug 2006 23:28:00 GMT

[vide: English version]

MySQLdb to główna biblioteka dla Pythona obsługująca bazę MySQL. Baza ta (od werseji 4.1 wzwyż) ma wbudowany wygodny mechanizm translacji znaków jakie mają być wyświetlane dla klienta. Oczywiście, należy założyć, że natywnym formatem danych dla bazy to UTF-8. Za pomocą prostej kwerendy (SET NAMES latin2) można wymusić aby wszelkie napisy były wypluwane do klienta w wybranym formacie (tu: iso-8859-2).

Ostatnio, przy okazji pracy z Pylons, przyjrzałem się bardzo ciekawemu projektowi: SQLAlchemy. Wszystko wskazuje na to, że SQLObject odejdzie do lamusa (tym bardziej że jego twórca coś go porzucił i planuje stworzyć nowy SQLObject2). SQLAlchemy jest nie tylko znacznie poteżniejszym ORM, ale ma także znacznie lepszą dokumentację niż SQLObject – ok. 117 stron samego tylko manuala.

Jedyny problem, jakie to typowe, to brak czytelnej opcji translacji polskich znaków. Podobny problem swego czasu znalazłem w Django (wysłałem patcha i to poprawili dodając na sztywno “SET NAMES utf8”) W wypadku SQLAlchemy nie ma opcji do definicji kodowania po stronie klienta. Manual wspomina tylko o ustawieniu kodowania dla bazy, a to nie to samo. Okazuje, się, że rozwiązanie jest bardzo proste. Podczas tworzenia połączenia do bazy wystarczy użyć dodatkowych parametrów. Nie ma potrzeby bawienia się w żadne kwerendy “SET NAMES”. Owe parametry to use_unicode i charset:

import MySQLdb
conn = MySQLdb.connect(
    user='root', 
    passwd='', 
    db='test', 
    use_unicode=False, 
    charset='cp1250')

W powyższym wypadku, MySQL będzie zakładał że klient ma odebrać teksty w formacie stringów cp1250. Zaś w wypadku użycia “use_unicode=True” zwracane będą śliczne obiekty unicodowe! Przykładowy plik konfiguracyjny dla Pylonsa korzystającego z SQLAlchemy (config/init.py) może zatem wyglądać np. tak:

from sqlalchemy import *
import sqlalchemy.pool as pool
import MySQLdb

def getconn():
    return MySQLdb.connect(
        user='root', 
        passwd='', 
        db='test', 
        use_unicode=False, 
        charset='utf8')

db = create_engine(
    'mysql://root:@localhost/test',
    pool=pool.QueuePool(getconn, pool_size=20, max_overflow=40), 
    strategy='threadlocal')
metadata = BoundMetaData(db)

test_table = Table('test', metadata, autoload=True)

class Test(object):  
    def __str__(self):  
        return self.title  

test_mapper = mapper(Test, test_table) 

W powyższym przykładzie, wymusiłem kodowanie utf8 dla klienta oraz połączenie z bazą działać będzie w puli 20-40 wątków. Właśnie tego mi brakuje w Django: pracy wielowątkowej, bo zużywa ona mniej pamięci. W/w model można użyć w kontrolerze Pylons (controllers/home.py) np. tak:

from myproject.lib.base import *
from myproject.models import *

class HomeController(BaseController):
  def index(self):        
      c.rows = select([test_table.c.id, test_table.c.name]).execute()
      return render_response('/home.myt')

Posted in , ,  | Tagi , , ,  | 4 comments

Spotkanie polskiej grupy RoR w Warszawie 18 sierpnia

Opublikowane przez Jarosław Zabiełło Mon, 14 Aug 2006 15:02:00 GMT

Polska grupa RoR postanowiła się po raz drugi spotkać na żywo. Spotkanie ustalono na 18 sierpnia o godzinie 18:00 w sali konferencyjnej firmy Sec-Bos w Warszawie.

Spotkanie odbedzie się na 99% w przypadku wystąpienia problemów z salą niezwłocznie zostanie dołączona adnotacja.

Polska społeczność RoR chciałaby również podziękować firmie Sec-Bos za pomoc w organizacji spotkania.

Szczegóły na forum http://forum.rubyonrails.pl .

Posted in  | Tagi  | brak comments

Skalowanie RoR

Opublikowane przez Jarosław Zabiełło Sun, 06 Aug 2006 12:54:00 GMT

Mit o rzekomej niskiej wydajności Railsów wydaje się być przesadzony. Interpreter Rubiego jest co prawda wolniejszy od interpretera Pythona (i tym samym Django jest szybsze od Railsów). Ale już porównanie Django, Railsów i pehapowego Symphony pokazuje że Rails jest wyraźnie szybszy od Symphony (który używa PHP5 i akceleratora!). Skoro PHP jest wolniejszy, to z szybkością Railsów nie jest aż tak źle, prawda? Ale to nie wszystko, Rails posiada wygodny mechanizm który pozwala bez trudu powalić na kolana typową aplikację Django.

Siła skalowania poziomego.

Gdy mamy na myśli wydajność całej aplikacji to nie sposób pominąć ważny jej element: skalowalność. Aplikację webową można skalować pionowo (dokładając więcej RAM’u, mocniejszy procesor) lub poziomo (dokładając po prostu kolejny serwer). Ta ostatnia opcja jest szczególnie interesująca, bo dołożenie mocniejszego procesora nie można ciągnąć bez końca.

Ruby on Rails świetnie skaluje się poziomo. Zamiast klasycznego zestawu Lighttpd + kilka procesów fasctgi polecam znacznie lepszy zestaw oparty na procesach mongrel.

Scenariusz 1:

Lighttpd na froncie obsługuje wszystkie statyczne pliki. Pozostałe kieruje do Pound (szybkiego i lekkiego load balancera1), który z kolei kieruje ruch do kilku procesów mongrela (każdy z nich może być nawet na innej maszynie).

Scenariusz 2:

Apache 2.2 na froncie z wbudowanym modułem load balancera rozkłada ruch na kilka procesów mongrela.

Przykład.

Zakładam, że użyję Lighttpd + Pound + 4 procesy mongrela z rozłożonym ruchem na 2 serwery. Fragment pliku konfiguracyjnego dla serwera Lighttpd:

$HTTP["host"] == "domena.host" {
  $HTTP["url"] !~ "^/(cgi-bin|images|stylesheets|javascripts)" {
    proxy.server = ( "/" => ( ( "host" => "127.0.0.1" , "port" => 9000 ) ))
  }
  server.document-root = "/home/httpd/domena.host/public"
  server.indexfiles = ("dispatch.fcgi")
  server.error-handler-404 = "/dispatch.fcgi"
}

Lighttpd obsługuje wszystkie pliki statyczne. Robi to wyjątkowo szybko, lepiej niż Apache. Cały pozostały ruch jest spięty za pomocą proxy z procesem na porcie 9000. Na tym porcie będzie nasłuchiwał Pound, który zajmie się rozkładaniem ruchu na pozostałe serwery. Plik konfiguracyjny dla Pound’a może wyglądać:


User "nobody"
ListenHTTP
  Address 0.0.0.0
  Port    9000
  Service
    BackEnd
      Address 127.0.0.1
      Port    9001
    End
    BackEnd
      Address 127.0.0.2
      Port    9002
    End
    BackEnd
      Address 127.0.0.3
      Port    9003
    End
    BackEnd
      Address 127.0.0.4
      Port    9004
    End
  End
End

Pozostało tylko uruchomić mongrela. Po zainstalowaniu (gem install mongrel), przechodzimy do folderu z naszą aplikacją railsów i odpalamy

mongrel_rails cluster::configure -e production \
    -p 9000 -N 4 -c /opt/ror/mojprojekt -a 127.0.0.1 \
    --user mongrel --group mongrel 

(Parametr -a chroni nas przed możliwością zewnętrznego odpalenia w przeglądarce Railsów działającej bezpośrednio na którymś z procesów mongrela).

Powinien powstać plik: config/mongrel_cluster.yml

---
port: "9000"
environment: production
address: 127.0.0.1
pid_file: log/mongrel.pid
servers: 4

Cluster odpalamy za pomocą

mongrel_rails cluster::start

Dodatkową zaletą używania mongrela jest możliwość wygodnego restartowania dowolnej aplikacji Railsów (a można ich mieć przecież wiele) bez konieczności restartowania serwera HTTP (mongrel_rails cluster::restart)

Poza tym mongrel świetnie nadaje się do zastąpienia Webricka, standardowego serwera HTTP zalecanego do tej pory do pracy developerskiej. Zamiast

ruby script/server
można użyć mongrela:
mongrel_rails start

Domyślnie podniesie się serwer HTTP na porcie 3000 i w trybie development (czyli identycznie jak Webrick). Mongrel jest jednak znacznie szybszy!

Dla użytkowników windozy też jest dobra wiadomość. Mongrel można zainstalować jako usługę windowsów i zarządzać standardowo tak jak inne usługi. Można np. odpalić sobie na jednym porcie Rails w trybie produkcyjnym, a na drugim – w trybie developerskim. W tym drugim wypadku nie trzeba restartować serwera bo zmiany w kodzie automatycznie przeładowują serwerek (tak jak Webrick)

Oczywiście to wszystko ślicznie działa z Capistrano, railsowym systemem automatycznie synchronizującym zmiany na n-serwerach.

Jak dla mnie, mongrel wnosi nową jakość do pracy z Railsami i warto przyjrzeć się mu dokładniej. Nie zdziwiłbym się, jakby następne wersje Railsów standardowo zalecały używanie mongrela, a nie Webricka.

Tak jak siłą Google są tysiące mniejszych maszyn a nie jedna – super szybka, tak nawet najszybszy serwer wyposażony w Apache i mod_python (który używa domyślnie Django) nie ma szans w starciu z clusterem Railsów opartym na procesach mongrel i korzystających z kilku serwerów.

Wykres z artykułu Scaling Rails with Apache 2.2, mod_proxy_balancer and Mongrel

-

1 Dlaczego nie wykorzystywany jest load balancer wbudowany w Lighttpd? Bo jest kiepsko zaimplementowany i są problemy z restartem procesów. Dlatego zalecany jest zewnętrzny load balancer lub użycie najnowszej wersji Apache 2.2 który ma ten moduł dobrze zaimplementowany.

Posted in  | Tagi  | 15 comments

RadRails 0.7, Aptana, Typo4

Opublikowane przez Jarosław Zabiełło Tue, 01 Aug 2006 03:09:00 GMT

Pojawiła się nowa wersja RadRails 0.7. Niestety, trochę się pośpieszono. Zawiera wiele błędów. Lepiej poczekać na następną wersję.

Pojawił się bardzo ciekawy edytor oparty na Eclipse: Aptana. Służy głównie do pracy z HTML, CSS i JavaScript. Warto jednak obejrzeć załączone filmy pokazujące w jaki sposób edytor podpowiada kod i uzupełnia metody, bo robi to lepiej niż Dramweaver 8.

Pojawiła się kolejna, stabilna wersja aplikacji do tworzenia bloga opartej na Ruby on Rails – Typo 4.

Posted in  | Tagi , ,  | brak comments