Ладжър, Имперо, Елементс, Софтуер по поръчка, Консултиране

сеп/08

19

Създаването на Seenews

LagerSeeNews – Know How to SEE: SeeNews e мащабен проект, разработка на Ладжър и AII Data Processing, дългогодишен наш клиент.
SeeNews е бизнес и финансов новинарски и информационен портал.
Основен фокус в информацията, предоставянa в уеб сайта, са държавите от Югоизточна Европа.

Структурата на съдържанието е организирана в следните части:

  • Материали (по вид, тема, избор на редактора, избор на читателите)
  • Профили (на държави, индустрии, компании, хора)
  • Отчети на брокери
  • Издатели (SeeNews – The Corporate Wire, Капитал, Дневник)
  • Анализи и проучвания
  • Събития (Exhibitions, Fairs, Conferences)
  • Сделки (Сливания, Придобивания, Договори)
  • Търгове
  • Институции
  • Партии
  • Избори
  • Информация за пазара (Котировки, Индекси, Обмяна, Валути)
  • Полезна информация (Празници, История, Информация за времето, Туристическа информация)

SeeNews дава възможност за превключването на информацията по editions – филтриране на информацията по държава от региона на Югоизточна Европа.

Уеб сайтът дава достъп на клиентите и потребителите си до детайлна информaция за бързоразвиващи се компании, икономики и финансови пазари от региона.
Източник е на информация за инвестиционното банкиране, управлението на фондове и активи, капиталовите пазари. Съдържа новини, коментари, анализи и проучвания на репортерите и кореспондентите на SeeNews – The Corporate Wire, както и информация за валути, търгове и сделки, профили на държави, компании и личности, отчети на боркери, събития от региона.

По-голямата част от уеб сайта е изградена върху Ajax.
Уеб сайтът предлага високо ниво на персонализация в дефиницията на продуктите – вследствие на силно динамична архитектура на приложението.

Interactive Context Blocks – templates – в целия уеб сайт са динамично контекстно ориентирани, а именно при преглед на информация, потребителят вижда съответна, свързана информация към нея.

SeeNews предлага набор от безплатни услуги: Alerts, които излизат в реално време, Бюлетини и RSS.

По-голямата част от публикуваните материали в уеб сайта са платени.
Публично-персоналната част е отделена в самостоятелен уеб сайт: SeePassport. През него потребителите могат да управлява своите профили, да закупуват онлайн материали от SeeNews, да управляват депозити и плащания, както и да персонализират съдържание и функционалностите в сайта.
Клиентът
АИИ Дейта Процесинг: „АИИ Дейта Процесинг“ ООД е българска осведомителна компания, специализирана в предоставянето на новини на английски език и на финансова информация. В дейността й са включени и разработването на софтуер, събирането на данни, както и други сходни услуги.
Нашето решение
A. Сървъри и приложения

A.1. Описание на проекта

SeeNews е портал за новини и профили на компании, държави и хора. Материалите и профилите се попълват от специално CMS приложение, което освен сайта, изпраща материали и на други клиенти.

Сайтът има общ login (общ за SeeNews и SeeWire), който се управлява от сайта SeePassport. SeeWire е сайт само за бутикови новинарски материали.

A.2. Схема

Решението на SeeNews е разположено на 2 сървъра. На Server 1 са разположени CMS приложението, банер системата за сайта и 2 приложения, които публикуват материали към външните клиенти на AII Data Processing и SeeNews (Publisher и SeePublisher). На Server 2 са разположени сайтовете SeeNews, SeeWire, SeePassport и CMS-а на сайта.

B. Процеси

B.1. Публикуване

От CMS приложението се публикуват записи в две посоки – към директни (външни) клиенти на приложението (логика, която не зависи от уеб сайта) и към уеб сайта.

B.1.1. Външни клиенти

След публикуване на материал от CMS-а, по предварително дефинирани динамични правила се определя на кои клиенти трябва да се прати. Всеки клиент има изискване за определен формат, в който да се изпраща материалът и метода на доставка (FTP, e-mail), като цялата логика около определянето на форматите и адреси е динамична и се контролира от CMS-а. Файловете и адресите, на които трябва да се пратят се попълват в специална база данни. Самото пращане се извършва от приложението Publisher.

Publisher-а е написан на Perl и работи резидентно. Следи опашки от новини, които трябва да се доставят и се грижи те да бъдат доставени, като прави допълнителни проверки в зависимост от метода на доставка за да гарантира успешна доставка.

