精华内容
下载资源
问答
  • 主要实现思路是通过在 Redis 中设置 key 过期时间以及该接口对应的访问次数,若大于访问次数,则不允许再访问接口,可页面进行重定向或给出提示信息,本文则简单地实现核心逻辑。   项目结构如下图所示: ...

    一、概述

      本文主要通过 Redis 和 Lua 脚本的方式实现接口的访问限流,限流的方式可基于客户端的访问 IP 进行限流,也可以对访问接口的频率进行限流。主要的实现思路是通过在 Redis 中设置 key 的过期时间以及该接口对应的访问次数,若大于访问次数,则不允许再访问接口,可对页面进行重定向或给出提示信息,本文则简单地实现核心的逻辑。

      项目结构如下图所示:

    在这里插入图片描述

    二、实现过程

    2.1 项目依赖

        <properties>
            <java.version>1.8</java.version>
            <hutool.version>5.3.4</hutool.version>
            <fastjson.version>1.2.60</fastjson.version>
            <commons-pool2.version>2.5.0</commons-pool2.version>
            <commons.codec>1.11</commons.codec>
        </properties>
    
        <dependencies>
            <!-- spring web 依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!--aop 依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    
            <!--Apache commons 工具包依赖-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
            </dependency>
            <dependency>
                <groupId>commons-codec</groupId>
                <artifactId>commons-codec</artifactId>
                <version>${commons.codec}</version>
            </dependency>
    
            <!-- lombok 依赖 -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <!--hutool 工具包-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>
    
            <!--spring boot 集成 redis 所需 common-pool2-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
                <version>${commons-pool2.version}</version>
            </dependency>
    
            <!-- fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
    
            <!-- redis 依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <!--guaua-->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>29.0-jre</version>
            </dependency>
        </dependencies>
    

    2.2 限流枚举

    public enum LimitType {
        CUSTOMER,
        IP;
    }
    

    2.3 限流注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Limit {
    
        // 资源名称,用于描述接口实现的功能
        String name() default "";
    
        // 资源的 key
        String key() default "";
    
        // key 的前缀
        String prefix() default "";
    
        // 时间,单位是 秒
        int period() default 30;
    
        // 在 period 的时间内,限制的访问次数
        int count() default 5;
    
        // 限流类型
        LimitType limitType() default LimitType.CUSTOMER;
    }
    

    2.4 通用的请求响应实体

    public class ResultDto extends HashMap<String, Object> {
    
        private static final long serialVersionUID = -4228527911405974961L;
    
        public ResultDto() {
        }
    
        public static ResultDto error(String msg) {
            return error(500, msg);
        }
    
        public static ResultDto error(int code, String msg) {
            ResultDto json = new ResultDto();
            json.put("code", code);
            json.put("msg", msg);
            return json;
        }
    
        public static ResultDto success(int code, Object value) {
            ResultDto json = new ResultDto();
            json.put("code", code);
            json.put("data", value);
            return json;
        }
    
        @Override
        public ResultDto put(String key, Object value) {
            super.put(key, value);
            return this;
        }
    }
    

    2.5 Redis 配置文件

    @Slf4j
    @Configuration
    @EnableCaching
    @ConditionalOnClass(RedisOperations.class)
    @EnableConfigurationProperties(RedisProperties.class)
    public class RedisConfig extends CachingConfigurerSupport {
    
        /*
         * 设置 redis 数据默认过期时间,默认 2 小时,即设置 @cacheable 序列化方式
         */
        @Bean
        public RedisCacheConfiguration redisCacheConfiguration() {
    
            FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
    
            // 获取 Redis 的默认配置
            RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
    
            configuration = configuration
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)) // 设置 value 值的序列化器
                    .entryTtl(Duration.ofHours(6)); // 设置默认的过期时间
    
            return configuration;
        }
    
        /*
         * 操作 redis 的模板
         */
        @SuppressWarnings("all")
        @Bean(name = "redisTemplate")
        @ConditionalOnMissingBean(name = "redisTemplate")
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<Object, Object> template = new RedisTemplate<>();
    
            // value 值的序列化器
            FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
    
            // value值的序列化采用 fastJsonRedisSerializer
            template.setValueSerializer(fastJsonRedisSerializer);
            template.setHashValueSerializer(fastJsonRedisSerializer);
    
            // 全局开启AutoType,这里方便开发,使用全局的方式
            ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    
            // 建议使用这种方式,小范围指定白名单
            // ParserConfig.getGlobalInstance().addAccept("club.wadreamer.domain");
    
            // key 的序列化器采用 StringRedisSerializer
            template.setKeySerializer(new StringRedisSerializer());
            template.setHashKeySerializer(new StringRedisSerializer());
            template.setConnectionFactory(redisConnectionFactory);
    
            return template;
        }
    
        /*
         * 自定义缓存 key 生成策略,默认将使用该策略,即在不指定缓存的 key 时,使用该 keyGenerator 生成 key
         */
        @Bean
        @Override
        public KeyGenerator keyGenerator() {
            return (target, method, params) -> {
                Map<String, Object> container = new HashMap<>(3);
                Class<?> targetClassClass = target.getClass();
                // 类地址
                container.put("class", targetClassClass.toGenericString());
                // 方法名称
                container.put("methodName", method.getName());
                // 包名称
                container.put("package", targetClassClass.getPackage());
                // 参数列表
                for (int i = 0; i < params.length; i++) {
                    container.put(String.valueOf(i), params[i]);
                }
                // 转为JSON字符串
                String jsonString = JSON.toJSONString(container);
                // 做SHA256 Hash计算,得到一个SHA256摘要作为Key
                return DigestUtils.sha256Hex(jsonString);
            };
        }
    
        /*
         * 异常处理,当Redis发生异常时,打印日志,但是程序正常走
         */
        @Bean
        @Override
        public CacheErrorHandler errorHandler() {
            log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
            return new CacheErrorHandler() {
                @Override
                public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
                    log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
                }
    
                @Override
                public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
                    log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
                }
    
                @Override
                public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
                    log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
                }
    
                @Override
                public void handleCacheClearError(RuntimeException e, Cache cache) {
                    log.error("Redis occur handleCacheClearError:", e);
                }
            };
        }
    
    }
    
    /*
     * value 值的序列化器
     */
    class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
    
        private final Class<T> clazz;
    
        FastJsonRedisSerializer(Class<T> clazz) {
            super();
            this.clazz = clazz;
        }
    
        @Override
        public byte[] serialize(T t) {
            if (t == null) {
                return new byte[0];
            }
    
            // SerializerFeature.WriteClassName: JSON标准是不支持自省的,即不知道 json 字符串代表的数据类型,
            // 所以通过 SerializerFeature.WriteClassName, json 中会带有 @type 属性指定该 json 字符串所指代的类型
            return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8);
        }
    
        @Override
        public T deserialize(byte[] bytes) {
            if (bytes == null || bytes.length <= 0) {
                return null;
            }
            String str = new String(bytes, StandardCharsets.UTF_8);
            return JSON.parseObject(str, clazz);
        }
    }
    
    /*
     * key 的序列化器
     */
    class StringRedisSerializer implements RedisSerializer<Object> {
    
        private final Charset charset;
    
        StringRedisSerializer() {
            this(StandardCharsets.UTF_8);
        }
    
        private StringRedisSerializer(Charset charset) {
            Assert.notNull(charset, "Charset must not be null!");
            this.charset = charset;
        }
    
        @Override
        public String deserialize(byte[] bytes) {
            return (bytes == null ? null : new String(bytes, charset));
        }
    
        @Override
        public byte[] serialize(Object object) {
            String string = JSON.toJSONString(object);
            if (StringUtils.isBlank(string)) {
                return null;
            }
            string = string.replace("\"", "");
            return string.getBytes(charset);
        }
    }
    
    

    2.6 IPUtils 工具类

    public class IPUtils {
        private static Logger logger = LoggerFactory.getLogger(IPUtils.class);
        private static final String IP_UTILS_FLAG = ",";
        private static final String UNKNOWN = "unknown";
        private static final String LOCALHOST_IP = "0:0:0:0:0:0:0:1";
        private static final String LOCALHOST_IP1 = "127.0.0.1";
    
        /**
         * 获取IP地址
         * 使用 Nginx 等反向代理软件, 则不能通过 request.getRemoteAddr() 获取IP地址
         * 如果使用了多级反向代理的话,X-Forwarded-For 的值并不止一个,而是一串 IP 地址,X-Forwarded-For 中第一个非 unknown 的有效IP字符串,则为真实IP地址
         */
        public static String getIpAddr(HttpServletRequest request) {
            String ip = null;
            try {
                //以下两个获取在 k8s 中,将真实的客户端IP,放到了 x-Original-Forwarded-For。而将 WAF 的回源地址放到了 x-Forwarded-For 了。
                ip = request.getHeader("X-Original-Forwarded-For");
                if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                    ip = request.getHeader("X-Forwarded-For");
                }
    
                //获取 nginx 等代理的 ip
                if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                    ip = request.getHeader("x-forwarded-for");
                }
                if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                    ip = request.getHeader("Proxy-Client-IP");
                }
                if (StringUtils.isEmpty(ip) || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                    ip = request.getHeader("WL-Proxy-Client-IP");
                }
                if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                    ip = request.getHeader("HTTP_CLIENT_IP");
                }
                if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                    ip = request.getHeader("HTTP_X_FORWARDED_FOR");
                }
    
                //兼容k8s集群获取ip
                if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                    ip = request.getRemoteAddr();
                    if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) {
                        //根据网卡取本机配置的IP
                        InetAddress iNet = null;
                        try {
                            iNet = InetAddress.getLocalHost();
                        } catch (UnknownHostException e) {
                            logger.error("getClientIp error: {}", e);
                        }
                        ip = iNet.getHostAddress();
                    }
                }
            } catch (Exception e) {
                logger.error("IPUtils ERROR ", e);
            }
    
            //使用代理,则获取第一个IP地址
            if (!StringUtils.isEmpty(ip) && ip.indexOf(IP_UTILS_FLAG) > 0) {
                ip = ip.substring(0, ip.indexOf(IP_UTILS_FLAG));
            }
            return ip;
        }
    }
    

    2.7 限流核心逻辑

    @Aspect
    @Component
    public class LimitAspect {
    
        private final RedisTemplate<Object, Object> redisTemplate;
    
        public LimitAspect(RedisTemplate<Object, Object> redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        @Pointcut("@annotation(club.wadreamer.limit.annotation.Limit)")
        public void pointcut() {
        }
    
        @Around("pointcut()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    
            // 获取 HTTP 请求
            HttpServletRequest httpServletRequest = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    
            // 获取应用了切面的方法签名
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    
            // 获取方法名
            Method method = methodSignature.getMethod();
    
            // 获取标志在方法上的注解
            Limit limit = method.getAnnotation(Limit.class);
    
            LimitType limitType = limit.limitType();
    
            String key = limit.key();
    
            if (StringUtils.isEmpty(key)) {
                key = "_" + methodSignature.getName();
            }
    
            if (limitType == LimitType.IP) {
                key += "_" + IPUtils.getIpAddr(httpServletRequest);
            }
    
            // 生成应用在 Redis 中的 key
            ImmutableList<Object> keys = ImmutableList.of(StringUtils.join(limit.prefix(), "_", key,
                    httpServletRequest.getRequestURI().replaceAll("/", "_")));
    
            // 获取 Lua 脚本
            String luaScript = buildLuaScript();
    
            // 生成执行 Lua 且返回值为 Number 的 redis 脚本
            DefaultRedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
    
            // 执行 Lua 脚本
            Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period());
    
            if (Objects.nonNull(count) && count.intValue() <= limit.count()) {
                String.format("第{}次访问key为 {},描述为 [{}] 的接口", count, keys, limit.name());
                return joinPoint.proceed();
            } else {
                return ResultDto.error("接口访问次数受限");
            }
        }
    
        /*
         * KEYS 表示 keys,ARGV 表示 [limit.count(), limit.period()]
         * Lua 的数组默认是从 1 开始的
         */
        private String buildLuaScript() {
            return "local c" +
                    "\nc = redis.call('get', KEYS[1])" +
                    "\nif c and tonumber(c) > tonumber(ARGV[1]) then" +
                    "\nreturn c" +
                    "\nend" +
                    "\nc = redis.call('incr', KEYS[1])" +
                    "\nif tonumber(c) == 1 then" +
                    "\nredis.call('expire', KEYS[1], ARGV[2])" +
                    "\nend" +
                    "\nreturn c";
        }
    }
    

    2.8 application.yml

    server:
      port: 8080
    
    spring:
      redis:
        #数据库索引
        database: ${REDIS_DB:1}
        host: ${REDIS_HOST:192.168.125.140}
        port: ${REDIS_PORT:6379}
        password: ${REDIS_PWD:}
        #连接超时时间
        timeout: 5000
    

    三、测试

      接下来设计一个测试接口,并标识限流注解。

    @RestController
    @RequestMapping("/api/limit")
    public class LimitController {
    
        private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger();
    
        /**
         * 测试限流注解,下面配置说明该接口基于 IP 进行限流, 60 秒内最多只能访问 10 次,保存到 redis 的键名为 limit_test_IP地址_接口,
         */
        @GetMapping
        @Limit(key = "test", period = 60, count = 10, name = "testLimit", prefix = "limit", limitType = LimitType.IP)
        public Object test() {
            return ResultDto.success(200, ATOMIC_INTEGER.incrementAndGet());
        }
    }
    

      使用 postman 进行测试,结果如下:

    在这里插入图片描述
      使用 redis desktop manager 中的数据如下:

    在这里插入图片描述

    展开全文
  • 用TCP/IP进行网际互联第三卷:客户-服务器编程与应用(Linux/POSIX套接字版) 基本信息 原书名:Internetworking With TCP/IP Vol Ⅲ:Client-Server Programming And Applications Linux/POSIX Sockets V 作者: ...
  • 在应答DNS查询时,DNS服务器每个查询将以DNS文件中主机记录的IP地址按顺序返回不同的解析结果,将客户端的访问引导到不同的机器上去,使得不同的客户端访问不同的服务器,从而达到负载均衡的目的。 2、HTTP重定向 ...

    1、DNS负载均衡

    DNS负载均衡技术的实现原理是在DNS服务器中为同一个主机名配置多个IP地址,在应答DNS查询时,DNS服务器对每个查询将以DNS文件中主机记录的IP地址按顺序返回不同的解析结果,将客户端的访问引导到不同的机器上去,使得不同的客户端访问不同的服务器,从而达到负载均衡的目的。

    2、HTTP重定向

    Http重定向可以将http请求进行转移,一般用于自动跳转,这种重定向由Http定义并由Http代理(如:浏览器)和Web服务器共同实现。正因为http重定向具备请求转移和自动跳转的本领,除了满足各种自动跳转外,还可以实现Web负载均衡达到web扩展的目的。

    3、IP负载均衡

    在网络层通过修改请求目标地址进行负载均衡。

    用户请求数据包,到达负载均衡服务器后,负载均衡服务器在操作系统内核进程获取网络数据包,根据负载均衡算法得到一台真实服务器地址,然后将请求目的地址修改为,获得的真实ip地址,不需要经过用户进程处理。

    真实服务器处理完成后,响应数据包回到负载均衡服务器,负载均衡服务器,再将数据包源地址修改为自身的ip地址,发送给用户浏览器。

    4、链路层负载均衡

    在通信协议的数据链路层修改mac地址,进行负载均衡。

    数据分发时,不修改ip地址,指修改目标mac地址,配置真实物理服务器集群所有机器虚拟ip和负载均衡服务器ip地址一致,达到不修改数据包的源地址和目标地址,进行数据分发的目的。

    实际处理服务器ip和数据请求目的ip一致,不需要经过负载均衡服务器进行地址转换,可将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成为瓶颈。也称为直接路由模式(DR模式)。

    5、混合型负载均衡

    由于多个服务器群内硬件设备、各自的规模、提供的服务等的差异,可以考虑给每个服务器群采用最合适的负载均衡方式,然后又在这多个服务器群间再一次负载均衡或群集起来以一个整体向外界提供服务(即把这多个服务器群当做一个新的服务器群),从而达到最佳的性能。将这种方式称之为混合型负载均衡。

    6、反向代理负载均衡

    反向代理服务器的核心工作就是转发Http请求,工作在HTTP层,因此也称为七层负载均衡。反向代理服务器是转发请求不是转移,前面的都是转移。

    7、直接路由

    这种方式工作在数据链路层。它修改数据包的目标MAC地址,并没有修改目标IP,然后发给实际的服务器,实际服务器的响应数据直接发回给用户,而不用经过调度器。但实际服务器必须接入外网,而且不能将调度器作为默认网关,要给实际服务器添加和调度器IP地址相同的IP别名。

    8、IP隧道

    基于IP隧道的负载均衡系统也可以使用LVS来实现,称为LVS-TUN,与LVS-DR不同的是,实际服务器和调度器可以不在同一个WAN网段,调度器通过IP隧道技术来转发请求到实际服务器,所以实际服务器必须有合法的IP地址。

    转载于:https://blog.51cto.com/13931430/2368849

    展开全文
  • TCP/IP教程TCP/IP基础

    热门讨论 2009-11-23 20:58:46
    20.5 异种协议的IP封装 219 20.6 小结 220 第21章 Windows 98 221 21.1 Windows 98网络体系结构 221 21.1.1 安装网卡 222 21.1.2 更改网卡配置 224 21.1.3 当Windows 98引导失败 224 21.2 配置Windows 98的TCP/IP ...
  • TCP/IP详解

    2013-07-25 11:17:06
    20.5 异种协议的IP封装 219 20.6 小结 220 第21章 Windows 98 221 21.1 Windows 98网络体系结构 221 21.1.1 安装网卡 222 21.1.2 更改网卡配置 224 21.1.3 当Windows 98引导失败 224 21.2 配置Windows 98的TCP/IP ...
  • TCP_IP详解卷1

    热门讨论 2010-12-29 10:53:54
    3.6 特殊情况的IP地址 33 3.7 一个子网的例子 33 3.8 ifconfig命令 35 3.9 netstat命令 36 3.10 IP的未来 36 3.11 小结 37 第4章 ARP:地址解析协议 38 4.1 引言 38 4.2 一个例子 38 4.3 ARP高速缓存 40 4.4 ARP的...
  • TCP-IP技术大全

    2011-12-01 16:46:51
    20.5 异种协议的IP封装 219 20.6 小结 220 第21章 Windows 98 221 21.1 Windows 98网络体系结构 221 21.1.1 安装网卡 222 21.1.2 更改网卡配置 224 21.1.3 当Windows 98引导失败 224 21.2 配置Windows 98的TCP/IP ...
  • TCP/IP详解part_2

    2010-12-29 10:58:48
    3.6 特殊情况的IP地址 33 3.7 一个子网的例子 33 3.8 ifconfig命令 35 3.9 netstat命令 36 3.10 IP的未来 36 3.11 小结 37 第4章 ARP:地址解析协议 38 4.1 引言 38 4.2 一个例子 38 4.3 ARP高速缓存 40 4.4 ARP的...
  • TCP-IP协议详解

    热门讨论 2008-11-24 20:46:50
    20.5 异种协议的IP封装 219 20.6 小结 220 第21章 Windows 98 221 21.1 Windows 98网络体系结构 221 21.1.1 安装网卡 222 21.1.2 更改网卡配置 224 21.1.3 当Windows 98引导失败 224 21.2 配置Windows 98的TCP/IP ...
  • TCP/IP技术大全

    2010-03-10 10:25:24
    24.4.1 NetWare 5的IP-Only安装 267 24.4.2 IPX-Only安装 267 24.4.3 混合TCP/IP安装 268 24.5 IP迁移辅助工具 268 24.5.1 NDS 268 24.5.2 DNS 269 24.5.3 DHCP 269 24.5.4 DDNS 269 24.5.5 SLP 269 24.5.6 兼容模式...
  • TCP IP教程 part1

    2008-08-26 09:18:48
    20.5 异种协议的IP封装 219 20.6 小结 220 第21章 Windows 98 221 21.1 Windows 98网络体系结构 221 21.1.1 安装网卡 222 21.1.2 更改网卡配置 224 21.1.3 当Windows 98引导失败 224 21.2 配置Windows 98的TCP/IP ...
  • 20.5 异种协议的IP封装 219 20.6 小结 220 第21章 Windows 98 221 21.1 Windows 98网络体系结构 221 21.1.1 安装网卡 222 21.1.2 更改网卡配置 224 21.1.3 当Windows 98引导失败 224 21.2 配置Windows 98的TCP/IP ...
  • 通过mdsip协议进行Tcp/Ip进行数据交流,mdsip在数据服务端运行,数据客户端进行数据检索和存储。针对数据客户端有两种配置方式: -Distributed Client -Thin Client 在分布式配置中,大多数数据的访问操作是在...
    通过mdsip协议进行Tcp/Ip进行数据交流,mdsip在数据服务端运行,数据客户端进行数据的检索和存储。针对数据客户端有两种配置方式:
    -Distributed Client
    -Thin Client
    在分布式配置中,大多数的数据的访问操作是在客户端这边进行,除了对服务器端进行磁盘的I/O操作外。在这种配置中,代码部分不需要更改,数据重定向只需要重新定义环境变量<experiment name>_path,更精确的是你可以包含一个搜寻列表,由分号隔开
    <mdsip server IP address>:<port>::<directory>
     在ThinClient模式下,所有的操作都是在数据服务器站点执行,客户端将TDI表达式发送到服务器上,服务器将结果返回。
    ThinClient/Distributed Client模式不同之处,ThinClient模式需要不同的应用程序的接口。

    转载于:https://www.cnblogs.com/qhzhang/p/7497976.html

    展开全文
  • 阻止本地特殊文件夹进行重定向 87 为用户会话配置音频 87 为已发布应用程序启用音频 88 启用或禁用已发布应用程序音频 88 限制音频吞吐量带宽 88 配置音频带宽限制 88 设置音频压缩和输出质量 89 配置音频...
  • 负载均衡 GSLB 全局负载均衡。...传输层控制,对客户端的请求,进行TCP/IP协议包转发,性能快。 七层负载均衡 可以处理应用层,如改写HTTP头信息、重定向等。 Nginx就是一个典型七层负载均衡...

    负载均衡

    在这里插入图片描述

    GSLB

    全局负载均衡。
    在这里插入图片描述

    如:张三访问某应用,先请求了边缘调度节点,边缘调度节点由中心调度节点调控,然后再去请求应用服务。

    SLB

    调度节点和服务节点通常在一个逻辑地域。
    在这里插入图片描述

    四层负载均衡和七层负载均衡

    四层负载均衡

    传输层控制,对客户端的请求,进行TCP/IP协议的包转发,性能快。

    七层负载均衡

    可以处理应用层,如改写HTTP的头信息、重定向等。
    Nginx就是一个典型的七层负载均衡的SLB

    Nginx负载均衡

    Nginx提供一个服务池(upstream server),配置了很多相同的服务节点,进行轮询访问。
    在这里插入图片描述

    配置语法

    syntax:upstream name{ ... }
    Default:
    Context:http
    

    简单场景测试

    /etc/nginx/conf.d/下配置三个服务节点,监听不同端口,模拟远程服务节点。
    server1.conf

    server {
        listen       8001;
        server_name  localhost;
    
        #charset koi8-r;
        #access_log  /var/log/nginx/host.access.log  main;
    
        location / {
            root   /opt/app/code1;
            index  index.html index.htm;
        }
        #......
    }
    

    server2.conf和server3.conf类似,端口分别为8002、8003。

    在此目录下配置负载均衡节点,upstream_test.conf

        upstream server-pool{
            server 192.168.174.132:8001;
            server 192.168.174.132:8002;
            server 192.168.174.132:8003;
        }
    
    server {
        listen       80;
        server_name  localhost;
    
        #charset koi8-r;
        #access_log  /var/log/nginx/host.access.log  main;
    
        location / {
            proxy_pass http://server-pool;
            include proxy_params;
        }
        #.....
    }
    

    注意:由于upstream需要配置在http结点下,而conf.d目录下的配置文件都在nginx.conf中属于http节点,股配置在最外层,这里配置了3个服务节点,服务开启时,Nginx会自动进行检测服务器的连通性,若开启前不连通,则Nginx无法开启。
    使用proxy_pass,基本原理也就是代理转发,这里应该是反向代理
    注:
    upstream名称,在某些条件下,可以当成主机名传给后端Java应用。当upstream名称中含有下划线的时候,Java会把主机名当做域名来按照[RFC2396]解析,结果就会返回Null,在某些版本的Spring框架里就会触发服务器内部错误,此类问题相当隐蔽。
    域名命名规则
    domainlabel = alphanum | alphanum *( alphanum | “-” ) alphanum
    建议:upstream名称不包含下划线,实践中使用驼峰命名规范貌似比较合适

    效果

    主机访问http://192.168.174.132:80,访问到Server1,刷新访问到Server2,刷新访问到Server3,再刷新访问到Server1,可见Nginx以轮询策略进行负载均衡访问。
    使用iptables -I INPUT -p tcp --dport 8002 -j DROP,宕掉8002的服务器,主机刷新访问只能访问到Server1和Server3。

    upstream举例

        upstream name{
    		server backend1.example.com weight=5;
    		server backend2.example.com:8080;
    		server unix:/tmp/backend3;
    
    		server backup1.example.com:8080 backup;
    		server backup2.example.com:8080 backup;
        }
    

    (1)server后可加IP、域名、Socket
    (2)server后可以加一些参数

    参数 作用
    down 当前的server暂时不参与负载均衡
    backup 预留的备份服务器(正常节点存活,不提供服务)
    max_fails 允许请求失败的次数
    fail_timeout 经过max_fails失败后,服务暂停的时间
    max_conns 限制最大的接收的连接数(尤其是存在性能不均的服务节点)

    backup演示

    将upstream_test.conf修改如下:

        upstream server_pool{
            server 192.168.174.132:8001 down;
            server 192.168.174.132:8002 backup;
            server 192.168.174.132:8003 max_fails=1 fail_timeout=10s;
        }
    

    其中8001不参与负载均衡,8002留作后备机,8003正常节点(失败次数1,失败后的暂停时间10s)
    服务启动后,刷新页面访问Server3,使用iptables -I INPUT -p tcp --dport 8003 -j DROP阻止8003端口访问,再刷新,等N秒后访问到Server2,使用iptables -F清空访问规则,再刷新立马可以访问Server3。

    调度算法

    调度算法 说明
    轮询 按时间顺序逐一分配到不同的后端服务器
    加权轮询 weight值越大,分配到的访问几率越高
    ip_hash 每个请求按访问IP的hash结果分配,这样来自同一个IP的固定访问一个后端服务器
    url_hash 按照访问的URL的hash结果来分配请求,是每个URL定向到同一个后端服务器
    least_conn 最少连接数,哪个机器连接数少就分配到哪个
    hash关键数值 hash自定义的key

    加权轮询

        upstream server_pool{
            server 192.168.174.132:8001;
            server 192.168.174.132:8002 weight=5;
            server 192.168.174.132:8003;
        }
    

    理论上,7个请求中会有5个访问到Server2。

    ip_hush

    为了让用户的Cookie有效,不造成频繁掉线,出现了ip_hash策略。

        upstream server_pool{
        	ip_hash;
            server 192.168.174.132:8001;
            server 192.168.174.132:8002;
            server 192.168.174.132:8003;
        }
    

    注意关键字放的位置。
    缺点:无法取到用户的IP

    least_conn

        upstream server_pool{
        	least_conn;
            server 192.168.174.132:8001;
            server 192.168.174.132:8002;
            server 192.168.174.132:8003;
        }
    

    注意关键字放的位置。

    url_hush

    syntax:hash key [consistent];
    Default:默认无
    Context:upstream
    This directive appeared in version 1.7.2.
    

    配置

        upstream server_pool{
        	hash $request_uri;
            server 192.168.174.132:8001;
            server 192.168.174.132:8002;
            server 192.168.174.132:8003;
        }
    
    效果

    访问同一个URL的时候,访问的是同一个服务器。

    自定义Key

    使用正则提取出响应的key,然后对该值进行hash即可。

    展开全文
  • 得正式指定的IP 地址也可以接入Internet.这些电脑可以隐藏在Linux 服务器后面存取Internet 上的信 息而不被发现,看起来就好象是服务器在使用Internet。Linux 服务器实现代理功能的方法如下:当一 个内部主机向外发包...
  • 还可以支持分组二级域名部署支持。 模板引擎:系统内建了一款卓越基于XML编译型模板引擎,支持两种类型 模板标签,融合了Smarty和JSP标签库思想,并内置布局模板功能和标签库扩展 支持。通过驱动还可以...
  • IIS6.0 IIS,互联网信息服务

    热门讨论 2010-08-23 21:20:55
    如果你已在DNS服务器中将所有你需要的域名都已经映射到了此惟一的IP地址,则用设不同“主机头名”的方法,可以让你直接用域名来完成不同Web站点的访问。 比如你本机只有一个IP地址为192.168.0.1,你已经建立(或...
  • 思讯补丁见解

    2013-05-25 17:08:32
    且通过树状结构方式可使用户多达上百个域名进行方便管理,亦可自主添加二级域名,自由设置 A 记录(IP 指向)、MX 记录、CName(别名)、URL 重定向等,用户操作界面清晰简单。 拥有花生壳只需以下步骤: 第一步:...
  • switch security.rar

    2012-07-25 16:53:23
    关键点:DHCP snooping会在接入的交换机上建立一个DHCP绑定表,为每一个分配的IP建立一个表项,其中包括客户端的IP地址、MAC地址、端口号、VLAN编号、租用和绑定类型等信息。也可手动向这个绑定表中添加表项。 SW1#...
  • jsp九大内置对象

    2011-10-21 23:25:22
     服务器启动后就产生了这个Application对象,当客户再所访问的网站的各个页面之间浏览时,这个Application对象都时同一个,直到服务器关闭。但是与Session对象不同的时,所有客户的Application对象都时同一个,即...
  • dd-wrt fantasia

    2012-07-01 14:01:01
    修复了“访问限制”里使用ipp2p过滤时只tcp协议过滤bug 添加缺失LOG模块 1.0 - build 20110101 发布VINT版,和20101228无功能上区别,今后将同时发布NEWD和VINT两种版本 1.0 - build 20110108 精简了部分...
  • TCPIP详解--共三卷

    2015-11-30 17:17:21
    3.6 特殊情况的IP地址 33 3.7 一个子网的例子 33 3.8 ifconfig命令 35 3.9 netstat命令 36 3.10 IP的未来 36 3.11 小结 37 第4章 ARP:地址解析协议 38 4.1 引言 38 4.2 一个例子 38 4.3 ARP高速缓存 40 4.4 ARP的...
  • 实例175 避免客户端访问的并发问题 实例176 在Servlet中使用JDBC访问数据库 实例177 利用Servlet访问数据库连接池 实例178 Servlet实现的个人所得税计算器 实例179 利用Servlet实现用户永久登录 第7章 过滤器...
  • 实例175 避免客户端访问的并发问题 实例176 在Servlet中使用JDBC访问数据库 实例177 利用Servlet访问数据库连接池 实例178 Servlet实现的个人所得税计算器 实例179 利用Servlet实现用户永久登录 第7章 过滤器...

空空如也

空空如也

1 2 3 4
收藏数 72
精华内容 28
关键字:

对客户端访问的ip进行重定向