你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:技术专栏 / Java专栏
java内部重定向死循环检测详解
 
   最近听到一个消息,据说腾讯的财付通在招聘 Java 方面的高手,"参与系统架构选型",要把底层架构从 C/C++ 迁移到 Java 架构上来。其实当你的项目到一定规模之后更多需要考虑的是多人协作开发的问题,可能当初财付通的核心代码也就是一两个人用C/C++搞定的,可是当业务扩大、复杂之后,发现多人C/C++来并行发开变的不是那么靠谱。于是乎java这种近乎工业标准的东西出来了。。。有点扯远了。引出我们今天的话题,java多人并行开发是很出色,但是只要是并行的那么就会存在一些问题。

      比如:A程序员在A页面写了以个逻辑,当逻辑判断满足就内部重定向到B程序员写的B页面,但是刚好B程序员在B页面中的逻辑是满足某些条件就跳转到A页面,于是乎当某些特定条件下,两个页面就在不停的重定向一直到jvm的溢出。

      平时我们遇到这种问题可能找一会就找到了,也基本上没有什么影响。但是在一个成熟的业务系统中遇到这样的问题就是致命的。可能会导致服务器挂掉。。或者整个集群的连锁反应(集体挂掉)。当然你的用户也就访问不到你的站点了,在工业标准中我们考虑更多的就是怎么让系统更稳定。比如:淘宝一天都宕机了,该有多少商家要发疯。

      再次进入正题,如何来解决这种问题保证系统的绝对稳定呢?很简单一个fielter就搞定了

view plaincopy to clipboardprint?
package org.fielter.test;  
 
import java.io.IOException;  
import java.util.ArrayList;  
import java.util.List;  
 
import javax.servlet.Filter;  
import javax.servlet.FilterChain;  
import javax.servlet.FilterConfig;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRequest;  
import javax.servlet.ServletResponse;  
import javax.servlet.http.HttpServletRequest;  
 
/** 
 * 过滤形成环路的fielter <p> 
 *  
 * @author yuezhen 
 */ 
public class CircleFilter implements Filter {     
    public void init(FilterConfig filterConfig) throws ServletException {     
          
    }     
     
    public void doFilter(ServletRequest request, ServletResponse response,     
            FilterChain filterChain) {     
        try{              
            boolean circle=false;  
            HttpServletRequest req = (HttpServletRequest) request;  
            List<String> list =new ArrayList<String>();  
              
            // 从request中取出list  
            if(req.getAttribute("circle.finder.forward.list")!=null){  
                list=(List<String>)req.getAttribute("circle.finder.forward.list");  
            }else{  
                //list为null表明是初次的请求,获取原始的请求来源 ,并放入list  
                list.add((String)req.getAttribute("javax.servlet.forward.request_uri"));  
            }             
              
            // 如果原始请求地址和需要跳转的地址一致,抛出异常  
            if(list.contains(req.getRequestURI())){               
                circle=true;  
            }  
 
            // 将本次需要forward的路径放入list,并保存  
            list.add(req.getRequestURI());  
            req.setAttribute("circle.finder.forward.list", list);  
              
            // 用System.out来代替log  
            System.out.println("");  
            System.out.println("[info] ===========================================");  
            System.out.println("[info] "+list);  
            System.out.println("[info] ===========================================");  
              
            if(circle){  
                System.out.println("[error] find circle");  
                // 清空list  
                list.clear();  
                req.removeAttribute("circle.finder.forward.list");         
                  
                // 可以选择抛出异常,或者用其他的处理方式  
                throw new Exception();                    
            }  
              
            filterChain.doFilter(request, response);              
        }catch(Exception e){  
            // 发现了重定向的死循环的后续处理  
            try {  
                // 页面显示发现死循环  
                response.getOutputStream().print("has circle");  
            } catch (IOException e1) {  
                e1.printStackTrace();  
            }  
        }  
          
    }  
      
    public void destroy() {     
    }     

package org.fielter.test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
 * 过滤形成环路的fielter <p>
 *
 * @author yuezhen
 */
public class CircleFilter implements Filter { 
    public void init(FilterConfig filterConfig) throws ServletException {  
     
    }  
  
    public void doFilter(ServletRequest request, ServletResponse response,  
            FilterChain filterChain) {  
     try{      
      boolean circle=false;
      HttpServletRequest req = (HttpServletRequest) request;
      List<String> list =new ArrayList<String>();
      
      // 从request中取出list
      if(req.getAttribute("circle.finder.forward.list")!=null){
       list=(List<String>)req.getAttribute("circle.finder.forward.list");
      }else{
       //list为null表明是初次的请求,获取原始的请求来源 ,并放入list
       list.add((String)req.getAttribute("javax.servlet.forward.request_uri"));
      }      
      
            // 如果原始请求地址和需要跳转的地址一致,抛出异常
            if(list.contains(req.getRequestURI())){             
             circle=true;
            }

            // 将本次需要forward的路径放入list,并保存
      list.add(req.getRequestURI());
         req.setAttribute("circle.finder.forward.list", list);
           
            // 用System.out来代替log
            System.out.println("");
            System.out.println("[info] ===========================================");
            System.out.println("[info] "+list);
            System.out.println("[info] ===========================================");
           
            if(circle){
             System.out.println("[error] find circle");
             // 清空list
             list.clear();
             req.removeAttribute("circle.finder.forward.list");      
             
             // 可以选择抛出异常,或者用其他的处理方式
             throw new Exception();                  
            }
           
      filterChain.doFilter(request, response);      
     }catch(Exception e){
      // 发现了重定向的死循环的后续处理
      try {
       // 页面显示发现死循环
    response.getOutputStream().print("has circle");
   } catch (IOException e1) {
    e1.printStackTrace();
   }
     }
     
    }
   
    public void destroy() {  
    }  
}
 

上面这个fielter会检测请求的原始来源地址,然后判断与当前需要跳转的地址是否一致,如果一致那么肯定会形成以个环路,于是jvm就在不停执行跳转一直到堆栈溢出。

对fielter进行了改进,用一个list来记录了forward的跳转路径,方便在页面显示出形成环路的过程

当我们检测到这种环路,我们就可以抛出异常,定义统一的错误页面。。。。等等来友好的解决之。。。

如下fielter的配置就可以看到,我们过滤了所有forward跳转的请求

view plaincopy to clipboardprint?
<filter> 
        <filter-name>CircleFilter</filter-name> 
        <filter-class>org.fielter.test.CircleFilter</filter-class> 
    </filter> 
    <filter-mapping> 
        <filter-name>CircleFilter</filter-name> 
        <url-pattern>/*</url-pattern> 
        <dispatcher>FORWARD</dispatcher> 
    </filter-mapping>  
<filter>
  <filter-name>CircleFilter</filter-name>
  <filter-class>org.fielter.test.CircleFilter</filter-class>
 </filter>
 <filter-mapping>
  <filter-name>CircleFilter</filter-name>
  <url-pattern>/*</url-pattern>
  <dispatcher>FORWARD</dispatcher>
 </filter-mapping> 

如果有其他需求,类似的配置就OK了

由于没有图片,只好上文字了,如果出现环,就会在页面显示,可以下个例子自己看效果

has circle,the cytle is : [/fielter/index.htm, /fielter/test2.htm, /fielter/test.htm, /fielter/test2.htm]

接下来附带一个maven搭的测试列子,直接mvn jetty:run,然后再浏览器中http://localhost:8080/fielter/index.htm  就可以看到拦截的效果了

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089