Archive for maj, 2006

Książki o zarządzaniu projektami i projektowaniu systemów

piątek, maj 26th, 2006 by Paweł Rutkowski

Poniżej kilka książek które polecam:

Szczególnie polecam dwie pierwsze pozycje. “Dom wariatów” opisuje zagadnienia związane z projektowaniem systemów pod kątem ludzkim - jakie problemy mogą stanąć przed użytkownikami i czym są spowodowane. Mała uwaga: Alan Cooper mniej więcej od połowy ksiązki pokazuje swoje bardzo negatywne nastawienie do programistów - dla mnie było ono bardzo denerwujące bo przy każdej możliwej okazji rozpisywał się jacy “trudni” są programiści.

Drugą pozycja jest o dziwo opowieścią fabularną której akcja dotyczy tworzenia zaawansowanego projektu dla pewnej korporacji. Czyta się to naprawde dobrze ponieważ liczba technikaliów jest ograniczona do minimum.

Być poetą kodu: pisanie serwera w Ruby a refaktoring

piątek, maj 26th, 2006 by Paweł Rutkowski

Zanim zacznę cokolwiek opisywać, wyjaśnię pojęcie “refaktoring“. Jest to proces króry odnosi się głównie do projektowania i programowania systemów. Istotą tego procesu jest wprowadzanie zmian w projekcie, które nie rozszerzają funkcjonalności projektu ale podnoszą jego standard architektoniczny. Dokonywuje się modyfikacji różnych funkcji, obiektów czy algorytmów w celu zwiększenia ich przejżystości (czy w przypadku programowania obiektowego - hermetyczności obiektów). Zazwyczaj ten proces jest pomijany ze względu na dodatkowy czas jaki trzeba poświęcić na jego przeprowadzenia, a większość managerów nie widzi takiej potrzeby - skoro coś działa to po co to zmieniać.

Jak pisałem serwer

Ostatnio pisałem serwer sieciowy który miał przyjąć pewne dane, przetworzyć je i zwrócić odpowiedni status. Zadanie nie było specjalnie trudne, gdyby nie fakt iż przewidziano dowolne rozszerzanie procedur przetwarzania danych.

Ponieważ była to pierwsza aplikacja serwerowa jaką pisałem w Ruby wyszedłem z założenia że najlepiej będzie napisać ją metodą przyrostową. Dzięki temu odrazu mogłem testować funkcjonalność i nanośić szybko modyfikacje. Wadą było oczywiście to że model systemu był nie do końca dobrze przemyślany (np.: system komunikatów przesyłanych pomiędzy klientem a serwerem).

Pierwszą działającą wersję miałem dość szybko. Była w stanie nasłuchiwać na zadanym gnieździe, odbierać jakieś dane i odsyłać odpowiedzi. Tutaj trafiłem na pierwszy problem. Dopóki komunikacja polegała na przesyłaniu pojedyńczych lini tekstu, to nie było problemu (funkcje puts i readline). Ale w momencie gdy potrzebowalem przesłać nie znanej długości tekst pojawił się problem. Mogłem odczytywać dane w pętli aż do napotkania jakiegoś znacznika, ale znacząco to wpływało na wydajność serwera ( nie wiem dlaczego). Zamiast tego zdecydowałem się wysyłać komunikat że przygotowałem do wysłania X bajtów i serwer po odczytaniu tego komunikatu odczytywał z gniazdka ilość bajtów przekazanych przez klienta.

Następnym problemem była obsługa kilku połączeń jednocześnie. Wypróbowałem kilka metod o których dowiedziałem się z doskonałej książki “UNIX. Programowanie usług sieciowych - tom 1 - API: gniazda i XTI” i najwydajniesza okazała się metoda “preforking” z accept( w sumie to dziwne, ale jeszcze to sprawdze). Zaimplementowanie roznych metod, ich benchmark, tuning zajeło mi troche czasu, ale nie było bardzo trudne.

Następnym krokiem było przetwarzanie informacji. To była już prostsza rzecz ponieważ tego typu parsery pisałem wcześniej w PHP, Pythonie ale też i Ruby.

Jak doszedłem do refaktoringu

Miałem już cały serwer. Spojżałem na jego kod i sie przeraźiłem. W kilku miejscach zduplikowana funkcjonalność, kod który bardziej przypominał programowanie strukturalne
niż obiektowe. Nie mogłem tego tak zostawić, więc wziąłem się za przepisywanie kodu. Rozplanowałem sobie nową strukture klas, strukture komunikatów, wszystkie potrzebne modele. Wydzieliłem także trzy podstawowe moduły:

  • Server - którego zadaniem była inicjalizacja gniazdek i pre-forking
  • Worker - jego zadaniem była obsługa komunikacji z klientem
  • Processor - on odpowiadał tylko za przetworzenie danych

Pierwsze rzeczą która mnie zachwyciła to fakt iż refaktoring w Ruby jest bardzo prosty. Wystarczyło przekopiować odpowiednie kawałki kodu do odpowiednich klas i dokonać ich drobnej modyfikacji.

Drugą natomiast jak błyskawicznie poprawiła się czytelność kodu. Wykorzystałem praktycznie większość zalet programowania obiektowego, przez co każdy obiekt operował na innym. Dało mi to wielką swobodę i pozwoliło uproscić także kod klienta poprzez współdzielenie niektórych modeli.

Ile czasu zajął mi refaktoring ? Myślę że całość zajeła mi ok 3h. Długo ? Raczej długo, natomiast architektura jaką sobie stworzyłem pozwala mi teraz na praktycznie dowolne modyfikowanie kodu które nie będzie wpływało znacząco na sam serwer.

Celem refaktoringu jest właśnie uproszczanie struktury programu i podnoszenie stanardu kodu. Aby móc to docenić trzeba być “poetą kodu który nie tylko zwraca uwagę na to czy kod działa, ale także czy jest on prosty, czytelny, zrozumiały i funkcjonalny. Nie znam zbyt wielu programistów którzy potrafili by to docenić.

Na sam koniec pozwolę sobie odnieść się trochę do biznesu. Niestety nie zawsze mamy czas i pieniądze żeby “tkać złotą nić, błękitnego kodu“. Jeżeli mamy ograniczony czas i budżet powinniśmy na samym początku projektu przyjąć pewien standard kodowania i wymagać trzymania się jego przez programistów. Natomiast refaktoring przeprowadzać tylko dla krytycznych częsci systemu które podejżewam że będą w przyszłości wymagały modyfikacji. Ja osobiście staram się przewidzieć w budżecie koszty refaktoringu, ponieważ jednak uważam się za “poetę kodu”.

UPDATES

26/05/2006 16:28

Sprawdziłem jeszcze raz różne modele obsługi równoczesnych połączeń i jednak wydajniejsze są wątki (Thread). Ruby na FreeBSD źle obsługuje mechanizm Copy-On-Write przez co każdy forkowany proces zabiera bardzo dużo pamięci (szczególnie że użyłem ActiveRecord). Tak więc 5 procesów RoR zabierało bagatela 100MB pamięci.

Dużo lepszym rozwiązaniem okazało się użycie wątków. Przeprowadziłem szybki refaktoring w wyniku czego otrzymałem (w uproszczeniu) taki kod:


# w @server mamy odpowiedni (TCP|UDP|UNIX)Server
loop {
Thread.start(@server.accept) { |socket|
Worker.run(socket)
}
}

Proste do implementacji i wydajne. Zmiany w stosunku do mojego poprzedniego kodu sprowadzały się w zasadzie do wymiany fork na Thread.start.

Zasoby dotyczące RubyOnRails

sobota, maj 20th, 2006 by Paweł Rutkowski

Poniżej zamieszczam blogów które dotyczą Ruby On Rails. Poniższa list jest wyciągnięta z mojego czytnika RSS więc część stron może być mało aktywna. Kolejność jest przypadkowa:

Jeżeli chodzi o usenet polecam grupę comp.lang.ruby. Istnieje także lista dyskusyjna rails@lists.rubyonrails.org choć moim zdaniem jest przeznaczona raczej dla początkujących.

Co do dokumentacji to polecam:

Chętnie zapoznam się z waszymi propozycjami odnośnie stron na które warto zaglądać w poszukiwaniu informacji o Ruby On Rails.

Jakie problemy stwarzają Access Control List (ACL) w systemach operacyjnych ?

niedziela, maj 7th, 2006 by Paweł Rutkowski

ACL - co to jest

W systemach uniksowych / linuksowych prawa dostępu do pliku czy katalogu są podzielone na 3 kategorie:

  • Właściciel pliku/katalogu
  • Grupa
  • Pozostali

Każdej z tych kategorii można przypisać tylko 3 prawa dostępu:

  • odczyt
  • zapis
  • wykonywanie

Jak widać system ten jest prosty - wręcz bardzo prosty. Pomimo tego że takie było założenie, aby prawa dostępu były proste (co ułatwiało audyt, czyli sprawdzenie ich poprawności), obecnie w wielu sytuacjach są one nie wystarczające, lub też znacznie komplikujące życie.

Zalety ACL

Naszczęście zauważono iż taki system w niektórych zastosowaniach jest niewystarczający i powstał standart POSIX.1e określający mechanizm ACL - Access Control List. Pozwala on na przypisanie wielu użytkowników lub grup do danego pliku/katalogu i określenie odzielnych uprawnień dla każego z nich. Dzięki temu administrator czy użytkownik nie są ograniczeni do trzech podstawowych kategorii. Pozwala to na lepsze zarządzanie uprawnieniami.

Weźmy przykładowo taki plik:

-rw-r-----  1 userblog  groupblog  976 May  7 17:39 plik

Jego właścicielem jest użytkownik userblog, przypisana do niego grupa to groupblog, rozmiar pliku wynosi 976 bajtów i został utworzony 7 maja o 17:39. Uprawnienia dla właściciela pliku (rw-) pozwalają na jego odczyt i zapis, zaś grupa (r–) może tylko odczytać ten plik. Pozostali użytkownicy (—) nie mają żadnych praw do niego.

Gdyby użytkownik userblog chciał nadać prawa użytkownikowi alfa do odczytu i zapisu a użytkownikowi beta do odczytu i wykonywania a cały czas uniemożliwić innym użykownikom dostęp do pliku - było by to nie możliwe. W przypadku nadania odpowiednich ACL listing tego pliku się odrobine zmieni:

-rw-r-----+  1 userblog  groupblog  976 May  7 17:39 plik

Zwróć uwagę na plus pojawiający się po uprawnieniach - oznacza on że plik ma zdefiniowaną ACL. Oto jak ona wygląda:

#file:plik
#owner:userblog
#group:groupblog
user::rw-
user:alfa:rw-
user:beta:r-x
group::r--
other::---

Jak widać pojawiły się wpisy dotyczące nowych użytkowników. Gdyby nie ACL taki problem byłby nie do obejścia.

Access Control List niosą ze sobą jednak pewne problemy.

Problemy związane z wykorzystaniem ACL

Pierwszym i podstawowym problemem związanym z Access Listami jest backup. Nie wszystkie narzędzia kopiują je poprawnie - chociażby podstawowy GNU tar. Powoduje to iż dane zarchiwizowane narzędziem które nie sprawdza ACL po odzyskaniu nie będą ich zawierać. Może to skutecznie utrudnić życie administratorom systemów, jeżeli będą chcieli je odzyskać “empirycznie”. Jeżeli chodzi o narzędzia które obsługują ACL to polecam: Rdiff-backup, dump, star (S tar)

Drugim problemem jest wcześniej wspomniany audyt uprawnień. Access Control List pozwalają na praktycznie dowolne definiowanie list dostępu. Ponieważ nie są one wyświetlane w standardowym listingu katalogu, łatwo je pominąć co może prowadzić do ujawnienia danych. Dodatkowo standart POSIX.1e wspiera tzn dziedziczenie uprawnień czyli ich przejmowanie z nadrzędnych katalogów, co może utrudnić ich sprawdzenie.

Trzeci problem to sposób pisania oprogramowania. Wielu programistów zamiast spróbować zapisać plik, najpierw sprawdza uprawnienia ręcznie. Stwarza to sytuację w której plik może być udostępniony do zapisu poprzez Access Liste natomiast jego natywne uprawnienia na to nie pozwalają. Jeżeli programista ręcznie sprawdza (po prawach uniksowych) czy plik można zapisać okaże się że nie, podczas gdy fizycznie jest to możliwe. O ile w przypadku własnych programów łatwo można je przerobić o tyle aplikacje zewnętrzne mogą wymagać dużych patchy.

Czwartym problemem jest dostępność obsługi ACLi. O ile są one dostępne na Linuksie, FreeBSD czy Solarisie to w innych systemach może ona nie być wprowadzona. Trzeba to wziąć pod uwagę projektując systemy multiplatformowe.

Wnioski

Osobiście testowałem ACL na FreeBSD 5.x oraz FreeBSD 6.x (na systemie plików UFS2 ). Znacznie on uprościł implementacje kilku funkcji, ponieważ mogliśmy udostępnić zapis czy odczyt różnych plików bez uciekania się do trików z sudo czy natywnymi modułami kernela modyfikującymi kontrolę dostępu (jednym z takich modułów jest dazuko).

Jedyny problem jaki się pojawił to archiwizacja - gdyby nie jeden z administratorów który ucześniczył w projekcie nie zapytał:

Czy jesteś pewien że dodatkowe atrybuty filesystemu lądują w zwykłym tarze ?

to pewnie do dziś byśmy spokojnie spali w przeświadczeniu że kopia zapasowa danych jest kompletna.

Jest to napewno mechanizm który warto poznać. Czy używać ? Moim zdaniem tak, jednak należy się nauczyć zwracać uwagę na “+” przy listowaniu katalogu i sprawdzić czy Twoje narzędzia do archiwizacji danych obsługują ACLe.