Към уеб сайта се публикува по-голямата част от информацията в CMS-a. Информацията се публикува на ниво база данни и по същество представлява репликиране на данните от единия на другия сървър. Има две основни логики на публикуване в CMS-а – изрично и по подразбиране. Определени типове данни минават дълъг процес на одобрение преди да бъдат публикувани и при тях процеса е изричен, като това се обслужва от специални скриптове, тъй като става дума за публикуване на информация в десетки релационни таблици наведнъж. Останалите типове данни се публикуват в момента на въвеждане в CMS-а. Кой тип данни се публикува и кой не, се определя също динамично от CMS приложението.

Публикуването на нов запис към уеб сайта е силно обвързано с кеширането на данните на сайта. Затова приложението, което публикува (SeePublisher) има възможност да му бъде указано, че след публикуването на записи от определени таблици да изпълнява определени команди на Server 2.

Публикуването на материали е относително просто, защото става дума само за 6 таблици, като информацията в 6-те таблици винаги се въвежда по едно и също време. В този случай единственото, което се указва, е уведомяването на Server 2 (което ще бъде разгледано по-надолу) за публикуването на нов материал.

От друга страна, публикуването на профилите предоставя няколко сериозни предизвикателства:
Данните стоят в 20+ релационни таблици
След публикуване на информацията във всички таблици е необходимо да бъде уведомен Server 2 за това.

Профилите на компании, индустрии и държави се публикуват изрично, т.е. след изрична итерация от потребителя в CMS-а. За целта на публикуването на профили към външни клиенти има изискване файла с информацията за профила да е в стила на уеб сайта. Тъй-като такава логика (по представянето на профила) имаше вече направена в уеб сайта беше непрактично да я повтаряме в CMS-а, защото става дума за огромно количество код (над 3000 реда) и логика. Затова решихме след публикуване на материал да взимаме output-а от уеб сайта и да го връщаме като файл на CMS сървъра. Тази операция се извършва от SeePublisher приложението. За да може да заработи тази логика обаче, трябва SeePublisher-а да знае точно кога е публикувал целия профил, за да може да извика скрипта за генериране на файла на профила, който да върне обратно и да уведоми локален процес, който пък поставя файла за одобрение от потребителите в CMS-а. За целта дефинирахме „партиди” – групи от записи в различни таблици, които фигурират срещу общо име и команди, които да се изпълняват при публикуване на всяка партида.

Освен записи от базата, SeePublisher-а публикува и файлове, качени в CMS-а – файловете се качват в приложението, resize-ват се, обработват се и всички резултатни файлове, заедно с кореспондиращия запис в базата се записват за публикуване.
B.2. Борсова информация и време

Борсовата информация и времето се импортват от отделни скриптове в cron-а на Server 2.
B.2.1. Борсова информаци

Тъй-като борсовата информация трябва да е за цяла Югоизточна Европа, данните се взимат от няколко различни доставчика – интегратори и борси. Данните се управляват от CRM приложението.

Логиката за това коя борса кога затваря е писана отделно за всеки случай.
C. Уеб сайтове
ERP
C.1. Генериране и кеширане на информацията

Още при проектирането на сайтовете бяхме наясно, че тук с обичайната логика (директна връзка с базата) няма да можем да постигнем желаният резултат и ще трябва да правим някакво кеширане на информацията.
C.1.1. Url-та на сайта

Доброто SEO на сайта изисква пътищата да бъдат „human-readable”. Тъй-като обаче сайтът има огромна структура преценихме, че използването на mod_rewrite на apache ще се окаже неефективно и не недостатъчно контолируемо – сайтът е с централизиран индекс файл и всички заявки минават през него, като се определя коя заявка кой template поема. Или трябваше да направим много малко rule-ове и да пращаме искания път към индекс файла (напр. news/latestnews), където той да се декодира, или да напишем декодиране. Другата опция беше да направим custom 404 страница и там да декодираме пътя. Това декодиране обаче щеше да е изпълнява на всяка заявка това щеше да товари базата данни – всички секции в сайта са динамични и се контролират от потребителите – съответно декодирането става чрез записи в базата. Затова решихме да генерираме физически директории за всички пътища, като във всяка директория има по един индекс файл, който препраща към главния и му предава информация за текущата секция. Това обаче ни отвори друг проблем – новините бяха доста над 200 000 и „ударихме” в ограничението на файловата система за брой директории. Тъй като за файлове ограничението е доста повече, решихме специално за материалите да генерираме директориите разпределени по групиращи директории и да правим symlink-ове към тях на нужното място.

При показването на линковете в сайта (тъй като те се дават по кодове на секции) се извършва кодиране – т.е. от код секция към Url. Тъй като това е също тежка работа (на челна страница има над 1000 линка), решихме освен да ги напълним в една таблица готовите Url-та за всяка секция и всеки запис, както и да кешираме използваните на всяка секция линкове във файлове. По този начин времето за генерирането на линкове се сведе до доста приемлива стойност.
C.1.2. Информация в сайта

След доста дебати първоначално решихме, че ще кешираме информацията в РНР масиви в отделни файлове.

За целта за всеки масив имаше специален скрипт, който генерира съответното количество файлове. Файловете се оказаха доста, защото освен, че имахме доста блокове, които ползваха масиви, сайта има 11 режима (по един филтриращ за всяка от 10 държави в Югоизточна Европа и един нефилтриран). Освен това има доста блокове, които показват информацията по индустрии или по теми (индустриите са около 130, а темите – 70), та се получиха доста файлове.

След това всеки блок в сайта include-ваше необходимия си файл и използваше данните от масива. Генерирахме масиви за почти всичко – новини, събития, търгове, навигацията на сайта, време, валутна информация, борсова информация.

Прегенерирането на информацията не беше проблем, защото цялата информация минава през SeePublisher-а, а той при всяко публикуване извиква Gateway приложението, което следи според това какво се публикува каква информация трябва да се прегенерира – всички масиви сложихме в базата заедно с условията им за инвалидиране.

Gateway процеса отговаря за няколко неща:
Извикване на нужните скриптове за прегенериране на масивите;
Генериране на директории за новите записи на нужните места;
Изпращане на Alert-и към клиенти на уеб сайта;
Ново напълване на кеширащите таблици за SeeWire (описани са по-подробно в частта за Wire сайта);
Опресняване на гъстотата на ключовите думи в сайта (в колко новини се среща всяка една).

Отделно скриптовете за борсовата информация, валутите и времето също викаха скриптовете за прегенериране.

След като сайта беше пуснат и натрупа достатъчно статистика за анализ се оказа, че не сме избрали коректен подход. Сайта се товареше от многото заявки и имаше усещането, че е бавен. Анализирайки статистиката намерихме възможности за оптимизация, както клиентска, така и сървърна.
C.2. Оптимизация

Затова решихме заедно с възложителя да оптимизираме сайта, за което избрахме друг подход за кеширане и да оптимизираме клиентската и сървърната скорост на приложението.
C.2.1. Сървърна скорост

След доста проучване и няколко теста решихме да се откажем от масивите и да генерираме направо HTML-а от всеки блок във файл. Логиката е следната:
Всеки блок/темплейт си се пише със заявки към базата, без никакво кеширане;
Тъй-като всеки блок и темплейт е отделен клас, ако има нужда да се кешира той наследява общ базов клас (CachedBlock), който всъщност поддържа кеширането;
Всеки клас предоставя някакъв идентификатор (име), което служи за име и път до генерирания файл;
Всеки клас за блок или темплейт има специален метод Render, който генерира HTML кода. Преименувахме го на RenderInternal (който е protected) и оставихме Render само в кеширащия клас като final. Така там сложихме логика – ако има генериран файл директно се връща съдържането му, без да се извиква RenderInternal, а ако няма – минава по нормалната логика и output-а на RenderInternal се записва във файл.

Има един общ клас (CacheManager), който съдържа логиката по конвертиране на имена към пътища на файлове, писане, четене и инвалидиране на кеширащи файлове.

Gateway-а беше „преквалифициран” да трие кеширащи файлове. Всички блокове сложихме в таблица, и по този начин динамично се определяше кои файлове да се изтрият. Например, ако се публикува материал, който е асоцииран с държавата България, се трият само файловете за нефилтрирания режим и този за България – очевидно тази новина няма да се вижда в другите режими и това не променя генерирания код за тях. След това първия потребител, който отвори сайта, практически „пуска” генератора за изтритите файлове. Това можехме да си го позволим, тъй като не съществува теза, в която генерирането е бавно и че няколко потребителя могат да стартират успоредно генериране.

Кеширането вдигна скоростта на сайта значително – постигнахме производителност, която е 30 до 35 пъти по-висока. Тъй като кеширахме реално всичко по страницата заявки към базата почти нямаше (освен един-два блока, които показваха неща на случаен принцип и нямаше как да ги кешираме). Където не можехме да кешираме (например контекстни новини към дадена новина, където комбинациите са милиони, а и логиката за триене на кеша щеше да е доста сложна), а дадения блок е бавен, просто го пускаме да се зарежда през AJAX, за да не бави страницата.

Логиката за изтриване е обща за SeeNews и SeeWire и по префикса на името на всеки блок в базата се разбира къде са файловете, които трябва да се изтрият.

Специално за SeeWire разработихме кеширане на второ ниво – база данни, понеже самите резултати от търсенето не могат да се кешират (или е твърде сложно). Тъй като в сайта може да се търси за последните 2 седмици, 1, 2, 3 и 6 месеца назад, направихме таблици с материали в тези периоди, които се прегенерират при всяко публикуване (отново отговорност на Gateway-a).
C.2.2. Клиентска скорост

След като оптимизирахме сървърната част на сайта се оказа, все още имахме проблем със зареждането на сайта – браузъра го показваше на части. Във SeeWire имахме същия проблем, само че доста по-изразен. Направихме тестове с няколко начина на сглобяване на HTML-а и общо взето разликите бяха минимални – единственото, което се оказа, че има някакъв видим ефект е таблици със значително по големина съдържание. Също така стана ясно, че inline style атрибутите на таговете също нямаха никакво (или пренебрежително) отношение към клиентската скорост на сайта.

След доста тестове в крайна сметка открихме проблема – и двата сайта използват много javascript и поради това, че всеки блок сам си include-ва файловете, които ползва, и генерираше нужния му inline скрипт, сайтовете се оказваха осеяни с include-ване на JS файлове и inline javascript код. Ако този код се сложи в началото или в края на HTML-а, сайтовете просто „излитаха” (Всъщност доста време вярвахме, че проблемът е в количеството HTML – SeeNews е 170к, a Wire-a е 490к, но опитът ни с други по-големи сайтове ни отказа от тази ни концепция).

За да се справим проблема, разработихме система в двата сайта, която позволява на блоковете да „регистрират” използваните JS файлове и генерирания скрипт. След това целия този код се изсипва преди counter-ите накрая на страницата. С това постигнахме няколко неща:
Намаляване на броя JS файлове в HEAD-а на сайта. Там оставихме само файлове, които се използват на всяка страница от сайта;
Премахване на скрипта от „средата” на страницата;
Намаляване на броя include-вания на JS файлове като цяло – ако два блока ползват един и същия файл, то при регистрирането на втория блок се проверяват досега регистрираните файлове и ако се намери същия файл, извикването се игнорира.

Това решение обаче отвори два проблема – първия е: какво става между „рисуването” на даден блок от браузъра и „изсипването” на нужния му скрипт на края на страницата? Реално всички блокове трябваше да започнат да проверяват дали им е зареден файла. В крайна сметка обаче, тъй като клиентското време сериозно се подобри, това не беше кой знае какъв проблем, защото потребителя трудно щеше да уцели точно този момент да кликне.

Вторият проблем се оказа по-сериозен:

Това е схема на извикванията методите Render() на блоковете – вижда се, че регистрирането на скриптовете става в същия код, където се генерира HTML-а и този код се извиква само когато не е намерен кеширащ файл. Съответно при наличие на такъв файл целия метод RenderInternal се пропуска и никой не регистрира кода и той не се добавя и блока остава без JS кода си, когато се използва кеш. За да се реши този проблем, класа CachedBlock добавя собствени методи RegisterScript и RegisterScriptFile и записва какъв код и файлове регистрира блока, който го е наследил. После, при писането на кеша се правят и два допълнителни файла – за inline кода и за файловете. В последствие когато се използва кеш, се използват двата файла за регистриране.

Изнасянето на скрипта реши огромна част от проблема. Оставаше да се направят стандартни оптимизации – почистване на CSS файловете (с цел намаляване на размера му) – той отнема прилична част от клиентското време и премахване на излишен HTML код.

Банерите също пречеха на доброто зареждане на страницата и с тях подходихме като с JS кода – изнесохме ги накрая. Този подход не е перфектен, защото ако се „рисуват” банерите off-screen, трудно после може да се познае кой банер за коя зона е, а няма как да се контролира document.write(), който използва банер системата. Но при наличието на 3-4 зони този проблем се избягва.

Ефектът на изнасянето на скриптовете беше особено виден при SeeWire, поради това, че той използва голямо количество JS (всички комбобоксове и scrollbar-ове са custom, не се използват стандартните контроли на браузъра) и като резултат сайта се зареждаше особено бавно клиентски и при изнасянето на кода разликата беше наистина голяма.
C.3. SeePassport

Този сайт служи изцяло за регистриране на потребителите и управление на профилите, кошницата, поръчките им, и абонаментите им за бюлетини и алерти. Цялата информация се записва и управлява от CRM приложението.
C.3.1. Логване на потребители

И трите сайта (SeeNews, SeeWire и SeePassport) ползват една и съща форма, предоставена от SeeWire, също и във вариант за floating layer. Самата логика по валидиране и логване също се изпълнява от SeePassport. Има разработена система, която предотвратява логване с един и същи акаунт от два компютъра едновременно.

Bookmark and Share

Все още няма коментари.

Коментирай тази статия

»

Върни се горе
Created by Calipers | Блогът на Ладжър е изграден върху WordPress Ладжър - Имперо, Елементс, Софтуер по поръчка, Консултиране