Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 779 сообщений
Cообщения с меткой

perl - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

Приглашаем на YAPC::Russia 2016

Четверг, 23 Июня 2016 г. 14:18 (ссылка)





В субботу 25 июня в московском офисе Mail.Ru Group пройдёт ежегодная конференция YAPC::Russia, посвящённая языку программирования Perl и его сообществу. В разные годы она проходила в Москве, Киеве и Санкт-Петербурге. На конференцию съезжаются Perl-профессионалы из разных городов и стран, чтобы обменяться опытом, завести новые знакомства в своей сфере и пообщаться с единомышленниками в неформальной и приятной обстановке. Программа встречи ждет вас под катом.



10.00 — сбор гостей



11.00 — Елена Шишкина, Mail.Ru Group. Валидаторы сложных структур, или как мы делали новый API



11.45 — Ксения Боброва, ACI Worldwide. Continious integration Perl кода с Jenkins и Gerrit

В этом докладе будет рассказано, как применить Gerrit Code Review и Jenkins для автоматизации доставки Perl-кода с локальной машины разработчика на продакшен.



12.30 — Николай Мишин, «Мастер-Домино». Использование Eclipse в качестве редактора Perl



12.55 — Евгений Вансевич, Mail.Ru Group. Анализ памяти в Perl. Tips&Tricks

Рано или поздно возникает ситуация, когда приложение начинает занимать много памяти, или начинает течь. Доклад Евгения посвящён тому, что делать в подобных ситуациях, и как их избежать. Будут рассмотрены инструменты анализа памяти в Perl, начиная от того, чем занята память процесса, оценкой оверхеда по данным, заканчивая поиском утечек памяти. Полученные знания будут применены на практике.



14.00 — ОБЕД



14.30 — Алексей Мележик, EPAM Systems. Sparrow — повторно используемые сценарии автоматизации

Sparrow — система распространяемых многоцелевых сценариев, позволяющая быстро создавать и повторно использовать любые сценарии автоматизации. Область применения: деплоймент, управление конфигурациями, автоматическое тестирование, аудит и мониторинг. Вы создаёте свои сценарии и загружаете их в виде плагинов в центральный репозитарий SparrowHub, а другие могут их использовать. Система написана на Perl5. Сценарии (плагины) пишутся на Perl, Bash, Ruby — на ваш выбор. Существует внутренний DSL для верификации работы сценариев (Outthentic::DSL). Отчёты сценариев предоставляются в виде TAP. Система может использоваться самостоятельно с помощью консольного клиента, а также легко интегрируется (используя другие средства как «транспорт») с существующими системами управления конфигурациями, такими как chef, ansible, rex.



15.15 — Алексей Машанов, Mail.Ru Group. Perl быстрее, чем Perl

Применение интерпретируемых языков, таких как Perl, для разработки сложных высоконагруженных Web-сервисов порой ставит перед разработчиками непростые задачи. Во-первых, необходимо успевать отвечать на запросы клиентов за комфортное для пользователя время (не более 300 мс), а во-вторых, обрабатывать все входящие запросы используя минимальное количество серверов. Из-за этих двух факторов задачи, которые зачастую решаются, например, на плюсах, на Perl реализуются с большим скрипом. В докладе речь пойдёт о возможных эволюционных/революционных способах улучшения производительности проекта, преимущественно написанного на Perl.



15.40 — Павел Лиморенко, Mail.Ru Group. Мониторинг: демон, который читает логи

Представьте, что у вас на машине запущено множество сервисов, каждый из которых постоянно (и интенсивно) что-то пишет в /var/log/service_name.log. Как уследить за их работой? И ведь хочется это делать в реальном времени! А теперь усложним задачу: у вас ферма из 500 таких машин. И 50 коллег, причём каждый хочет отыскивать в логах что-то свое. Мы в Почте Mail.Ru научились с этим справляться и написали на Perl удобный инструмент, принципом работы которого хотим поделиться с вами.



16.25 — Николай Мишин, «Мастер-Домино». Реализация паттерна «Наблюдатель» на Moose



16.50 — Иван Панченко, Postgres Professional. Как асинхронно работать из Perl с PostgreSQL

Замечательный AnyEvent::HTTP::Server многоуважаемого Монса позволяет строить очень компактные и производительные перловые микросервисы. Как-то раз мне нужно было поднять такой сервис в качестве интерфейса к PostgreSQL (даже к нескольким). Конечно, существуют хорошие альтернативные неперловые решения — C2H5ОН или просто модуль ngx_postgres. Но, как известно, Perl гибче. С PostgreSQL из Perl все привыкли работать с DBI — а для асинхронной работы он не годится. Оказывается, есть альтернатива. В этом коротком докладе будет показано, как с помощью AnyEvent строить асинхронные микросервисы, внутри которых — PostgreSQL.



17.15 — КОФЕ-БРЕЙК



17.30 — Михаил Озеров, Mail.Ru Group. Реализация конечного автомата для работы со сложными сценариями платежных систем (и не только)



18.15 — Андрей Еньшин, Mail.Ru Group. Perl method invocation



18.40 — Lighting Talks



20.00 — After Party



Для всех, кто не сможет присутствовать, мы организуем прямую видеотрансляцию.



Участие бесплатное, но необходимо зарегистрироваться.



Адрес: Москва, Ленинградский пр. 39, стр. 79, офис Mail.Ru Group.



До встречи!

Original source: habrahabr.ru.

https://habrahabr.ru/post/303856/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Разработка Docker контейнеров с помощью системы многоцелевых сценариев Sparrow

Четверг, 02 Июня 2016 г. 14:28 (ссылка)

В этой статье я хотел бы рассказать как можно создавать сценарии сборки имиджей для Docker контейнеров с помощью системы многоцелевых сценариев Sparrow*.



Читать дальше →

https://habrahabr.ru/post/302278/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Вышла версия 2.0 Perl плагина для IntelliJ IDEA

Четверг, 26 Мая 2016 г. 19:32 (ссылка)






Стала доступна для загрузки вторая версия Perl плагина для IDE от JetBrains. В этой версии появилась последняя из крупных фич, которую я хотел реализовать — отладчик.

Читать дальше →

https://habrahabr.ru/post/301910/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

