+

Владислав Смирнов

FullStack Web Developer

PowerThesaurus в цифрах

  • 19 миллионов синонимов
  • Более 2 миллионов антонимов
  • Более 3 миллионов пользователей в месяц
  • Более 25 гигабайт данных

Нас стали парсить!

Признаки парсинга

  • Длительное ожидание ответа от сервера
  • Большая нагрузка на железо
  • Странные запросы

Чем плох париснг

  • Большая нагрузка на сервер
  • Повышение отказа у пользователей
  • Воровство контента
  • Негативная реакция со стороны поисковых систем

Антипарсинг

Возможные решения

  • Nginx: 
  • PHP + Redis (Memcached)
  • CloudFlare
     

ngx_http_limit_req_module

+

NGINX - это HTTP-сервер и обратный прокси-сервер, почтовый прокси-сервер, а также TCP/UDP прокси-сервер общего назначения

  • Скорость
  • Отказоустойчивость
  • Гибкость конфигурации
  • Асинхронность
  • Кэширование
  • Модульность

Lua - высокоуровневый, скриптовый язык с динамической типизацией.

  • Легкий
  • Быстрый
  • Низкий порог вхождения
  • Встраиваемый
name = 'Yoda'

function bar(a, b)
  print(a, b)
  return 4, 8, 15
end

x, y = bar(name)

-- "Yoda  nil nil"
-- x = 4, y = 8

Кто использует Lua?

Преимущества NGINX + Lua

NGINX как сервер приложений

Lua исполняется в самом процессе Nginx, а не выносится в отдельный процесс, как это происходит в случае с другими языками.

Контекст

NGINX API

  • ngx.status
  • ngx.header.HEADER
  • ngx.resp.get_headers
  • ngx.req.is_internal
  • ngx.req.start_time
  • ngx.req.http_version
  • ngx.req.raw_header
  • ngx.req.get_method
  • ngx.req.set_method
  • ngx.req.set_uri
  • ...

Код на Lua в контексте Nginx выполняется в неблокирующем режиме, включая запросы к БД и внешние HTTP-запросы, порожденные веб-приложением (например, запрос к API другого сайта).

Неблокирующий I/O

Расширение возможностей конфигурации NGINX

 server {
     location /hello {
         content_by_lua_block {
             ngx.say('Hello,world!')
         }
     }
}

Что можно писать?

  • Аналитика запросов
  • Объединение сервисов
  • API
  • Middleware

OpenResty - это web платформа основанная на NGINX и LuaJIT.

worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 8080;
        location / {
            default_type text/html;
            content_by_lua '
                ngx.say("<p>hello, world</p>")
            ';
        }
    }
}
curl http://localhost:8080/
If everything is okay, we should get the output

<p>hello, world</p>
server {
    listen 8080;
    location / {
        default_type text/html;
        content_by_lua_file /path/to/content.lua;
    }
}
uri = ngx.var.uri
ngx.say(uri)

-- try access /test?a=hello,world
ngx.say(ngx.var.arg_a)
curl http://localhost:8080/test?a=hello,world
If everything is okay, we should get the output

/test
hello,world

Компоненты

  • LuaRestyMemcachedLibrary

  • LuaRestyMySQLLibrary

  • LuaRestyRedisLibrary

  • LuaRestyWebSocketLibrary

  • LuaRestyLimitTrafficLibrary

  • PostgresNginxModule

  • ...

local redis = require "resty.redis"
local red = redis:new()

local ok, err = red:connect("127.0.0.1", 6379)

if not ok then
    ngx.say("failed to connect: ", err)
    return
end

res, err = red:get("jedi")

ok, err = red:set("jedi", "Obi-Wan Kenobi")
if not ok then
    ngx.say("failed to set dog: ", err)
    return
end

ngx.say(res)

Работа с Redis

curl http://localhost:8080/
null

curl http://localhost:8080/
Obi-Wan Kenobi

Как мы это используем?

Lua-Firewall

Что нам нужно?

  • Определение поисковых роботов
  • Определение парсера и пользователя
  • Определение пользователей за одним IP
  • Лимитирование количества обращений к Nginx
  • White list для ip
  • Ban list
  • Сбор статистики

Определение поисковых ботов

local crawlers = {}


function crawlers.user_agent_check_bot(user_agent)
    -- Проверяет user_agent клиента по списку ботов на совпадение
end


function crawlers.host_check_bot(host, bot)
    -- Проверяет хост клиента на совпадение со списком хостов ботов
end


function crawlers.is_bot(storage, user_agent, ip)
    -- Проверяет является ли клиент поисковым ботом
end

Определение парсера и пользователя

client = {}

function client:new(id, type, rps_storage, bans_storage, path_to_banlist)
    local obj = {}

    function obj:check_ban()
        -- проверяем бы ли забанен ip        
    end

    function obj:add_ban(time)
        -- добавить в бан
    end

    -- тело класса

    function obj:count_success_captcha()
        -- сколько каптч отгадал пользователь
    end

    
    return setmetatable(obj, self)
end 

Используем капчу что бы определить человека

Определение пользователей за одним IP

Что еще?

  • Лимитирование количества обращений к Nginx
  • White list для ip
  • Ban list
  • Сбор статистики

Как это реализовали в 2GIS

https://habrahabr.ru/company/2gis/blog/199504/

http://www.highload.ru/2013/abstracts/934.html

Владимир Протасов

OpenResty: превращаем NGINX в полноценный сервер приложений

https://www.youtube.com/watch?v=silrEB0Brvo&t=1802s

Спасибо!

Made with Slides.com