On the importance of looking at the source code, the second year written to the programmer

Reporter: Since I graduated in June 2018, I am about to enter a turning point for programmers in the second to third years.

Before writing code, I always made my own head, and I wrote more and more crud, but the real technical progress is rarely seen. Now that I have entered the semi-hanging period, the basic business requirements can be written down, but the real On some bizarre problems, I feel a deep sense of powerlessness.

The scene that appears is as follows:

1. The company's business needs, integrated users, want to achieve single sign-on, log-out method, choose cas framework.

2. Strange bug, the original project integrates the cas client occasionally, the exception is not intercepted by the global exception, and the stack information is printed directly on the page.

For scenario one:

  The company's original old project is connected to cas and integrated into the cas client in the project, but it often fails to log out single-point. The method used is to repeat the scene of the bug, and locate it to see if it is generated regularly. As we know, the machine will not be deceptive. After many repeated test and reproduction, the problem is reproduced steadily . As follows: System A and System B If the user calls the logout interface on System A, System B must log out. If the user calls the single-point logout interface on System B, System A will log out with a certain probability.

  The cas project was previously written by a colleague of the company. Now that colleague has left the job and took over the project, he can only look at it dullly. In view of the problem of the recurring scenario, the deployment scenario was asked, and the guess was caused by the problem of the cluster. Project A is deployed in a cluster, java has two machines, and project B is a single machine. The initial guess may be that when project A logs out, project B is notified, and the single machine must be notified. When project B notifies project A of the occasional notification. (In order to understand this description simply, the actual notification process is: Project A-> cas-> Project B or Project B-> cas-> Project A).

  When searching for solutions on the Internet, I saw three solutions: (1) Change the configuration of cluster project A to ip hash, the actual online method is ip polling (2) ip broadcast is used for notification Form, broadcast the IP deployed by the machine, and if the machine is not successfully executed, it will be forwarded to the next machine. (3) Rewrite session storage.

  The above is the solution, but there is no very detailed explanation about why this problem occurs in cas. Personal speculation is because the logo used as the login is stored in memory, not in redis, but when taking over the project, colleagues clearly told that the logo is stored in redis, and the situation seen in the redis library is indeed stored. And when it fails, this key in redis also has no value, but in the case of logout failure, this value in redis still exists. The solution to reading blogs on the Internet is that I saw an article that said that -cas itself does not support clustering. I suddenly realized that I went to look at the code that stores the session ID in the source code, as follows:

public final class HashMapBackedSessionMappingStorage implements SessionMappingStorage {
    private final Map<String, HttpSession> MANAGED_SESSIONS = new HashMap();
    private final Map<String, String> ID_TO_SESSION_KEY_MAPPING = new HashMap();
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    public HashMapBackedSessionMappingStorage() {
    }

    public synchronized void addSessionById(String mappingId, HttpSession session) {
        this.ID_TO_SESSION_KEY_MAPPING.put(session.getId(), mappingId);
        this.MANAGED_SESSIONS.put(mappingId, session);
    }
}

  It can be seen that the implementation class of SessionMappingStorage actually stores the content of session and session and mapping relationship mapping is stored in the map, which is written in memory. Then it is not difficult to solve this problem. Rewrite the implementation of this method and add it to the filter. Both relationships are stored using redis. End, sprinkle flowers. Some people on the Internet also use the method of broadcasting, which is also very good, but comparatively speaking, the implementation cost is relatively large, and it is not a fundamental solution to the problem. It is also feasible to build a cluster on the test server using the ip hash method, but the leader does not allow the use of ip hash enenen online, so this is a dead end.

  In summary, the source code is not so terrible. Find ideas and focus on the problem to see the problem, and the solution comes out.

For scenario two:

  When writing a java project, we usually write a global exception handling class at the beginning of the project creation, and encapsulate the exceptions caused by code errors or other problems in the system into a fixed form of code message and return it to the front page. Before the project has been running smoothly, after the introduction of the cas client, when the single sign-out problem is solved: the interface access 500, the stack information is printed directly in the foreground.

  I still need to locate the problem first. I suspected that it was related to the exception type. In the login interface, the ticket verification did not pass. If the login fails, it is printed out as TicketValidationException. There has also been a redisConnectionFailureException. Simulate locally

@GetMapping ("/ test") 
    public void test () throws Exception { 
        throw new TicketValidationException ("ticket validation exception"); 
        // throw new RedisConnectionFailureException ("redis connection exception"); 
    }

  Both exceptions can be intercepted and returned normally. So far, this way is not working, there is no reason for the exception type, to find the location of the exception, and found the following code:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        if(!this.handlerInitialized.getAndSet(true)) {
            HANDLER.init();
        }

        if(HANDLER.process(request, response)) {
            filterChain.doFilter(servletRequest, servletResponse);// 发生异常的代码行
        }

    }

  Well, it ’s not easy. I ’ll try to catch you. I ’ll first catch the TicketValidationException to simulate the failure of the ticket on the line, and then I was shocked. I still printed the stack information on the front page, and then Friday It's almost time to get off work, everyone knows, this question is put on hold. After returning home, I was still thinking about this problem. I turned on the computer and tried RedisConnectionFailureException, but the page returned was still incorrect. After rewriting the response here, return normally. The method of rewriting the response is as follows:

public static void outResponse(HttpServletResponse response, Object object) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = null;
        try {
            out = response.getWriter();
            out.append(JSONObject.toJSONString(object));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

  The redis connection is abnormally resolved. What about the failure of ticket verification? After catching the redis connection exception, catch to Exception big exception class, output response and find that it can be intercepted. So why can't this exception be intercepted if the ticket verification fails, look at the implementation in the source code:

catch (TicketValidationException var8) {
    this.logger.debug(var8.getMessage(), var8);
    this.onFailedValidation(request, response);
    if (this.exceptionOnValidationFailure) {
      throw new ServletException(var8);
    }
    response.sendError(403, var8.getMessage());
    return;

  Good guys, what he threw here is actually the ServletException modification code. The content captured when a ticket exception occurs should capture the ServletException.

  When I heard the source code, it felt like a big tiger standing there, but when I seriously studied him, I was really gentle and kind like a kitten. It is not ruled out that the code written by the boss is obscure and difficult to understand, but after all, it is written by people ~, as long as you are willing to read it with heart, you will definitely gain something. I hope the epidemic will pass quickly and everyone will be treated well in 2020.

Guess you like

Origin www.cnblogs.com/cswxl/p/12691370.html