Обзор Netty — углубленное обсуждение механизма обнаружения пульса Netty: принципы, практика и анализ исходного кода IdleStateHandler.
Обзор Netty — углубленное обсуждение механизма обнаружения пульса Netty: принципы, практика и анализ исходного кода IdleStateHandler.
Каталог статей
  • Обзор
  • Обнаружение сердцебиения
  • Code
    • Имитировать тайм-аут пульса
    • нормальная ситуация
  • Анализ исходного кода IdleStateHandler
    • channelRead
    • channelActive
      • initialize

Обзор


Обнаружение сердцебиения

Механизм Netty из Обнаружения сердцебиения Механизм поддержания активности сети,Он периодически отправляет и получает определенные сообщения (пакеты пульса), чтобы гарантировать, что изоединение остается действительным между клиентом и сервером. Этот механизм очень важен для приложений, которые необходимо поддерживать в течение длительного времени (например, связь в реальном времени, мониторинг, push-сервисы и т. д.).,Потому что это может помочь определить, отключено ли соединение из-за проблем с сетью или сбоя клиента.

Netty поставлять Понятно Обнаружение механизм сердцебиения, позволяющий определить, активно ли соединение. существовать TCP Если во время соединения соединение будет разорвано, сервер и клиент не сразу узнают, что оно было отключено. Таким образом, отправив контрольное сообщение и ожидая ответа от другой стороны, вы можете определить, активно ли соединение.

Netty Предоставляет два способа достижения Обнаружения. сердцебиения:

  1. использовать TCP слойиз KeepAlive механизм. Этот механизм по умолчанию использует время пульса да 2 Он небольшой, для реализации опирается на операционную систему и недостаточно гибок.
  2. использовать Netty из IdleStateHandler。IdleStateHandler да Netty Предоставляет процессор состояния простоя и может настраивать интервал обнаружения. приведите настройки IdleStateHandler Параметр из в конструкторе может указывать время обнаружения простоя чтения, время обнаружения простоя записи, а также время обнаружения простоя чтения и записи. установите их на 0 Указывает, что этот тип обнаружения простоя отключен.

Конструктор IdleStateHandler выглядит следующим образом:

Язык кода:javascript
копировать
public IdleStateHandler(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit)
  • readerIdleTime:читатьизсвободное время,По истечении этого времени будет отправлена ​​посылка «Обнаружение сердца».,Обнаружениеданетсоединять。
  • writerIdleTime:Писатьизсвободное время,По истечении этого времени будет отправлена ​​посылка «Обнаружение сердца».,Обнаружениеданетсоединять。
  • allIdleTime:читать Писатьизсвободное время,По истечении этого времени будет отправлена ​​посылка «Обнаружение сердца».,Обнаружениеданетсоединять。
  • unit:свободное времяединица,По умолчанию — секунды (TimeUnit.SECONDS).

При выполнении одного из вышеперечисленных условий оно автоматически запустится. IdleStateEvent будет передан следующему в конвейере. handler из userсуществуют Вставьте сюда фрагмент кодаEventTriggered событие для обработки.

На стороне сервера вы можете добавить IdleStateHandler Обнаружение процессор сердцебиения и добавить пользовательскую обработку handler реализация класса userEventTriggered() Методы логически обрабатываются как таймауты.

Например, вы можете установить каждый 5 Проверка чтения выполняется каждую секунду. 5 в течение нескольких секунд ChannelRead() Если метод не вызывается, он будет запущен один раз. userEventTrigger() метод.

На стороне клиента после запуска клиента сначала отправьте сообщение «привет», а затем дождитесь сообщения «ping» от сервера. После получения сигнала сердцебиения ответьте на ответ «ok». Сообщения Heartbeat можно определить по мере необходимости.

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


Code

