Showing posts with label nginx. Show all posts
Showing posts with label nginx. Show all posts

Saturday, December 21, 2019

從 高可用 到 微服務 初探 (一) 架構演變

建立 高可用 集群 nginx + keepalived + tomcat

https://www.javazhiyin.com/33054.html
自己如果大概實作也可以(我是懶了xd
這邊如果要自幹的話當然也可以,比較常見就是兩台伺服器自行編寫程式碼去監測服務有沒有掛掉,就是成本有點貴這樣,好像也不能解決問題,在之前教學文章我之前有嘗試過用兩層 nginx 去做(沒意義 只是模擬) 單點故障就沒用了,正確的高可用作法應該是搭配 keepalived 詳細作法上面就有,再經過一段時間較主流應該是zookeeper+grpc or restful 協議 由 zookeeper 控管 服務的狀態, service 和 service 再透過 grpc 或者 restful 來進行溝通 , 其中 溝通時 可以實現 負載平衡的功能與實現,這只是大概看了一陣子東西如果有什麼可以補充或錯誤應該可以後續文章發布後會不定時回來修補。
https://blog.csdn.net/xiangjai/article/details/56844400
這一篇講得很好,把其中的 service 可以想成是一個 集群

nginx vs Zookeeper?

建立 高可用集群 解決方案
nginx
Zookeeper
最簡單來解釋服務上的演變就是解決了單點故障等等,可能會說這樣的話怎麼實現負載平衡,服務與服務之間就要有一個共同的協議,進行傳遞資料 ,比較常見就是 dubbo 配 zookeeper ,比較常見就是 rpc 與 restful 通訊協定進行傳遞。

Friday, July 12, 2019

OpenResty + nginx + redis + spring 如何達到高吞吐量伺服器的架構? (五) 使用openresty 打造 推送server

一開始本來打算使用 firebase. 去做推送server ,後來發現這項技術,在大陸地區訪問google 或是 其他服務是很有問題的xd
這邊我們才用websocket來實現
為什麼要用websocket?
传统HTTP客户端与服务器请求响应模式如下图所示:


WebSocket模式客户端与服务器请求响应模式如下图:



,接下來就是客製化細部微調羅

B/S結構的軟體項目中有時客戶端需要實時的獲得伺服器消息,但默認HTTP協議只支持請求響應模式,這樣做可以簡化Web伺服器,減少伺服器的負擔,加快響應速度,因為伺服器不需要與客戶端長時間建立一個通信連結,但不容易直接完成實時的消息推送功能,如聊天室、後台信息提示、實時更新數據等功能,但通過polling、Long polling、長連接、Flash Socket以及HTML5中定義的WebSocket能完成該功能需要。
一、Socket簡介
Socket又稱"套接字",應用程式通常通過"套接字"向網絡發出請求或者應答網絡請求。Socket的英文原義是「孔」或「插座」,作為UNIX的進程通信機制。Socket可以實現應用程式間網絡通信。



Socket可以使用TCP/IP協議或UDP協議。
TCP/IP協議
TCP/IP協議是目前應用最為廣泛的協議,是構成Internet國際網際網路協議的最為基礎的協議,由TCP和IP協議組成:
TCP協議:面向連接的、可靠的、基於字節流的傳輸層通信協議,負責數據的可靠性傳輸的問題。
IP協議:用於報文交換網絡的一種面向數據的協議,主要負責給每台網絡設備一個網絡地址,保證數據傳輸到正確的目的地。
UDP協議
UDP特點:無連接、不可靠、基於報文的傳輸層協議,優點是發送後不用管,速度比TCP快。
二、WebSocket簡介與消息推送
B/S架構的系統多使用HTTP協議,HTTP協議的特點:
1 無狀態協議
2 用於通過 Internet 發送請求消息和響應消息
3 使用埠接收和發送消息,默認為80埠
底層通信還是使用Socket完成。



HTTP協議決定了伺服器與客戶端之間的連接方式,無法直接實現消息推送(F5已壞),一些變相的���決辦法:
雙向通信與消息推送
輪詢:客戶端定時向伺服器發送Ajax請求,伺服器接到請求後馬上返迴響應信息並關閉連接。 優點:後端程序編寫比較容易。 缺點:請求中有大半是無用,浪費帶寬和伺服器資源。 實例:適於小型應用。
長輪詢:客戶端向伺服器發送Ajax請求,伺服器接到請求後hold住連接,直到有新消息才返迴響應信息並關閉連接,客戶端處理完響應信息後再向伺服器發送新的請求。 優點:在無消息的情況下不會頻繁的請求,耗費資小。 缺點:伺服器hold連接會消耗資源,返回數據順序無保證,難於管理維護。 Comet異步的ashx,實例:WebQQ、Hi網頁版、Facebook IM。
長連接:在頁面里嵌入一個隱蔵iframe,將這個隱蔵iframe的src屬性設為對一個長連接的請求或是採用xhr請求,伺服器端就能源源不斷地往客戶端輸入數據。 優點:消息即時到達,不發無用請求;管理起來也相對便。 缺點:伺服器維護一個長連接會增加開銷。 實例:Gmail聊天
Flash Socket:在頁面中內嵌入一個使用了Socket類的 Flash 程序JavaScript通過調用此Flash程序提供的Socket接口與伺服器端的Socket接口進行通信,JavaScript在收到伺服器端傳送的信息後控制頁面的顯示。 優點:實現真正的即時通信,而不是偽即時。 缺點:客戶端必須安裝Flash插件;非HTTP協議,無法自動穿越防火牆。 實例:網絡互動遊戲。
Websocket:
WebSocket是HTML5開始提供的一種瀏覽器與伺服器間進行全雙工通訊的網絡技術。依靠這種技術可以實現客戶端和伺服器端的長連接,雙向實時通信。
特點:
事件驅動
異步
使用ws或者wss協議的客戶端socket
能夠實現真正意義上的推送功能
缺點:
少部分瀏覽器不支持,瀏覽器支持的程度與方式有區別。



websocket 打造推播


參考:https://kknews.cc/zh-tw/other/qvormg.html
作者:腾讯云技术社区
链接:https://www.zhihu.com/question/20215561/answer/157908509

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>