Роутер на Golang

    image Добро пожаловать, или Посторонним вход воспрещён
    (С) Э.Г.Климов 1964


    Написанный на языке Go роутер (или как его ещё иногда называют — маршрутизатор), который оказался достаточно быстрым для того, чтобы его не стыдно было сравнить с лидерами go-роутинга: Bone, Httprouter, Gorilla, Zeus. Название роутеру дало простое русское слово «Вход», набранное английскими буквами в кодировке волапюк en.wikipedia.org/wiki/Volapuk_encoding


    Никогда со мной такого не бывало: нет входа! Куда ни ткнись — везде сплошные окна.
    (С) Аркадий и Борис Стругацкие. Хромая судьба


    Bxog (английские буквы), читается как Бииксоуджи. В комментариях предлагаю называть роутер на выбор: Вход, Bxog, Бииксоуджи, Биксодж. Для модуля в приложении, которое первым встречает запрос пользователя, название, с моей конечно точки зрения, вполне подходящее. По аналогии с Windows хотелось бы назвать свой роутер Door, но боюсь быть неверно понятым ;)

    Ссылки на исходники:
    github.com/claygod/Bxog
    github.com/claygod/BxogTest

    Зачем

    И нафига мне эти перья?
    (С) Из анекдота


    Скажу сразу, во всём виноват кризис: зарплаты не хватает, с Адсенса на старых сайтах ничего уже не капает, а жить надо. Нужен дополнительный заработок, а на чём его делать? На пыхе надоело, а на другом ничего не умею, ну не идти же в самом деле, в верстальщики… Фронтэнд, это как-то не моё.

    Изначально я приглядывался к С/С++, притом скорее склонялся к Си (тут каждому своё). Но учитывая указатели, указатели на указатели, указатели на функции и указатели на функции, которые принимают на вход указатели на функции, мой неокрепший мозг потребовал вернуться от безалкогольного пива к классическому, чему как-то воспротивился организм (единство и борьба противоположностей).

    После приятного и интересного Lua под руку попался новый (для меня) Golang: простой, быстро осваеваемый, похожий на Си. Поставил LiteIDE, и можно работать. Важный плюс — на рабочий компьютер под виндой всё можно поставить даже с правами пользователя (ну строгие корпоративные правила у меня на работе, это факт).

    Осталось определиться с тем, что писать в процессе изучения языка (я считаю, что по другому язык нормально никак не изучить). Фреймворк, это как-то многовато… А если изучать даже всего по одному языку в год, это ж сколько фреймворков придётся наклепать? Боюсь, планета такого не выдержит, я ведь на ней не один :) Решил написать роутер, и на этом, пожалуй, лирическое отступление закончу.

    Разработка

    Поскольку языка Go на момент начало разработки я ещё не знал, то и структура роутера, и принципы его хранения и поиска маршрутов менялись несколько раз (кардинально, раза четыре). Первая итерация была совершенно ознакомительной. Посмотрев на неё (со слезами, а как же), я уяснил основы Golang и сделал вторую версию, в которой даже на код глядеть было не слишком страшно. Однако даже простое ab тестирование показало мне, что моя поделка довольно тормознутая.

    Поменяв алгоритмы и внеся ещё некоторые правки, я решил ознакомиться с другими Go-роутерами. Из списка мне понравился Bone, не удивлюсь, если во многом из-за названия и прикольной картинки. Посмотрев на этот роутер я подумал, что эх, мне бы так смочь… Количество звёзд под тысячу у этого роутера тоже как-то воодушевляли.

    У Bone есть бенч, который толкнул-таки меня на третью итерацию разработки своего творения. Написав себе похожий бенч, я опять поменял структуру хранения и алгоритм поиска. Теперь, о чудо, мой Биксоджей смог обогнать Gorilla Pat (Gorilla Mux более медленный, и с ним тягаться не сложно). Одновременно я понял, что любимец Bone совсем не заточен под большие динамические урлы, хотя неплохо справляется с короткими статическими, и его бенч как раз под это и написан.

    Король умер, да здравствует король! Теперь я буду сражаться с HttpRouter. По ходу пьесы я уяснил, что мапы в Go мягко говоря не блещут скоростью по сравнению с массивами. Очередной виток разработки начал выводить роутер на финишную прямую, где бенч лидера стал близок, можно сказать соседским. Отстранёно взглянув на свой код, я решил, что он слишком многословен, и кроме того, требует тонкой настройки.

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

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

    Быстрый старт

    В принципе, я постарался организовать код так, чтобы работа роутера была проста и понятна, т. е. надеюсь, код говорит сам за себя. Ну а ниже даю комплексный пример использования роутера, демонстрирующий все его методы
    комплексный пример использования роутера
    package main
    
    import (
    	"io"
    	"net/http"
    	"github.com/claygod/Bxog"
    )
    
    // Handlers
    func IHandler(w http.ResponseWriter, req *http.Request, r *bxog.Router) {
    	io.WriteString(w, "Welcome to Bxog!")
    }
    func THandler(w http.ResponseWriter, req *http.Request, r *bxog.Router) {
    	params := r.Params(req, "/abc/:par")
    	io.WriteString(w, "Params:\n")
    	io.WriteString(w, " 'par' -> "+params["par"]+"\n")
    }
    func PHandler(w http.ResponseWriter, req *http.Request, r *bxog.Router) {
    	// Получить параметры из урла
    	params := r.Params(req, "country")
    	io.WriteString(w, "Country:\n")
    	io.WriteString(w, " 'name' -> "+params["name"]+"\n")
    	io.WriteString(w, " 'capital' -> "+params["city"]+"\n")
    	io.WriteString(w, " 'valuta' -> "+params["money"]+"\n")
    	// Сгенерировать строку урла
    	io.WriteString(w, "Creating a URL from route:\n")
    	io.WriteString(w, r.Create("country", map[string]string{"name": "Russia", "capital": "Moscow", "money": "rouble"}))
    }
    
    // Main
    func main() {
    	m := bxog.New()
    	m.Add("/", IHandler)
    	m.Add("/abc/:par", THandler)
    	m.Add("/country/:name/capital/:city/valuta/:money", PHandler).
    		Id("country"). // Для удобства короткий ID
    		Method("GET") // Тут GET можно было не указывать, это для примера
    	m.Start(":80")
    }
    
    


    После запуска приведённого примера перейдите по ссылкам:


    Конфигурирование

    Настраивать роутер нужно через редактирование файла конфигурации github.com/claygod/bxog/config.go В файле обозначены константы, которые можно изменять.

    HTTP_METHOD_DEFAULT
    Метод, используемый роутером по умолчанию (GET, POST etc.). Эта опция позволяет писать меньше кода и по сути просто сахар.

    HTTP_SECTION_COUNT
    Максимальное количество секций в урле. Под секцией подразумевается набор символов между двух слэшей. Устанавливать эту цифру вплотную к количеству секций в самом большом роуте не обязательно. Если 32 маловато, то пишите 64, чтобы не заморачиваться, ну и т.д.

    HTTP_PATTERN_COUNT
    Максимальная длина урла. Тут понятно без объяснений.

    READ_TIME_OUT
    Максимальное время ожидания при чтении

    WRITE_TIME_OUT
    Максимальное время ожидания при записи

    FILE_PREF
    Локальный путь от корня сайта в урле для файлов, доступных для скачивания.

    FILE_PATH
    Полный путь к каталогу, из которого файлы будут доступны для скачивания.

    Бенчмарк

    Роутер получился достаточно быстрым для того, чтобы было не стыдно испытать его в бенчмарке совместно с такими популярными роутерами, написанными на Go, как Bone, Httprouter, Gorilla, Zeus.Код бенчмарка и тесты роутера лежит тут — github.com/claygod/bxogtest

    Результат бенчмарка для указанных роутеров в сравнении с Bxog со 150 сгенерированными 6-секционными роутами (конфигурацию компьютера не указываю, ибо тут скорее важны не абсолютные цифры, а их сравнение):

    • BenchmarkBxogMux-4 5000000 330 ns/op
    • BenchmarkHttpRouterMux-4 3000000 395 ns/op
    • BenchmarkZeusMux-4 100000 23772 ns/op
    • BenchmarkGorillaMux-4 50000 30223 ns/op
    • BenchmarkGorillaPatMux-4 1000000 1253 ns/op
    • BenchmarkBoneMux2-4 20000 63656 ns/op

    Очень порадовал Gorilla Pat, который демонстрирует хорошую, хотя и не лучшую производительность. Если вас устраивают программы от Gorilla, то для поиска быстрого решения Gorilla Pat очень хорошее решение.
    Если же говорить о выборе роутера вообще, то тут немаловажным будет то, насколько он подходит вам изнутри (это моё мнение, кто-то считает, что кроме API ничего глядеть и не надо). Но то, что есть выбор, это уже хорошо. На картинке ниже видно, что у тройки лидеров в зависимости от количества маршрутов скорость сильно не меняется.

    image

    Тесты

    Возможно, тестов маловато, или какой-то момент ими охвачен недостаточно, буду рад получить подсказки в комментариях. В общем, DISCLAIMER, в косяках, которые наверняка есть, не вините меня, я тут просто, мимо проходил.

    API

    API у роутера простое, как апельсин. В качестве небольшой сладости (если апельсин кислый), добавлена возможность не только обращаться к параметрам из урла в хэндлере, но и генерировать новый урл. Это может оказаться удобным при разработке сайта на Go (или фреймворка под такие задачи).

    Методы:
    • New — создать мультиплексор
    • Add — добавить роут. По умолчанию метод GET, и ID роута в виде строки его правила, соответственно, это не обязательные опции. Пример с указанием метода и ID: m.Add("/abc/:param", YourHandler).Method(«POST»).Id(«abc»)
    • Start — запустить сервер с указанием порта, который нужно слушать
    • Params — получить из URL параметры
    • Create — создать URL из параметров роута
    • Test — аналог Start (только для тестирования)

    Простой пример:
    	m := bxog.New()
    	m.Add("/", IndexHandler)
    	m.Start(":80")
    


    Секции

    Как уже упоминал выше, урл слэшами делится на секции. Секция может быть статическая (неизменяемая) и динамическая (аргумент). Динамическая секция начинается с двоеточия (это общеупотребительная практика роутеров). Последняя секция не должна заканчиваться на слэш.

    Статические файлы

    С помощью констант FILE_PREF и FILE_PATH необходимо указать роутеру, по какому пути будут доступны статические файлы в браузере и какой путь к этим файлам на жёстком диске.

    Golang

    Споры о Go не утихают. Одни его критикуют, другие хвалят. И то, и другое, возможно, обосновано. Мне понравилось принудительное форматирование кода: чтение чужих исходников теперь не вызывает никаких проблем, всё однородно и читаемо. Иногда мешают напоминания о созданных и не использованных переменных, но в конечном итоге, просто меняешь свой стиль программирования, чтобы компиляция сразу прошла успешно. Буду ли я в дальнейшем использовать этот язык? Да. Посоветую ли его другим? Да. Нужно ли его делать главным и единственным языком в своей работе? Нет, и не потому, что он плохой, просто свой кругозор надо расширять.

    Подытоживая

    Вот все пишут «Запасной выход». И хоть бы одна зараза написала «запасной вход».
    (С) Дмитрий Емец. Мефодий Буслаев. Месть валькирий


    Начиная писать свой Биксодж, я не задавался целью утереть нос лидеру go-роутеров *Httprouter* в скорости обработки запроса. То, что мой код оказался быстрым, это скорее приятный бонус. На мой взгляд, у приложения, использующего роутер, этот самый роутер вряд-ли будет узким горлышком. Но если задаваться целью написать действительно быстрое приложение, то… другое дело. Скорей всего это будет маленькое приложение, сервис или ещё что-то в таком духе. Для больших и сложных приложений скорость перестаёт быть единственным и самым важным критерием.

    А как же работа?

    Работу я не нашёл, поскольку ещё не искал. Очень бы хотелось в комментариях прочитать про реальные ситуации рынка Go-программистов, а то меня тут разобрали сомнения, может сразу покупать новый учебник по языку ХХХ :)

    github.com/claygod/Bxog
    github.com/claygod/BxogTest

    UPD: 17/01/2017


    Сделал клон роутера под Fast HTTP server:
    github.com/claygod/door

    UPD: 28/02/2017


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

    • BenchmarkBxogMux-4 5000000 277 ns/op
    • BenchmarkHttpRouterMux-4 3000000 307 ns/op
    • BenchmarkZeusMux-4 100000 18631 ns/op
    • BenchmarkGorillaMux-4 50000 25302 ns/op
    • BenchmarkGorillaPatMux-4 1000000 1253 ns/op
    • BenchmarkBoneMux2-4 20000 63656 ns/op

    Для GorillaPat и Bone ситуация не изменилась, поэтому я оставил старые цифры. Также отмечу, что для роутов с меньшим количеством секций ситуация ещё лучше, но раз уж решил мерить в шестисекционных, то так тому и быть.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 28

      +4
        0
        Упс, это мой так сказать внутренний юмор(напоминалочка)
        Пожалуй, спрячу от неокрепших умов )) Спс
      +1

      Не "Биксоджей", а "Биксодж".

        0
        Поскольку учил я французский, то за консультацией обращался к переводчику Гугла,
        он в конце говорит «джей», ну а с корпорацией добра спорить…
          +1
          В гугл би-экс-оу-джи )) Джей в английском это J.
            0
            Когда нажимаю кнопку Прослушать, мне слышится в конце джей, а вам?

            Странно, но мне нравятся все вариации чтения названия, уже предложенные )
            Осталось спросить, а что думают по этому поводу носители языка

              +1
              Это потому что там джи с долгим и [i:]. Да, русскоязычными он воспринимает как «ий»/«ей», как пример, нидерландское koffie с долгим [i:] превратился в «кофей» (еще есть форма «кофий»). У вас видимо так же получилось)))
                0
                deniskreshikhin, а на ваш взгляд, предложенный rumkin вариант Биксодж может быть правильным?
                  +1
                  Я думаю, даже англичане на это не ответят, т.к. «x» не может в английском следовать за согласным.
                  Если читать по буквам, то «би-экс-оу-джи». Если читать как слово, то будет что-то вроде «бксог» или «бзог».
                    +1
                    Спасибо, короткое название просто поудобней, буду пользоваться им.
                      +5
                      Вспомнился анекдот :)
                      Русский, французский и китайский лингвисты решили написать имена друг друга, каждый на своём языке.

                      — Моя фамилия Ге, — сказал француз китайцу.
                      — В китайском языке два иероглифа Ге, но, к сожалению, ни один из них не подходит для фамилии.
                      — Почему?
                      — Потому что один имеет значение «колесо», а другой передает звук, с которым лопается мочевой пузырь осла.
                      — А что плохого в колесе?
                      — Мужское имя не может быть круглым. Для твоего имени мы возьмем иероглиф Шэ, означающий «клавиатура», «корнеплод», «страница», а также прилагательное «бесснежный» и дополним его иероглифом Нгу, означающим мужской род. В конце я пишу иероглиф Мо — «девственный».
                      — Но это, мягко говоря, не совсем…
                      — Никто не будет считать тебя девственником, просто без иероглифа Мо иероглифы Ше-Нгу означают «сбривающий мамины усы».

                      — Хорошо, теперь я напишу твое имя.
                      — Моя фамилия Го.
                      — Отлично, я начну твою фамилию с буквы G.
                      — Что означает буква G?
                      — У нас, европейцев, сами по себе буквы ничего не значат, но чтобы проявить к тебе уважение, я поставлю перед G букву H — во французском она все равно не читается.
                      — Отлично! Дальше O?
                      — Нет, чтобы показать, что G — произносится как Г, а не как Х, надо после G поставить букву U, а также H — чтобы показать, что U не читается сама по себе, а только показывает, как правильно читать G, и буквы EY, показывающие, что слово не длинное и скоро закончится.
                      — Hguhey… дальше O?
                      — Нет, О во французском произносится как А или Ё, в зависимости от стоящих по соседству букв, ударения и времени года. Твое чистое О записывается как AUGHT, но слово не может кончаться на T, поэтому я добавлю нечитаемое окончание NGER. Вуаля!

                      Русский лингвист поставил бокал на стол, взял листочек и написал «Го» и «Ге».
                      — И всё?
                      — Да.

                      Француз с китайцем почесали в затылке.
                      — Хорошо, а какая у тебя фамилия?
                      — Щекочихин-Крестовоздвиженский.
                      — А давайте просто выпьем? — первым нашёлся китаец.

                      Русский кивнул и француз с облегчением поднял тост за шипящие дифтонги.
        0

        deleted

          +1
          Бииксоуджи, если быть точным. У гугла какой-то свой, видимо, «английский».
            0
            Если не будет новых версий, поправлю в статье на Бииксоуджи.
            Название мне стало напоминать не безызвестный фильм Джуманджи.
              0
              Не-е. deniskreshikhin правильно сказал. b — би, x — экс, о — оу, g — джи
              0
              Может быть этот «какой-то свой английский» делала команда из Гарварда или MIT'а (3-е и 6-е места)? ;)
              +3

              When in Go, do as Gophers do.


              Не следует тесты в другой репозиторий отправлять. Они мешать не будут. Достаточно, чтоб имя у них подходило под маску *_test.go. По этой теме можно ещё документацию пакета go/build почитать.


              Кроме того не принято использовать CAPS_CASE для констант. Effective Go #mixed-caps.


              Тип Node не используется нигде, кроме внутренностей. Можно его скрыть из видимости. Достаточно написать node. Аналогично с Section, Index и внутренними константами. Кстати golint вам об этом укажет.


              Ещё по можно почитать CodeReviewComments.


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


              По мимо прочего неплохо иметь ссылку на документацию прямо сверху README. Вот она: https://godoc.org/github.com/claygod/Bxog

                0
                Не следует тесты в другой репозиторий отправлять. Они мешать не будут. Достаточно, чтоб имя у них подходило под маску *_test.go. По этой теме можно ещё документацию пакета go/build почитать.

                Знаю, это я из своих соображений.

                Кроме того не принято использовать CAPS_CASE для констант. Effective Go #mixed-caps.

                Как-то не обращал внимания. В принципе, поправить можно, хотя привычно большими буквами.

                Тип Node не используется нигде, кроме внутренностей. Можно его скрыть из видимости. Достаточно написать node. Аналогично с Section, Index и внутренними константами. Кстати golint вам об этом укажет.

                Кстати да, собирался убрать из области видимости, но хотел оставить в названиях большие буквы.
                Впишется ли в норму вариант вместо Node -> bNode?

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

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

                По мимо прочего неплохо иметь ссылку на документацию прямо сверху README. Вот она: https://godoc.org/github.com/claygod/Bxog

                Добавил.

                lain8dono, спасибо за комментарий, побольше бы таких!
                  0
                  Впишется ли в норму вариант вместо Node -> bNode?

                  Не особо, но лучше. Это будет выглядеть странновато для читающих код, хотя чуть лучше для читающих только документацию. Кстати node не на столько уж большая структура для того, чтоб выделять её в отдельный файл. Лично я оставил бы её в файле index.go.


                  Кстати нижнего подчёркивание в именах вообще следует избегать.


                  Линтёр ругается вот так:

                  `


                  $ golint -min_confidence 0
                  config.go:3:1: should have a package comment, unless it's in another file for this package
                  config.go:8:6: don't use underscores in Go names; type type_hash should be typeHash
                  config.go:12:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:13:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:20:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:24:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:27:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:30:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:33:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:36:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:39:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:44:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:45:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:46:2: don't use ALL_CAPS in Go names; use CamelCase
                  config.go:47:2: don't use ALL_CAPS in Go names; use CamelCase
                  index.go:1:1: should have a package comment, unless it's in another file for this package
                  index.go:12:6: exported type Index should have comment or be unexported
                  index.go:26:2: don't use underscores in Go names; var c_hashes should be cHashes
                  index.go:28:6: don't use underscores in Go names; var c_node should be cNode
                  index.go:45:10: if block ends with a return statement, so drop this else and outdent its block
                  index.go:73:3: don't use underscores in Go names; var c_node should be cNode
                  index.go:80:3: don't use underscores in Go names; var c_hash should be cHash
                  index.go:102:5: don't use underscores in Go names; var new_node should be newNode
                  index.go:123:57: don't use underscores in Go names; method parameter c_hashes should be cHashes
                  node.go:3:1: should have a package comment, unless it's in another file for this package
                  node.go:7:1: comment on exported type Node should be of the form "Node ..." (with optional leading article)
                  node.go:14:2: don't use underscores in Go names; var new_node should be newNode
                  route.go:1:1: should have a package comment, unless it's in another file for this package
                  route.go:13:6: exported type Route should have comment or be unexported
                  route.go:40:9: if block ends with a return statement, so drop this else and outdent its block
                  route.go:46:1: exported method Route.Method should have comment or be unexported
                  route.go:51:1: exported method Route.Id should have comment or be unexported
                  route.go:51:17: method Id should be ID
                  route.go:56:17: method parseUrl should be parseURL
                  route.go:57:6: don't use underscores in Go names; var array_sec should be arraySec
                  router.go:3:1: should have a package comment, unless it's in another file for this package
                  router.go:14:1: comment on exported type Router should be of the form "Router ..." (with optional leading article)
                  router.go:21:1: comment on exported function New should be of the form "New ..."
                  router.go:26:1: comment on exported method Router.Add should be of the form "Add ..."
                  router.go:31:9: if block ends with a return statement, so drop this else and outdent its block
                  router.go:36:1: comment on exported method Router.Start should be of the form "Start ..."
                  router.go:52:1: comment on exported method Router.Params should be of the form "Params ..."
                  router.go:55:5: don't use underscores in Go names; var c_route should be cRoute
                  router.go:66:1: comment on exported method Router.Create should be of the form "Create ..."
                  router.go:81:1: comment on exported method Router.Test should be of the form "Test ..."
                  section.go:3:1: should have a package comment, unless it's in another file for this package
                  section.go:7:1: comment on exported type Section should be of the form "Section ..." (with optional leading article)
                  section.go:10:2: don't use underscores in Go names; struct field type_sec should be typeSec
                  section.go:13:29: don't use underscores in Go names; func parameter type_s should be typeS
                  server.go:3:1: should have a package comment, unless it's in another file for this package
                  server.go:16:9: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)
                  

                  `


                  Кстати go vet тоже ругается. Недостижимый код в файлах route.go:42 и router.go:30.


                  Знаю, это я из своих соображений.

                  Если вы о том, что там другое имя пакета, то это не аргумент. Попробуйте. Всё заработает. http://stackoverflow.com/a/31443271 Плюсом будет возможность сделать так:


                  $ go get github.com/claygod/Bxog
                  $ cd $GOPATH/src/github.com/claygod/Bxog
                  $ go test
                  ....

                  Кстати, а у вас есть бенчмарк на костыль с хешированием? На сколько это лучше/хуже чем map[string]T?

                    +1
                    Кстати, а у вас есть бенчмарк на костыль с хешированием? На сколько это лучше/хуже чем map[string]T?

                    Прямого нет, делал две версии поиска и сравнивал по общему бенчмарку. Но однозначно могу сказать, что сгенерировать хэш дешевле. Если бы вернуть старый алгоритм со строками, то думаю, сотня ns/op тут же набежала бы. И ещё я заметил, что с строками результат какой-то менее стабильный (больший разброс), хотя возможно это сугубо субъективно.
                      +1
                      Но однозначно могу сказать, что сгенерировать хэш дешевле.

                      Однозначно об этом может сказать ТОЛЬКО правильно написанный бенчмарк. Более того. Вы заменяете стандартную функциональность языка собственным костылём. Следовательно далеко не лишним будет описание вашего алгоритма хеширования и объяснения, почему так лучше/быстрее/сильнее/выше и т.д. Желательно ещё и минусы (которые в любом случае есть) такого подхода обозначить.


                      Споры без бенчмарков о производительности можно приравнять к разговорам о политике.


                      Если очень хочется посоревноваться, то можно начать с https://github.com/gin-gonic/go-http-routing-benchmark


                      Кстати. Ваши тесты не заработают практически везде, кроме винды
                      $ go test -v            
                      # github.com/claygod/BxogTest
                      bxog_test.go:22:2: cannot find package "github.com/claygod/bxog" in any of:
                          /usr/lib/go/src/github.com/claygod/bxog (from $GOROOT)
                          /home/lain/gocode/src/github.com/claygod/bxog (from $GOPATH)
                      FAIL    github.com/claygod/BxogTest [setup failed]
                      
                        +1
                        Ваши тесты не заработают практически везде, кроме винды

                        Не соображу сразу… из-за того, что bxog в импортах написан с маленькой буквы? Плз, подскажите.

                        Если очень хочется посоревноваться, то...

                        Нет, я сделал бенч только для того, чтобы показать, что роутер не тормозной.

                        Следовательно далеко не лишним будет описание вашего алгоритма хеширования и объяснения, почему так лучше/быстрее/сильнее/выше и т.д. Желательно ещё и минусы (которые в любом случае есть) такого подхода обозначить.

                        Согласен, ключевой момент стоит осветить.
                  0
                  Тип Node не используется нигде, кроме внутренностей. Можно его скрыть из видимости. Достаточно написать node. Аналогично с Section, Index и внутренними константами. Кстати golint вам об этом укажет.


                  Поправил, в https://godoc.org/github.com/claygod/Bxog теперь только публичные функции роутера для API
                  +1
                  На Гитхабе в
                  Settings
                  Necessary changes in the configuration of the multiplexer can be made in the configuration file config.go
                  ссылка с config.go ведёт в никуда.
                    0
                    Спасибо, пофиксил
                    0
                    В связи с тем, что в Golang теперь request содержит нативный контекст, роутер переделан на передачу параметров через него, что с одной стороны, немного замедлило роутер, с другой, позволило из аргументов хэндлера исключить третий параметр. Кроме того, получение параметров через встроенный контекст достаточно наглядно, к примеру вот так: req.Context().Value(«name»)).(string)
                      0
                      Ради большего перфоманса вернул старый вариант и сделал ещё пару изменений (см. в конце статьи).

                    Only users with full accounts can post comments. Log in, please.