Язык кода:javascript
копировать
package com.artisan.heartbeat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;
/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class HeartBeatArtisanServer {
    public static void main(String[] args) throws Exception {
        // Создайте группу цикла событий «главный-подчиненный».
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            // Создать класс запуска сервера
            ServerBootstrap bootstrap = new ServerBootstrap();
            // Настройка группы цикла событий
            bootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class) // использоватьNioServerSocketChannel в качестве канала
                    .childHandler(new ChannelInitializer<SocketChannel>() { // Инициализировать подканал
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline(); // возьми трубу
                            pipeline.addLast("decoder", new StringDecoder()); // Добавить декодер
                            pipeline.addLast("encoder", new StringEncoder()); // Добавить кодировщик
                            // Добавьте процессор состояния простоя и установите время простоя чтения на 3 секунды.
                            pipeline.addLast(new IdleStateHandler(3, 0, 0, TimeUnit.SECONDS));
                            pipeline.addLast(new HeartBeatArtisanServerHandler()); // Добавить собственный процессор
                        }
                    });
            System.out.println("netty server start。。"); // Распечатать информацию о запуске
            // Привяжите порт и синхронно ждите успеха
            ChannelFuture future = bootstrap.bind(9000).sync();
            // Подождите, пока сокет сервера закроется
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace(); // Распечатать информацию об исключении
        } finally {
            // Освободите ресурсы и корректно закройте группу цикла событий.
            worker.shutdownGracefully();
            boss.shutdownGracefully();
        }
    }
}

Простой из Netty Пример сервера, который использует NioServerSocketChannel в качестве канала сервера и настройте IdleStateHandler для обработки состояния простоя. Если клиент 3 в течение нескольких секунд Сообщение не было отправлено, сервер инициирует IdleStateEvent событие и передается следующему процессору в конвейере, т.е. HeartBeatArtisanServerHandler。Этот специальный процессор должен быть реализован. userEventTriggered Метод обработки этого события, например отправка контрольного пакета для поддержания активного соединения.

существовать main метод, мы создаем ServerBootstrap Экземпляр, настроенный с использованием группы цикла событий, типа канала, обработчика состояния простоя и настраиваемого обработчика. Затем мы связали порт и подождали, пока сервер запустится и выключится.

Уведомление:существуют в практическом применении,HeartBeatArtisanServerHandler Класс должен реализовать userEventTriggered метод борьбы с IdleStateEvent,пожалуйстапродолжатьсмотреть


Этот код представляет собой NettyChannelHandler, используемый для обработки пакетов Heartbeat на стороне сервера.

Язык кода:javascript
копировать
package com.artisan.heartbeat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class HeartBeatArtisanServerHandler extends SimpleChannelInboundHandler<String> {
    // Определите счетчик для записи количества неактивных операций чтения.
    int readIdleTimes = 0;
    // Этот метод будет вызываться при чтении сообщения из канала.
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        System.out.println(" ====== > [server] message received : " + s);
        // Если из сообщения да"Heartbeat получено Пакет", затем ответьте "ок"
        if ("Heartbeat Packet".equals(s)) {
            ctx.channel().writeAndFlush("ok");
        } else {
            // Если да другая информация, выведите другую информацию обработки. ...
            System.out.println(" Другая обработка информации ... ");
        }
    }
    // Этот метод будет вызываться, когда Netty инициирует тайм-аут.
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent event = (IdleStateEvent) evt;
        // Определяет строку, используемую для хранения типа события тайм-аута.
        String eventType = null;
        // Переключение в зависимости от статуса события
        switch (event.state()) {
            case READER_IDLE:
                eventType = «Читать вхолостую»;
                // Чтение простоя с увеличением счетчика на 1
                readIdleTimes++; 
                break;
            case WRITER_IDLE:
                eventType = «Пиши без дела»;
                // Не обработано
                break;
            case ALL_IDLE:
                eventType = «Чтение и запись в режиме ожидания»;
                // Не обработано
                break;
        }
        
        // Распечатайте информацию о тайм-ауте
        System.out.println(ctx.channel().remoteAddress() + "тайм-аутсобытие:" + eventType);
        // Если количество простоев чтения превышает 3 раза, закройте соединение, чтобы освободить больше ресурсов.
        if (readIdleTimes > 3) {
            System.out.println(" [server] Прочитайте в режиме ожидания более 3 раз, закройте соединение, чтобы освободить больше ресурсов");
            ctx.channel().writeAndFlush("idle close");
            ctx.channel().close();
        }
    }
    // Этот метод будет вызываться при активации канала, т.е. при успешном установлении соединения.
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.err.println("=== " + ctx.channel().remoteAddress() + " is active ===");
    }
}

Этот код определяет NettyизChannelHandler.,для работы с сетьюсоединятьсерединаиз Сумка Heartbeat。Этот процессор наследует отSimpleChannelInboundHandler<String>,Это означает, что он в основном используется для обработки сообщений строкового типа.

  • channelRead0метод:Этот метод будет вызываться при чтении сообщения из канала.。существоватьздесь,Проверяет, было ли получено сообщение «Heartbeat Packet».,Если да,тогда ответь "ок",нетзатем распечатайте Другая обработка информации。
  • userEventTriggeredметод:Долженметоддля обработкиNettyизтайм-аутсобытие。NettyКаналы будут регулярно проверятьсяданетпраздныйсостояние,Здесь изIdle означает, что операции чтения или записи не выполняются. Если произошло событие тайм-аута,Netty запустит этот метод.существовать в этом методе,Он подсчитывает количество ожидающих операций чтения.,если более 3 раз,затем отправьте «холостой ход закрыть» и закрыть соединение.
  • channelActiveметод:Этот метод будет вызываться при активации канала, т.е. при успешном установлении соединения.。существоватьздесь,Он распечатывает удаленный адрес.

Суммируя: Этот процессор в основном обрабатывает три типа таймаутов: простое чтения, простое записи и простое чтения и записи. Когда получен пакет Heartbeat,Отвечу "ок",Если количество чтений в режиме простоя превышает 3 раза,Совместительство будет закрыто.


【Client】

этот кодда Простой пример клиента изNetty,Используется для отправки пакетов пульса на сервер. Ниже приведены подробные китайские примечания:

Язык кода:javascript
копировать
package com.artisan.heartbeat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.Random;
/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class HeartBeatArtisanClient {
    public static void main(String[] args) throws Exception {
        // Создайте NioEventLoopGroup для обработки циклов Nettyизсобытие.
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            // Создайте объект Bootstrap для настройки клиента.
            Bootstrap bootstrap = new Bootstrap();
            // Установите типы EventLoopGroup и Channel.
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                    // Добавьте ChannelInitializer для инициализации ChannelPipeline.
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // получатьChannelPipelineи Добавить декодер, кодер и обработчик обработки пульса
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("decoder", new StringDecoder());
                            pipeline.addLast("encoder", new StringEncoder());
                            pipeline.addLast(new HeartBeatArtisanClientHandler());
                        }
                    });
            // Распечатайте информацию о запуске клиента Netty.
            System.out.println("netty client start。。");
            // подключиться к серверу и получить объект Channel
            Channel channel = bootstrap.connect("127.0.0.1", 9000).sync().channel();
            // Определите текст отправляемого пакета Heartbeat.
            String text = "Heartbeat Packet";
            // Создайте объект Random для генерации случайных чисел.
            Random random = new Random();
            // существующий случайный отправляет пакеты пульса в цикле
            while (channel.isActive()) {
                int num = random.nextInt(8);
                Thread.sleep(num * 1000);
                channel.writeAndFlush(text);
            }
        } catch (Exception e) {
            // Распечатать информацию об исключении
            e.printStackTrace();
        } finally {
            // Грамотно закройте EventLoopGroup и освободите ресурсы.
            eventLoopGroup.shutdownGracefully();
        }
    }
    
}

HeartBeatArtisanClientHandler,он наследует отSimpleChannelInboundHandler<String>。Этот процессор используется для обработкиизстроковое сообщение。

Язык кода:javascript
копировать
package com.artisan.heartbeat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
 * @author маленький мастер
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class HeartBeatArtisanClientHandler extends SimpleChannelInboundHandler<String> {
    // Переопределить метод ChannelRead0 для обработки получения сообщений.
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        // Распечатать полученное сообщение
        System.out.println(" client received :" + msg);
        // Если получено сообщение да"idle закрыть", затем закрыть соединение
        if (msg != null && msg.equals("idle close")) {
            // Распечатайте информацию об отключении сервера
            System.out.println(" Сервер закрыт соединять и клиент тоже закрыт");
            // Закрыть клиентсоединять
            ctx.channel().closeFuture();
        }
    }
}

существуют в этом процессоре,когда сообщение получено,channelRead0методбудет вызван。если полученоизда"idle close"информация,Процессор напечатает сообщение о том, что сервер закрыт.,И немедленно закрыть клиент изоединять. В этом случае,Клиент не будет отправлять пакеты Heartbeat,Потому что сервер больше не принимает соединение.


Пожалуйста, посмотрите В клиенте HeartBeatArtisanClient

Посмотрите еще раз HeartBeatArtisanServerконецизнастраивать

Имитировать тайм-аут пульса

Разработано, как указано выше, случайный 0–7.

Запускаем Сервер и Клиент последовательно, чтобы наблюдать

нормальная ситуация

Попробуйте еще раз

Сообщения передаются одно за другим.


Анализ исходного кода IdleStateHandler

channelRead

Давайте сначаласмотреть ВнизIdleStateHandlerсерединаизchannelReadметод

Только да выполнен сквозной переход,Никакая обработка бизнес-логики не выполняется.,Пусть следующий обработчик в ChannelPipe обрабатывает метод ChannelRead.

channelActive

initialize

initializeизметод,этотдаIdleStateHandlerиз Основной исходный код

Язык кода:javascript
копировать
private void initialize(ChannelHandlerContext ctx) {
        // Avoid the case where destroy() is called before scheduling timeouts.
        // See: https://github.com/netty/netty/issues/143
        switch (state) {
        case 1:
        case 2:
            return;
        }

        state = 1;
        initOutputChanged(ctx);

        lastReadTime = lastWriteTime = ticksInNanos();
        if (readerIdleTimeNanos > 0) {
            readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx),
                    readerIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
        if (writerIdleTimeNanos > 0) {
            writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx),
                    writerIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
        if (allIdleTimeNanos > 0) {
            allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx),
                    allIdleTimeNanos, TimeUnit.NANOSECONDS);
        }
    }

существуют В этом методе,первыйпроходитьswitchоператор проверяет текущийизсостояние。еслисостояниеужеда1или2,Тогда инициализация не требуется. Статус 1 обычно указывает на то, что инициализация завершена.,Статус 2 означает, что канал закрыт.

Затем установите статус на 1,ивызовinitOutputChangedметод борьбы Изменение вывода, обычно это делается для того, чтобы размер выходного буфера канала соответствовал размеру, установленному при инициализации. Затем установите время последнего чтения и записи на текущее время, и эти временные метки будут использоваться для последующего обнаружения состояния простоя. Затем,в соответствии снастраиватьизчитатьпраздныйтайм-аутвремя(readerIdleTimeNanos)、Писатьпраздныйтайм-аутвремя(writerIdleTimeNanos)和所有праздныйтайм-аутвремя(allIdleTimeNanos),Запланируйте соответствующие задачи тайм-аута отдельно. Эти задачи будут выполнены по истечении указанного периода ожидания.,Для обработки состояния простоя канала.

  • ReaderIdleTimeoutTask:Если каналсуществоватьreaderIdleTimeNanosНе включеночитать Получить операцию,Эта задача будет запущена.
  • WriterIdleTimeoutTask:Если каналсуществоватьwriterIdleTimeNanosНе включено Писать Введите операцию,Эта задача будет запущена.
  • AllIdleTimeoutTask:Если каналсуществоватьallIdleTimeNanosНет ни того, ни другогочитать Получить операцию也没有Писать Введите операцию,Эта задача будет запущена.

Обычно мы используем readIdleTimeout, поэтому нам нужно сосредоточиться на

Язык кода:javascript
копировать
 if (readerIdleTimeNanos > 0) {
            readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx),
                    readerIdleTimeNanos, TimeUnit.NANOSECONDS);
        }

продолжать ReaderIdleTimeoutTask

прямойсмотретьrunметод

Язык кода:javascript
копировать
@Override
        protected void run(ChannelHandlerContext ctx) {
            // Определите время задержки перед переходом в состояние ожидания в следующий раз.
            long nextDelay = readerIdleTimeNanos;
            // Если текущий статус не читается
            if (!reading) {
                // Вычтите количество наносекунд с момента последнего чтения, чтобы настроить следующую задержку.
                nextDelay -= ticksInNanos() - lastReadTime;
            }

            // Если время задержки меньше или равно 0, это означает, что чтение бездействовало достаточно долгое время.
            if (nextDelay <= 0) {
                // Установите новый тайм-аут и отправьте уведомление об обратном вызове
                readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);

                // да Нода Первый триггер читается в режиме ожиданиясобытие
                boolean first = firstReaderIdleEvent;
                firstReaderIdleEvent = false; // Установите флаг в значение false, чтобы избежать повторного срабатывания последующих

                try {
                    // Создать событие состояния простоя
                    IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);
                    // существуют, вызывают это событие в контексте канала
                    channelIdle(ctx, event);
                } catch (Throwable t) {
                    // Если возникает исключение, распространите это исключение на контекст канала.
                    ctx.fireExceptionCaught(t);
                }
            } else {
                // Если операция чтения происходит до истечения времени существования, запланируйте более короткую задержку по времени ожидания.
                readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
            }
        }
    }
  1. runметодпервыйопределение ПонятноодинnextDelayпеременная,этотиндивидуальныйпеременная表示Вниз一次курокпраздныйсостояниесобытиевпередиз Время задержки。этотиндивидуальный Время задержки Зависит отreaderIdleTimeNanosРешать,этодасуществоватьIdleStateHandlerКонструкторсерединанастраиватьиз。
  2. еслиreadingФлагfalse,Означает, что канал в данный момент не находится в состоянии чтения.,Тогда это начнется сnextDelayсередина Вычтите количество наносекунд с момента последнего чтения, чтобы настроить следующую задержку.。
  3. еслиnextDelayменьше, чемилиравный0,Это означает, что читатель простаивал достаточно долго.,нуждаться Установите новый тайм-аут и отправьте уведомление об обратном вызове。здесьиспользовать Понятноscheduleметодсуществоватьctx通道上Вниз文середина安排одинIdleStateHandlerизосуществлять,осуществлятьвремядляreaderIdleTimeNanos
  4. firstReaderIdleEventлоготип для идентификацииданетдапервыйкурокчитать者праздныйсобытие。Если дапервый,этотиндивидуальный标志会被настраиватьдляfalse,Во избежание последующего повторного срабатывания.
  5. Код пытается создатьIdleStateEventсобытие,ииспользоватьchannelIdleметодсуществуют, вызывают это событие в контексте канала。этотиндивидуальныйсобытиебудет переданосуществоватьChannelPipelineсерединазарегистрироватьсяизIdleStateHandlerизперезвонить。
  6. Если во время вышеуказанного процесса возникает исключение,использоватьctx.fireExceptionCaught(t)методсуществовать通道上Вниз文серединараспространениеэтотиндивидуальный异常。
  7. еслиnextDelayбольше, чем0,Описание существует Операция чтения произошла до истечения таймаута,Поэтому будет запланирована более короткая задержка и тайм-аут.

Давайте возьмем пример, чтобы понять

Язык кода:javascript
копировать
nextDelay -= ticksInNanos() - lastReadTime;

Вычтите время последнего вызова метода ChannelRead из текущего времени. Если результат равен да6с. , указывая, что последний раз, когда ChannelRead вызывался, был 6 секунд. Извпередизиметь значение Понятно,Вы устанавливаете изда5s,Тогда nextDelay равен -1,Время ожидания описания истекло

Это вызовет channelIdle(ctx, event);

Исходный код выглядит следующим образом

Язык кода:javascript
копировать
    /**
     * Is called when an {@link IdleStateEvent} should be fired. This implementation calls
     * {@link ChannelHandlerContext#fireUserEventTriggered(Object)}.
     */
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
        ctx.fireUserEventTriggered(evt);
    }

Если тайм-аут отсутствует, метод userEventTriggered не запускается.

Если таймаута нет, иди

Язык кода:javascript
копировать
// Если операция чтения происходит до истечения времени существования, запланируйте более короткую задержку по времени ожидания.
readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);

этотиндивидуальныйrunметоддаNetty处理通道праздныйсостояниеизключевая часть,Это гарантирует, что соответствующая логика обработки может быть запущена, когда существующий канал не читается в течение длительного времени.,Тем самым избегая растраты ресурсов и потенциальных проблем.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose