Недавно родилась мысль написать скрипт для хорошего распределения трафика между всеми своими партнерками. Использовать чужие не всегда удобно, ведь своя рубаха ближе к телу. К тому же его можно всегда расширить без проблем.
Итак, начнем. Основная идея такова: создадим нечто вроде очереди, замкнутой в кольцо. В очереди каждый узел – это отдельный URL платника. Далее при обращении к скрипту происходит выбор. Я организовал 2 типа выбора из набора платников: равномерный рандом и выбор по очереди в цепочке. Давайте начнем смотреть на код и станет все понятно.
Первое – это класс отдельного узла. Как я говорил узел – это URL платника с определенной служебной информацией. Рассмотрим:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
class Node { private $url; private $is_enable;//доступен ли url protected $prev;//указатель на предыдущий объект в очереди protected $next;//указатель на следующий объект в очереди protected $is_root; function __construct($url,$is_enable=true,$is_root=false){ $this->is_enable=$is_enable; $this->url=$url; $this->is_root=$is_root; } function enable(){ $this->is_enable=true; } function disable(){ $this->is_enable=false; } function getStatus(){ return $this->is_enable; } function getUrl(){ return $this->url; } function setNext($node){ $this->next=$node; } function getPrev(){ return $this->prev; } function getNext(){ return $this->next; } function setPrev($node){ $this->prev=$node; } function getRoot(){ return $this->is_root; } function setRoot($bool){ $this->is_root=$bool; } } |
Тут все просто. Создаем узел со свойствами $url, $is_enable, и т.д. Все они перечислены в коде и даже с комментариями. Скажу отдельно о $is_root. Его я сделал пока исключительно для порядка, как определитель корня, то есть условного начала кольца. Возможно в будущем пригодиться, хотя и сейчас можно использовать его для постановки определенных требований. В остальном же методы класса просты. Это сам конструктор, которые инициализирует наш узел, а далее ряд сеттеров и геттеров для получения и установка служебных значений. Например enable() делает наш адрес активным, а disable() деактивирует его (например платник закрылся).
К настоящему моменту у нас есть узел. Напишем теперь класс кольца.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
class Circle{ protected $nodes=array(); protected $root; function addNode(Node $node){//функция добавления узла в нашу цепь if(empty($this->nodes)){ $node->setPrev($node); $node->setNext($node); $node->setRoot(true); $this->nodes[0]=$node; $this->root=$node; }else{ $node->setPrev($this->nodes[count($this->nodes)-1]); $node->setNext($this->root); $this->nodes[count($this->nodes)-1]->setNext($node); $this->nodes[]=$node; } } function getNodes(){ return $this->nodes; } function getNode($num){ return $this->nodes[$num]; } function getCurrentUrlByRandom($need=0){ $bads=array(); if(count($this->getNodes())-1>=$need && $this->getNode($need)->getStatus()==true){ return $this->getNode($need)->getUrl();//если партнерка доступна, то направим на нее }else{ $bads[]=$need; while(1){//будем искать доступную партнерку при помоща равномерного рандома $r=rand(0,count($this->nodes)-1); if(count($bads)>3*count($this->nodes)) break;//если так и не нашли, то выходим if($this->getNode($r)->getStatus()==true){ return $this->getNode($r)->getUrl(); }else{ $bads[]=$r; } } } return 'http://aabramoff.ru/';//иначе скидываем трафик сюда } function getCurrentUrlByOrder($need=0){ $counts=0; if(count($this->getNodes())-1>=$need && $this->getNode($need)->getStatus()==true){ return $this->getNode($need)->getUrl();//если партнерка доступна, то направим на нее }else{ $cur=$this->getNode($need)->getNext(); while($counts<=count($this->nodes)){ if($cur->getStatus()==true){ return $cur->getUrl(); } $counts++; $cur=$cur->getNext(); } } return 'http://aabramoff.ru/';//иначе скидываем трафик сюда } } |
Как видим тут посложнее. Ну в первую очередь тут есть сам массив для хранения узлов кольца $nodes и корневой элемент кольца $root. Далее имеется метод addNode добавления к кольцу узла. Не будем на нем подробно останавливаться, так как по поводу очередей много написано в сети, в частности о добавлении удалении узла и т.д. Скажу только, что первое что мы делаем это проверяем имеется в кольце у нас узлы. Если нет, то добавляем текущий и делаем его root-элементом, а далее выставляем указатели на себя же. А если элементы в кольце уже имеются просто приписываем их в конец и направляем указатели нового узла и бывшего последним. Дальше пара служебных методов по извлечению массива всей очереди и извлечения из массива определенного элемента.
Самый интерес представляют функции getCurrentUrlByRandom и getCurrentUrlByOrder. Они являются составляющими всего алгоритма управления. Первый – будет производить направление на url, а в случае недоступности выберет по равномерному распределению из имеющихся доступных. Второй метод – также произведет направление на url, а в случае недоступности выберет следующий в очереди до тех пор, пока не найдет доступный, если таковой имеется. Я не случайно в обоих случаях сказал просто “направление на url”. Тут можно использовать методы по необходимости. Можно указать определенный элемент для направления и отталкиваться от этого. Но если не указывать параметров при вызове, то началом будет считаться root-элемент и первым мы будет “стучаться” по его url. Так же есть ситуация, когда ни один из url платников не доступен (редкая, конечно, но все же). В таком случае оба метода после определенного количества итераций поиска заканчивают работу, вернув какой-то url-по-умолчанию. Я его пометил в коде, можно заменить на свой.
В целом вышло не так уж и сложно. Давайте взглянем как этим пользоваться на практике.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
require_once 'Node.php'; require_once 'Circle.php'; $nodes=array('http://www.mihuuudeem.ru/','http://www.protectpc.ru/','http://www.adult.ru/','http://www.pelengator.ru/'); $circle=new Circle(); foreach($nodes as $node){ if($ch=curl_init()){ curl_setopt ($ch, CURLOPT_URL, $node); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); curl_setopt ($ch, CURLOPT_REFERER, $node); curl_setopt ($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_HEADER, 1); curl_exec ($ch); $code=curl_getinfo($ch,CURLINFO_HTTP_CODE); if($code!='200' && $code!='301') $status=false; else $status=true; } $circle->addNode(new Node($node,$status)); } header('location: '.$circle->getCurrentUrlByOrder()); |
Итак, использование тоже очень важно. Сперва подключили наши классы. Далее завели массив, где перечислили наши url всех наших платников. Это пожалуй единственное место для активного редактирования. Далее создаем экземпляр кольца и начинаем в него писать. Причем очень хитро. Узлы мы создаем налету, проверяя их доступность. Для этого используем cURL. Если мы получаем от данного url заголовок с HTTP_CODE равным либо 200, либо 301, то можно считать, что все с ним в порядке и пометить этот url как доступный (true), иначе он лежит (false). Таким образом пробегаем по url в цикле и создаем узлы, сразу добавляя в кольцо. Ну а дальше все просто. Пишем header(‘location: ‘.$circle->getCurrentUrlByOrder())или header(‘location: ‘.$circle->getCurrentUrlByRandom()). От этого будет зависеть политика выбора. Кроме того мы могли сделать так:
1 2 3 4 5 6 7 |
if(isset($_GET['id'])){ $id=$_GET['id']; header('location: '.$circle->getCurrentUrlByOrder($id)); } |
Так бы мы могли получить из запроса $id и попытаться постучаться по конкретному url, а далее уже, отталкиваясь от него искать замену в случае необходимости.
В принципе все. Использовать данные скрипты можно очень разнообразно, постоянно расширяя под свои нужды. Самое главное, на мой взгляд, удобство – это возможность создания целого ряда колец. Никто не запрещает создавать множество очередей (например, разной тематики), определять свой порядок следования url в очереди (по важности, к примеру) и выставлять для них свои политики управления трафиком. Повторюсь, все зависит от нужд.
На предложения и замечания отвечу в комментариях.
Классно, как раз то о чем мы думали!)
Только вот код для меня сложноват, не все понял. А как его можно применить на практике без особых знаний? Просто мой самописный уже меня совсем не устраивает, а вот ТДС юзать не охота, на фришные вроде фильтры стоят..
@Ярик, использовать довольно просто. можно взять этот код без изменений и только внести url’ы в массив в начале последнего скрипта. а внутри класса Circle только поправить те url’ы, куда будет литься трафик по умолчанию (ну я там записал, для примеры, адрес своего сайта). При желании можно сделать несколько колец аналогичным способом и разделить массив с url’ами платников на несколько.
партнерки дело хорошее, молодцы что код написали))
еле добил полученный код, только все-равно сложновато вышло с кодом, может полегче будет что?
на самом деле это наиболее логичное и правильное построение (зявляю как сторонник ООП :))
здесь довольно прозрачные код и говорящие имена. если есть вопросы задавай.