• Grizzly HTTP CoDec ThreadCache 浅析


    Grizzly 的 HTTP CoDec 实现方法更 Netty 的 CoDec 完全不同, 他们思想上的差异主要在于:

    1. 解码方式

    Grizzly 使用流式解码, 它的HttpHeader对象内部都是用的DataChunk这样的类来存储数据, 就是像是将一个类都分好格子, 里面有 InitialLine, HeaderName1, HederVal1, HeaderName2, HeaderVal2 这样, 这些格子都是用来放二进制数据的, 然后再读取到Buffer后将Buffer的数据往 类里面的格子里填进去. 只有在真正需要使用这些数据的时候才会将二进制数据转为字符串

    Netty 的解码则是使用 ReplayingDecoder 的特性来实现, ReplayingDecoder 的特性是可以设置checkpoint, 如果说当前checkpoint还没有完成, 但是buffer的数据又读完了, 则会将当前的数据累计起来, 在下一次收到新buffer的时候从当前checkpoint重新decode, 如果checkpoint完成, 则转移到一下checkpoint, 并释放成功decode的数据. 他的好处在于当buffer被读完的时候(数据不够, 例如header才decode到一半buffer就读完了)则会抛出一个SIGNAL(SIGNAL是Netty自定义的ERROR), 捕捉到这个error以后则会自动累计数据. 这样我们就不需要自己去判断当前数据够不够, 而是当做当前数据已经足够来做decode(因为会自动累计)

    在解码方式上, 实际上我更倾向于 Grizzly 的这种方式, 因为它不需要累计数据, 这样就减少了一次ByteBuf的分配, 更节省内存. 而从decode方法上看, 流式填数据decode的方法也更容易理解, 不像replaying decode这么绕难以理解

    2. Cache

    Grizzly 对 Decode 过程中出现的所有对象都做了Cache, 例如 HttpHeader, HttpContent 等对象, 这些对象的生存周期是整个Decode过程, 也就是在Decode开始的时候会获取这个Cache对象, 然后在Decode完成以后会清理Cache对象内部的数据, 再将它放回 Cache里, 另外, 这些Cache都是ThreadLocal的, 所以他们是肯定线程安全的. 而 Netty 中这些对象都是新生成的. 下面对Grizzly的Cache体系做一个介绍. 这些代码都在 ThreadCache类里.  先看图

    最后在看看ThreadCache的代码

    public final class ThreadCache {
    
        private static final ObjectCacheElement[] INITIAL_OBJECT_ARRAY = new ObjectCacheElement[16];
    
        /** 这个map用于通过className获取对应的CachedTypeIndex */
        private static final Map<String, CachedTypeIndex> typeIndexMap =
                new HashMap<String, CachedTypeIndex>();
    
        /** 全局的index计数器, 用来初始化CachedTypeIndex */
        private static int indexCounter;
    
        private static final ThreadLocal<ObjectCache> genericCacheAttr =
                new ThreadLocal<ObjectCache>();
    
        public static synchronized <E> CachedTypeIndex<E> obtainIndex(
                Class<E> clazz, int size) {
            return obtainIndex(clazz.getName(), clazz, size);
    
        }
    
        /** 
         * 获取CachedTypeIndex的方法, 也就是说每个类在获取这个CachedTypeIndex的时候, 
         * 他在ObjectCache的ObjectCacheElement的index就已经定下来了
         * */
        @SuppressWarnings("unchecked")
        public static synchronized <E> CachedTypeIndex<E> obtainIndex(String name,
                                                                      Class<E> clazz, int size) {
    
            CachedTypeIndex<E> typeIndex = typeIndexMap.get(name);
            if (typeIndex == null) {
                // 注意这里的indexCounter++, 每个类获取一次index就会+1
                typeIndex = new CachedTypeIndex<E>(indexCounter++, name, clazz, size);
                typeIndexMap.put(name, typeIndex);
            }
    
            return typeIndex;
        }
    
        public static <E> boolean putToCache(final CachedTypeIndex<E> index, final E o) {
            return putToCache(Thread.currentThread(), index, o);
        }
    
        public static <E> boolean putToCache(final Thread currentThread,
                                             final CachedTypeIndex<E> index, final E o) {
            if (currentThread instanceof DefaultWorkerThread) {
                return ((DefaultWorkerThread) currentThread).putToCache(index, o);
            } else {
                ObjectCache genericCache = genericCacheAttr.get();
                if (genericCache == null) {
                    genericCache = new ObjectCache();
                    genericCacheAttr.set(genericCache);
                }
    
                return genericCache.put(index, o);
            }
        }
    
        /**
         * Get the cached object with the given type index from cache.
         * Unlike {@link #takeFromCache(org.glassfish.grizzly.ThreadCache.CachedTypeIndex)}, the
         * object won't be removed from cache.
         *
         * @param <E>
         * @param index the cached object type index.
         * @return cached object.
         */
        public static <E> E getFromCache(final CachedTypeIndex<E> index) {
            return getFromCache(Thread.currentThread(), index);
        }
    
        /**
         * Get the cached object with the given type index from cache.
         * Unlike {@link #takeFromCache(org.glassfish.grizzly.ThreadCache.CachedTypeIndex)}, the
         * object won't be removed from cache.
         *
         * @param <E>
         * @param currentThread current {@link Thread}
         * @param index the cached object type index.
         * @return cached object.
         */
        public static <E> E getFromCache(final Thread currentThread,
                                         final CachedTypeIndex<E> index) {
            assert currentThread == Thread.currentThread();
    
            if (currentThread instanceof DefaultWorkerThread) {
                return ((DefaultWorkerThread) currentThread).getFromCache(index);
            } else {
                final ObjectCache genericCache = genericCacheAttr.get();
                if (genericCache != null) {
                    return genericCache.get(index);
                }
    
                return null;
            }
        }
    
        /**
         * Take the cached object with the given type index from cache.
         * Unlike {@link #getFromCache(org.glassfish.grizzly.ThreadCache.CachedTypeIndex)}, the
         * object will be removed from cache.
         *
         * @param <E>
         * @param index the cached object type index
         * @return cached object
         */
        public static <E> E takeFromCache(final CachedTypeIndex<E> index) {
            return takeFromCache(Thread.currentThread(), index);
        }
    
        /**
         * Take the cached object with the given type index from cache.
         * Unlike {@link #getFromCache(org.glassfish.grizzly.ThreadCache.CachedTypeIndex)}, the
         * object will be removed from cache.
         *
         * @param <E>
         * @param currentThread current {@link Thread}
         * @param index the cached object type index
         * @return cached object
         */
        public static <E> E takeFromCache(final Thread currentThread,
                                          final CachedTypeIndex<E> index) {
            if (currentThread instanceof DefaultWorkerThread) {
                return ((DefaultWorkerThread) currentThread).takeFromCache(index);
            } else {
                final ObjectCache genericCache = genericCacheAttr.get();
                if (genericCache != null) {
                    return genericCache.take(index);
                }
    
                return null;
            }
        }
    
        /** 实际存储cache的类 */
        public static final class ObjectCache {
            private ObjectCacheElement[] objectCacheElements;
    
            public boolean put(final CachedTypeIndex index, final Object o) {
                if (objectCacheElements != null &&
                        index.getIndex() < objectCacheElements.length) {
                    ObjectCacheElement objectCache = objectCacheElements[index.getIndex()];
                    if (objectCache == null) {
                        objectCache = new ObjectCacheElement(index.size);
                        objectCacheElements[index.getIndex()] = objectCache;
                    }
    
                    return objectCache.put(o);
                }
    
                final ObjectCacheElement[] arrayToGrow =
                        (objectCacheElements != null) ?
                                objectCacheElements : INITIAL_OBJECT_ARRAY;
                final int newSize = Math.max(index.getIndex() + 1,
                        (arrayToGrow.length * 3) / 2 + 1);
    
                objectCacheElements = Arrays.copyOf(arrayToGrow, newSize);
    
                final ObjectCacheElement objectCache = new ObjectCacheElement(index.getSize());
                objectCacheElements[index.getIndex()] = objectCache;
                return objectCache.put(o);
            }
    
            /**
             * Get the cached object with the given type index from cache.
             * Unlike {@link #take(org.glassfish.grizzly.ThreadCache.CachedTypeIndex)}, the
             * object won't be removed from cache.
             *
             * @param <E>
             * @param index the cached object type index.
             * @return cached object.
             */
            @SuppressWarnings("unchecked")
            public <E> E get(final CachedTypeIndex<E> index) {
                final int idx;
                if (objectCacheElements != null &&
                        (idx = index.getIndex()) < objectCacheElements.length) {
    
                    final ObjectCacheElement objectCache = objectCacheElements[idx];
                    if (objectCache == null) return null;
    
                    return (E) objectCache.get();
                }
    
                return null;
            }
    
            /**
             * Take the cached object with the given type index from cache.
             * Unlike {@link #get(org.glassfish.grizzly.ThreadCache.CachedTypeIndex)}, the
             * object will be removed from cache.
             *
             * @param <E>
             * @param index the cached object type index.
             * @return cached object.
             */
            @SuppressWarnings("unchecked")
            public <E> E take(final CachedTypeIndex<E> index) {
                final int idx;
                if (objectCacheElements != null &&
                        (idx = index.getIndex()) < objectCacheElements.length) {
    
                    final ObjectCacheElement objectCache = objectCacheElements[idx];
                    if (objectCache == null) return null;
    
                    return (E) objectCache.take();
                }
    
                return null;
            }
        }
    
        public static final class ObjectCacheElement {
            private final int size;
            private final Object[] cache;
            private int index;
    
            public ObjectCacheElement(int size) {
                this.size = size;
                cache = new Object[size];
            }
    
            public boolean put(Object o) {
                if (index < size) {
                    cache[index++] = o;
                    return true;
                }
    
                return false;
            }
    
            /**
             * Get (peek) the object from cache.
             * Unlike {@link #take()} the object will not be removed from cache.
             *
             * @return object from cache.
             */
            public Object get() {
                if (index > 0) {
                    final Object o = cache[index - 1];
                    return o;
                }
    
                return null;
            }
    
            /**
             * Take (poll) the object from cache.
             * Unlike {@link #get()} the object will be removed from cache.
             *
             * @return object from cache.
             */
            public Object take() {
                if (index > 0) {
                    index--;
    
                    final Object o = cache[index];
                    cache[index] = null;
                    return o;
                }
    
                return null;
            }
        }
    
        public static final class CachedTypeIndex<E> {
            private final int index;
            private final Class clazz;
            private final int size;
            private final String name;
    
            public CachedTypeIndex(final int index, final String name,
                                   final Class<E> clazz, final int size) {
                this.index = index;
                this.name = name;
                this.clazz = clazz;
                this.size = size;
            }
    
            public int getIndex() {
                return index;
            }
    
            public String getName() {
                return name;
            }
    
            public Class getClazz() {
                return clazz;
            }
    
            public int getSize() {
                return size;
            }
        }
    }

    最后来看看 CachedTypeIndex 都是在哪里生成的, 以 HttpClientFilter 中的 ClientHttpResponseImpl 为例

    private static final class ClientHttpResponseImpl
                extends HttpResponsePacket implements HttpPacketParsing {
    
            private static final ThreadCache.CachedTypeIndex<ClientHttpResponseImpl> CACHE_IDX =
                    ThreadCache.obtainIndex(ClientHttpResponseImpl.class, 16);

    可见这些Cacheable的类在首次使用的时候就会把这个index给申请下来, 然后在ObjectCache的ObjectCacheElement[]里占上一个位子.之后它的index就已经是固定的了

  • 相关阅读:
    JAVA常见面试题之Forward和Redirect的区别
    [转载]vm文件
    [转载]vm文件
    [转载]心灵丨愿你早一点发现,你才是自己最重要的粉丝
    [转载]心灵丨愿你早一点发现,你才是自己最重要的粉丝
    iBatis整理——Spring环境下批处理实现
    iBatis整理——Spring环境下批处理实现
    SAP HANA 三大特点
    在解决方案中搜索 文件的位置
    我这边测了一下,发现#后面参数变化浏览器不会刷新,但是#一旦去掉就会刷新了,你那边的url拼的时候能不能在没参数的时候#也拼在里面,这样应该就OK了
  • 原文地址:https://www.cnblogs.com/zemliu/p/3771552.html
Copyright © 2020-2023  润新知