Задать вопрос
Админил сеть из примерно 15 000 хостов, целиком покрывающую территорию трёх стран. Зарабатывал программированием на пятнадцати языках. Тринадцать лет руководил службами ИТ. Сейчас проектирую архитектуру банковских систем.

5f8144bb265ba785202355.png
Контакты
Местоположение
Россия, Санкт-Петербург и область, Санкт-Петербург

Достижения

Все достижения (862)

Наибольший вклад в теги

Все теги (606)

Лучшие ответы пользователя

Все ответы (6216)
  • ДДос атака на nginx пакетами 1 байт?

    sergey-gornostaev
    @sergey-gornostaev
    Седой и строгий
    500 строк в секунду - это не мощно и, вероятно, даже не DDoS. Если адрес один, то просто закройте ему доступ брандмауэром, а если адреса разные, то настройте лимит запросов в Nginx.

    nginx.conf
    http {
        ...
        limit_req_zone $binary_remote_addr zone=reqlimit:10m rate=30r/s;
        ...
    }

    some_site.conf
    server {
        ...
        location / {
            ...
            limit_req zone=reqlimit burst=10 nodelay;
        }
    }

    После этого запросы с одного ip-адреса начиная с 31-го в секунду будут отбрасываться.

    Как вишенку на торт, можно добавить ещё фильтр для fail2ban:

    nginx-req-limit.conf
    [Definition]
    
    failregex = limiting requests, excess: .* by zone .*, client: <HOST>
    ignoreregex =

    и правило в jail.local
    [nginx-req-limit]
    enabled = true
    port = http,https
    filter = nginx-req-limit
    logpath = /var/www/*/*/logs/error.log # Здесь укажите свой путь к логам виртуального хоста
    findtime = 600
    maxretry = 10
    bantime = 7200

    После этого адреса DoS'еров будут автоматически блокироваться брандмауэром на два часа. Что разгрузит Nginx от обработки паразитного трафика.
    Ответ написан
    11 комментариев
  • Истина в Python?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Python
    Седой и строгий
    Выражение 8 == True не истинно потому, что булевые значения в Python - подтипы целых чисел. True - это фактически единица, а 1 не равно 8.

    В выражении if это срабатывает потому, что в контексте условных выражений производится неявное преобразование числа 8 в булевое значение. То есть интерпретатор сначала преобразовывает число (или что-либо другое) в 1 или 0 в соответствии с правилами преобразования, а потом выполняет с получившимся значение логическую операцию.
    Ответ написан
    Комментировать
  • Как переопределить if в python?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Python
    Седой и строгий
    Прежде всего хочу заметить, что настолько больших возможностей по расширению языка, как в Lisp'ах, никакой другой язык не предоставляет. Но в некоторых языках, в том числе в Python, можно немного поколдовать с кодом на этапе его разбора.

    dumb_translator.py
    import ast
    import sys
    
    class DumbLisp(ast.NodeTransformer):
        def fix(self, source, result):
            ast.copy_location(result, source)
            ast.fix_missing_locations(result)
            return result
    
        def visit_Expr(self, node):
            if isinstance(node.value, ast.Tuple):
                t = node.value
                if isinstance(t.elts[0], ast.Name):
                    if t.elts[0].id == 'newif':
                        _else = [ast.Expr(value=t.elts[3])] if len(t.elts) == 4 else []
                        _if = ast.If(test=t.elts[1],
                                    body=[ast.Expr(value=t.elts[2])],
                                    orelse=_else)
                        self.generic_visit(_if)
                        return self.fix(node, _if)
                    elif t.elts[0].id == 'define':
                        assign = ast.Assign(targets=[ast.Name(id=t.elts[1].id, ctx=ast.Store())],
                                            value=t.elts[2])
                        return self.fix(node, assign)
                    else:
                        call = ast.Expr(value=ast.Call(func=t.elts[0], args=t.elts[1:], keywords=[]))
                        return self.fix(node, call)
            return node
    
    
    with open(sys.argv[1]) as fh:
        tree = ast.parse(fh.read())
        DumbLisp().visit(tree)
        code = compile(tree, filename=sys.argv[1], mode="exec")
        exec(code)

    test.dl
    (define, a, 1)
    
    (newif, a == 1, (print, 'Yes'), (print, 'No'))

    Запускаем
    python dumb_translator.py test.dl

    Естественно, пример предельно простой, а потому в качестве s-форм приходится использовать кортежи. Но есть полноценный фронтенд компилятора Python, транслирующий в python-байткод диалект Lisp близкий к Clojure - Hy.
    Ответ написан
    1 комментарий
  • Возможет ли отрицательный хешкод и расширение капасити при плохом хешкоде? А также как соотносится хешкод с адресом?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Java
    Седой и строгий
    Во-первых, важно определиться с тем, что генерируемый по умолчанию хэш не соответствует требованиям к хорошему хэшу. Поэтому метод hashCode всегда надо переопределять в своих классах. Во-вторых, важно понимать, что способ генерации хэшкода по умолчанию не оговорен в спецификаций Java, а потому может отличать в разных реализациях виртуальной машины или даже в разных версиях одной и той же реализации. Я дальше буду писать про HotSpot.

    Несмотря на то, что в документации написано, что генерируемый по умолчанию хэш - "this is typically implemented by converting the internal address of the object into an integer", этот internal address не имеет никакого отношения к положению объекта в памяти. Попробуем разобраться откуда он берётся. Для этого заглянем в исходный код Object. Как видно, hashCode() - это нативный метод, а значит придётся покопаться в сишном коде. Находим его определение:
    JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
      JVMWrapper("JVM_IHashCode");
      // as implemented in the classic virtual machine; return 0 if object is NULL
      return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
    JVM_END

    Ага, хэшкод генерирует функция ObjectSynchronizer::FastHashCode(). Посмотрим, что в ней. Интересная часть:
    mark = monitor->header();
    ...
    hash = mark->hash();
    if (hash == 0) {
      hash = get_next_hash(Self, obj);
    ...
    }
    ...
    return hash;

    Функция пытается получить хэш из заголовка объекта, а если его там нет, то генерирует вызовом get_next_hash. В этой функции определяются несколько методов генерации хэша. В HotSpot шестой и седьмой версии использовался первый - генерация случайного числа. Вообще ни разу не internal address! С восьмой версии используется пятый - "Marsaglia's xor-shift scheme with thread-specific state". Почитать про этот алгоритм можно здесь. Если отбросить нюансы, опять случайное число, не internal address.

    Если я переопределю хешкод и он иногда ( или всегда, например -1) будет выдавать отрицательные целые, что будет? Очевидно это число как-то преобразуется под капотом? потому что должен выдаваться номер бакета?

    Заглядываем в исходный код HashMap
    if ((p = tab[i = (n - 1) & hash]) == null)
      tab[i] = newNode(hash, key, value, null);

    Переменная n в этой точке равна количеству бакетов - положительному числу, а значит i тоже будет положительным числом.

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

    P.S. Виртуальная машина Zing генерирует хэшкоды по умолчанию на основе адреса объекта в памяти.
    Ответ написан
    4 комментария
  • Как запустить 10000 классов в java?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Java
    Седой и строгий
    Прежде всего нужно заметить, что запустить класс нельзя, его можно только объявить, а запускать можно методы экземпляров этого класса. Почему я и подумал, что вопрос неправильно сформулирован.

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

    Генерировать можно как java-код, так и байткод. Первый способ использует популярная библиотека Lombok. Для него понадобиться механизм работы с AST и механизм взаимодействия с компилятором. Oracle JDK и Open JDK предоставляют Compiler API, реализующий оба механизма. Кроме того, есть и другие инструменты для работы с AST, например Eclipse JDT. Я покажу применение пакета com.sun.tools.javac из Compiler API.
    Сноска
    До Java 9 пакет com.sun.tools.javac был упакован в tools.jar, поставляемый вместе с JDK. Начиная с 9-ки API компилятора вынесли в модуль jdk.compiler, не экспортирующие свои пакеты. Теперь можно не указывать путь до tools.jar в classpath, но нужно добавлять экспорты. В остальном ничего не изменилось.
    import java.lang.reflect.Method;
    import java.net.URI;
    import java.util.ArrayList;
     
    import javax.tools.JavaCompiler;
    import javax.tools.JavaCompiler.CompilationTask;
    import javax.tools.JavaFileObject;
    import javax.tools.SimpleJavaFileObject;
    import javax.tools.ToolProvider;
    
    import com.sun.tools.javac.code.Flags;
    import com.sun.tools.javac.code.Type;
    import com.sun.tools.javac.file.JavacFileManager;
    import com.sun.tools.javac.model.JavacElements;
    import com.sun.tools.javac.processing.JavacProcessingEnvironment;
    import com.sun.tools.javac.tree.JCTree;
    import com.sun.tools.javac.tree.TreeMaker;
    import com.sun.tools.javac.util.Context;
    import com.sun.tools.javac.util.List;
    import com.sun.tools.javac.util.Name;
    
    
    /**
     * Класс эмулирующий для компилятора файлы исходного кода
     * и позволяющий компилировать код прямо из памяти
     */
    class JavaSourceFromString extends SimpleJavaFileObject {
        private final String code;
    
        public JavaSourceFromString(String name, String code) {
            super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE);
            this.code = code;
        }
    
        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return code;
        }
    }
     
    public class CompilerDemo {
        private static final String BASE_NAME = "DynamicHello";
        private static final ClassLoader classLoader = ToolProvider.getSystemToolClassLoader();
        private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    
        /**
         * Метод формирующий абстрактное синтаксическое дерево
         * и преобразующий его в исходный код
         */
        private static String generateSource(String className) {
            Context ctx = new Context();
            JavacFileManager.preRegister(ctx);
            TreeMaker treeMaker = TreeMaker.instance(ctx);
            JavacElements elements = JavacElements.instance(ctx);
    
            JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);
    
            // Тело генерируемого метода
            // выводящее имя класса
            JCTree.JCBlock methodBody = treeMaker.Block(0, List.of(
                treeMaker.Exec(
                    treeMaker.Apply(
                        List.<JCTree.JCExpression>nil(),
                        treeMaker.Select(
                            treeMaker.Select(
                                treeMaker.Ident(
                                    elements.getName("System")
                                ),
                                elements.getName("out")
                            ),
                            elements.getName("println")
                        ),
                        List.<JCTree.JCExpression>of(
                            treeMaker.Binary(
                                JCTree.Tag.PLUS,
                                treeMaker.Literal("I am "),
                                treeMaker.Apply(
                                    List.<JCTree.JCExpression>nil(),
                                    treeMaker.Select(
                                        treeMaker.Apply(
                                            List.<JCTree.JCExpression>nil(),
                                            treeMaker.Select(
                                                treeMaker.Ident(
                                                    elements.getName("this")
                                                ),
                                                elements.getName("getClass")
                                            ),
                                            List.<JCTree.JCExpression>nil()
                                        ),
                                        elements.getName("getName")
                                    ),
                                    List.<JCTree.JCExpression>nil()
                                )
                            )
                        )
                    )
                )
            ));
    
            // Определение генерируемого метода
            JCTree.JCMethodDecl method = treeMaker.MethodDef(
                modifiers,
                elements.getName("introduceYourself"),
                treeMaker.Type(new Type.JCVoidType()),
                List.<JCTree.JCTypeParameter>nil(),
                List.<JCTree.JCVariableDecl>nil(),
                List.<JCTree.JCExpression>nil(),
                methodBody,
                null
            );
    
            // Определение генерируемого класса
            JCTree.JCClassDecl tree = treeMaker.ClassDef(
                modifiers,
                elements.getName(className),
                List.<JCTree.JCTypeParameter>nil(),
                null,
                List.<JCTree.JCExpression>nil(),
                List.of(method)
            );
    
            return tree.toString();
        }
    
        /**
         * Метод компилирующий исходный код
         */
        public static void compile(Iterable<? extends JavaFileObject> compilationUnits) {
            CompilationTask task = compiler.getTask(null, null, null, null, null, compilationUnits);
            task.call();    
        }
    
    
        /**
         * Метод запускающий сгенерированные классы с помощью рефлексии
         */    
        public static void loadAndRun(String className) throws ReflectiveOperationException {
            Class<?> cls = classLoader.loadClass(className);
            
            Method method = cls.getDeclaredMethod("introduceYourself");
            method.invoke(cls.newInstance());    
        }
    
        public static void main(String[] args) throws Exception {
            java.util.List<JavaFileObject> sources = new ArrayList<>();
    
            // Генерируем исходный код десятка классов
            for (int x = 0; x < 10; x++) {
                String className = BASE_NAME + x;
                sources.add(new JavaSourceFromString(className, generateSource(className)));
            }
    
            // Компилируем сгенерированный код
            compile(sources);
            
            // Запускаем скомпилированные классы
            for (int x = 0; x < 10; x++) {
                loadAndRun(BASE_NAME + x);
            }
        }
    }

    Конечно, можно было не заморачиваться с AST, просто засунуть код в строковый литерал, подставлять в этот литерал разные имена и подсовывать компилятору. Но это не так интересно!

    Продолжение в первом комментарии, а то я в лимит символов не укладываюсь.
    Ответ написан
    3 комментария

Лучшие вопросы пользователя

Все вопросы (8)