«Код выигрывает споры». С днем рождения, Марк

Суббота, 14 Мая 2016 г. 15:54 (ссылка)

«На самом деле хакинг — это просто способ сделать что-то быстро или проверить границы того, что может быть сделано. Как и многое другое, его можно использовать во благо и во вред, но подавляющее большинство хакеров, которых я встречал, — идеалисты, желающие оставить положительный след в мире.»

— Марк Цукерберг, «Путь Хакера»



imageСегодня день рождения Марка Цукерберга (14 мая 1984). Каким он был программистом?




  • В 11 лет написал «домашний мессенджер» ZuckNet, чтоб не бегать по дому, а посылать сообщения родным через компьютер.

  • В старших классах написал Synapse Media Player — прообраз Last.fm. Алгоритм понимал музыкальные предпочтения пользователей Winamp. Microsoft and AOL предложили выкупить программу и устроиться к ним на работу. Но Марк выложил ее в свободный доступ.

  • CourseMatch помогал студентам Гарварда выбрать курсы. Он отображал сколько студентов уже записались на курсы, а так же показывал, на какие курсы записался конкретный студент.

  • FaceMash — «пузырьковая сортировка» людей по привлекательности. Он показывал 2 фотки и предлагал выбрать более симпатичную. Хабраэффект от этого сервиса обрушил сетку Гарварда. Марку выкатили выговор за то, что он использовал фотки людей без их согласия.





«Если ты ничего не разрушаешь, ты, вероятно, растешь не слишком быстро.»

— Марк Цукерберг



Под катом — «исходный код» раннего Фейсбука, письмо Марка инвесторам «Путь хакера», парочка скриншотов и видосы его лекций в Росссии.





image

В школьные годы занимался компьютерным программированием, разработал сетевую версию игры «Риск».



image

Synapse Media Player



image

«Вовочка, встань и выйди»



image

THEfacebook



Was this written by Mark Z.?
#!/usr/bin/perl



use Mysql;
use strict;
use vars qw($school_name);
use vars qw($pass);

require "./cgi-lib.pl";
#do "./school_name.pl";
do "../password.pl";

my (%input, $text, $field);
&ReadParse(\%input);

my @rawCookies = split (/; /,$ENV{'HTTP_COOKIE'});
my %cookies;

foreach(@rawCookies){
my ($key, $val) = split (/=/,$_);
$cookies{$key} = $val;
}

my $id = $input{id};
my $user = $input{user};
my $code = $input{code};
my $course = 0;#$input{course};
my @node;
my @edge;
#my $db_data = Mysql->connect("69.28.179.12", "login", "mark", $pass);
my $db_data = Mysql->connect("$cookies{host}", "$cookies{db}", "mark", $pass);
my $map;
if ($ENV{'HTTP_HOST'} =~ m/^(.*)\.thefacebook\.com/) {
$map = $1;
}
my $cookie_host = $cookies{host};
my $cookie_db = $cookies{db};
my $sql = "SELECT * FROM school_data where map='$map'";
my $retval = $db_data->query($sql);
my %rs = $retval->fetchhash();
my $host = $rs{ip};
my $dbname = $rs{db};
#my $sql = "INSERT INTO viz (user,map,host,db) VALUES ('$user', '$map','$host', '$dbname')";
#$db_data->query($sql);
my $db = Mysql->connect($host, $dbname, "mark", $pass);
#my $db = Mysql->connect("69.28.179.11", "facebook", "mark", $pass);
my $retval; my $sql;
my %privacy;
my $num_degrees = 1;
my @already_expanded;
my %cs;

sub morph {
my ($number) = @_;
return ((((($number % 7) * 13) % 17) * 19) % 23);
}

sub share_course {
my ($user1, $user2) = @_;
$sql = "SELECT count(*) as count FROM course as course1, course as course2 WHERE " .
"course1.id = '$user1' AND course2.id = '$user2' AND course1.course_id = course2.course_id";
$retval = $db->query($sql);
my %rs = $retval->fetchhash();
return $rs{count};
}

sub is_one_degree {
my ($user1, $user2) = @_;
$sql = "SELECT count(*) as count FROM friend WHERE user1 = '$user1' AND user2 = '$user2'";
$retval = $db->query($sql);
my %rs = $retval->fetchhash();
return $rs{count};
}

sub is_two_degrees {
#my ($user1, $user2) = @_;
#$sql = "SELECT count(*) as count FROM friend as f1, friend as f2 " .
# "WHERE f1.user1 = '$user1' AND f1.user2 = f2.user1 AND f2.user2 = '$user2'";
#my %rs = $retval->fetchhash();
#return $rs{count};
return 0;
}

sub is_three_degrees {
#my ($user1, $user2) = @_;
return 0;
}

sub can_see {
my ($user, $id, $type) = @_;
if ($user eq $id) { return 1; }
$sql = "SELECT house, year, " . $type . "_domain, " . $type . "_type, " .
$type . "_allow FROM info WHERE info.id = '$id'";
$retval = $db->query($sql);
my %control = $retval->fetchhash();
my $allow = $control{$type . '_allow'};
my $domain = $control{$type . '_domain'};
my $privacy_type = $privacy{$type};
if (($domain eq "" or ($privacy{email} =~ m/$domain$/)) and
($control{$type . '_type'} =~ m/-$privacy_type/)) {
if ($allow =~ m/-1/) {
$cs{$id} = 1;
return 1;
} elsif (($allow =~ m/-2/) and $privacy{year} eq $control{year}) {
$cs{$id} = 1;
return 1;
} elsif (($allow =~ m/-3/) and $privacy{house} eq $control{house}) {
$cs{$id} = 1;
return 1;
} elsif (($allow =~ m/-4/) and share_course($user, $id)) {
$cs{$id} = 1;
return 1;
} elsif (($allow =~ m/-5/) and is_one_degree($user, $id)) {
$cs{$id} = 1;
return 1;
} elsif (($allow =~ m/-6/) and is_two_degrees($user, $id)) {
$cs{$id} = 1;
return 1;
} elsif (($allow =~ m/-7/) and is_three_degrees($user, $id)) {
$cs{$id} = 1;
return 1;
}
}
return 0;
}

