• Lettuce连接池


           Lettuce 连接被设计为线程安全,所以一个连接可以被多个线程共享,同时lettuce连接默认是自动重连.虽然连接池在大多数情况下是不必要的,但在某些用例中可能是有用的.lettuce提供通用的连接池支持. 如有疏漏后续会更新 https://www.cnblogs.com/wei-zw/p/9163687.html

    连接池是否有必要?


         Lettuce被线程安全的,它满足了多数场景需求. 所有Redis用户的操作是单线程执行的.使用多连接并不能改善一个应用的性能. 阻塞操作的使用通常与获得专用连接的工作线程结合在一起.
            使用Redis事务是使用动态连接池的典型场景,因为需要专用连接的线程数趋于动态.也就是说,动态连接池的需求是有限的.连接池总是伴随着复杂性和维护成本提升.

    同步连接池

      使用命令式编程,同步连接池是正确的选择,因为它在用于执行执行Redis命令的线程上执行所有操作.

       前提条件
           Lettuce需要依赖 Apache的 common-pool2(至少是2.2)提供连接池. 确认在你的classpath下包含这个依赖.否则你就不能使用连接池.
    如果使用Maven,向你的pom.xml添加如下依赖

    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.4.3</version>
    </dependency>

     连接池支持

    Lettuce提供通用连接池支持,它需要一个用于创建任何支持类型连接(单个,发布订阅,哨兵,主从,集群)的提供者. ConnectionPoolSupport 将根据你的需求创建一个 GenericObjectPool或SoftReferenceObjectPool. 连接池可以分配包装类型或直接连接

    • 包装实例在调用StatefulConnection.close()时,会将连接归还到连接池
    • 直接连接需要调用GenericObjectPool.returnObject(...)归还到连接池

    基本用法:

      包装连接

      GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
            poolConfig.setMaxIdle(2);
    
            GenericObjectPool<StatefulRedisConnection<String, String>> pool = ConnectionPoolSupport.createGenericObjectPool(
                    () -> client.connect(), poolConfig);
            for (int i = 0; i < 10; i++) {
                StatefulRedisConnection<String, String> connection = pool.borrowObject();
                RedisCommands<String, String> sync = connection.sync();
                sync.ping();
                connection.close();
            }

    直接连接

         GenericObjectPool<StatefulRedisConnection<String, String>> pool = ConnectionPoolSupport.createGenericObjectPool(
                    () -> client.connect(), new GenericObjectPoolConfig(), false);
    
            for (int i = 0; i < 10; i++) {
                StatefulRedisConnection<String, String> connection = pool.borrowObject();
                RedisCommands<String, String> sync = connection.sync();
                sync.ping();
           //主动将连接归还到连接池  pool.returnObject(connection); }

      

    相关源码分析

     public static <T extends StatefulConnection<?, ?>> GenericObjectPool<T> createGenericObjectPool(
                Supplier<T> connectionSupplier, GenericObjectPoolConfig config, boolean wrapConnections) {
    
            LettuceAssert.notNull(connectionSupplier, "Connection supplier must not be null");
            LettuceAssert.notNull(config, "GenericObjectPoolConfig must not be null");
    
            AtomicReference<ObjectPool<T>> poolRef = new AtomicReference<>();
    
            GenericObjectPool<T> pool = new GenericObjectPool<T>(new RedisPooledObjectFactory<T>(connectionSupplier), config) {
    
                @Override
                public T borrowObject() throws Exception {
                    //如果wrapConnection 设置为true,则对连接创建动态代理
                    return wrapConnections ? wrapConnection(super.borrowObject(), this) : super.borrowObject();
                }
    
                @Override
                public void returnObject(T obj) {
    
                    if (wrapConnections && obj instanceof HasTargetConnection) {
                        super.returnObject((T) ((HasTargetConnection) obj).getTargetConnection());
                        return;
                    }
                    super.returnObject(obj);
                }
            };
    
            poolRef.set(pool);
    
            return pool;
        }
    

      创建一个包装类型到连接

     private static <T> T wrapConnection(T connection, ObjectPool<T> pool) {
            
            //创建调用处理器
            ReturnObjectOnCloseInvocationHandler<T> handler = new ReturnObjectOnCloseInvocationHandler<T>(connection, pool);
    
            Class<?>[] implementedInterfaces = connection.getClass().getInterfaces();
            Class[] interfaces = new Class[implementedInterfaces.length + 1];
            interfaces[0] = HasTargetConnection.class;
            System.arraycopy(implementedInterfaces, 0, interfaces, 1, implementedInterfaces.length);
            //创建代理连接
            T proxiedConnection = (T) Proxy.newProxyInstance(connection.getClass().getClassLoader(), interfaces, handler);
            //向连接调用处理器设置代理连接
            handler.setProxiedConnection(proxiedConnection);
            //返回代理连接
            return proxiedConnection;
        }
    

      包装类型连接的动态调用处理器

      private static class ReturnObjectOnCloseInvocationHandler<T> extends AbstractInvocationHandler {
            //被代理对连接
            private T connection;
            private T proxiedConnection;
            private Map<Method, Object> connectionProxies = new ConcurrentHashMap<>(5, 1);
            //连接池
            private final ObjectPool<T> pool;
    
            ReturnObjectOnCloseInvocationHandler(T connection, ObjectPool<T> pool) {
                this.connection = connection;
                this.pool = pool;
            }
            
            //设置代理连接
            void setProxiedConnection(T proxiedConnection) {
                this.proxiedConnection = proxiedConnection;
            }
    
            @Override
            protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
                 //如果调用方法是  getStatefulConnection则返回代理连接
                if (method.getName().equals("getStatefulConnection")) {
                    return proxiedConnection;
                }
                //如果调用的方法是getTargetConnection 则返回真实连接
                if (method.getName().equals("getTargetConnection")) {
                    return connection;
                }
                //如果真实连接为null则抛出异常
                if (connection == null) {
                    throw new RedisException("Connection is deallocated and cannot be used anymore.");
                }
                //如果调用的方法是close则将代理连接归还到连接池,并将真实连接设置和代理连接设置为null
                if (method.getName().equals("close")) {
                    pool.returnObject(proxiedConnection);
                    connection = null;
                    proxiedConnection = null;
                    connectionProxies.clear();
                    return null;
                }
    
                try {
                    //如果调用方法是获取连接则从代理连接池中获取,如果没有则创建代理连接并放入缓存
                    if (method.getName().equals("sync") || method.getName().equals("async") || method.getName().equals("reactive")) {
                        return connectionProxies.computeIfAbsent(
                                method, m -> getInnerProxy(method, args));
                    }
                    //其它方法不在多任何拦截
                    return method.invoke(connection, args);
    
                } catch (InvocationTargetException e) {
                    throw e.getTargetException();
                }
            }
    
            @SuppressWarnings("unchecked")
            private Object getInnerProxy(Method method, Object[] args) {
    
                try {
                    Object result = method.invoke(connection, args);
    
                    result = Proxy.newProxyInstance(getClass().getClassLoader(), result.getClass().getInterfaces(),
                            new DelegateCloseToConnectionInvocationHandler<>((AutoCloseable) proxiedConnection, result));
    
                    return result;
                } catch (IllegalAccessException e) {
                    throw new RedisException(e);
                } catch (InvocationTargetException e) {
                    throw new RedisException(e.getTargetException());
    
                }
            }
    
            public T getConnection() {
                return connection;
            }
        }
    

      

  • 相关阅读:
    spring cloud eureka 服务端开启密码认证后,客户端无法接入问题
    微信小程序 获取用户信息 encryptData解密 C#版本
    Chrome浏览器离线安装 Postman 5.X 报错
    framework7使用问题汇总
    centos 6 防火墙开启端口无效问题
    ASP.NET下使用xml反序列化、缓存实现个性化配置文件的实时生效
    Swagger+SpringBoot整理
    baseController
    微信小程序-扫码点餐系统设计
    redis+Spring初级应用
  • 原文地址:https://www.cnblogs.com/wei-zw/p/9163687.html
Copyright © 2020-2023  润新知