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


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

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

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

Эффективное кеширование. От теории к практике

Пятница, 29 Июля 2016 г. 19:07 (ссылка)

image



Как правило, статьи о кешировании начинаются за здравие, а заканчиваются LRU кешем. Попробуем переломить эту тенденцию? Начнем с того, чем LRU плох, а закончим за здравие. Я надеюсь.



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



Спрашивая на собеседованиях какие алгоритмы кеширования вы знаете — как правило слышишь в ответ, ммм… LRU Cache… Зато если спросить про алгоритмы сортировки, вероятность услышать что-то помимо «Пузырек» — значительно выше. Человек готов потратить несколько дней на поиск оптимальной библиотеки ресайза изображений или веб фреймворка, не понимая, что реализовав эффективный кеш, он может взять в принципе любую библиотеку со схожими характеристиками, так как кеш — минимизирует обращения к ней, сгладив разницу в быстродействии.



Для Relap.io, как для хайлоад сервиса, кеширование особенно важно. Например, вчера мы показали рекомендации на различных сайтах 789301033 раз. Поэтому у нас густо обмазано кешем все: рекомендации, картинки, реклама и так далее.



Не все кеши одинаково полезны





Хороший пример LRU Cache.



На конкурсы алгоритмов его обычно не берут. Никто не хочет иметь ничего общего с неудачником. Сложно придумать более неэффективный алгоритм. Единственный алгоритм, у которого LRU Cache выигрывает по эффективности — это, наверно, просто очередь, например, FIFO. Тем не менее, LRU встроен везде и всюду как дефолтный и, к сожалению, часто единственный алгоритм, так как он прост в реализации.



Вам хотелось бы пользоваться сайтом, приложением или операционной системой, которая тормозит, неэффективна и жрет ресурсы как не в себя, но зато она написана на простом в реализации языке, например, условном бейсике? Если нет — добро пожаловать под кат.





Я люблю правило Парето. На стат значимой выборке его можно применять абсолютно ко всему.



Давайте попробуем:


  • 20% усилий приносят 80% результата,

  • 20% товаров приносят 80% прибыли,

  • на 20% урлов приходится 80% просмотров,

  • 20% кода реализуют 80% функционала.





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



<Лирическое отступление>

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



Признаюсь, я поначалу думал что все дело в имаджлоадере. Достаточно выбрать эффективный и вуаля. Я пересмотрел все. Пикассо, Фейсбуковский fresco, UIL не помню уже все названия. Но проблема оставалась. Картинки грузились где то чуть быстрее, где то чуть плавнее, но они грузились. Тогда я сел, и написал свой. Простой. Чистый. Легкий. И это не помогло. Глупые имаджлоадеры продолжали постоянно теребить картинки нервируя пользователя и никак не могли отделить зерна от плевел. Тогда я вспомнил о правиле Парето.





Если предположить, что 20% картинок — показываются 80% раз — все встает на свои места. Единственное что осталось понять — какие именно картинки надо хранить.



Как работает LRU cache?





Давайте рассмотрим сферическое приложение в вакууме. Пусть это будет мессенджер, его проще всего представить.



скриншот_из_телеграмм.jpg



Если внимательно посмотреть на скриншот, то можно увидеть, что сообщения пользователей сопровождаются аватарками, а в теле сообщений — встречаются картинки. Перед вами стоит задача — сделать максимально плавный интерфейс. Давайте еще раз взглянем на скриншот выше. Мы видим 2 повторяющиеся автарки в диалоге, и затем юзер 1 прислал большую картинку.




  • Пришла аватарка 1 — 100 на 100 пикселей, мы записали в кеш 100*100*4 байт.

  • Пришла аватарка 2 — 100 на 100 пикселей, мы записали в кеш 100*100*4 байт.

  • Пришла аватарка 1 — мы подняли ее в очереди наверх.





Пока все идет неплохо.



Пришла картинка 1024 на 768 пикселей, мы записали в кеш 1024*768*4 байт — и БАМ! Наши прекрасные аватарки выбило напрочь из кеша. Теперь там торжественно валяется картинка, которую нужно было показать один раз и не нужно было кешировать.



Как победить?





Если посмотреть, например, на библиотеку AQuery, то разработчик предлагает разделить кеш на несколько кучек. Отдельная кучка для маленьких аватарок, отдельная кучка для больших картинок. Уже хлеб кстати, но это решение не универсально, требует программирования и внимания, а хочется всего и сразу. Так как все интересное уже было придумано до нас — самое время взглянуть на то, какие еще существуют алгоритмы кеширования.



Статья в вики



Простите, что я тут чуть чуть сжухлю, и опишу очень коротко прописные истины.



LRU — не использованный дольше всех вылетает из кеша.

MRU — последний использованный вылетает из кеша (специфичный кейс, бережем старье).

LFU — реже всего использованный вылетает из кеша.



Это база. Вас может испугать что затраты на вычисления растут с ростом сложности алгоритмов, но не критично. Попробуйте сравнить время лукапов по ключам в памяти со временем рендеринга картинки 1024 на 768. А именно это произойдет если алгоритм «промахнулся».



SNLRU (сегментированный LRU) — заводим несколько «коробочек» с LRU. Сперва кладем в первую коробочку, при повтороном запросе перекладываем во вторую из второй — в третью.



Если назвать коробочки — будет понятнее:


  • Cold — первая коробочка,

  • Warm — вторая,

  • Hot — третья.





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



Mid point LRU — сегментированный LRU в котором всего две коробочки.



MQ — сегментированный LRU в котором запоминаем. Запоминается позиция с которой элемент вылетел — и при повторном запросе — возвращается туда, где был, если не вылетел из очереди запомненных позиций. По сути кеш быстрее прогревается в случае циклической ротации элементов (какашечки могут стать популярными). Выглядит как довольно странный юзкейс.



ARC, GCLOCK — и прочие более сложные алгоритмы придется на время вынести за скобки. Не то чтобы они плохие или неинтересные, тот же ARC используется (точнее, наверное, использовался, судя по данной преисполненной боли статье: www.varlena.com/GeneralBits/96.php) в postgreSQL. Не удержусь от небольшой цитаты:



Database systems often use LRU algorithms but many are switching to other algorithms to better handle a variety of behaviors not handled well by LRU. For example, one one-time very large sequential scan might flood the cache with pages that are not expected to be used again any time soon. The cache then is not helpful until it is re-populated with more commonly used pages.




2Q — или две очереди, примечателен тем, что сохраняя простоту реализации, он прекрасно адаптируется. Кеш разделяется на три части, как в сегментированном LRU, но с более сложной стратегией:




  • Первая часть In — FIFO входящий кеш в который помещаются новые элементы.

  • Вторая часть Out — FIFO исходящий кеш, в который перемещаются элементы, вытесненные из коробочки In.

  • Третья часть Hot LRU кеш для элементов, запрошенных из Out.





Стратегия вытеснения из кеша:




  • элементы запрошенные из In никуда не двигаются. Вытесненные из In элементы — перемещаются в Out.

  • элементы запрошенные из Out — попадают в рай, в коробочку Main. Вытесненные же из Out (не использованные) — попадают сразу в ад (null).





Ссылка на каноническое описание.



Во первых — это красиво. Коробочку Main — делаем, например, 20% (помните о Парето?) именно тут скопятся наши аватарки. А вот Out — надо сделать побольше, процентов 60. Так как это «отстойник».



В чем прелесть In — новые элементы спокойно спускаются по FIFO трубе из In в Out, не подпрыгивая и никуда не перемещаясь по мере запросов. А вот если опять запросили (например пользователь подскролил вверх) и, картинка успела перейти из In в Out — вот она картинка победительница. Кеш на уровне архитектуры корректирует некие типичные корреляции, присутствующие в обычной жизни. И после внедрения исчезли постоянные перезагрузки в условиях ограниченного размера памяти. Парето сработал. Но мы еще не раз вернемся к Парето.



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



Реализация на java, бам!
import java.util.*;

/**
* 2Q: A Low Overhead High Performance Buffer Management Replacement Algorith
* Based on description: http://www.vldb.org/conf/1994/P439.PDF
* Created by recoilme on 22/08/15.
* email: vadim-kulibaba@yandex.ru
*/
public class TwoQueuesCache {
/** Primary container */
private final HashMap map;
/** Sets for 2Q algorithm */
private final LinkedHashSet mapIn, mapOut, mapHot;

private final float quarter = .25f;
/** Size of this cache in units. Not necessarily the number of elements. */
//private int size;
private int sizeIn;
private int sizeOut;
private int sizeHot;

private int maxSizeIn;
private int maxSizeOut;
private int maxSizeHot;

private int putCount;
private int createCount;
private int evictionCount;
private int hitCount;
private int missCount;

/**
* Two queues cache
* @param maxSize for caches that do not override {@link #sizeOf}, this is
* this is the maximum sum of the sizes of the entries in this cache.
*/
public TwoQueuesCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}

calcMaxSizes(maxSize);

map = new HashMap(0,0.75f);

mapIn = new LinkedHashSet();
mapOut = new LinkedHashSet();
mapHot = new LinkedHashSet();
}

/**
* Sets sizes:
* mapIn ~ 25% // 1st lvl - store for input keys, FIFO
* mapOut ~ 50% // 2nd lvl - store for keys goes from input to output, FIFO
* mapHot ~ 25% // hot lvl - store for keys goes from output to hot, LRU
* @param maxSize
*/
private void calcMaxSizes(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
synchronized (this) {
//sizes
maxSizeIn = (int) (maxSize * quarter);
maxSizeOut = maxSizeIn * 2;
maxSizeHot = maxSize - maxSizeOut - maxSizeIn;
}
}
/**
* Sets the size of the cache.
*
* @param maxSize The new maximum size.
*/
public void resize(int maxSize) {

calcMaxSizes(maxSize);
synchronized (this) {
HashMap copy = new HashMap(map);
evictAll();
Iterator it = copy.keySet().iterator();
while (it.hasNext()) {
K key = it.next();
put(key,copy.get(key));
}
}
}

/**
* Returns the value for {@code key} if it exists in the cache or can be
* created by {@code #create}. If a value was returned, it is moved to the
* head of the queue. This returns null if a value is not cached and cannot
* be created.
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}

V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
if (mapHot.contains(key)) {
// add & trim (LRU)
mapHot.add(key);
sizeHot += safeSizeOf(key, mapValue);
trimMapHot();
}
else {
if (mapOut.contains(key)) {
mapHot.add(key);
sizeHot += safeSizeOf(key, mapValue);
trimMapHot();
sizeOut -= safeSizeOf(key, mapValue);
mapOut.remove(key);
}
}
return mapValue;
}
missCount++;
}

/*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/

V createdValue = create(key);
if (createdValue == null) {
return null;
}

synchronized (this) {
createCount++;

if (!map.containsKey(key)) {
// There was no conflict, create
return put(key,createdValue);
}
else {
return map.get(key);
}
}
}

/**
* Caches {@code value} for {@code key}.
* @return the previous value mapped by {@code key}.
*/
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}

if (safeSizeOf(key, value) > maxSizeIn) {
//throw new IllegalArgumentException("value size is too big for store");
System.out.println("Warning! TwoQueuesCache:"+"value size is too big for store in cache.\n" +
"MaxSizeIn: "+maxSizeIn+ "\nStored: "+safeSizeOf(key, value)+
"\nKey:"+key.toString()
);
}

if (map.containsKey(key)) {
// if already have - replace it.
// Cache size may be overheaded at this moment
synchronized (this) {
V oldValue = map.get(key);
if (mapIn.contains(key)) {
sizeIn -= safeSizeOf(key, oldValue);
sizeIn += safeSizeOf(key, value);
}
if (mapOut.contains(key)) {
sizeOut -= safeSizeOf(key, oldValue);
sizeOut += safeSizeOf(key, value);
}
if (mapHot.contains(key)) {
sizeHot -= safeSizeOf(key, oldValue);
sizeHot += safeSizeOf(key, value);
}
}
return map.put(key, value);
}
V result;
synchronized (this) {
putCount++;
final int sizeOfValue = safeSizeOf(key, value);
//if there are free page slots then put value into a free page slot
boolean hasFreeSlot = add2slot(key, safeSizeOf(key, value));
if (hasFreeSlot) {
// add 2 free slot & exit
map.put(key, value);
result = value;
}
else {
// no free slot, go to trim mapIn/mapOut
if (trimMapIn(sizeOfValue)) {
//put X into the reclaimed page slot
map.put(key, value);
result = value;
}
else {
map.put(key, value);
mapHot.add(key);
sizeHot += safeSizeOf(key, value);
trimMapHot();
result = value;
}
}

}
return result;
}

/**
* Remove items by LRU from mapHot
*/
public void trimMapHot() {
while (true) {
K key;
V value;
synchronized (this) {
if (sizeHot < 0 || (mapHot.isEmpty() && sizeHot != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}

if (sizeHot <= maxSizeHot || mapHot.isEmpty()) {
break;
}
// we add new item before, so next return first (LRU) item
key = mapHot.iterator().next();
mapHot.remove(key);
value = map.get(key);
sizeHot -= safeSizeOf(key, value);
map.remove(key);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}

/**
* Remove items by FIFO from mapIn & mapOut
* @param sizeOfValue
* @return
*/
private boolean trimMapIn(final int sizeOfValue) {
boolean result = false;
if (maxSizeIn < sizeOfValue) {
return result;
}
else {
while (mapIn.iterator().hasNext()) {
K keyIn = null;
V valueIn;
if (!mapIn.iterator().hasNext()) {
System.out.print("err");
}
keyIn = mapIn.iterator().next();
valueIn = map.get(keyIn);
if ((sizeIn + sizeOfValue) <= maxSizeIn || mapIn.isEmpty()) {
//put X into the reclaimed page slot
if (keyIn == null) {
System.out.print("err");
}
mapIn.add(keyIn);
sizeIn += sizeOfValue;
result = true;
break;
}
//page out the tail of mapIn, call it Y
mapIn.remove(keyIn);
final int removedItemSize = safeSizeOf(keyIn, valueIn);
sizeIn -= removedItemSize;

// add identifier of Y to the head of mapOut
while (mapOut.iterator().hasNext()) {
K keyOut;
V valueOut;
if ((sizeOut + removedItemSize) <= maxSizeOut || mapOut.isEmpty()) {
// put Y into the reclaimed page slot
mapOut.add(keyIn);
sizeOut += removedItemSize;
break;
}
//remove identifier of Z from the tail of mapOut
keyOut = mapOut.iterator().next();
mapOut.remove(keyOut);
valueOut = map.get(keyOut);
sizeOut -= safeSizeOf(keyOut, valueOut);
}
}
}
return result;
}

/**
* Check for free slot in any container and add if exists
* @param key
* @param sizeOfValue
* @return true if key added
*/
private boolean add2slot(final K key, final int sizeOfValue) {
boolean hasFreeSlot = false;
if (!hasFreeSlot && maxSizeIn >= sizeIn + sizeOfValue) {
mapIn.add(key);
sizeIn += sizeOfValue;
hasFreeSlot = true;
}
if (!hasFreeSlot && maxSizeOut >= sizeOut + sizeOfValue) {
mapOut.add(key);
sizeOut += sizeOfValue;
hasFreeSlot = true;
}
if (!hasFreeSlot && maxSizeHot >= sizeHot + sizeOfValue) {
mapHot.add(key);
sizeHot += sizeOfValue;
hasFreeSlot = true;
}
return hasFreeSlot;
}


/**
* Removes the entry for {@code key} if it exists.
*
* @return the previous value mapped by {@code key}.
*/
public final V remove(K key, V replace) {
if (key == null) {
throw new NullPointerException("key == null");
}

V previous;
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
if (mapIn.contains(key)) {
sizeIn -= safeSizeOf(key, previous);
mapIn.remove(key);
}
if (mapOut.contains(key)) {
sizeOut -= safeSizeOf(key, previous);
mapOut.remove(key);
}
if (mapHot.contains(key)) {
sizeHot -= safeSizeOf(key, previous);
mapHot.remove(key);
}
}
}

if (previous != null) {
entryRemoved(false, key, previous, null);
}

return previous;
}

/**
* Called for entries that have been evicted or removed. This method is
* invoked when a value is evicted to make space, removed by a call to
* {@link #remove}, or replaced by a call to {@link #put}. The default
* implementation does nothing.
*
*

The method is called without synchronization: other threads may
* access the cache while this method is executing.
*
* @param evicted true if the entry is being removed to make space, false
* if the removal was caused by a {@link #put} or {@link #remove}.
* @param newValue the new value for {@code key}, if it exists. If non-null,
* this removal was caused by a {@link #put}. Otherwise it was caused by
* an eviction or a {@link #remove}.
*/
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}

/**
* Called after a cache miss to compute a value for the corresponding key.
* Returns the computed value or null if no value can be computed. The
* default implementation returns null.
*
*

The method is called without synchronization: other threads may
* access the cache while this method is executing.
*
*

If a value for {@code key} exists in the cache when this method
* returns, the created value will be released with {@link #entryRemoved}
* and discarded. This can occur when multiple threads request the same key
* at the same time (causing multiple values to be created), or when one
* thread calls {@link #put} while another is creating a value for the same
* key.
*/
protected V create(K key) {
return null;
}

private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
}
return result;
}

/**
* Returns the size of the entry for {@code key} and {@code value} in
* user-defined units. The default implementation returns 1 so that size
* is the number of entries and max size is the maximum number of entries.
*
*

An entry's size must not change while it is in the cache.
*/
protected int sizeOf(K key, V value) {
return 1;
}

/**
* Clear the cache, calling {@link #entryRemoved} on each removed entry.
*/
public final void evictAll() {
Iterator it = map.keySet().iterator();
while (it.hasNext()) {
K key = it.next();
it.remove();
remove(key, map.get(key));
}
mapIn.clear();
mapOut.clear();
mapHot.clear();
sizeIn = 0;
sizeOut = 0;
sizeHot = 0;
}

/**
* For caches that do not override {@link #sizeOf}, this returns the number
* of entries in the cache. For all other caches, this returns the sum of
* the sizes of the entries in this cache.
*/
public synchronized final int size() {
return sizeIn + sizeOut + sizeHot;
}

/**
* For caches that do not override {@link #sizeOf}, this returns the maximum
* number of entries in the cache. For all other caches, this returns the
* maximum sum of the sizes of the entries in this cache.
*/
public synchronized final int maxSize() {
return maxSizeIn + maxSizeOut + maxSizeHot;
}

/**
* Returns the number of times {@link #get} returned a value that was
* already present in the cache.
*/
public synchronized final int hitCount() {
return hitCount;
}

/**
* Returns the number of times {@link #get} returned null or required a new
* value to be created.
*/
public synchronized final int missCount() {
return missCount;
}

/**
* Returns the number of times {@link #create(Object)} returned a value.
*/
public synchronized final int createCount() {
return createCount;
}

/**
* Returns the number of times {@link #put} was called.
*/
public synchronized final int putCount() {
return putCount;
}

/**
* Returns the number of values that have been evicted.
*/
public synchronized final int evictionCount() {
return evictionCount;
}

/**
* Returns a copy of the current contents of the cache, ordered from least
* recently accessed to most recently accessed.
*/
public synchronized final Map snapshot() {
return new HashMap(map);
}

@Override public synchronized final String toString() {
int accesses = hitCount + missCount;
int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
return String.format("Cache[size=%d,maxSize=%d,hits=%d,misses=%d,hitRate=%d%%," +
"]",
size(), maxSize(), hitCount, missCount, hitPercent)
+"\n map:"+map.toString();
}
}







Обратите внимание на контейнеры:

    /** Primary container */
private final HashMap map;
/** Sets for 2Q algorithm */
private final LinkedHashSet mapIn, mapOut, mapHot;




Управление «кучками» реализовано на супербыстрых и экономичных по памяти LinkedHashSet, нам не важно значение, важно лишь в какой «кучке» какой ключ находится в данный момент. Это ключ к быстродействию.



Больше практики. Допустим мы хотим прикрутить его к имадж лоадеру — пул реквест к пикассо: github.com/square/picasso/pull/1134

Но вообще это не обязательно. Нормальные либы — позволяют подключить произвольный алгоритм кеширования — достаточно скопипастить класс и переопределить кеш (glide вроде умел, picasso, начиная с какой то версии)



Я уже не помню точных цифр по хитрейту в своем случае. Помню только что у LRU — хитрейт был более 70% но менее 80. У 2Q — чуть более 80%. Но чудо произошло. Потому что все, что нам надо — это закешировать 20% инфы, которая составит 80% трафика. Чудо еще кстати состояло в том, что по скорости 2Q был быстрее LRU.



У нас в Relap.io, несколько реализаций кешей, например моя — github.com/recoilme/2qcache (вообще я не перл программист, это моя первая и надеюсь единственная программа на этом языке, единственный ее плюс — она простая).



Поэтому рекомендую посмотреть на реализацию 2Q на перле от нашего ведущего разработчика:



Реализация на перле, бам: github.com/grinya007/2q



Итак, не важно, что вы пишете: сайт, хайлоад-сервис, мобильное приложение или операционную систему, реализовав единожды умный алгоритм кеширования, вы можете использовать его повсеместно, экономя ресурсы и нервы пользователя.
Original source: habrahabr.ru.

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

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

Запуск сценариев автоматизации sparrow на удаленных серверах с помощью инструменатрия SparrowDO/SparrowUP

Пятница, 29 Июля 2016 г. 14:34 (ссылка)

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



До недавнего времени запуск подобных сценариев был ограничен консольным клиентом ( sparrow или sparrowdo ), сегодня я бы хотел презентовать пилотный проект по автоматической установке и запуску sparrow сценариев на удаленных серверах посредством централизованного web сервиса — SparrowUP.





Установка SparrowUP



SparrowUP — это web приложение, которое ставится на отдельный сервер, с которого инициируется установка и запуск sparrow сценариев на других серверах. Транспортом в данном системе выступает ssh. SparrowUP инициирует ssh сессии в рамках которых происходит:




  • установка sparrow клиента на целевом сервере ( операция "bootstrap" )

  • установка необходимого набора sparrow плагинов на целевом сервере

  • запуск установленных плагинов с набором указанных параметров на целевом сервере ( параметры запускаемых плагинов определяются в так называемом sparrow сценарии, смотрите далее )



Для того, что бы все это работало необходимо обеспечить беспарольный доступ со SparrowUP сервера на целевые сервера, так же sudo привилегии для пользователя, из под которого выполняются ssh команды.



Итак, установим SparrowUP:



Т.к. SparrowUP делегируют всю логику выполнения сценариев клиенту SparrowDO, необходимо сначала данный клиент.



В свою чоереь SparrowDO написан на Perl6, поэтому сначала установим необходимые perl6* зависимости ( Perl6, panda ), все подробности установки описаны здесь — http://rakudo.org/how-to-get-rakudo, для экономии времени не буду приводить здесь весь цикл установки. Установив Perl6 и pandа, ставим Sparrowdo как Perl6 модуль:



$ panda install Sparrowdo


Теперь ставим web приложение SparrowUP ( оно свою очередь написано на Perl5/Mojolicious ):



$ git clone https://github.com/melezhik/sparrowup.git
$ cd sparrowup
$ carton


Создадим базу данных, необходимую для работы SparrowUP:



$ bash utils/populate_db.bash


Скачаем репозитарий со sparrow сценариями:



$ git clone https://github.com/melezhik/sparrowdo-test.git


Структура репозитария должна быть следующей:



project-dir/sparrowfile
project2-dir/sparrowfile
project3-dir/sparrowfile
и так далее


Таким образом, каждая папка-проект должна содержать один файл — sparrowfile с описанием sparrow сценария, формат и структура которого описаны в документации по SparrowDO. Вот как, например может выглядеть сценарий по установки CPAN пакетов Moose и DBIx::Class :



#!/usr/bin/env perl6

use v6;

use Sparrowdo;

task_run %(
task => 'cpan-package',
plugin => 'cpan-package',
parameters => %(
list => 'Moose DBIx::Class',
install-base => '/opt/lib/perl'
),
);


А вот как выглядит список проектов в репозитарий со сценариями https://github.com/melezhik/sparrowdo-test:



$ ls -1

complex
cpan-package
cpan-package2
df-check
git-base
group
nano-setup
package-generic
perl-app
proc-validate
README.md
service
templater


Далее мы должны настроить конфигурационный файл SparrowUP ( /etc/sparrowup.conf ), указав директорию, в которую мы зачекаутили репозиторий со сценариями и так же директорию, в которой SparrowUP будет сохранять файлы отчетов ( директория должна быть доступна на запись пользователю из под которого будет запущен сервис SparrowUP ), вот как это выглядит на моей машине:



$ /etc/sparrowup.conf
{
'repo' => '/home/melezhik/projects/sparrowdo-test/',
'reports_dir' => '/home/melezhik/whatsup-reports'
}


Окей, теперь можно запускать web сервис SparrowUP, для просты примера воспользуюсь обычным "nohup" ( для production системы можно конечно же написать отдельный инит скрипт ):



$ cd sparrowup
$ nohup carton exec morbo app.pl


Зайдя по адресу 127.0.0.1:3000 убедимся, что интерфейс доступен:



image



Запуск sparrow сценариев



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



$ cat df-check/sparrowfile

use v6;

use Sparrowdo;

task_run %(
task => 'check my disk',
plugin => 'df-check',
parameters => %(
threshold => 70
)
);


image



Запущенная задача добавляется в очередь задач, о чем нас уведомляют.



image



Выполнение задач происходит асинхронно. Что бы задачи попавшие в очереди начали выполнятся необходимо запустить обработчик задач ( это можно было сделать еще в самом начале установке перед запуском сервиса, просто в момент написания статьи я забыл об этом :)



$ cd sparrowup
$ nohup carton exec ./app.pl minion worker &


Приложение SparrowUP написано на фреймворке Mojolicious и в качестве шедулера задач как следствие используется модуль Minion — все кому интересно могут обратится к документации для знакомства с данными инструментами.



Окей, спустя какое-то время шедулер начнет выполнять задачу:



image



image



И мы сможем увидеть результат в виде SparrowUP отчета:



image



А вот как, например, будет выглядеть отчет сценария proc-validate, который проверяет, что процесс nginx сервер запущен в системе:



$ cat proc-validate/sparrowfile

use v6;

use Sparrowdo;

task_run %(
task => 'check my process',
plugin => 'proc-validate',
parameters => %(
pid_file => '/var/run/nginx.pid',
footprint => 'nginx.*master'
)


);



image



Заметим, что запуск новой задачи возможен и программным способом посредством HTTP API:



$ curl 127.0.0.1:3000/df-check -d server=127.0.0.1 


Вы так же можете указывать дополнительные параметры, например, задать имя пользователя и номер порта для ssh соединения:



$ curl 127.0.0.1:3000/df-check -d server=192.168.0.1 -d ssh_user=sparrow -d ssh_port=23


Перезапуск задачи может быть осуществлен либо повторным выполнением заданного проекта для того же сервера, либо кнопкой "Reschedulle Job".



Ограничения ( что еще предстоит сделать или обсудить )




  • операция "bootstrap" — установка sparrow клиента на целевом сервере ( если клиент еще не установлен ) — реализована только для серверов под управлением дистрибутива CentOS. Если заинтересует — пишите — добавлю поддержку других дистрибутивов.




  • нет возможности настраивать через web интерфейс SparrowUP параметры ssh соединения для различных целевых серверов, на которых запускаются sparrow сценарии, сейчас я правлю конфиг ~/.ssh/config и пробрасываю ключи (ssh-copy-id) вручную на сервере, на котором запущен SparrowUP. Не уверен, правда, что такого рода настройки вообще есть смысл вытаскивать наружу, через web интерфейс.




  • сделать возможным настраивание цветовой схемы отчетов ( сейчас черный фон — не самое приятное для глаз решение ;)



Заключение



На этом знакомство с сервисом SparrowUP можно завершить. Спасибо за внимание!



Как обычно, жду вопросов и конструктивных замечаний.



PS Всех, кого это касается — поздравляю с днем системного администратора!



PS2 В конце статьи традиционно опрос, напрямую не касающийся основного топика.





какую систему мониторинга вы используете


























































Никто ещё не голосовал. Воздержавшихся нет.





Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.


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

https://habrahabr.ru/post/306684/

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

Как развивался Perl — необычный язык, созданный лингвистом для программистов

Понедельник, 11 Июля 2016 г. 16:44 (ссылка)





Язык программирования Perl создал Ларри Уолл (Larry Wall) в 1987 году, когда он работал системным программистом в американской компании Unisys. Цели, которые преследовал Ларри при разработке нового языка программирования, отражены в его названии — PERL, которое позднее стало расшифровываться как Practical Extraction and Report Language, то есть «практический язык извлечения „данных“ и „создания“ отчетов».



Одна из легенд о происхождении Perl гласит, что в то время уже существовал язык с таким названием, поэтому Ларри сократил название на одну букву, не изменив произношения. По иронии судьбы, сегодня тот язык не помнит никто, кроме историков, а Perl известен всему миру.



Хотя, по другой версии, Ларри назвал созданный им язык по имени своей невесты.





Ларри Уолл



На становление Perl повлияли языки Ada, Basic, Fortran, Lisp, Pascal и, конечно, язык C. Perl впитал в себя разные течения компьютерной науки: процедурное, модульное, функциональное и объектно-ориентированное программирование, макрообработку текста, а кроме этого — культуру ОС Unix, с ее богатым набором команд и утилит, стройной продуманной архитектурой и унифицированным подходом к представлению данных.



Наследие Unix



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



Для тех, кто знаком с операционной системой Unix, этот материал не будет новым. Но многим программистам, работающим с MS-DOS и Windows, важно познакомиться с принципами программирования, основанного на инструментальных средствах (softwaretools), которое зародилось и развивалось вместе с ОС Unix. О них написали замечательную книгу известные программисты Брайан Керниган (Brian W. Kernighan) и Филип Плоджер (Philip Plauger). Вот основные положения этого подхода.



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



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



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



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



• Объединение инструментальных средств в приложения производится средствами командного языка ОС: перенаправление ввода-вывода, создание программных конвейеров (направление выходного потока одной программы на вход другой).



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



• Для эффективной обработки слабо структурированной текстовой информации применяются регулярные выражения.



Благоприятные факторы



Язык быстро распространился среди пользователей сети Usenet. Несмотря на то, что в операционной системе (ОС) Unix, для которой был создан Perl, уже имелись многочисленные и разнообразные средства для обработки текстовой информации (awk, csh, grep, sed и другие), новый язык полюбился огромному числу системных администраторов и программистов. Он был легок в изучении и применении: синтаксис похож на С, Perl-программы не требовалось предварительно компилировать, исходные тексты было легко модифицировать. А самое главное — это был действительно очень практичный язык: с его помощью легко решалось большинство повседневных задач — от самых простых до очень сложных.







Активно пользуясь языком Perl, программисты из разных стран направляли Ларри Уоллу предложения добавить в него новые возможности или улучшить имеющиеся. Постепенно Perl превратился из средства обработки текстов в среде Unix в мощную универсальную систему программирования. В середине 1990-х годов, по мере развития интернета, Perl стал излюбленным инструментом web-мастеров для создания динамических сайтов и Internet-программирования.



Благодаря своим мощным встроенным средствам работы с текстовыми данными Perl широко используется для обработки информации в форматах HTML и XML.



Perl стал непременным компонентом любой поставки ОС семейства Unix (например, FreeBSD, Linux или Solaris). Кроме этого, к настоящему времени Perl реализован практически для всех современных аппаратных платформ (суперкомпьютеры, RISC, Macintosh, PC, наладонные компьютеры) и операционных систем (AIX, Linux, MacOS, MS-DOS, NetWare,OS/2, QNX, Solaris, Windows, VMS и так далее).



Это дает возможность легко переносить популярные Perl-программы из одного операционного окружения в другое. (К слову сказать, примеры к этому курсу лекций проверялись под операционными системами SuSE Linux 10.0, MS Windows XP Professional SP2, и MS Pocket PC 2003Premium Edition v. 4.20).



Несомненно, его широкому применению способствовало и то, что он распространяется бесплатно на условиях одной из лицензий: либо GNU GeneralPublic License (GPL), либо Artistic License — на выбор. Но главное, что в нем есть все средства, чтобы отлично выручать профессионалов и неопытных программистов, когда требуется быстро решать разные системные и прикладные задачи — от самых простых до весьма сложных. В результате Perl стал одним из самых успешных проектов движения open source (с открытыми исходными кодами) — наряду с Apache, Linux, PHP и Python.



Необычный язык программирования



Принципы разработки Perl сильно отличаются от применявшихся в программировании до этого. Новаторский характер языка был связан с уникальными особенностями личности автора, Ларри Уолла, и его разносторонними интересами.



Во время обучения в университете Ларри Уолл получил лингвистическое образование. Кроме того, он занимался программированием и работал в университетском компьютерном центре.



Возможно, главная заслуга Ларри как автора Perl заключается в том, что ему удалось создать язык, учитывающий свойства и принципы естественного языка: ситуативную многозначность конструкций, разумную избыточность (и за счет этого стилевое разнообразие), максимальную адаптированность к различному уровню знаний носителей языка, гибкость и выразительность синтаксических средств. Все это придает особый творческий вкус самому процессу «сочинительства» на этом языке.



Пишущие о языке Perl невольно употребляют термины «идиома», «метафора», «синоним», «контекст» и т.п., обычно применяемые к естественным языкам. А сам Ларри Уолл, описывая язык Perl, широко пользуется лингвистическими терминами: существительное, глагол, предложение, единственное и множественное число, местоимение.



Выдающийся лингвист Б.Л. Уорф заметил: «Язык формирует наш способ мыслить и определяет, о чем мы можем думать». Эту же мысль подтверждает Дж. Бентли в книге «Жемчужины творчества программистов», описывая случай, когда именно особенности языка программирования APL натолкнули разработчика на оригинальное решение задачи с массивами. В этом смысле Perl как язык для воплощения замыслов программиста, не сковывает фантазию разработчика, дает ему максимальную свободу самовыражения, а иногда даже подсказывает нестандартные решения.



В качестве недостатков языка упоминают:


  • отсутствие механизма статической типизации;

  • избыточность языковых средств;

  • отсутствие модульности;

  • «извращенную» реализацию ООП.



Лингвистические принципы Perl



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



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



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

$we->shall(do($it)) if $you->want($it); # или

if $you->want($it) { $we->shall(do($it)) }


Принцип свободы в естественных языках предполагает, что всегда есть несколько вариантов выражения одной и той же мысли. Как и в естественных языках, в Perl одного и того же результата можно достичь несколькими способами, используя различные выразительные средства языка. Это так называемый принцип TIMTOWTDI- сокращение читается «Тим Тоуди» и расшифровывается как «There is more than one way to do it»: «есть более одного способа сделать что-то». В соответствии с ним каждый разработчик выбирает тот вариант языковой конструкции, который больше подходит ему в конкретной ситуации, больше соответствует его стилю или уровню знания языка.



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



Первые версии и «Верблюжья книга»



Ларри Уолл начал разработку Perl в 1987 году, когда работал программистом в Unisys. Версия 1.0 была выпущена и анонсирована 18 декабря 1987 года как «„замена“ для awk и sed».



Perl 2 был выпущен в 1988 году. Основным нововведением в нём был переработанный механизм регулярных выражений.



Perl 3, выпущенный в 1989 году, получил возможность обрабатывать потоки двоичных данных.



Изначально, единственной документацией для Perl была единственная (огромной длины) man-страница. В 1991 году была выпущена книга «Programming Perl» (известная многим программистам как «Верблюжья книга» (Camel Book) из-за изображения на обложке), которая, де-факто, стала стандартом языка.







В то же самое время, версия языка была «поднята» до 4 й — не столько чтобы указать на значительные изменения, сколько чтобы обозначить, что эта версия языка документирована книгой. Perl 4 прошёл через серию релизов, остановившись на Perl 4.036 в 1993 году.



Perl 5



Разработка Perl 5 была начата в 1994 году. В то же время был создан список рассылки perl5-porters для координации работы над портированием Perl 5 на различные платформы. Он до сих пор остаётся основным форумом по разработке, обслуживанию и портированию Perl 5.



Perl 5.000 был выпущен 17 октября 1994 года. Он включал в себя полностью переписанный интерпретатор, а также много новых языковых возможностей, таких, как объекты, ссылки, локальные переменные (my $var_name) и модули. Особенно важной частью были модули, так как они предоставили механизм расширения языка без модифицирования интерпретатора. Это позволило стабилизировать интерпретатор, но при этом, дало возможность обычным Perl-программистам добавлять в язык новые возможности. Perl 5 находится в активной разработке по сей день.



Perl 5.001 был выпущен 13 марта 1995 года. Perl 5.002 увидел свет 29 февраля 1996 года с новой поддержкой прототипов. Это позволило авторам модулей создавать функции, которые вели себя так же, как и встроенные функции Perl. Версия Perl 5.003 появилась 25 июня 1996 года и устранила обнаружившиеся проблемы с безопасностью.



Одно из важнейших событий в истории Perl 5 случилось за пределами собственно языка и было следствием поддержки модулей. 26 октября 1995 года начала свою работу CPAN (Comprehensive Perl Archive Network — «всеобъемлющая сеть архивов Perl»), которая стала репозиторием модулей Perl, а также исходного кода самого языка. На данный момент сеть насчитывает более 25 000 модулей, созданных более чем 8000 программистами.



Perl 5.004 был выпущен 15 мая 1997 года и содержал, кроме всего прочего, пакет UNIVERSAL, который давал языку основной объект, от которого автоматически происходили все классы. Была также включена возможность запрашивать версию модулей. В дополнение к этому Perl стал поддерживать Microsoft Windows, а также ряд других операционных систем.



Perl 5.005 увидел свет 22 июня 1998 года. Этот релиз включал в себя несколько улучшений процессора регулярных выражений, новые перехваты в бэкенд с помощью модулей B::*, оператор кавычек qr// для регулярных выражений, большой выбор других новых модулей, а также поддержку ряда операционных систем, в частности, BeOS.



2000-е годы



Perl 5.6 был выпущен 22 марта 2000 года. Главные изменения включали поддержку 64-битных систем, представление строк в стандарте Юникод, поддержку больших файлов (файлы больше 2 Гб) и ключевое слово our. Во время разработки Perl 5.6 было принято решение изменить схему наименования версий на более близкую к другим проектам с открытым исходным кодом. За версией 5.005_63 следовала версия 5.5.640; согласно плану, версиям в разработке должны были присваиваться нечётные номера, стабильным версиям — чётные.



В 2000 году Ларри Уолл выступил с призывом к сообществу активно обсуждать предложения к следующей версии Perl. Результатом этого обсуждения стал361 документ RFC (Request for Comments — «запрос комментариев»), которые были использованы в разработке Perl 6.



В 2001 году была начата работа над окончательным документом, подводящим итог предложениям к созданию нового поколения Perl. Результат был представлен не в виде формального документа, а в качестве сводки всех RFC. К этому времени Perl 6 существовал исключительно в виде описания языка.



Perl 5.8 был впервые выпущен 18 июля 2002 года и с этого времени получал ежегодное обновление. Последняя версия Perl 5.8, под номером 5.8.9, увидела свет 14 декабря 2008 года. Perl 5.8 улучшил поддержку Юникода, добавил новую реализацию ввода-вывода, добавил поддержку многопоточности, увеличил числовую точность и добавил несколько новых модулей.



В 2004 году началась работа над Синопсисом (Synopsis) — первоначально задуманном как ряд документов, подводящих итоги окончательного документа, упоминавшегося выше. Однако постепенно этот ряд документов и стал спецификацией Perl 6. В феврале 2005 года Отриус Тан начала работу над Pugs — интерпретатором Perl 6, написанном на Haskell. Это было первой настоящей попыткой воплотить Perl 6 в реальность. Эта попытка заглохла в 2006 году.



Когда тебе за 20



18 декабря 2007 года, в день 20-летней годовщины Perl 1.0, была выпущена версия Perl 5.10.0. Эта версия содержала в себе ряд заметных нововведений, приближающих её к Perl 6. Одними из таких нововведений стали новые операторы switch (названных «given»/«when»), обновление регулярных выражений, а также «умный» оператор совпадения «~~».





«Жемчужный» юбилей



Примерно в это же время всерьёз началась разработка новой реализации Perl 6, известной как Rakudo Perl, разработанной в связке с виртуальной машиной Parrot. С ноября 2009 года Rakudo Perl регулярно обновляется каждый месяц и является на данный момент самой полной реализацией Perl 6.



Значительное изменение процесса разработки Perl 5 произошло после появления Perl 5.11. Сообщество разработчиков перешло на ежемесячный цикл выпусков, с планированием даты выпуска на три месяца вперёд.



12 апреля 2010 года Perl 5.12.0 был представлен общественности. Выдающиеся нововведения включают в себя поддержку нового синтаксиса package NAME VERSION, оператор Yada Yada (служащего для маркировки кода-заполнителя, который ещё не реализован), полное решение «проблемы 2038 года» (Y2038), перегрузку операторов регулярных выражений, поддержку DTrace (фреймворка динамической трассировки), а также полную поддержку стандарта Unicode 5.2.



7 сентября 2010 года был выпущен Perl 5.12.2, а 21 января 2011 — Perl 5.12.3. Эти выпуски содержат обновление модулей, а также некоторые изменения в документации. Версия 5.12.4 выпущена 20 июня 2011. Последняя версия этой ветви, 5.12.5, выпущена 10 ноября 2012.



Крайняя пятилетка



14 мая 2011 выпущен Perl 5.14. Поддержка JSON стала встроенной, начиная с версии 5.14.2. Последняя версия этой ветви, 5.14.4, выпущена 10 марта 2013.



20 мая 2012 выпущен Perl 5.16. Значимым новшеством является возможность указать нужную версию Perl, которую надо эмулировать, позволяя пользователям обновить Perl, но сохранить работоспособность старых скриптов, которые иначе стали бы несовместимыми с новой версией. Ядро Perl 5.16 поддерживает Unicode 6.1.



18 мая 2013 выпущен Perl 5.18. Значимые нововведения включают новые зацепки dtrace, лексические функции, увеличение числа включенных в ядро функций, пересмотр реализации хешей из соображений безопасности, поддержка Unicode 6.2.



27 мая 2014 года была выпущена новая стабильная версия — Perl 5.20.0. Разработка велась примерно 12 месяцев, начиная с Perl 5.18.0, и содержит примерно 470 000 изменённых строк среди 2900 файлов от 124 авторов.



В декабре 2015 года вышел Perl 5.22.1. Разработка велась 6 месяцев.



9 мая 2016 года была выпущена новая стабильная версия языка программирования Perl 5.24.0. Разработка велась примерно 11 месяцев, начиная с Perl 5.22.0, и содержит примерно 360 000 изменённых строк среди 1800 файлов от 77 авторов.



Согласно данным на июль 2016 года, индекс TIOBE, который измеряет рост популярности языков программирования, показал, что Perl занимает 9 место:





Это на 2 позиции выше, чем в прошлом году.



Perl 6



Как отмечается в «Википедии», Perl 6 — очередная версия языка программирования Perl (с точки зрения строения, Perl 6 удобнее рассматривать как самостоятельный язык программирования). Является революционным развитием языка, последний написан практически «с чистого листа». Впервые за всю историю Perl разработка новой версии была начата с написания спецификаций, которые претерпевают изменения и по сей день.



Perl 6, компилируя программу, создаёт байт-код, который в дальнейшем исполняет виртуальная машина. Тот же подход применяется в технологиях Java и .NET Framework.



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



Разработка Perl 6 началась в 2000 году и продолжается и по сей день.



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



Ларри Уолл в шутку провозгласил три главных добродетели программиста: лень, нетерпение и самомнение. По его мнению, Perl спроектирован так, чтобы соответствовать этим качествам разработчика.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/305402/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
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)КомментироватьВ цитатник или сообщество

Следующие 30  »

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

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

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