sub find_node {
my ($id, $type) = @_;
for (my $i = 0; $i < @node; $i++) {
if ($node[$i]{id} eq $id and $node[$i]{type} eq $type) {
return $i;
}
}
return -1;
}

sub find_connections {
my ($this, $degree, $is_course) = @_;
my ($type, $type_id);
if (!$is_course) {
$already_expanded[@already_expanded] = $this;
$sql = "SELECT * FROM friend WHERE user1 = '$this' LIMIT 0, 200";
$type = "friend";
$type_id = 1;
} else {
$sql = "SELECT * FROM course WHERE course_id = '$this'";
$type = "course";
$type_id = 0;
}
my $return = $db->query($sql);
while (my %row = $return->fetchhash()) {
my $new_id;
if (!$is_course) {
# get the new id from the friend pair
#if ($row{user1} eq $this) {
# $new_id = $row{user2};
#} else {
# $new_id = $row{user1};
#}
$new_id = $row{user2};
} else {
$new_id = $row{id};
}
if ($degree > $num_degrees or $cs{$new_id} or can_see($user, $new_id, $type)) {
# see if the id is already a node; if not, add it
my $node_exists = 0;
for (my $i = 0; $i < @node and !$node_exists; $i++) {
if ($node[$i]{id} eq $new_id and $node[$i]{type} eq 1) {
$node_exists = 1;
}
}
if (!$node_exists and $degree <= $num_degrees) {
my $next_node = @node;
$node[$next_node]{id} = $new_id;
$node[$next_node]{type} = 1;
}
# see if there's an edge between the two already; if not, add it
if ($node_exists or $degree <= $num_degrees) {
my $edge_exists = 0;
my $this_index = find_node ($this, $type_id);
my $new_index = find_node ($new_id, 1);
for (my $i = 0; $i < @edge and !$edge_exists; $i++) {
if (($edge[$i][0] eq $this_index and $edge[$i][1] eq $new_index) or
($edge[$i][0] eq $new_index and $edge[$i][1] eq $this_index)) {
$edge_exists = 1;
}
}
if (!$edge_exists) {
my $next_edge = @edge;
$edge[$next_edge][0] = $this_index;
$edge[$next_edge][1] = $new_index;
}
}
# if we want to consider friends farther out, do so now
if ($degree < $num_degrees + 1) {
my $expansion_exists = 0;
for (my $i = 0; $i < @already_expanded and !$expansion_exists; $i++) {
if ($already_expanded[$i] eq $new_id) {
$expansion_exists = 1;
}
}
if (!$expansion_exists) {
if (!$is_course or can_see ($user, $new_id, "friend")) {
find_connections ($new_id, $degree + 1, 0);
}
}
}
}
}
}

sub identify_nodes {
for (my $i = 0; $i < @node; $i++) {
if ($node[$i]{type}) {
$sql = "SELECT name FROM info WHERE id = '" . $node[$i]{id} . "'";
} else {
$sql = "SELECT name FROM course_list WHERE id = '" . $node[$i]{id} . "'";
}
$retval = $db->query($sql);
my %row = $retval->fetchhash();
$node[$i]{name} = $row{name};
}
}

if ($code == &morph($user) and &can_see($user, $id, "friend")) {
# figure out what's going into the graph
$sql = "SELECT email, house, year FROM info WHERE info.id = '$user'";
$retval = $db->query($sql);
%privacy = $retval->fetchhash();
$node[0]{id} = $id;
if ($course) {
$node[0]{type} = 0;
} else {
$node[0]{type} = 1;
}
find_connections ($id, 1, $course);
identify_nodes ();
# generate the graph file
my $o;
my $outfile = "/tmp/thefacebook-$id-graph-" . time();
open $o, "> $outfile";
# headers
print $o "graph g {\n";
print $o "start=\"yes\"\n";
print $o "size=\"20,20\"\n";
print $o "page=\"20,20\"\n";
print $o "maxiter=1000\n";
print $o "resolution=100\n";
print $o "center=true\n";
print $o "bgcolor=white\n";
print $o "title=\"A Graph\"\n";
# nodes
print $o "node [shape=box,fontname=\"Tahoma\",style=filled]\n";
for (my $i = 0; $i < @node; $i++) {
my $name = $node[$i]{name};
$name =~ s/[^A-Z0-9'. ]//gi;
my ($red, $green, $blue);
do {
$red = int(rand() * 201);
$green = int(rand() * 201);
$blue = int(rand() * 56) + 200;
} while ($red + $green + $blue < 400);
my $extra = "";
if ($i eq 0) {
$extra = ",fontsize=32,label=\"$name\"";
} else {
$extra = ",fontsize=24,label=\"$name\"";
}
printf $o "n$i [color=\"#%02x%02x%02x\"$extra]\n", $red, $green, $blue;
}
# edges
print $o "edge [len=8,color=\"#555555\"]\n";
for (my $i = 0; $i < @edge; $i++) {
my $name1 = $node[$edge[$i][0]]{name};
my $name2 = $node[$edge[$i][1]]{name};
print $o "n$edge[$i][0] -- n$edge[$i][1] [dir=both,weight=1]\n";
#print $o "$name1 -- $name2 [dir=both,weight=1]\n";
}
# footer
print $o "}\n\n";
# compile the graph and output
my $cmd = "nice neato -Tsvgz $outfile|";
print "Content-Encoding: gzip\n";
print "Content-Type: image/svg+xml\n\n";
#print "Content-Type: text/html\n\n";
my $file;
my $pid = open ($file, $cmd);
while (my $line = <$file>) {
print $line;
}
close $o;
unlink $outfile;
#print "what is up..\n";
} else {
print &PrintHeader;
print "Authentication failed. Return home.\n";
}

#print "end...\n";





Источник





Путь хакера
Взято отсюда.



Из письма Марка Цукерберга будущим акционерам Facebook





Facebook не создавался для того, чтобы быть компанией. Facebook создавался для того, чтобы выполнять социальную миссию: сделать мир более связанным и открытым. Нам кажется важным, чтобы каждый, кто инвестирует в Facebook, понимал, что эта миссия для нас означает, как мы принимаем решения и почему мы делаем то, что делаем. Я попытаюсь описать наш подход в этом письме.



Мы вдохновляемся технологиями, которые изменили потребление и распространение людьми информации. Мы часто говорим об изобретениях вроде печатного станка и телевидения: просто облегчив коммуникацию, они привели к полной трансформации важных частей общества. Они дали многим людям голос. Они вдохновляли прогресс. Они изменили принципы организации человеческого сообщества. Они сделали нас ближе друг к другу.



Сегодня общество достигло новой переломной точки. Большинство людей в мире имеют доступ к интернету или мобильным телефонам — новым инструментам, позволяющим делиться своими мыслями, чувствами и действиями с кем только захочется. Facebook надеется построить сервисы, которые дадут людям возможность делиться [друг с другом] и помогут им снова трансформировать многие ключевые отрасли и общественные институты.



Существует огромная потребность и удивительная возможность связать всех в мире между собой, дать каждому голос и трансформировать общество для будущего. Масштаб технологий и инфраструктуры, которые для этого понадобится создать, не имеет прецедентов, и мы верим, что это самая важная проблема, на которой мы должны сфокусироваться.



Наша миссия и наш бизнес





Как я уже говорил, Facebook создавался не для того, чтобы быть компанией. Мы всегда заботились в первую очередь о своей общественной миссии, о своих сервисах и людях, которые ими пользуются. Это не самый обычный подход для публичной компании, так что я хочу объяснить, почему, с моей точки зрения, он оправдан.



Когда я писал первую версию Facebook, я делал это сам, потому что мне хотелось, чтобы он был. С тех пор большая часть идей — и программного кода — были созданы лучшими людьми, которых мы привлекли в свою команду.



Лучшие люди беспокоятся в первую очередь о том, чтобы делать большие проекты и хотят быть частью больших проектов, но они также хотят зарабатывать деньги. В процессе создания команды — а также сообщества разработчиков, рекламодателей и инвесторов — я осознал, что строительство сильной компании с сильной экономикой и быстрым ростом может быть самым удачным способом объединения многих людей для решения важных проблем.



Попросту говоря, мы не создаем сервисы, чтобы зарабатывать деньги, мы зарабатываем деньги для того, чтобы создавать лучшие сервисы.

С нашей точки зрения это хороший способ что-нибудь сделать. Сейчас я думаю, что все больше людей хотят пользоваться услугами компаний, верящих во что-то большее, чем максимизация прибылей.



Мы считаем, что, фокусируясь на своей миссии и создавая отличные сервисы, мы создадим для наших инвесторов больше стоимости в длительной перспективе. А это, в свою очередь, позволит нам и дальше привлекать лучших людей и создавать отличные сервисы. Мы не просыпаемся по утрам, думая о том, где бы добыть деньжат, но понимаем, что лучший способ выполнить свою миссию — это построить сильную и дорогую компанию.



Вот так же мы думаем и про наше IPO. Мы становимся публичной компанией ради наших сотрудников и наших инвесторов. Давая им долю в компании, мы обещали, что она будет дорого стоить и станет ликвидной, и наше IPO — подтверждение этого обещания. Становясь публичной компанией, мы обещаем то же и нашим новым инвесторам и будем работать изо всех сил, чтобы снова выполнить это обещание.



Путь хакера





Создавая сильную компанию, мы много работаем над тем, чтобы сделать Facebook лучшим местом, где лучшие люди могли бы менять мир и учиться у лучших. Мы создали уникальную культуру и систему управления, которую мы называем «путь хакера».



Слово «хакер» имеет — несправедливо — негативные коннотации. Хакеров изображают в медиа людьми, которые взламывают компьютеры. На самом деле хакинг — это просто способ сделать что-то быстро или проверить границы того, что может быть сделано. Как и многое другое, его можно использовать во благо и во вред, но подавляющее большинство хакеров, которых я встречал, — идеалисты, желающие оставить положительный след в мире.



«Путь хакера» — это подход к созиданию, который включает постоянные улучшения и повторы. Хакеры верят, что все можно улучшить и ничто не может быть совершенным. Надо всего лишь исправить — часто вопреки воле людей, которые говорят, что это невозможно, и довольны статус-кво.

Хакеры стараются строить лучшие сервисы, выпуская частые релизы и обучаясь на небольших итерациях, вместо того чтобы постараться сделать все правильно с первого раза. Чтобы поддержать этот подход, мы построили тестовую среду, способную в каждый момент времени испытывать тысячи версий Facebook. На стенах нашего офиса написаны слова, призванные постоянно подгонять нас: Done is better than perfect.



Хакинг по своей природе активная и практическая работа. Вместо того чтобы днями спорить о возможностях или способах реализации новой идеи, хакеры просто сделают прототип и посмотрят, что работает, а что нет. В стенах Facebook вы часто можете услышать мантру хакера: [программный] код выигрывает споры.



Хакерская культура исключительно открыта и меритократична. Хакеры верят, что выигрывать должна лучшая идея и ее лучшее исполнение, а не люди, которые лучше болтают или имеют больше подчиненных.



Поощряя этот подход, каждые несколько месяцев мы проводим «хакатлон», во время которого каждый строит прототипы своих новых проектов. В конце вся команда собирается вместе и смотрит, что получилось. Многие наши успешные продукты появились таким образом, включая Timeline, чат, видео, среду мобильной разработки и некоторые важнейшие инфраструктурные разработки.



Чтобы увериться, что все наши программисты разделяют наш подход, мы заставляем всех новичков — даже менеджеров, которые не будут программировать, — пройти через программу Bootcamp, где они изучают нашу программную среду, наши инструменты и наш взгляд на мир. В индустрии есть много людей, которые хотят управлять программистами, но не хотят писать код, но те люди, которых мы ищем, хотят и могут пройти через Bootcamp.



Приведенные выше примеры адресованы программистам, но из этих принципов мы выделили пять ценностей, ключевых для развития Facebook:



Фокусироваться на результате. Если мы хотим добиться высокого результата, то всегда должны быть уверены, что концентрируемся на решении самых важных проблем. Звучит просто, но многие компании, как нам кажется, плохо с этим справляются и теряют кучу времени. Мы надеемся, что каждый сотрудник Facebook умеет находить самые важные проблемы и решать их.



Быстро развиваться. Быстрый рост позволяет нам делать больше и учиться быстрее. Большинство компаний замедляются по мере роста, потому что они больше боятся сделать ошибку, чем упустить хорошую возможность из-за своей медлительности. У нас есть присказка: «Двигайся быстро и разрушай». Идея в том, что если ты ничего не разрушаешь, ты, вероятно, растешь не слишком быстро.



Быть смелым. Нельзя сделать больших проектов, не рискуя. Страх перед риском мешает большинству компаний совершить смелые поступки, которые следовало бы совершить. Но в мире, который меняется с такой скоростью, отказ от риска просто гарантирует неудачу. У нас есть присказка и на эту тему: «Самый большой риск — не идти на риск». Мы поощряем принятие смелых решений, даже если иногда это значит, что приходится ошибаться.



Быть открытым. Мы верим, что открытый мир лучше, потому что хорошо информированные люди принимают лучшие решения и успевают сделать больше. Это относится и к управлению нашей компанией. Мы много работаем над тем, чтобы каждый в Facebook имел доступ ко всей возможной информации, чтобы принять наиболее правильные решения и достичь высокого результата.



Создавать социальный капитал. Еще раз: Facebook существует для того, чтобы сделать мир более связанным и открытым, а не просто как компания для зарабатывания денег. Мы ожидаем, что каждый сотрудник Facebook сфокусирован на том, как принести больше реальной пользы для мира каждым своим действием.



Оригинал письма на английском языке











Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/283544/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Не подлежит забвению

Воскресенье, 01 Мая 2016 г. 12:14 (ссылка)

Вы не находите странным черту освистывать нечто до появления некого более функционального аналога оного и проявлять к этому нечто интерес после? На протяжении всего своего существования командная оболочка Windows неоднократно подвергалась жесткой критике, дескать, ее функциональность оставляет желать лучшего, что, казалось бы, должно было сойти на нет с появлением PowerShell, призванного устранить недочеты первой и упростить жизнь разработчикам и системным администраторам. Нет, PowerShell снискал должную популярность, но появился интерес и к самой командной строке особенно после того, как «селекционерами» были открыты способы запускать командные сценарии как WS[H|F] и HTA. Собственные эксперименты и наблюдения показали, что этим дело не ограничивается.



Некоторые символы в cmd наделены особой логикой, например, двоеточие в начале команды означает метку, а потому если командный сценарий состоит из единственной строки :label или даже одного двоеточия, интерпретатор просто ее проигнорирует; если после двоеточия стоит один из знаков перенаправления потока, сути это также не изменит — интерпретатор по-прежнему будет считать данную строку меткой, что в общем-то логично. Вообще, изменяя порядок использования некоторых спецсимволов командной строки, можно добиться любопытных побочных эффектов, таких как, скажем, запуск PHP кода в виде командного сценария:



:


Или то же, но — bash:



:<nul bash "%~f0" %*
echo:End
exit /b
EOF
for i in $@;do echo $i;done


Конечно же при этом обыгрываются особенности самих вызываемых интерпретаторов, но основная суть в том, что такое вообще возможно. Возможен также и запуск сценариев Perl, Ruby или Python как командных сценариев в виду наличия у перечисленных интерпретаторов ключа -x, заставляющий последние игнорировать первую строку передаваемого на исполнение кода. Строго говоря, данное утверждение справедливо для Python, а вот Perl и Ruby с данным ключом будут игнорировать все до тех пор, пока не встретят шебанги #!perl и #!ruby соответственно.



Perl:

@echo off
echo:Start
2>nul perl -x "%~f0" %*
echo:End
exit /b
#!perl
foreach my $i (@ARGV) {print $i, "\n";}


Ruby:

@echo off
echo:Start
2>nul ruby -x "%~f0" %*
echo:End
exit /b
#!ruby
ARGV.each do |i| puts i;end


Python:

@echo off&echo:Start&python3.4m -x "%~f0" %*&echo:End&exit /b
from sys import argv
for i in range(1, len(argv)): print(i)


Подобный подход применим и к PowerShell, а вот утверждать, что описанное выше применимо и к другим интерпретируемым языкам, не стану, могу лишь допустить такую возможность причем с совсем другого ракурса.



Original source: habrahabr.ru.

https://habrahabr.ru/post/282816/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

5 стадий API: что мы поняли, написав две версии

Понедельник, 25 Апреля 2016 г. 14:52 (ссылка)

Сегодня мы хотим поговорить о сокровенном — у нас есть API.



Мы писали, а затем переписывали его заново на протяжении четырех лет. И за это время прошли почти все классические стадии “принятия неизбежного”. Кроме одной — четвертой. И хотим поделиться нажитыми непосильным трудом выводами, что делать и не делать, если вы решите делать свой “мощный эпиай”.







Процесс создания API uCoz иногда напоминал сюжет сериала The Knick («Больница Никербокер») — с неудачными операциями, кишками и экспериментами на живых людях.



Стадия первая – Отрицание



По концепции пяти стадий:
На первом этапе человек отказывается принимать то, что с ним случилось.



Вообще, API для конструкторов сайтов — редкость и сейчас. А в 2010-м такого инструмента не было еще ни у кого на рынке.



С одной стороны, определенная потребность в API была: мы видели, что люди активно гуглили “скрипты для uCoz”, получали запросы напрямую — часть пользователей хотела создавать свои дополнения и модификации. С другой стороны — и это была проблема, — в те времена все ресурсы были брошены на другие проекты.



Вопрос «быть или не быть» решился просто. API потребовалось нам самим — для запуска функции поддержки PHP в конструкторе. Мы выделили одного разработчика, и он за полгода сделал наше “начальное API” — это был get-only интерфейс, к которым можно было получать данные страниц из 11 модулей. Данные отдавались только в XML. К моменту запланированного анонса PHP мы не успевали наладить еще хотя бы добавление контента, но у API были и плюсы: с ним можно было запускать пхп-скрипты в бесплатном варианте юкоза.



В общем, мы вышли к пользователям. Получилось прям по классике:







Нас не очень-то приняли… Точнее, в основном хвалили. Но за PHP. А в самом “эпиай” люди не увидели того, что представлялось им при этом волшебном слове. В техподдержку посыпались вопросы: «Как добавлять материалы? Как редактировать? Как получить в json?» А этого всего не было.



Стадия вторая – Гнев



По концепции пяти стадий:
На этом этапе проявляется агрессия ко всему окружающему миру.



К вопросу, что API нужно развивать, в компании вернулись где-то через год. В приоритетах были мобильные клиенты — и мы решили писать все заново с учетом требований ребят, делавших клиенты для iOS и Android. Начальная версия осталась жить сама по себе (и даже до сих пор жива, потому что некоторые ей все же пользуются), а мы стали подбирать исполнителя на новый проект.



“Сам себе менеджер”. В ростовский офис как раз пришел толковый парень Илья: он знал Perl, на котором была написана часть старого uCoz, а когда ему по традиции предложили несколько задач, он выбрал из них работу над API. Проблема была в том, что на время парню пришлось стать самому себе менеджером.



Тут наступил гнев: «Как выяснилось в процессе, синтаксис Perl я понимал, а вот дух — нет.







Потому я долго пытался не работу, а мир лучше сделать: думал все переписать, объекты-классы-паттерны внедрить. Очень много времени ушло, чтобы освоиться с идеологией языка. И вот тогда то, что мне поначалу казалось страшным и хотелось переписать, стало казаться нормой».



К моменту просветления появился менеджер и стал требовать отчет. А была готова только отдача контента по нескольким модулям… В общем, еще раз гнев, теперь с другой стороны, — и Илью перевели на новый проект. Его версии API так и не суждено было увидеть свет.





… Между стадий ...



“Идеальная обучающая задача”. К тому моменту компания пробовала открыть офис R&D в Казани. На месте нужен был носитель знания о всей системе — и появилась идея “вырастить” его через работу над API, которое затронет основные модули системы.



Так в этой истории появился Ринат:







С одной стороны, он был в меру азартен, чтобы взяться за проект (он у нас вообще немного экстремал). С другой — в работе спокоен и рассудителен: все же за плечами не только 700+ прыжков с парашютом, но и 20 лет опыта в ИТ.



Он также был знаком с Perl и имел свежий опыт работы с чужими API — интегрировал «Метрику» и GA в панель веб-мастера.



Первое, что нам пригодилось, так это его рассудительность. Ринат попросил паузу, чтобы детальнее поразбираться в запутанных “кишках” системы, — и только затем озвучить сроки и план реализации. Спустя месяц он вышел с таким предложением:



* Переписываем и дублируем часть функций чисто под API — за время создания системы в ней скопилось много старого кода. И некоторые функции, попробуй ты туда еще что-нибудь засунуть, получились бы «километровыми». Значит, нужны подчищенные двойники чисто под API.



* Используем REST — для упрощения архитектуры запроса, что поможет увеличить производительность.



* Используем Oauth 1.0a — аутентификацию, показавшуюся самой защищенной на тот момент.



* Отдаем в разных форматах — JSON, XML, Text Plain.



* Ну и: get, post, put, delete, мир, труд, май…





Третья стадия – Торг



По концепции пяти стадий:
Тут у вас появляются мысли о том, чтобы договориться с кем-то о лучшей участи



К общему пониманию, как сделать удобно, мы вроде пришли. Но дьявол крылся в деталях и спорах.



Принципиальным вопросом стала область видимости токенов. Наша серверная система, как говорит Ринат, «это законченная инкапсулированная фиговина»: на каждом сервере крутится N юзеров, а когда место на нем кончается, берется новое железо.



Ринат хотел использовать один токен, чтобы получить доступ ко всем сайтам. “Ок, давай поговорим об этом”, — и мы позвали в скайп-чат еще коллег. Как это бывает в коллективных чатах, спорили-спорили, но к коллективному решению не пришли. А в угаре разработки тема подзабылась и вновь всплыла, когда была готова интеграция для половины модулей.







Иного варианта, как тиражировать токен, у нас не было: так появилась архитектура с одним основным авторизационным сервером — получив один токен там, его можно использовать везде.



Оформить как массив или объект? Для нас это была не проблема разряда “что правильно, а что нет”. Это была проблема деталей.



Например, мы отдаем данные в JSON — и возникает проблема с типизированными языками. Не все структуры, полученные от API, удобно распарсить — потому что увеличивается количество кода на клиентской стороне.



А ведь API ориентировано на веб-приблуды, поэтому мы прислушались к мнению разработчиков на Java и C++ и пришли к стандарту: поля отдаем в любом случае, именованные параметры дополняем кодом.



Поля и параметры. Тут торги и обсуждения шли все время реализации. Например, стоит ли выдавать пустой параметр? Поскольку мы ориентировались и под мобильную разработку с использованием API, Ринат в процессе доказал — не нужно: ведь в мобильных приложениях важен трафик.



А какие поля нужны? Тут уже мне, менеджеру проекта, нужно было искать и приводить примеры из реальной практики. Часто я брал помощь зала — опрашивал будущих пользователей API. Если присланный случай стороннего разработчика казался ценными, мы реализовывали нужные поля в самом API, чтобы люди потом не делали через костыли. А позже придумали более элегантное решение — о нем еще расскажем ниже.







Четвертая стадия –… (и о сроках)



По концепции пяти стадий:
На четвертой стадии у вас наступает депрессия. [Мы как-то миновали этот этап]



Работа, запланированная на год, заняла почти два. В процессе возникали совсем непредвиденные сложности. Мы быстро поняли, что:







Проблемы со вторым разработчиком. По плану, после того как Ринат сделает авторизацию, к интеграции API с модулям должен был подключиться второй разработчик.



Мы расписали гугл-док, поставив модули в алфавитном порядке. Работу по ним разделили между исполнителями пополам. Определили график — один модуль в месяц от каждого. Когда пора было приступать, второй человек ушел. А мы уже делали новый проект — конструктор uKit, на который бросили основные ресурсы. С потерей второго программиста, к сроку реализации добавились почти 7 месяцев.



Проблемы с тестированием. В теории процесс задумывался таким: после сервера разработчика все уходит на альфа-сервер, а если тестер говорит “давай дальше”, то проходит бета-тестирование и обновление разливается по сотне серверов.



Но оказалось, что наши обычные тестировщики не очень подходили для работы с API — ведь они “заточены” под тестинг веб-страниц. API же — тонкая вещь. Например, когда происходило изменение внутри модуля, с которым мы интегрировались, что-то могло отвалиться — но тестировщики сначала этого не замечали (потом мы их научили).



Тогда мы открыли 4 тестовых сервера и пригласили продвинутых пользователей на самых ранних этапах. Такое крауд, а потому не очень контролируемое (ты не уверен, когда человек что-то сделает и не бросит ли), тестирование тоже сказалось на увеличении сроков.





Пятая стадия – Смирение (и выводы)



По концепции пяти стадий:
По канонам, наступает согласие с неизбежным



В конце концов, мы смирились с неизбежным — API, как и ремонт, можно начать, но не так-то просто закончить. Вот несколько рецептов, как организовать процесс, чтобы вам было проще.



1. Наладьте обратную связь. Больше связи.



Помимо ветки на форуме и постов в блог, т.е. публичных средств обмена комментариями, я завел на сайте с новой документацией раздел “Лаборатория”. По сути, это была форма обратной связи, где можно было обсудить свой случай в личной переписке.



О новом API мы сообщили ранним пользователям в феврале 2015-го, а процесс выкатки всех модулей на сервера завершили лишь в текущем году. Все это время мы получали через “Лабораторию” репорты, предложения и интересные случаи от пользователей (которые я в том числе использовал в «торгах» с Ринатом).





Поток обращений снизился лишь в последние два месяца



Каждый раз, получив обращение «как сделать вот это или как тут поступить», мы выделяли время и садились смотреть, а как же лучше? Иногда из этого рождались интересные истории.



2. Лично помогите тем, кто не разобрался или [Скрипт в подарок].



Бывало, сообщение из “Лаборатории” заинтересовывало настолько, что мы начинали писать скрипт сами. А затем дарили готовое решение пользователю,



В чем профит? Как говорится, «хочешь найти баг – будь багом». А если хочешь погрузиться в проект – сделай на нем как можно больше всего, чтобы проверить работоспособность и удобство. Заодно так на бирже uScript появились первые решения с использованием uAPI — авторизация через соцсети и неглючный вывод картинок в блоке рекомендованных материалов.



3. Проведите внутренний хакатон.



Как вы помните, мы испытывали недостаток в тестировщиках. Так появилась идея внутреннего хакатона для проверки продукта. Для части наших разработчиков работа с API была совершенно новой и непривычной задачей, так что мы имели возможность пристально наблюдать, как действую люди с разными скиллами.





Ринат обычно не общался напрямую с пользователями, но на хакатон к ростовским разработчикам приехал из Казани. Говорит, “за исключением троллинга, остался доволен собранными в процессе данными и критикой”.





4. Попробуйте что-нибудь автоматизировать.



Моей любимой фишкой для работы с API стало полуавтоматическое создание приложения и токена.







Изначально эта система появились из моих личных потребностей — в один момент устал тратить время на подписывание запросов при создании приложения. Тогда решил написать режим полуавтомата — чтобы ни о чем не думать, а скопировал данные — и приложение готово. Как оказалось, сейчас люди в основном пользуются полуавтоматическим решением.



5. Советуйтесь и меняйте документацию.



Изначально мы расписывали пример запроса в PHP и CURL.





Так было. Как выяснилось в процессе, CURL никто не пользовался.



Но люди спрашивали, например, в каком формате и по какому пути отправлять запрос. Стало понятно, что первая реализация была не очевидна для всех.



Мы решили, что должен быть дополнительный модуль (написан по современному, ООП), где будет автоматически генерироваться подпись для oauth. Один раз вызвал этот модуль – и далее просто пишешь путь и метод запроса.



Параллельно я прошелся по нашим программистам и спросил – документацию к каким API они считают достойным примером для подражания? Посмотрел рекомендованные примеры и с их учетом составил новую версию документации:







А в качестве теста перед публикацией, попросил коллегу из второго нашего офиса проверить соответствие работы API описаниям в документации для всех методов.



6. Реализуйте мобильность.



Во-первых, это поможет получать хорошие отзывы и расширит вашу аудиторию:



Давно была мечта о создании мобильного приложения для своего сайта. Буду изучать uAPI, чтобы наконец-таки воплотить ее в жизнь


Во-вторых, может стать ценным и новым опытом для внутренней команды. Например, на хакатоне несколько наших ребят запилили вот такое приложение:





Тут можно посмотреть код приложения



7. Не ограничивайте желания пользователей.



Вернемся к вопросу полей. Так как все случаи не предусмотришь, в итоге мы сделали конструктор запросов, с которым можно реализовать любые смелые фантазии на эту тему:







Наконец, мы пришли к тому, что API — это двустороннее движение. Сегодня с помощью uAPI можно сделать авторизацию для любого сайта, переносить материалы с uCoz и наоборот, использовать только нашу БД, но выводить данные на стороннем сайте. Чем больше юзкейсов вы вбираете в инструмент, тем дольше реализация. Но одновременно растет и область его применения.





P.S.



Весь процесс выкатки и боевого тестирования новой версии занял у нас 14 месяцев и 20 обновлений. Вот визуализация.



Бывает, после очередного обновления нам пишут: “Когда же вы его уже допишете?” Но процесс и правда очень сложно остановить (мы не шутили про ремонт). Иногда — по техническим причинам: скажем, когда апдейт системы требует изменений в API. А иногда — по творческим. Например, сейчас мы думаем: когда все интеграции с модулями закончены, почему бы не проработать темы изменения дизайна и настроек сайта по API?



Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/282385/

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Быстрый анализ транзитного трафика

Вторник, 05 Апреля 2016 г. 14:43 (ссылка)

Перед любым системным администратором рано или поздно возникает задача количественного анализа трафика (откуда / куда, по каким протоколам / портам, в каких объемах и т. п.), проходящего по его сети. Особенно неприятно, когда эта задача возникает спонтанно, как побочный результат DDoS-а, а денег на серьезные решения от Cisco или Arbor, как обычно, нет. И хорошо еще, если шлюзом для сети выступает сервер, на котором можно запустить tcpdump или wireshark, но что делать если:




  • шлюзом выступает устройство провайдера, а в сети есть только файл-сервер;

  • данные о трафике нужны не постоянно, а от времени к времени;

  • устройство не поддерживает возможность запуска на нем сторонних программ;

  • трафика столько, что сервер после запуска tcpdump-а «клеит ласты»;

  • или наоборот, настолько мало, что его уровень сравним с долей (хотя и значительной) обычного трафика?



Дополнительную ложку дегтя в эту «бочку меда» задачи добавляет еще и отсутствие как у tcpdump, так и у tshark чудесного ключа «сгруппировать, просуммировать / усреднить и отсортировать».



Так что, при всем нежелании изобретать велосипед, пришлось закатить рукава и написать инструмент, удовлетворяющий следующим требованиям:




  • источником данных выступает маршрутизатор или коммутатор, поддерживающий протокол sFlow;

  • коллектор (tcpdump, tshark или sflowtool) данные в формате PCAP либо пишет в файл, либо передает на STDOUT;

  • соответсвтенно, исходные данные могут быть вычитаны инструментом либо из файла, либо с STDIN-а;

  • базовой единицей для анализа является пакет, а не соединение;

  • результат должен включать информацию о направлении трафика, количестве прошедших пакетов, объеме трафика и среднем размере пакета;

  • должна быть предусмотрена возможность базовой группировки и сортировки результата;

  • ну и разные попутные мелочи;

  • и при всем этом этот инструмент не должен дублировать функционал существующих общеизвестных инструментов.



Вот исходя из этого и был написан PCAParse — максимально простой инструмент для получения обобощенной информации о проходящем по сети трафике. Для его использования, в простейшем случае, достаточно коммутатора типа D-Link DGS-3XXX или аналога других производителей и запущенного на вышеупомянутом файл-сервере sflowtool либо tcpdump-а на офисном шлюзе. Как показывает практика, эти устройства давным давно утратили статус экзотических и встретить их можно даже в небольших офисах.



Для того, чтобы было понятно, о чем идет речь, приведу небольшой пример:



Пример вывода скрипта

$ tshark -c 100 -w - | pcaparse
Filename: -
File size: -
Parsed: 100 samples in 4.90 seconds
Matched: 100 samples / 104.21kB
tcp: 20 samples / 2.04kB
udp: 76 samples / 101.79kB
icmp: 4 samples / 392B
other: 0 samples / 0B

Samples Summary Average
Destination count size size
212.XX.XXX.XX 86 102.43kB 1.19kB
tcp: 10 660B 66B
udp: 76 101.79kB 1.34kB
icmp: 0 0B 0B
other: 0 0B 0B
212.XX.XXX.XX 5 550B 110B
212.XX.XXX.XX 5 878B 176B
212.XX.XXX.XXX 4 392B 98B




Разумеется, сразу же бросается в глаза несуразное время работы, но на самом деле это — результат tshark-а, а не скрипта. Реальная его производительность на AMD A8-6600K @ 3,9 ГГц / 8 ГБ RAM составляет 15-25 килопакетов/с в зависимости от источника данных (чтение из файла, sFlow и т. д.).



Более требовательные пользователи могут затребовать более развернутую информацию:



Развернутый пример вывода скрипта

$ tcpdump -w - | pcaparse -f - -d 212.XX.XX.XX
Filename: -
File size: 32.65MB
Parsed: 280692 samples in 27.58 seconds
Matched: 4383 samples / 3.75MB
tcp: 4 samples / 513B
udp: 4378 samples / 3.75MB
icmp: 0 samples / 0B
other: 0 samples / 0B

Samples Summary Average
Destination count size size
212.XX.XX.XX 4.38k 3.75MB 897B
tcp: 4 513B 128B
80 (http) 4 513B 128B
udp: 4378 3.75MB 898B
7 (echo) 2819 2.82MB 1.02kB
3389 (ms-wbt-server) 659 670.15kB 1.02kB
5538 273 86.15kB 323B
9584 87 24.62kB 290B
18002 92 26.55kB 295B
27186 167 55.68kB 341B
32302 279 89.50kB 328B
icmp: 0 0B 0B
other: 0 0B 0B




Если принять во внимание, что дамп для примера взят для обычного web-сервера, эта информация позволяет судить о наличии атаки DoS / DDoS по udp:* на ресурс. Ну, а это знание, позволяет уже принять какие-то адекватные меры. Для удобства дальнейшей обработки данных предусмотрен parser-friendly вывод:



Parser-friendly вывод

$ pcaparse -f tcpdump-212-XX-XX-XX -d 212.XX.XX.XX -p
212.XX.XX.XX total 4382 3931684 897
212.XX.XX.XX tcp 4 513 128
212.XX.XX.XX tcp:80 4 513 128
212.XX.XX.XX udp 4378 3931171 897
212.XX.XX.XX udp:7 2819 2955528 1048
212.XX.XX.XX udp:3389 659 686237 1041
212.XX.XX.XX udp:5538 273 88222 323
212.XX.XX.XX udp:9584 87 25208 289
212.XX.XX.XX udp:18002 92 27185 295
212.XX.XX.XX udp:27186 167 57019 341
212.XX.XX.XX udp:32302 279 91644 328
212.XX.XX.XX icmp 0 0 0
212.XX.XX.XX other 0 0 0


Скрипт написан на perl-е с использованием модулей Net::PCAP и NetPacket::*, что обеспечивает достаточную производительность и кроссплатформенность. По крайней мере, на свежих linux-ах и FreeBSD проблем с запуском и работой не возникало.



Из известных минусов:




  • отсутствие поддержки IPv6 (надеюсь, пока — над этим ведется работа);

  • проверка диапазонов IP-адресов с использованием Data::Validate::IP (опять же, надеюсь, временно);

  • отсутствие опции «сделать хорошо» у tcpdump или tshark (но, надеюсь, в будущем она может там появиться, если скрипт понравится авторам и контрибьюторам упомянутых инструментов и его функционал будет туда спортирован).



P.S. постфактум оказалось, что существует аналогичный инструмент — Fastnetmon, но он, все-таки, ориентирован под «стационарное» использование, поскольку подразумевает использование коллектора данных, с которым и взаимодействует клиентская часть.



Ссылки:





Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/280986/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<perl - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda