Wednesday, July 10, 2019

OpenResty + nginx + redis + spring 如何達到高吞吐量伺服器的架構? (四) 負載平衡下的 session 共存

在一般的傳統下最簡單的方法就是 跟伺服器 登入帳號密碼就取得一個 key
然後伺服器返回 一個 hash 然後我們的 就可以 照著這個 key 去判斷到底是
不是維持這組帳號 是不是 中斷,然後 我們伺服器後端 也必須要去記錄到底
是誰使用這個 cookie ,這組帳號 能不能 從其他地放登入等等,
為什麼要使用 spring boot + redis 去設計,比較可能的原因就是
在每一個 method 的請求 都必須要夾帶 cookie 去判別到底是否維持登入的狀態
類似簽章的功能那我們的系統大致上來模擬一下,比較需要注意的是下面 nginx 我們要做的功能有大致上這些
首先我們要到
  • vue axios 開啟每次請求都夾帶cookie
  • install redis
  • 配置 spring boot
  • 編譯 spring boot 成 兩個 port (以提供測試
  • 搭配 nginx 使用負載平衡 觀看 key 值是否有不同
  • 在 login 的頁面 同一個瀏覽器下 ,是否可以達到 不用 登入帳號密碼直接導向內部畫面
這個 chrome 插件已經用很久了

Install redis

apt-get install redis
redis-server
redis-cli
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> <!-- 2.1.5.RELEASE--> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> </dependencies> <build> <defaultGoal>compile</defaultGoal> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.19.1</version> </plugin> </plugins> </build> </project>
使用vue anios 模組必須要注意
需要再
axios.defaults.withCredentials=true;//让ajax携带cookie
這樣才能 讓 spring boot 內建 的 redis 共享內存工作
然後 我們也要讓後端 的
這邊 加上
再來 就是配上 nginx 共享內存就完成囉!

由 spring boot 幫我們管理 session

        ///跨網域
   @Bean
     public WebMvcConfigurer corsConfigurer() {
         return new WebMvcConfigurer() {
             @Override
             public void addCorsMappings(CorsRegistry registry) {
                 registry.addMapping("/httpMethod/**")
                 .allowCredentials(true)
                  .allowedOrigins("*");//允许域名访问,如果*,代表所有域名
                 //.allowedOrigins("http://localhost:9527");//允许域名访问,如果*,代表所有域名
                 registry.addMapping("/httpMethod2**")
                 .allowedMethods("GET")
                 .allowCredentials(true)
                    .allowedOrigins("http://localhost:8080");//允许域名访问,如果*,代表所有域名
       
                 registry.addMapping("/setSession")
                 .allowCredentials(true)
                    .allowedOrigins("*");//允许域名访问,如果*,代表所有域名
              //.allowedOrigins("http://localhost:9527");//允许域名访问,如果*,代表所有域名
              registry.addMapping("/getSession")
              .allowCredentials(true)
              .allowedMethods("GET")
            .allowedOrigins("*");//允许域名访问,如果*,代表所有域名
              registry.addMapping("/removeSession")
              .allowCredentials(true)
              .allowedMethods("GET")
            .allowedOrigins("*");//允许域名访问,如果*,代表所有域名
               }
         };
     }


    @RequestMapping(value = "/setSession", method = RequestMethod.GET)
 @CrossOrigin
    public void setSession(HttpServletRequest request, HttpServletResponse response) {
     System.out.println(request.getSession().getId().toString());
     request.getSession().setAttribute("name", "test");
   //     request.getSession().setAttribute("name2", "tom2");
     
    }
    
    
    @RequestMapping(value = "/getSession", method = RequestMethod.GET)
 @CrossOrigin
    public List getInterestPro(HttpServletRequest request, HttpServletResponse response) throws JsonProcessingException{
     System.out.println(request.getSession().getId().toString());
     System.out.println(request.getSession().getAttribute("name"));
     ObjectMapper objectMapper = new ObjectMapper();
     
     if( request.getSession().getAttribute("name") != null)
      listest2.setCode(200);
     else
      listest2.setCode(404);
     
     //select 所有 redis 
     Set<byte[]> keys = jedisConnectionFactory().getConnection().keys("*sessions:expires*".getBytes());

     Iterator<byte[]> it = keys.iterator();

     while(it.hasNext()){

         byte[] data = (byte[])it.next();

         System.out.println(new String(data, 0, data.length));
     }
     
     
     String userJsonStr = objectMapper.writeValueAsString(listest2);
     //System.out.print(userJsonStr);
     return listest2;
    }
 
 
    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory jedisConFactory
        = new JedisConnectionFactory();
      jedisConFactory.setHostName("localhost");
      jedisConFactory.setPort(6379);
      return jedisConFactory;
    }
     
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(jedisConnectionFactory());
        return template;
    }
    

進階

這邊比較進階的是除了 在 spring boot 內建的 sesion 可以操作 redis 的話
我們可能要考慮後期配置 ,我們在 加開一個 可以額外操作key 的地方 ,或許
可以達到監控,那我們可以做到 一個 key 只能登入 一台電腦的效果
那麼實際運作的圖就是
     //select 所有 redis 
     Set<byte[]> keys = jedisConnectionFactory().getConnection().keys("*sessions:expires*".getBytes());

     Iterator<byte[]> it = keys.iterator();

     while(it.hasNext()){

         byte[] data = (byte[])it.next();

         System.out.println(new String(data, 0, data.length));
     }
     

配置檔案篇

pom.xml檔案已經也丟在 github 上面以提供瀏覽
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.5.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
   <!-- 2.1.5.RELEASE-->
 </parent>
 <groupId>com.example</groupId>
 <artifactId>demo</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>demo</name>
 <description>Demo project for Spring Boot</description>

 <properties>
  <java.version>1.8</java.version>
 </properties>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>
 <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>
  <dependency>
           <groupId>org.springframework.session</groupId>
           <artifactId>spring-session-data-redis</artifactId>
   </dependency>
  <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.0.3.RELEASE</version>
 </dependency>
 
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    <type>jar</type>
</dependency>
 </dependencies>
 
 <build>
 <defaultGoal>compile</defaultGoal>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
          <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.19.1</version>
        </plugin>
  </plugins>
 </build>

</project>