Netty踩坑日记

记录一下变秃路上的十万个为什么之Netty踩坑日记

  • 记为什么netty不走编码解码
  • 记怎么约定一个包的长度
  • 记为什么netty的client会丢包
  • 记netty的单线程处理
  • 记如何处理返回值

记为什么netty不走编码解码

有一篇文章,叫做漫谈Pipeline,作者非常有意思,讲的对新手非常友好,特别适合新手食用的博客。
链接我放在这里了,对于我这种菜鸟来说真的有点坑了http://www.sohu.com/a/283947536_781946(博)

记怎么约定一个包的长度

设置一下这个东西 new LengthFieldBasedFrameDecoder(102410241024, 0, 4,0,4)记得放在pipeline的最上边,然后在解码的时候先读一个int,然后就ok了,结合上边的对于不了解pipeline的新手来说也是个难活了,然后你就可以发送超过1024个字节的数据了

记为什么netty的client会丢包

内存不够啊,15年的mac air伤不起,4g内存干点啥都崩,不过也好,内存不够的时候还一个劲的发送同一条数据,然后内存就一路飙升,我猜,我猜昂,我一看随后改成发送不同的数组之后丢包的问题就没了(因为内存不足速度明显慢了下来,说明可能系统采取了措施),然后就不看源码了,当多次发送同一条数据的时候(比如一个byte数组)netty会复制,但是当你发送不同的数据的时候netty只会引用,所以netty可能没有oom这个错误(都是我猜的昂)

记netty的单线程处理

netty的单线程是对于channel来说的,一个channel归属与一个线程,始终由这个线程来处理,所以不用加锁,但是我还是加了,用了ConcurrentHashMap来保存所有的通道,万一呢,万一有点啥事是吧,咱不就亏了么,然后包的排序又是一个大问题,我自己写了个类贴出来,但是这个类有点费内存,我应该改成链表的结构,但是链表应该会慢的是吧,应该不如数组快。我写的这个还有点问题没有调整,那就是当部分数据包迟迟发不过来的时候应该加超时判断,然后判定任务失败,释放内存

/**
 * 任务队列管理器
 *
 * @author shangyouren
 */
class QueueManager {

    private static QueueManager self;

    static QueueManager instance() {
        if (self == null) {
            synchronized (QueueManager.class) {
                if (self == null) {
                    self = new QueueManager();
                }
            }
        }
        return self;
    }

    private QueueManager() {
        processTaskCache = new ConcurrentHashMap<>();
    }

    private ConcurrentHashMap processTaskCache;

    /**
     * 仅用于QueueEventHandler中,注册时间,以供后续得到
     */
    void register(BaseProcess process) {
        processTaskCache.computeIfAbsent(process.protocol.getId(), k -> new ProcessQueue(process.protocol.getAmount()));
        processTaskCache.get(process.protocol.getId()).getQueue()[process.protocol.getSerial()] = process;
    }

    /**
     * 获得下一个事件,如果没有则为空
     * 
     * ProcessQueue中只有两个值next是个int,queue是个BaseProcess的数组
     * BaseProcess中有个属性类 NetworkProtocol(一个数据包),这个属性类中有几个值
     * 			有个id(任务标识)
     * 			有个amount(表示任务拆了几个数据包发送)
     * 			有个serial(表是当前数据包是这个任务中的第几个)
     */
    BaseProcess nextProcess(long taskId) {
        ProcessQueue queue = this.processTaskCache.get(taskId);
        if (queue == null){
            return null;
        }
        int present = queue.getNext();
        if (present >= queue.getQueue().length) {
            //任务完成垃圾回收
            queue.setQueue(null);
            this.processTaskCache.remove(taskId);
            return null;
        }
        BaseProcess result = queue.getQueue()[present];
        if (result != null) {
            queue.setNext(queue.getNext() + 1);
            queue.getQueue()[present] = null;
        }
        return result;
    }

}

记如何处理返回值

netty的任务和数据包发到服务端了,那么我怎么追踪netty的返回呢,还是自己来,写了一个任务管理,这个任务管理还是有点问题,配合之前客户端丢包的事来说,包发过去(至少从客户端来看是发过去了)但是服务端没有收到,那就没有返回,那客户端wait之后就死掉了,醒不来了

/**
 * 任务管理器
 *
 * @author shangyouren
 * @since 2019/11/19
 */
public class TaskManager {

    private static TaskManager self;

    public static TaskManager instance(){
        if (self == null){
            synchronized (TaskManager.class){
                if(self == null){
                    self = new TaskManager();
                }
            }
        }
        return self;
    }

    private TaskManager(){
        taskList = new ConcurrentHashMap<>();
    }

    private ConcurrentHashMap taskList;

    /**
     * 注册任务,注册过之后wait才会真正等待
     */
    public boolean registerTask(long id){
        if (taskList.get(id) != null){
            return false;
        }
        taskList.put(id, new Task());
        return true;
    }

    /**
     * 等待,等待返回期间线程沉睡
     */
    public Object waitTask(long id) throws InterruptedException {
        if (taskList.get(id) == null){
            return 0L;
        }
        if (taskList.get(id).isEnd()){
            return taskList.get(id).time;
        }
        taskList.get(id).countInc();
        synchronized (taskList.get(id)) {
            taskList.get(id).wait();
        }
        Task task = taskList.get(id);
        int count = taskList.get(id).countDec();
        if (count <= 0){
            taskList.remove(id);
        }
        return task.msg == null ? task.time : task.msg;
    }

    /**
     * 结束任务,netty的channelHandler调用,唤醒沉睡的线程
     */
    public void finishTask(long id, Object msg){
        if (taskList.get(id) == null){
            return ;
        }
        if(taskList.get(id).isEnd()){
            return ;
        }
        taskList.get(id).end(msg);
        synchronized (taskList.get(id)) {
            taskList.get(id).notifyAll();
        }
    }

    private static class Task{

        long time;

        boolean end;

        AtomicInteger count;

        Object msg;

        boolean isEnd() {
            return end;
        }

        void countInc(){
            count.incrementAndGet();
        }

        int countDec(){
            return count.decrementAndGet();
        }

        void end(Object msg){
            this.end = true;
            this.time = System.currentTimeMillis() - this.time;
            this.msg = msg;
        }

        Task(){
            this.time = System.currentTimeMillis();
            this.end = false;
            this.count = new AtomicInteger(0);
            this.msg = null;
        }
    }

}

你可能感兴趣的:(java)