02 . Tomcat集群會話共享

redis簡介

redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set –有序集合)和hash(哈希類型)。與memcached一樣,為了保證效率,數據都是緩存在內存中。區別的是redis會周期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,並且在此基礎上實現master-slave(主從)同步。

Redis詳細請看我專門寫的redis

https://www.cnblogs.com/you-men/tag/Redis/

如何保持session會話

目前,為了使web能適應大規模的訪問,需要實現應用的集群部署。集群最有效的方案就是負載均衡,而實現負載均衡用戶每一個請求都有可能被分配到不固定的服務器上,這樣我們首先要解決session的統一來保證無論用戶的請求被轉發到哪個服務器上都能保證用戶的正常使用,即需要實現session的共享機制。

在集群系統下實現session統一的有如下幾種方案:

1、請求精確定位:sessionsticky,例如基於訪問ip的hash策略,即當前用戶的請求都集中定位到一台服務器中,這樣單台服務器保存了用戶的session登錄信息,如果宕機,則等同於單點部署,會丟失,會話不複製。

2、session複製共享:sessionreplication,如tomcat自帶session共享,主要是指集群環境下,多台應用服務器之間同步session,使session保持一致,對外透明。 如果其中一台服務器發生故障,根據負載均衡的原理,調度器會遍歷尋找可用節點,分發請求,由於session已同步,故能保證用戶的session信息不會丟失,會話複製,。

此方案的不足之處:

必須在同一種中間件之間完成(如:tomcat-tomcat之間).

session複製帶來的性能損失會快速增加.特別是當session中保存了較大的對象,而且對象變化較快時, 性能下降更加顯著,會消耗系統性能。這種特性使得web應用的水平擴展受到了限制。

Session內容通過廣播同步給成員,會造成網絡流量瓶頸,即便是內網瓶頸。在大併發下錶現並不好

3、基於cache DB緩存的session共享

基於memcache/redis緩存的 session 共享

即使用cacheDB存取session信息,應用服務器接受新請求將session信息保存在cache DB中,當應用服務器發生故障時,調度器會遍歷尋找可用節點,分發請求,當應用服務器發現session不在本機內存時,則去cache DB中查找,如果找到則複製到本機,這樣實現session共享和高可用。

nginx+tomcat+redis實現負載均衡、session共享

環境
主機 操作系統 IP地址 硬件/網絡
Nginx CentOS7.3 39.108.140.0 1C2G / 公有雲
Tomcat-1 CentOS7.3 121.36.43.2 1C2G / 公有雲
Tomcat-2 CentOS7.3 49.233.69.195 1C2G / 公有雲
Redis CentOS7.3 116.196.83.113 1C2G / 公有雲
MySQL CentOS7.3 116.196.83.113 1C2G / 公有雲
實驗拓撲

在這個圖中,nginx做為反向代理,實現靜動分離,將客戶動態請求根據權重隨機分配給兩台tomcat服務器,redis做為兩台tomcat的共享session數據服務器,mysql做為兩台tomcat的後端數據庫。

nginx安裝配置

使用Nginx作為Tomcat的負載平衡器,Tomcat的會話Session數據存儲在Redis,能夠實現零宕機的7×24效果。因為將會話存儲在Redis中,因此Nginx就不必配置成stick粘貼某個Tomcat方式,這樣才能真正實現後台多個Tomcat負載平衡。

部署nginx

#!/usr/bin/env bash
# Author: ZhouJian
# Mail: 18621048481@163.com
# Time: 2019-9-3
# Describe: CentOS 7 Install Nginx Source Code Script

version="nginx-1.14.2.tar.gz"
user="nginx"
nginx=${version%.tar*}
path=/usr/local/src/$nginx
echo $path
if ! ping -c2 www.baidu.com &>/dev/null
then
	echo "網絡不通,無法安裝"
	exit
fi

yum install -y gcc gcc-c++ openssl-devel pcre-devel make zlib-devel wget psmisc
if [ ! -e $version ];then
	wget http://nginx.org/download/$version
fi
if ! id $user &>/dev/null
then
	useradd $user -M -s /sbin/nologin
fi

if [ ! -d /var/tmp/nginx ];then
	mkdir -p /var/tmp/nginx/{client,proxy,fastcgi,uwsgi,scgi}
fi
tar xf $version -C /usr/local/src
cd $path
./configure \
--prefix=/usr/local/nginx \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_flv_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_gzip_static_module \
--with-http_auth_request_module \
--with-http_random_index_module \
--with-http_realip_module \
--http-client-body-temp-path=/var/tmp/nginx/client \
--http-proxy-temp-path=/var/tmp/nginx/proxy \
--http-fastcgi-temp-path=/var/tmp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \
--http-scgi-temp-path=/var/tmp/nginx/scgi \
--with-pcre \
--with-file-aio \
--with-http_secure_link_module && make && make install
if [ $? -ne 0 ];then
	echo "nginx未安裝成功"
	exit
fi

killall nginx
/usr/local/nginx/sbin/nginx
#echo "/usr/local/nginx/sbin/nginx" >> /etc/rc.local
#chmod +x /etc/rc.local
#systemctl start rc-local
#systemctl enable rc-local
ss -antp |grep nginx

配置nginx反向代理:反向代理+負載均衡+健康探測,nginx.conf文件內容:

vim /usr/local/nginx/conf/nginx.conf
worker_processes  4;
events {
        worker_connections  1024;
}
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';

    #blog lb by oldboy at 201303
        upstream backend_tomcat {
        #ip_hash;
        server 192.168.6.241:8080   weight=1 max_fails=2 fail_timeout=10s;
        server 192.168.6.242:8080   weight=1 max_fails=2 fail_timeout=10s;
        #server 192.168.6.243:8080   weight=1 max_fails=2 fail_timeout=10s;
        }

        server {
            listen       80;
            server_name  www.98yz.cn;
            charset utf-8;
            location / {
                root html;
                index  index.jsp index.html index.htm;
                    }
            location ~* \.(jsp|do)$ {
            proxy_pass  http://backend_tomcat;
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
                }
        }

    }
安裝部署tomcat應用程序服務器

在tomcat-1和tomcat-2節點上安裝JDK

在安裝tomcat之前必須先安裝JDK,JDK的全稱是java development kit,是sun公司免費提供的java語言的軟件開發工具包,其中包含java虛擬機(JVM),編寫好的java源程序經過編譯可形成java字節碼,只要安裝了JDK,就可以利用JVM解釋這些字節碼文件,從而保證了java的跨平台性。

安裝JDK,Tomcat 程序

tar xvf jdk-8u151-linux-x64.tar.gz -C /usr/local/
wget https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.55/bin/apache-tomcat-8.5.55.tar.gz
tar xf apache-tomcat-8.5.55.tar.gz -C /usr/local/
cd /usr/local/
mv apache-tomcat-8.5.55/ tomcat
mv jdk1.8.0_151/ jdk

按照相同方法在tomcat-2也安裝

vim conf/server.xml

// 設置默認虛擬主機,並增加jvmRoute
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat-1">
  
// 修改默認虛擬主機,並將網站文件路徑指向/web/webapp1,在host段增加context段  
<Host name="localhost"  appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context docBase="/web/webapp1" path="" reloadable="true"/>
</Host>
  
  
// 增加文檔目錄與測試文件  
mkdir -p /web/webapp1
cd /web/webapp1
cat index.jsp 
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
    <head>
        <title>tomcat-1</title>
    </head>
    <body>
        <h1><font color="red">Session serviced by tomcat</font></h1>
        <table aligh="center" border="1">
        <tr>
            <td>Session ID</td>
            <td><%=session.getId() %></td>
                <% session.setAttribute("abc","abc");%>
            </tr>
            <tr>
            <td>Created on</td>
            <td><%= session.getCreationTime() %></td>
            </tr>
        </table>
    tomcat-1
    </body>
<html>  
  
  
// 接下來我們將tomcat和nginx都啟動起來,可以發現用戶訪問index.jsp會一會跳轉tomcat1,一會tomcat2,session還不一致  

Tomcat-2節點與tomcat-1節點配置基本類似,只是jvmRoute不同,另外為了區分由哪個節點提供訪問,測試頁標題也不同(生產環境兩個tomcat服務器提供的網頁內容是相同的)。其他的配置都相同。

用瀏覽器訪問nginx主機,驗證負載均衡

驗證健康檢查的方法可以關掉一台tomcat主機,用客戶端瀏覽器測試訪問。

從上面的結果能看出兩次訪問,nginx把訪問請求分別分發給了後端的tomcat-1和tomcat-2,客戶端的訪問請求實現了負載均衡,但sessionid並一樣。所以,到這裏我們準備工作就全部完成了,下面我們來配置tomcat通過redis實現會話保持。

安裝redis
yum -y install gcc
wget http://download.redis.io/releases/redis-4.0.14.tar.gz
tar xvf redis-4.0.14.tar.gz -C /opt/
cd /opt/redis-4.0.14

編譯安裝

# Redis的編譯,只將命令文件編譯,將會在當前目錄生成bin目錄
make && make install  PREFIX=/usr/local/redis
cd ..
mv redis-4.0.14/* /usr/local/redis/

# 創建環境變量
echo 'PATH=$PATH:/usr/local/redis/src/' >> /etc/profile
source /etc/profile

# 此時在任何目錄位置都可以是用redis-server等相關命令
[root@redis1 ~]# redis-
redis-benchmark  redis-check-rdb  redis-sentinel   redis-trib.rb    
redis-check-aof  redis-cli        redis-server 

配置Redis

# 設置後台啟動
# 由於Redis默認是前台啟動,不建議使用.可以修改為後台
daemonize yes


# 禁止protected-mode yes/no(保護模式,是否只允許本地訪問)
protected-mode


# 設置遠程訪問
# Redis默認只允許本機訪問,把bind修改為bind 0.0.0.0 此設置會變成允許所有遠程訪問,如果指定限制訪問,可設置對應IP。
# bind指定是redis所在服務器網卡的IP,不指定本機網卡IP,可能導致你的Redis實例無法啟動
# 如果想限制IP訪問,內網的話通過網絡接口(網卡限定),讓客戶端訪問固定網卡鏈接redis
# 如果是公網,通過iptables指定某個IP允許訪問
bind 0.0.0.0

# 配置Redis日誌記錄
# 找到logfile,默認為logfile "",改為自定義日誌格式
logfile  /var/log/redis_6379.log

# 把requirepass修改為123456,修改之後重啟下服務
requirepass "123456"
# 不重啟Redis設置密碼
# 在配置文件中配置requirepass的密碼(當Redis重啟時密碼依然生效)
127.0.0.1:6379> config set requirepass test123
# 查詢密碼
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "test123"

# 密碼驗證
127.0.0.1:6379> auth test123
OK
127.0.0.1:6379> set name flying
OK
127.0.0.1:6379> get name
"flying"

# 遠程主機連接
# redis-cli  -h  redis_ip -p redis_port -a password

啟動測試

# 放到後台輸出,redis自帶日誌了,可以輸出到黑洞
nohup redis-server /usr/local/redis/redis.conf &> /usr/local/redis/redis.log &

# 關閉命令
redis-cli -h 127.0.0.1 -p 6379 -a 123456 shutdown
# 注意:不建議使用 kill -9,這種方式不但不會做持久化操作,還會造成緩衝區等資源不能優雅關閉。極端情況下造成 AOF 和 複製丟失數據 的情況。
# shutdown 還有一個參數,代表是否在關閉 redis 前,生成 持久化文件,命令為 redis-cli shutdown nosave|save。


# 設置開機自啟動
echo "redis-server /usr/local/redis.conf" >> /etc/rc.local

配置tomcat session redis同步

通過TomcatClusterRedisSessionManager,這種方式支持redis3.0的集群方式
下載TomcatRedisSessionManager-2.0.zip包,https://github.com/ran-jit/tomcat-cluster-redis-session-manager,放到$TOMCAT_HOMA/lib下,並解壓

cd /usr/local/tomcat/lib/
wget https://github.com/ran-jit/tomcat-cluster-redis-session-manager/releases/download/2.0.4/tomcat-cluster-redis-session-manager.zip
unzip tomcat-cluster-redis-session-manager.zip 
cp tomcat-cluster-redis-session-manager/lib/* ./
cp tomcat-cluster-redis-session-manager/conf/redis-data-cache.properties ../conf/
cat ../conf/redis-data-cache.properties     
#-- Redis data-cache configuration
//遠端redis數據庫的地址和端口
#- redis hosts ex: 127.0.0.1:6379, 127.0.0.2:6379, 127.0.0.2:6380, ....
redis.hosts=192.168.6.244:6379
//遠端redis數據庫的連接密碼
#- redis password (for stand-alone mode)
redis.password=pwd@123
//是否支持集群,默認的是關閉
#- set true to enable redis cluster mode
redis.cluster.enabled=false
//連接redis的那個庫
#- redis database (default 0)
#redis.database=0
//連接超時時間
#- redis connection timeout (default 2000)
#redis.timeout=2000
//在這個<Context>標籤裏面配置

vim ../conf/context.xml
<Valve className="tomcat.request.session.redis.SessionHandlerValve" />
<Manager className="tomcat.request.session.redis.SessionManager" />

配置會話到期時間在../conf/web.xml

<session-config>
<session-timeout>60</session-timeout>
</session-config>

啟動tomcat服務

[root@linux-node2 lib]# ../bin/startup.sh

Tomcat-2節點與tomcat-1節點配置相同

測試,我們每次強刷他的sessionID都是一致的,所以我們認為他的session會話保持已經完成,你們也可以選擇換個客戶端的IP地址來測試

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

信道估計(channel estimation)圖解——從SISO到MIMO原理介紹

1. 引言

在所有通信中,信號都會通過一個介質(稱為信道),並且信號會失真,或者在信號通過信道時會向信號中添加各種噪聲。正確解碼接收到的信號而沒有太多錯誤的方法是從接收到的信號中消除信道施加的失真和噪聲。為此,第一步是弄清信號經過的信道的特性。表徵信道的技術/過程稱為信道估計(channel estimation)。此過程將說明如下。

信道估計有很多不同的方法,但是基本概念是相似的。該過程如下進行。

i)設置一個數學模型,以使用“信道”矩陣將“發射信號”和“接收信號”相關。

ii)發射已知信號(我們通常將其稱為“參考信號”或“導頻信號”)並檢測接收到的信號。

iii)通過比較發送信號和接收信號,我們可以找出信道矩陣的每個元素。

作為此過程的示例,這裏簡要介紹LTE中的此過程。當然,很多細節取決於實現(這意味着具體算法可能會因每個特定的芯片組實現而有所不同)。但是,總體概念將是相似的。

2. 通用算法

我們如何找出信道的屬性?即,我們如何估計信道?從高的角度來看,可以如下圖所示。此圖显示以下內容:

i)我們嵌入了一組預定義信號(這稱為參考信號)

ii)當這些參考信號通過信道時,它會與其他信號一起失真(衰減,相移,噪聲)

iii)我們在接收方檢測/解碼接收到的參考信號

iv)比較發送的參考信號和接收的參考信號,並找到它們之間的相關性。

3. SISO的信道估計

現在讓我們考慮LTE SISO的情況,看看如何估計信道屬性(信道係數和噪聲估計)。由於考慮的是SISO系統,因此參考信號僅嵌入到一個天線端口(端口0)中。資源圖中的垂直線表示頻域。因此,這裏用f1,f2,f3 … fn索引了每個參考信號。每個參考符號可以是一個複數(I / Q數據),可以如下所示進行繪製。左側(發送側)的每個複數(參考符號)被修改(失真)為右側的每個對應符號(接收的符號)。信道估計是在左側的複數數組與右側的複數數組之間找到相關性的過程。

估計的詳細方法可能非常取決於實現方式。這裏將描述的方法基於開源:srsLTE(請參閱[1])

3.1 信道係數的估計

由於這裏只有一根天線,因此每個發射參考信號和接收參考信號的系統模型可以表示如下。y()表示接收到的參考信號的數組,x()表示發送的參考信號()的數組,h()表示信道係數的數組。f1,f2,…只是整數索引。

我們知道x()是什麼,因為給定了它,而y()也知道,因為它是從接收者處測量/檢測到的。有了這些,我們可以很容易地計算出係數陣列,如下所示。

現在我們有了參考信號所在位置的所有信道係數。但是我們需要在所有位置(包括那些沒有參考信號的點)處的信道效率。這意味着我們需要在沒有參考信號的情況下找出那些位置的信道係數。為此,最常見的方法是對測得的係數數組進行插值。在srsLTE的情況下,它首先進行平均,然後對平均信道係數進行插值。

3.2 噪聲的估計

下一步是估計噪聲特性。從理論上講,噪聲可以如下計算。

但是,我們需要的是噪聲的統計屬性,而不是確切的噪聲值。我們可以僅使用測得的信道係數和平均信道來估算噪聲,如下所示(實際上,準確的噪聲值沒有太大意義,因為噪聲值會不斷變化,使用那些特定的噪聲值沒有用)。在srsLTE中,作者使用了這種方法。

4. 2 x 2 MIMO的信道估計

假設我們有一個如下所示的通信系統。x(t)表示發送信號,y(t)表示接收信號。當x(t)傳輸到空中(信道)時,它會變形並獲得各種噪聲,並且可能會相互干擾。因此接收到的信號y(t)不能與發射信號x(t)相同。

發射信號,接收信號和信道矩陣之間的關係可以用數學形式建模,如下所示。

在此等式中,我們知道值x1,x2(已知的發射信號)和y1,y2(檢測/接收的信號)。我們不知道的部分是H矩陣和噪聲(n1,n2)。

為簡單起見,我們假設該信道中沒有噪聲,這意味着我們可以將n1,n2設置為0。(當然,在實際信道中總會存在噪聲,估計噪聲是信道估計中非常重要的一部分,但是我們在此示例中假設沒有噪音,只是為了使其簡單。稍後,當我有更好的知識以通俗的語言描述案件時,我將在案件中添加噪音)。

由於我們具有數學模型,因此下一步是傳輸已知信號(參考信號)並從參考信號中找出信道參數。

假設我們僅通過一個天線發送了幅度為1的已知信號,而另一個天線現在處於關閉狀態。由於信號通過空氣傳播,並且接收方的兩個天線都會檢測到該信號。現在,假設第一個天線接收幅度為0.8的參考信號,第二個天線接收幅度為0.2的參考信號。有了這個結果,我們可以得出如下所示的一行信道矩陣(H)。

假設我們僅通過另一個(第二個)天線發送了幅度為1的已知信號,並且第一個天線現在處於關閉狀態。由於信號通過空氣傳播,並且接收方的兩個天線都會檢測到該信號。現在,假設第一個天線接收到幅度為0.3的參考信號,第二個天線接收到幅度為0.7的參考信號。有了這個結果,我們可以得出如下所示的一行信道矩陣(H)。

夠簡單嗎?我認為理解這個基本概念沒有任何問題。但是,如果完全按照上述方法使用此方法,則可能會導致效率低下。根據上面解釋的概念,應該有一個時刻,僅發送參考信號而沒有實際數據,只是為了估計信道信息,這意味着由於信道估計過程,數據速率將降低。為了消除這種效率低下的問題,實際的通信系統會同時發送參考信號和數據。

現在的問題是“如何在同時傳輸參考信號和數據的同時實現上述概念?”。可以有幾種不同的方法來執行此操作,並且不同的通信系統將使用一些不同的方法。

以LTE為例,我們使用如下所示的方法。在LTE中為2 x 2 MIMO的情況下,每個子幀具有用於每個天線的參考信號的不同位置。天線0的子幀發送了分配給天線0的參考信號,不發送分配給天線1的參考信號的信號。天線1的子幀發送了分配給天線1的參考信號的信號,不發送給參考天線的任何信號。為天線0分配的信號。因此,如果在兩個接收器天線上解碼為天線0的參考信號分配的資源元素,則可以估計h11,h12。(在這裏,為了簡單起見,我們還假設沒有噪音)。如果在兩個接收器天線上解碼分配給天線1參考信號的資源元素,則可以估計h21,h22。

4.1 信道係數的估計

上面說明的過程是針對LTE OFDMA符號中的頻域中的一個特定點測量 \(H\) 矩陣。如果您在對符號的其他部分進行解碼的過程中照原樣應用測量的H值,則解碼的符號的準確性可能不盡人意,因為上一步中使用的測量數據會包含一定程度的噪聲。因此,在實際應用中,對通過上述方法測得的 \(H\) 值進行某種后處理,在此後處理過程中,我們可以找出噪聲的總體統計屬性(例如,噪聲的均值,方差和統計分佈))。要記住的一件事是,在此過程中獲得的特定噪聲值本身並沒有太多意義。從參考信號獲得的特定值將與用於解碼其他數據的噪聲值(非參考信號)不同,因為噪聲值是隨機變化的。然而,那些隨機噪聲的總體特性可以是重要的信息(例如,在SNR估計等中使用)。

在繼續之前,讓我們再次簡單地考慮一下數學模型。即使我們將系統方程式描述如下,其中包括噪聲項,但這並不意味着您可以直接測量噪聲。是不可能的。該方程式僅表明檢測到的信號(y)包含噪聲分量的某些部分。

因此,當我們測量信道係數時,我們使用了沒有噪聲項的設備,如下所示。

在LTE的特定應用中,我們在OFDM符號中有多個測量點(多個參考信號)。這些測量點在頻域上表示。因此,讓我們如下重寫信道矩陣以指示每個信道矩陣的測量點。

現在,假設您已經測量了整個OFDM符號上的H矩陣,那麼您將擁有多個 \(H\) 矩陣,如下所示,每個矩陣都以一個特定的頻率指示H矩陣。

現在你有了一個 \(H\) 矩陣數組。該陣列由四個不同的組組成,每個組用不同的顏色突出显示,如下所示。

當應用后處理算法時,該算法需要分別應用於這些組中的每一個。因此,為簡單起見,我將 \(H\) 矩陣的數組重新排列為多個獨立數組(在本例中為4個數組),如下所示。

對於這些數組中的每一個,我將進行如下所示的相同處理。(每個芯片組製造商都可以應用稍微不同的方法,但是總體思路是相似的)。在下面說明的方法中,數據(每個頻點中的信道係數陣列)使用IFFT進行處理,這意味着將dta轉換為時域,從而生成標記為(2)的時域數據陣列。實際上,這是特定信道路徑的脈衝響應。然後,我們對該時域數據應用特定的過濾(或加窗)。在此示例中,將某個點的數據替換為零,並創建標記為(3)的結果。您可以應用更複雜的過濾器或窗口,而不是這種簡單的調零。然後,通過將濾波后的信道脈衝數據轉換回頻域,

通過對所有四個陣列執行相同的過程,您可以獲得“估計信道係數陣列”的四個陣列。從這四個陣列中,您可以按以下方式重建估計信道矩陣的陣列。

4.2 噪聲的估計

使用此估算的信道矩陣,您可以使用以下公式估算每個點的噪聲值。這與本頁開頭的原始系統方程式相同,除了將H矩陣替換為“估計的H”矩陣外,現在我們知道除噪聲值以外的所有值。因此,通過插入所有已知值,我們可以在每個測量點計算(估計)噪聲值。

如果將此方程式應用於所有測量點,則將獲得所有測量點的噪聲值,並從這些計算出的噪聲值中獲得噪聲的統計屬性。如上所述,此處計算出的每個單獨的噪聲值沒有太大意義,因為該值不能直接應用於解碼其他信號(非參考信號),但是這些噪聲的統計特性對於確定噪聲而言可能是非常有用的信息。渠道的性質。

注意:如果您對在實際應用中如何使用此算法感興趣,強烈建議閱讀/嘗試使用Ref [2]和[3]。

參考:

[1] srsLTE:\ srslte \ lib \ ch_estimation \ chest_dl.c-srslte_chest_dl_estimate_port()

[2] 信道估計(Mathworks,LTE工具箱)

[3] NR同步程序

[4] http://www.sharetechnote.com/html/Communication_ChannelEstimation.html#General_Algorithm

更多精彩內容請關注訂閱號優化與算法和加入QQ討論群1032493483獲取更多資料

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

「從零單排canal 01」 canal 10分鐘入門(基於1.1.4版本)

1.簡介

canal [kə’næl],譯意為水道/管道/溝渠,主要用途是基於 MySQL 數據庫增量日誌解析,提供增量數據 訂閱 和 消費。應該是阿里雲DTS(Data Transfer Service)的開源版本。

2.提供的能力

Canal與DTS提供的功能基本相似:

1)基於Mysql的Slave協議實時dump binlog流,解析為事件發送給訂閱方。

2)單Canal instance,單DTS數據訂閱通道均只支持訂閱一個RDS,提供給一個消費者。

3)可以使用canal-client客戶端進行消息消費。

4)也可以通過簡單配置,也可以不需要自行使用canal-client消費,可以選擇直接投遞到kafka或者RocketMQ集群,用戶只需要使用消息隊列的consumer消費即可。

5)成功消費消息后需要進行Ack,以確保一致性,服務端則會維護客戶端目前的消費位點。

3.工作原理

MySQL的主從複製分成三步:

  • master將改變記錄到二進制日誌(binary log)中(這些記錄叫做二進制日誌事件,binary log events,可以通過show binlog events進行查看);
  • slave將master的binary log events拷貝到它的中繼日誌(relay log);
  • slave重做中繼日誌中的事件,將改變反映它自己的數據。

 

canal 就是模擬了這個過程。

  • canal模擬 MySQL slave 的交互協議,偽裝自己為 MySQL slave ,向 MySQL master 發送 dump 協議;
  • MySQL master 收到 dump 請求,開始推送 binary log 給 slave (即 canal );
  • canal 解析 binary log 對象(原始為 byte 流);

 

4. canal 架構

4.1 admin版本整體架構

canal 1.1.4開始支持admin管理,通過canal-admin為canal提供整體配置管理、節點運維等面向運維的功能,提供相對友好的WebUI操作界面,方便更多用戶快速和安全的操作,替代了過去繁瑣的配置文件管理。

整體部署架構如下。

 

  • 多個canal-server可以組成集群模式,每個instance任務通過zookeeper在集群中實現高可用
  • 通過多個集群,可以實現同步資源的物理隔離
  • 可以直接抓取消費投遞MQ,可以實現生產/消費解耦、消息堆積、消息回溯
  • 可以抓取消費投遞給canal-client,在用戶的服務中進行消息處理,減少中間過程

4.2 canal-server架構

 

說明:

  • server代表一個canal-server運行實例,對應於一個jvm
  • instance對應於一個數據隊列,是真正的變更抓取的實體 (1個server可以對應多個instance)

Instance模塊

  • EventParser :數據源接入,模擬slave協議和master進行交互,協議解析
  • EventSink :Parser和Store鏈接器,進行數據過濾,加工,分發的工作
  • EventStore :數據存儲
  • MetaManager:增量訂閱&消費信息管理器

1)EventParser子模塊

EventParser模塊的類圖設計如下

 

每個EventParser都會關聯兩個內部組件:CanalLogPositionManager , CanalHAController

  • CanalLogPositionManager:記錄binlog最後一次解析成功位置信息,主要是描述下一次canal啟動的位點
  • CanalHAController:支持Mysql主備,判斷當前該連哪個mysql(基於Heartbeat實現,主庫失去心跳則連備庫)

EventParser根據HAController獲知連到哪裡,通過LogPositionManager獲知從哪個位點開始解析,之後便通過Mysql Slave協議拉取binlog進行解析,推入EventSink

2)EventSink子模塊

目前只提供了一個帶有實際作用的實現:GroupEventSink

GroupEventSink用於將多個instance上的數據進行歸併,常用於分庫后的多數據源歸併。

3)EventStore子模塊

EventStore的類圖如下

 

官方提供的實現類是
MemoryEventStoreWIthBuffer,內部採用的是一個RingBuffer:

 

  • Put : Sink模塊進行數據存儲的最後一次寫入位置
  • Get : 數據訂閱獲取的最後一次提取位置
  • Ack : 數據消費成功的最後一次消費位置

這些位點信息通過MetaManager進行管理。這也解釋了為什麼一個canal instance只能支撐一個消費者:EventStore的RingBuffer只為一個消費者維護信息。

4.3 客戶端使用

數據格式已經在前文給出,Canal和DTS客戶端均採取:

拉取事件 -> 消費 -> 消費成功后ACK

這樣的消費模式,並支持消費不成功時進行rollback,重新消費該數據。

下面是一段簡單的客戶端調用實例(略去異常處理):

// 創建CanalConnector, 連接到localhost:11111

CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(AddressUtils.getHostIp(),11111), destination, "", "");

connector.connect(); // 連接

connector.subscribe(); // 開始訂閱binlog

// 開始循環拉取

while (running) {

Message message = connector.getWithoutAck(1024); // 獲取指定數量的數據

long batchId = message.getId();

for (Entry entry : message.getEntries()){

// 對每條消息進行處理

}

connector.ack(batchId); // ack

}

5.總結分析

5.1 優點

1)性能優異、功能全面

  • canal 1.1.x 版本(release_note),性能與功能層面有較大的突破,重要提升包括:
  • 整體性能測試&優化,提升了150%. #726
  • 原生支持prometheus監控 #765
  • 原生支持kafka消息投遞 #695
  • 原生支持aliyun rds的binlog訂閱 (解決自動主備切換/oss binlog離線解析) (無法拒絕它的理由!)
  • 原生支持docker鏡像 #801

2)運維方便

  • canal 1.1.4版本,迎來最重要的WebUI能力,引入canal-admin工程,支持面向WebUI的canal動態管理能力,支持配置、任務、日誌等在線白屏運維能力
  • Standalone的一體化解決方案,無外部服務依賴,運維更簡單,在某種程度上也意味着更穩定。
  • 開箱即用,節約開發與定製成本。
  • 有良好的管理控制平台與監控系統(如果你已經有promethus監控,可以秒接canal監控)

3)多語言支持

  • canal 特別設計了 client-server 模式,交互協議使用 protobuf 3.0 , client 端可採用不同語言實現不同的消費邏輯
  • canal 作為 MySQL binlog 增量獲取和解析工具,可將變更記錄投遞到 MQ 系統中,比如 Kafka/RocketMQ,可以藉助於 MQ 的多語言能力

5.2 缺點

  • 單instance/訂閱通道只支持訂閱單個數據庫,並只能支持單客戶端消費。每當我們需要新增一個消費端->MySQL的訂閱:對於Canal而言,就要給MySQL接一個“Slave”,可能會對主庫有一定影響。
  • 消息的Schema很弱,所有消息的Schema均相同,客戶端需要提前知道各個表消息的Schema與各字段的上下文才能正確消費。

好了,花了10分鐘應該對canal有大致了解了,下一期,阿丸計劃手把手教你搭建canal集群和admin管理平台,記得關注哦。

 

都看到最後了,原創不易,點個關注,點個贊吧~

知識碎片重新梳理,構建Java知識圖譜: github.com/saigu/JavaK…(歷史文章查閱非常方便)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※推薦台中搬家公司優質服務,可到府估價

除了邁騰帕薩特雅閣 20萬的B級車我們還能選什麼?

99萬上汽大眾斯柯達 速派售價:16。98-27。68萬東風悅達起亞 K5售價:15。98-24。88萬總結:今天所介紹的這幾款中級車都各有特色,但卻是實力出眾的值得購買的車型。最新的C6作為雪鐵龍再次征戰中型車市場的車型,有着優雅大氣的外觀造型與寬敞的空間,配以較低的售價,整體還是很值得購買的。

目前SUV市場是很火,但作為傳統領域的中級車市場仍然保持以往的穩定;這是廠商努力穩住的細分市場;而這個級別當中有着眾多的車型可供選擇;但預算只有20萬的話要想購置一輛品質出眾的中級車還是有難度的,不過看完今天的推薦將會解除你的困惑。

廣汽豐田 凱美瑞

售價:18.48-32.98萬

東風雪鐵龍 C6

售價:18.99-27.99萬

上汽大眾斯柯達 速派

售價:16.98-27.68萬

東風悅達起亞 K5

售價:15.98-24.88萬

總結:今天所介紹的這幾款中級車都各有特色,但卻是實力出眾的值得購買的車型。

最新的C6作為雪鐵龍再次征戰中型車市場的車型,有着優雅大氣的外觀造型與寬敞的空間,配以較低的售價,整體還是很值得購買的。

現款速派已經上市了一段時間,整體外觀造型年輕了不少,出自MQB平台的產品,整體的做工精良,相比邁騰價格更低,除了品牌力差一些之外,其實是一款出眾的車型。

K5當年可謂是一時風頭,只是太過於犀利的外觀造型使得它比較適合年輕消費者,但偏低的起步價使得擁有一輛的中級車的門檻降低了很多。

凱美瑞作為這個級別長年的領跑者,在性價比、可靠性和實際體驗上都沒有明顯的短板,可以說誰選都不會錯的大眾情人,同時遇上網約車新規的出台,就如為凱美瑞定製一般,在跑滴滴方面有着特別的優勢。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

可以寫入教科書的設計 自主品牌又要憋大招了

十來萬的家轎 為什麼需要超低風阻。儘管SUV熱銷,市場佔有率也攀升到36%,但是中國市場依然是青睞轎車的,畢竟轎車還貢獻着53%的乘用車銷量,所以上汽在綜合了爆款SUV RX5及高品質家轎360的能量之上,即將推出一款准B級轎車,目前定名為i6,作為榮威品牌旗下的全新轎車產品序列。

隨着爆款SUV榮威RX5的熱銷,在後台與讀者聊天過程中又有了越來越多坐等提RX5的車主。等提車的過程是難受的,也深有體會。日前,就到了上汽安亭工廠,給大家去扒一扒近期上汽乘用車的狀況。

RX5月銷2萬 產能攀升至月產3萬

早在榮威RX5上市前,其實已經試駕過當中的20T和30T的車型,只是由於微信不太方便查找,很多車友可能會沒看到,以至於依然有不少新加上的同學們還在後台問這款車怎麼樣。下面先來個回顧:

上山劈彎 妹紙在後排葛優躺 這款十來萬的SUV真有那麼舒服?

試駕完這款20T的SUV,只有6個字評價…

這款由中國最大的車企和最大的互聯網公司共同打造的SUV能成為爆款,覺得一點都不意外。如今上市僅3個月的榮威RX5月銷量已經攀升至2萬台,目前還有大量的訂單還等待工廠排產,從廠方得來的消息,目前榮威RX5的產能在一直擴大,上海臨港加上南京浦口的工廠在雙線生產中,產能攀升到41JpH,即每小時能生產41台,估摸下來每月能生產3萬台,就在前天11月9日就迎來了第5萬台的下線。也就是說,各位正在等待提車的准RX5車主們,也不用等很久了。按照這個勢頭,11月可以輕易做到2萬5千台的銷量,衝進月銷前5也不是什麼難事。

說好的插電混動eRX5什麼時候上市?

北京車展上與RX5一同亮相的還有eRX5,這是一款插電式混合動力的SUV,大嘴格柵的設計比RX5更為大氣。目前市面上熱銷的混動SUV真不多,不插電的主要有雷克薩斯NX和RX,以及豐田明年可能會上榮放及漢蘭達的混動版(雙擎),插電混動的目前有沃爾沃XC90 T8,以及比亞迪唐宋元。除此以外,並沒有更多的選擇。也正期待着,加上混合動力這項上牌優勢的eRX5的市場表現如何。

上汽在新能源上也非常有建樹,不僅新能源轎車e550、e950獲得銷量領先。就在上月的10月28日,中國汽車工業科學技術獎揭曉,上汽新能源憑藉在EDU電驅、HCU電控和ESS電池方面的創新,成為今年插電混動領域唯一獲一等獎的車企,這也是上汽時隔5年再次獲得中國汽車界最高榮譽。

據本次上汽工廠行的了解,eRX5也將在11月18日在廣州車展上宣布上市。外形上,主要與RX5是只有前格柵、霧燈、輪轂等明顯的區別,內飾依然是大面積的軟包,新增了12.3英寸的全尺寸液晶儀錶盤。顏色配置新增了更能體現高級感的銀恭弘=叶 恭弘金漆面及松露棕內飾,見過實車之後確實感覺比RX5更上檔次。動力方面,採用與RX5 20T一樣的1.5TGI發動機,配合電驅單元並匹配EDU智能電驅變速箱,可提供最大704牛米的峰值扭矩,純電續航里程為60KM,綜合百公里油耗1.6L。

十來萬的家轎 為什麼需要超低風阻?

儘管SUV熱銷,市場佔有率也攀升到36%,但是中國市場依然是青睞轎車的,畢竟轎車還貢獻着53%的乘用車銷量,所以上汽在綜合了爆款SUV RX5及高品質家轎360的能量之上,即將推出一款准B級轎車,目前定名為i6,作為榮威品牌旗下的全新轎車產品序列。

作為律動設計的原點–上汽榮威Vision-R概念車,相信很多讀者都對其印象深刻,榮威i6沿用榮威Vision-R的設計理念,可以看作是最接近Vision-R概念車的量產車型。i6的空氣動力學設計簡直可以收到國產車教科書中作為案例,其有着0.25cd的超低風阻係數。0.25cd是什麼概念?列舉一系列車型對比下:

CLA

E-CLASS

A4

Model S

榮威 i6

威朗轎跑

凌渡

普通家用轎車領域,近年來奔馳的空力設計是最出色的,而轎車的風阻係數越低,就意味着越省油,行業內同行的說法是風阻係數每降低10%,油耗能降低3%。榮威i6由於大量沿用了概念車的設計,加上上汽工程師團隊進行了大量的試驗調整,精心處理每一處細節,使得i6能實現0.25的超低風阻係數。在主流轎車當中,可以排得上前五名,是上汽多年技術積累的一次實力表現。

i6的車身尺寸為4671*1835*1464毫米,軸距2715毫米,整體車身比例以“優雅寬體比例”為基礎,擁有同級別最寬的車身,達到1835mm,無論外觀和內在都符合榮威律動設計當中橫向舒展的理念。長達2715毫米的軸距同時也造就了同級最大的後排空間。內飾及配置將會與RX5一致,同樣是大面積的軟包,加上10.4寸搭配YunOS的斑馬智行互聯網車載系統。

新車將搭載上汽的藍芯、綠芯系列動力總成,即包括了傳統的燃油、混合、純電動力系統。燃油發動機方面,將會有1.0T及1.5T的配置。全新的1.0TGI,融合了目前先進的技術,包括缸內中置直噴、低慣量渦輪,採用全鋁合金打造,排量1.0L,最大功率92Kw/125匹馬力,峰值扭矩170牛•米,動力性能優於1.6L四缸發動機,整機採用缸蓋機體裙架曲軸連桿設計,以高度輕量化降低整車油耗和排放,提升動力。

據目前最新的消息,榮威i6將會在廣州車展上亮相,屆時大家便可以一睹這款真正屬於“互聯網”時代全新轎車的真容。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

提車后六大惡俗習慣 老實交代你有沒有上榜…

時常看到日系車車尾貼着壁虎,難道在庇護不要被…。(大家自行腦補吧)。第三、放鞭炮放鞭炮寓意着有好事到來,在4S店提車的時候,提車儀式時銷售通常會為客戶提車而燒串鞭炮賀一賀,滿地紅看着心情都開心一點。燒鞭炮同樣是好事,不過好事千萬就不要醞釀成壞事了。

也是農村長大的娃,不論是很久之前還是現在,這樣的情況還是會經常遇到的。拒絕吧,可能令家裡的老人不開心;接受吧,又怕太彆扭,不小心還會對新車造成損害。

全國各地的風俗習慣都不同,買新車之後老百姓們會有不同的慶祝方式。下面我們來看看,你中招了嗎?

第一、系紅繩

紅繩一般都是4S店為新車客戶而準備的,按照風俗習慣來說,紅色代表吉利、喜慶,而且可以避邪,所以很多車主都願意去為新車繫上紅繩,代表好意頭,為自己的平安出行作禱告。

系紅繩是好事,不過我們也要注意系紅繩的方式。一般來說紅繩系在後視鏡即可,而且不能太長太寬,不然行車過程中紅繩可能會影響后視鏡的視野,特別是雨天大風天氣。另一方面就是,紅繩的質量不好,可能會掉色,假若顏色粘在車身上,你肯定是極為不樂意的。同樣,不建議將紅繩綁在輪圈上,而且系紅繩的時間避免過長。

第二、“壁虎”車標

不少朋友上老司機的車,經常會遇到問為什麼前面的車後面貼着一隻壁虎的事情,通常都笑答說這是為了好看,其實真正的含義是什麼呢?

“壁虎”“庇護”,你沒想錯,也可以理解為“避禍”的意思,同樣是圖個吉利意頭。另外,其實壁虎也是奧迪Quattro全時四驅系統的Logo,不過現在很少奧迪會有這些裝飾了。時常看到日系車車尾貼着壁虎,難道在庇護不要被….(大家自行腦補吧)。

第三、放鞭炮

放鞭炮寓意着有好事到來,在4S店提車的時候,提車儀式時銷售通常會為客戶提車而燒串鞭炮賀一賀,滿地紅看着心情都開心一點。

燒鞭炮同樣是好事,不過好事千萬就不要醞釀成壞事了。不少新聞報道,客戶提車回家,因放炮而把新車炸得面目全非,眼睜睜看着自己辛勤勞動而得來的成果瞬間化為烏有,心疼啊有木有!這是多痛的領悟啊!放鞭炮是好事,不過千萬別把自己的車給炸了!

第四、祭拜汽車

老實說,買車提車放鞭炮就算了,但如果說要祭拜、開光汽車,是真的不建議,而且我真系接受唔到咯!

買新車回家通常不是自己一個人的事兒了,老一輩估計都會給新車祭拜一下,燒香蠟燭的是常事了,如果家裡老一輩的人要求,也不會太強硬地去拒絕,畢竟老人家也是為了往後孩子們的出入平安,並沒有錯。找法師幫汽車開光,雖說存在這樣的事情,但也沒真實見過,很多人都表示,聽過擇日提車而沒有聽過為汽車開光之事…

第五、車內掛件擺佛像

前段時間從西藏的活動歸來,也體驗到了那邊西域的風俗文化,他們本地居民的汽車大多車內都擺有佛像以及不同顏色哈達,可見西藏的文化是隨處可見的。

而在南方大多車主們也就擺香水或者公仔在中控台上,並沒有那麼雜亂,對了,還有擺車模的(注意是汽車模型,不是美女車模..)。雖說這些擺件確實漂亮好看有寓意,但是對於行車安全來說還是有一定的隱患的,車輛發生碰撞時擺件可能會脫落對車內人員造成不同程度的傷害,所以擺件一定要固定好!另外,建議擺件不擺就不擺,在中后視鏡掛上一个中國結圖意頭還是不錯的選擇。

第六、貼春聯

汽車貼春聯貌似現在不多見了,或許是孤陋寡聞?

同樣,汽車貼春聯也是圖個好意頭,本身是好事,不過貼這麼耀眼的春聯,不知道交警會怎樣想,雖說沒有明文規定是否合法,畢竟這樣做也是會影響到行車安全,萬一春聯在行車中脫離,無論是影響到自己的車輛還是後車的視野,都是十分危險的!

總結:

常話說,對於傳統文化或是風俗習慣,我們不能全部否定,要取其精華棄其糟粕。各地方民族的風俗習慣都不一樣,我們不要去破壞或是貶低,畢竟風俗習慣是前人所留下給我們子孫的。對於汽車的風俗習慣,大家看了的分析,也清楚到了不少是對行車安全有影響的,所以大家一定要千萬注意,不要為了討平安而過於盲目了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

SpringSecurity(2)—記住我功能實現

SpringSecurity(2)—記住我功能實現

上一篇博客實現了認證+授權的基本功能,這裡在這個基礎上,添加一個 記住我的功能

上一篇博客地址:SpringSecurity(1)—認證+授權代碼實現

說明:上一遍博客的 用戶數據用戶關聯角色 的信息是在代碼里寫死的,這篇將從mysql數據庫中讀取。

一、數據庫建表

這裏建了三種表

一般權限表有四張或者五張,這裡有關 角色關聯資源表 沒有創建,角色和資源的關係依舊在代碼里寫死。

建表sql

/*創建用戶表*/
CREATE TABLE `persistent_logins` (
  `username` varchar(64) NOT NULL,
  `series` varchar(64) NOT NULL,
  `token` varchar(64) NOT NULL,
  `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*創建j角色表*/
CREATE TABLE `roles` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

/*創建用戶關聯角色表*/
CREATE TABLE `roles_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `rid` int DEFAULT '2',
  `uid` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=133 DEFAULT CHARSET=utf8;


/*這裏密碼對應的明文 還是123456*/
INSERT INTO `user` (`id`, `username`, `nickname`, `password`, `enabled`)
VALUES
	(1, '小小', '小小', 'e10adc3949ba59abbe56e057f20f883e', 1);

/*三種角色*/
INSERT INTO `roles` (`id`, `name`)
VALUES
	(1, '校長'),
	(2, '教師'),
	(3, '學生');
	
/*小小用戶關聯了 教師和校長角色*/
INSERT INTO `roles_user` (`id`, `rid`, `uid`)
VALUES
	(1, 2, 1),
	(2, 3, 1);

說明:這裏數據庫只有一個用戶

用戶名 :小小

密碼:123456

她所擁有的角色有兩個 教師學生

二、Spring Security的記住我功能基本原理

概念 記住我在登陸的時候都會被用戶勾選,因為它方便地幫助用戶減少了輸入用戶名和密碼的次數,用戶一旦勾選記住我功能那麼 當服務器重啟后依舊可以不用登陸就可以訪問

Spring Security的“記住我”功能的基本原理流程圖如下所示:

這裏大致流程如下:

第一次登陸

用戶請求的時候 remember-me參數為true 時,用戶先進行 認證+授權過濾器。然後走記住我過濾器這裏需要做兩,這裏主要做兩件事。

1.將Token數據存入數據庫 2.將token數據存入cookie中。

服務重啟后

如果服務重啟的話,那麼之前的session信息已經不在了,但是cookie中的Token還是存在的。所以當用戶重啟後去訪問需要認證的接口時,會先通過cookie中的Token

去數據庫查詢這條Token信息,如果存在那麼在通過用戶名去查詢數據庫獲取當前用戶的信息。

三、代碼實現

因為上面項目已經完成了整個授權+認證的過程,那麼這裏就很簡單添加一點點代碼就可以了。

在WebSecurityConfig中添加一個Bean,配置完這個Bean就基本完成了 記住我 功能的開發,然後在將這個Bean設置到configure方法中即可。

    @Bean
    public PersistentTokenRepository tokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        //tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

上面的代碼 tokenRepository.setCreateTableOnStartup(true) ;是自動創建Token存到數據庫時候所需要的表,這行代碼只能運行一次,如果重新啟動數據庫,

必須刪除這行代碼,否則將報錯,因為在第一次啟動的時候已經創建了表,不能重複創建。保險起見我們還是註釋掉這段代碼,手動建這張表。

CREATE TABLE `persistent_logins` (
  `username` varchar(64) NOT NULL,
  `series` varchar(64) NOT NULL,
  `token` varchar(64) NOT NULL,
  `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在配置里再加上這些就可以了。

四、測試

主要測試兩個點地方,

1、當我登陸時選擇記住我功能,看下數據庫persistent_logins是否有一條token記錄
2、當使用記住我功能后,關閉服務器在重啟服務器,不再登陸直接訪問需要認證的接口,看是否能夠訪問成功。

1、首次登陸

我們在看數據庫token表

很明顯新增了一條token數據。

2、重啟服務器

這個時候我們重啟服務器訪問需要認證的接口

發現就算重啟也不需要重啟登陸就可以反問需要認證的接口。

五、源碼分析

同樣這裏也分為兩部分 1、第一次登陸源碼流程。 2、重啟后未認證再去訪問需要認證的接口源碼流程。

1、首次登陸源碼流程

第一步

當用戶發送登錄請求的時候,首先到達的是UsernamePasswordAuthenticationFilter這個過濾器,然後執行attemptAuthentication方法的代碼,代碼如下圖所示:

 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
 //從這裏可以看出登陸需要post提交
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }

            username = username.trim();
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

之後所走的流程就是 ProviderManager的authenticate方法 ,之後再走AbstractUserDetailsAuthenticationProvider的authenticate方法,再走DaoAuthenticationProvider的方法retrieveUser方法

 protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        this.prepareTimingAttackProtection();

        try {
            //這裏就走我們自定義的獲取用戶認證和授權信息的代碼了
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
            } else {
                return loadedUser;
            }
        } catch (UsernameNotFoundException var4) {
            this.mitigateAgainstTimingAttack(authentication);
            throw var4;
        } catch (InternalAuthenticationServiceException var5) {
            throw var5;
        } catch (Exception var6) {
            throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
        }
    }

這樣一來,認證的流程就已經走完了。那就要走記住我功能的過濾器了。

第二步

驗證成功之後,將進入AbstractAuthenticationProcessingFilter 類的successfulAuthentication的方法中,首先將認證信息通過代碼
SecurityContextHolder.getContext().setAuthentication(authResult);將認證信息存入到session中,緊接着這個方法中就調用了rememberMeServices的loginSuccess方法

 protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
        }

        SecurityContextHolder.getContext().setAuthentication(authResult);
        //記住我
        this.rememberMeServices.loginSuccess(request, response, authResult);
        if (this.eventPublisher != null) {
            this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }

        this.successHandler.onAuthenticationSuccess(request, response, authResult);
    }

再走PersistentTokenBasedRememberMeServices的onLoginSuccess方法

    protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
        String username = successfulAuthentication.getName();
        this.logger.debug("Creating new persistent login for user " + username);
        PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(username, this.generateSeriesData(), this.generateTokenData(), new Date());

        try {
            //這裏就是關鍵的兩步 1、將token存入到數據庫 2、將token存入cookie中
            this.tokenRepository.createNewToken(persistentToken);
            this.addCookie(persistentToken, request, response);
        } catch (Exception var7) {
            this.logger.error("Failed to save persistent token ", var7);
        }

    }

這個方法中調用了tokenRepository來創建Token並存到數據庫中,且將Token寫回到了Cookie中。到這裏,基本的登錄過程基本完成,生成了Token存到了數據庫,

且寫回到了Cookie中。

2、第二次訪問

重啟項目,這時候服務器端的session已經不存在了,但是第一次登錄成功已經將Token寫到了數據庫和Cookie中,直接訪問一個服務,並且不輸入用戶名和密碼。

第一步

首先進入到了RememberMeAuthenticationFilter的doFilter方法中,這個方法首先檢查在session中是否存在已經驗證過的Authentication了,如果為空,就進行下面的

RememberMe的驗證代碼,比如調用rememberMeServices的autoLogin方法,代碼如下:

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            //走記住我流程
            Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response);
            //省略不重要的代碼
            chain.doFilter(request, response);
        } else {
            chain.doFilter(request, response);
        }
    }

我們在看this.rememberMeServices.autoLogin(request, response)方法。最終實現在AbstractRememberMeServices的autoLogin方法

    public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
        //1、獲取token
        String rememberMeCookie = this.extractRememberMeCookie(request);
        if (rememberMeCookie == null) {
            return null;
        } else {
    
                UserDetails user = null;
                try {
                    String[] cookieTokens = this.decodeCookie(rememberMeCookie);
                    //這步是關鍵
                    user = this.processAutoLoginCookie(cookieTokens, request, response);
                    this.userDetailsChecker.check(user);
                    this.logger.debug("Remember-me cookie accepted");
                    return this.createSuccessfulAuthentication(request, user);
                } catch (CookieTheftException var6) {
                    this.cancelCookie(request, response);
                    throw var6;
                } 
                this.cancelCookie(request, response);
                return null;
            }
        }
    }

我們在看 this.processAutoLoginCookie(cookieTokens, request, response);在PersistentTokenBasedRememberMeServices中實現,到這一步就已經很明白了

 protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) {
        if (cookieTokens.length != 2) {
            throw new InvalidCookieException("Cookie token did not contain 2 tokens, but contained '" + Arrays.asList(cookieTokens) + "'");
        } else {
            String presentedSeries = cookieTokens[0];
            String presentedToken = cookieTokens[1];
            //1、去token表中查詢token
            PersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries);
            if (token == null) {
                throw new RememberMeAuthenticationException("No persistent token found for series id: " + presentedSeries);
                //2校驗數據
            } else if (!presentedToken.equals(token.getTokenValue())) {
                this.tokenRepository.removeUserTokens(token.getUsername());
                throw new CookieTheftException(this.messages.getMessage("PersistentTokenBasedRememberMeServices.cookieStolen", "Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));
                //3、查看token是否過期
            } else if (token.getDate().getTime() + (long)this.getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) {
                throw new RememberMeAuthenticationException("Remember-me login has expired");
            } else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Refreshing persistent login token for user '" + token.getUsername() + "', series '" + token.getSeries() + "'");
                }

                PersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(), this.generateTokenData(), new Date());

                try {
                //4、更新這條token 沒更新一次有效時間就都變成了之間設置的時間
                    this.tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate());
                    this.addCookie(newToken, request, response);
                } catch (Exception var9) {
                    this.logger.error("Failed to update token: ", var9);
                    throw new RememberMeAuthenticationException("Autologin failed due to data access problem");
                }
                 //5、這裏拿着用戶名 就又獲取當前用戶的認證和授權信息
                return this.getUserDetailsService().loadUserByUsername(token.getUsername());
            }
        }
    }

這樣整個流程就完成了,我們可以看出源碼的過程和上面圖片展示的流程還是非常像的。

別人罵我胖,我會生氣,因為我心裏承認了我胖。別人說我矮,我就會覺得好笑,因為我心裏知道我不可能矮。這就是我們為什麼會對別人的攻擊生氣。
攻我盾者,乃我內心之矛(18)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

Shell中傻傻分不清楚的TOP3

Shell中傻傻分不清楚的TOP3
發布文章

近來小姐姐又犯憨憨錯誤,問組內小夥伴export命令不會持久化環境變量嗎?反正我是問出口了。。然後小夥伴就甩給了我一個《The Linux Command Line》PDF鏈接。感謝老大不殺之恩~

Shell是命令解釋器,它會接受用戶輸入的各種命令,並傳遞給操作系統執行。它的作用類似於Windows系統的命令行。在UNIX或Linux系統中,Shell即是用戶交互的界面,也是控制系統的腳本語言。當然現在用戶也可以選擇圖形化界面做一些和操作系統的交互。層次示意圖如下:

對於初學者來說,可能搞不清楚Shell怎麼會有那麼多分類,Shell的語法怎麼那麼隨便…

小姐姐結合自己初學Shell傻傻分不清的問題點,主要從Shell的種類,變量的分類,條件測試的表達三個部分來介紹。

Shell的種類

shell程序有shbash,zsh等分類,我從網上找到一張圖可以看出shell程序的發展史。

對於這些Shell程序,其語法或多或少有一些差異,不過我們通常使用的都是bash。

  • Shell程序信息

在Linux系統我們可以通過一些命令查看或修改當前Shell程序信息。

一般發行版的Linux系統中,默認的shell程序就是bash。我們在寫shell腳本時,通常也會在腳本文件頭部指定bash作為腳本解釋器。

這裏多說一句,zsh有時也作為猿媛們的默認shell。zsh語法大多是和bash匹配的,也不會影響shell腳本的執行(因為腳本頭部指定bash就還是bash:),也不會影響像小姐姐這樣的渣渣使用。用它是因為它有神奇的開源框架 Oh My God.. 哦不,是 Oh My Zsh !!!

後面的內容我們還是以Linux系統中的bash為例來介紹:)

變量的分類

Shell是一門動態類型語言和弱類型語言,我們可以把變量理解為KV對,key是變量名,value是變量值。變量大體可以分為環境變量系統變量用戶定義的變量三類。

  • 環境變量

比如我們經常配置的JAVA_HOME就屬於環境變量,這些變量是所有Shell程序運行時都可以使用的變量。關於環境變量的操作命令舉例如下:

使用export命令定義的環境變量只在當前運行的shell進程中有效,結束進程就沒了。所以我們要將配置變量定義在令小姐姐懵逼的一系列配置文件中,持久化下來。

說起配置文件,又不得不先提下shell程序和用戶的Interactive和Login模式:)

  • Interactive & Non-Interactive`

Interactive通常是指讀入寫出數據都是從用戶的terminal,也就是我們平時用命令行打開終端就是Interactive模式,而執行一個shell腳本就是Non-interactive模式。怎麼檢驗當前shell運行的模式是不是Interactive呢?小姐姐從GNU網站拷貝了一段裝X腳本:

case "$-" in
*i*)	echo This shell is interactive ;;
*)	echo This shell is not interactive ;;
esac

結果如上所述。

  • Login & Non-Login`

Login模式指的是用戶成功登錄后開啟的shell進程,這時候會讀取/etc/passwd下用戶所屬的shell去執行。

Non-login模式指的是非登錄用戶狀態下開啟的shell進程,我們可以通過echo $0區分。

扯這麼多是因為配置文件的加載順序和shell進程是否運行在Interactive和Login模式有關係:D

這是阿姨從網上粘的圖。bash支持的配置文件有/etc/profile,~/.bashrc等。

當調用一個Interactive&Login模式的shell進程時,配置文件的加載順序為:

/etc/profile —>( ~/.bash_profile, ~/.bash_login, ~/.profile)其中之一 —>~/.bash_loginout(退出shell時調用)

當調用一個Interactive&non-Login模式的shell進程時,配置文件的加載順序為:

/etc/bash.bashrc —> ~/.bashrc
當調用一個non-nteractive模式的shell進程時,通常是執行腳本時,此時配置項是從環境變量中讀取和執行的,也就是env命令輸出的配置項。

另外,在開啟一個shell進程中,有一些參數的值也會影響到配置文件的加載。如–rcfile ,–norc等。這些參數的含義值可以使用 man bash進一步了解。只要保持默認值,其實就是我們上面介紹的配置文件加載順序。

還有,在發行版的Linux系統中,Interactive&Login模式下的~/.bash_profile,
~/.bash_login, ~/.profile並不一定是三選一,看一下這三個腳本的內容會發現他們會繼續調用下一個它想調用的配置文件,這樣就可以避免配置項可能需要在不同的配置文件多次配置的弊端了。如centos7.2 中 ~/.bash_profile文件中實際調用了 ~/.bashrc文件。

按照模式和參數設置啟動的shell程序的配置文件加載流程圖如下:

好了,到目前我們總算把環境變量中配置文件的加載順序理清了。下面列舉一些常用的Shell環境變量吧。

  • 系統變量
    Shell中系統變量主要在對參數判斷和命令返回值判斷時使用,包括腳本和函數的參數和返回值判斷。沒啥可說的,主要難記且腳本中經常出現:

  • 用戶自定義的變量
    是指我們在使用命令或腳本時定義的變量,因為shell是弱類型語言且語法XX,這裏主要談談初學時的幾個坑爹點:

  • “=” 左右兩邊不能有空格

    你懂我說的意思了吧。。

  • 引用
    所謂引用,指的是將字符串用引用符號包括起來,以防止其中的特殊符號被Shell解釋為其他涵義。

常用的引用符號如下:

  • $

前面我們其實一直在用 “ $變量名 ” 來表示某個 變量名的值,這其實也正是 $ 的作用。

  • shell中變量名的大小寫是敏感的

好了就這麼多吧。

條件測試的表達

shell腳本中除了變量,還經常出現的語法就是條件測試的判斷。不會寫腳本的開發小姐姐不是好運維,我們來一起侃侃吧。

  • 基本語法

在Shell程序中,當指定的條件為真時,整個條件測試的返回值為 0;反之,如果指定的條件為假時,整個條件測試的返回值為 非0。

條件測試表達式的書寫有test expression[ expression ]兩種形式,注意後者的空格一定不能省!!

腳本中經常出現的有字符串測試、整數測試、文件測試、邏輯操作符測試。我們一起看下:)

  • 字符串測試

注意:這裏運算符 左右兩邊又一定要有空格了(下同),這樣shell才能將之當成命令執行。

  • 整數測試

  • 文件測試

邏輯操作符測試

收藏在看轉發起來,小姐姐就算你條件測試過關了>_<

感謝大佬這麼優秀還關注我~

參考資料:
[1].《Shell從入門到精通》
[2].https://www.edureka.co/blog/types-of-shells-in-linux/
[3].http://www.penguintutor.com/linux/basic-shell-reference
[4].https://apple.stackexchange.com/questions/361870/what-are-the-practical-differences-between-bash-and-zsh
[5].https://sunlightmedia.org/bash-vs-zsh/
[6].https://unix.stackexchange.com/questions/439042/debian-read-order-of-bash-session-configuration-files-inconsistent
[7].https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html
[8].http://howtolamp.com/articles/difference-between-login-and-non-login-shell/
[9].https://shreevatsa.wordpress.com/2008/03/30/zshbash-startup-files-loading-order-bashrc-zshrc-etc/

Shell中傻傻分不清楚的TOP3
同步滾動:

近來小姐姐又犯憨憨錯誤,問組內小夥伴export命令不會持久化環境變量嗎?反正我是問出口了。。然後小夥伴就甩給了我一個《The Linux Command Line》PDF鏈接。感謝老大不殺之恩~

Shell是命令解釋器,它會接受用戶輸入的各種命令,並傳遞給操作系統執行。它的作用類似於Windows系統的命令行。在UNIX或Linux系統中,Shell即是用戶交互的界面,也是控制系統的腳本語言。當然現在用戶也可以選擇圖形化界面做一些和操作系統的交互。層次示意圖如下:

對於初學者來說,可能搞不清楚Shell怎麼會有那麼多分類,Shell的語法怎麼那麼隨便…

小姐姐結合自己初學Shell傻傻分不清的問題點,主要從Shell的種類,變量的分類,條件測試的表達三個部分來介紹。

Shell的種類
shell程序有sh,bash,zsh等分類,我從網上找到一張圖可以看出shell程序的發展史。

對於這些Shell程序,其語法或多或少有一些差異,不過我們通常使用的都是bash。
Shell程序信息
在Linux系統我們可以通過一些命令查看或修改當前Shell程序信息。

一般發行版的Linux系統中,默認的shell程序就是bash。我們在寫shell腳本時,通常也會在腳本文件頭部指定bash作為腳本解釋器。

這裏多說一句,zsh有時也作為猿媛們的默認shell。zsh語法大多是和bash匹配的,也不會影響shell腳本的執行(因為腳本頭部指定bash就還是bash:),也不會影響像小姐姐這樣的渣渣使用。用它是因為它有神奇的開源框架 Oh My God.. 哦不,是 Oh My Zsh !!!

後面的內容我們還是以Linux系統中的bash為例來介紹:)

變量的分類
Shell是一門動態類型語言和弱類型語言,我們可以把變量理解為KV對,key是變量名,value是變量值。變量大體可以分為環境變量,系統變量,用戶定義的變量三類。

環境變量
比如我們經常配置的JAVA_HOME就屬於環境變量,這些變量是所有Shell程序運行時都可以使用的變量。關於環境變量的操作命令舉例如下:

使用export命令定義的環境變量只在當前運行的shell進程中有效,結束進程就沒了。所以我們要將配置變量定義在令小姐姐懵逼的一系列配置文件中,持久化下來。

說起配置文件,又不得不先提下shell程序和用戶的Interactive和Login模式:)

Interactive & Non-Interactive`
Interactive通常是指讀入寫出數據都是從用戶的terminal,也就是我們平時用命令行打開終端就是Interactive模式,而執行一個shell腳本就是Non-interactive模式。怎麼檢驗當前shell運行的模式是不是Interactive呢?小姐姐從GNU網站拷貝了一段裝X腳本:

case “$-” in
i) echo This shell is interactive ;;
*) echo This shell is not interactive ;;
esac
結果如上所述。

Login & Non-Login`
Login模式指的是用戶成功登錄后開啟的shell進程,這時候會讀取/etc/passwd下用戶所屬的shell去執行。

Non-login模式指的是非登錄用戶狀態下開啟的shell進程,我們可以通過echo $0區分。

扯這麼多是因為配置文件的加載順序和shell進程是否運行在Interactive和Login模式有關係:D

這是阿姨從網上粘的圖。bash支持的配置文件有/etc/profile,~/.bashrc等。

當調用一個Interactive&Login模式的shell進程時,配置文件的加載順序為:

/etc/profile —>( ~/.bash_profile, ~/.bash_login, ~/.profile)其中之一 —>~/.bash_loginout(退出shell時調用)

當調用一個Interactive&non-Login模式的shell進程時,配置文件的加載順序為:

/etc/bash.bashrc —>~/.bashrc
當調用一個non-nteractive模式的shell進程時,通常是執行腳本時,此時配置項是從環境變量中讀取和執行的,也就是env命令輸出的配置項。

另外,在開啟一個shell進程中,有一些參數的值也會影響到配置文件的加載。如–rcfile ,–norc等。這些參數的含義值可以使用man bash進一步了解。只要保持默認值,其實就是我們上面介紹的配置文件加載順序。

還有,在發行版的Linux系統中,Interactive&Login模式下的~/.bash_profile,
~/.bash_login, ~/.profile並不一定是三選一,看一下這三個腳本的內容會發現他們會繼續調用下一個它想調用的配置文件,這樣就可以避免配置項可能需要在不同的配置文件多次配置的弊端了。如centos7.2 中 ~/.bash_profile文件中實際調用了 ~/.bashrc文件。

按照模式和參數設置啟動的shell程序的配置文件加載流程圖如下:

好了,到目前我們總算把環境變量中配置文件的加載順序理清了。下面列舉一些常用的Shell環境變量吧。

系統變量
Shell中系統變量主要在對參數判斷和命令返回值判斷時使用,包括腳本和函數的參數和返回值判斷。沒啥可說的,主要難記且腳本中經常出現:

用戶自定義的變量
是指我們在使用命令或腳本時定義的變量,因為shell是弱類型語言且語法XX,這裏主要談談初學時的幾個坑爹點:

“=” 左右兩邊不能有空格

你懂我說的意思了吧。。

引用
所謂引用,指的是將字符串用引用符號包括起來,以防止其中的特殊符號被Shell解釋為其他涵義。

常用的引用符號如下:

$
前面我們其實一直在用 “ 變量名 ” 來表示某個 變量名的值,這其實也正是 的作用。

shell中變量名的大小寫是敏感的
好了就這麼多吧。

條件測試的表達
shell腳本中除了變量,還經常出現的語法就是條件測試的判斷。不會寫腳本的開發小姐姐不是好運維,我們來一起侃侃吧。

基本語法
在Shell程序中,當指定的條件為真時,整個條件測試的返回值為 0;反之,如果指定的條件為假時,整個條件測試的返回值為 非0。

條件測試表達式的書寫有test expression 和 [ expression ]兩種形式,注意後者的空格一定不能省!!

腳本中經常出現的有字符串測試、整數測試、文件測試、邏輯操作符測試。我們一起看下:)

字符串測試

注意:這裏運算符 左右兩邊又一定要有空格了(下同),這樣shell才能將之當成命令執行。

整數測試

文件測試

邏輯操作符測試

收藏在看轉發起來,小姐姐就算你條件測試過關了>_<

感謝大佬這麼優秀還關注我~

參考資料:
[1].《Shell從入門到精通》
[2].https://www.edureka.co/blog/types-of-shells-in-linux/
[3].http://www.penguintutor.com/linux/basic-shell-reference
[4].https://apple.stackexchange.com/questions/361870/what-are-the-practical-differences-between-bash-and-zsh
[5].https://sunlightmedia.org/bash-vs-zsh/
[6].https://unix.stackexchange.com/questions/439042/debian-read-order-of-bash-session-configuration-files-inconsistent
[7].https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html
[8].http://howtolamp.com/articles/difference-between-login-and-non-login-shell/
[9].https://shreevatsa.wordpress.com/2008/03/30/zshbash-startup-files-loading-order-bashrc-zshrc-etc/

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

Hive的基本介紹以及常用函數

一、Hive的簡介:

  Hive是基於Hadoop的一個數據倉庫工具,可以將結構化的數據文件映射為一張表,並提供類SQL查詢功能。

優點:

1) 操作接口採用類SQL語法,提供快速開發的能力(簡單、容易上手)。

2) 避免了去寫MapReduce,減少開發人員的學習成本。

3) Hive的執行延遲比較高,因此Hive常用於數據分析,對實時性要求不高的場合。

4) Hive優勢在於處理大數據,對於處理小數據沒有優勢,因為Hive的執行延遲比較高。

5) Hive支持用戶自定義函數,用戶可以根據自己的需求來實現自己的函數。

缺點:

1)Hive的HQL表達能力有限
2)Hive的效率比較低

 

二、Hive的架構圖

          

三、Hive的數據類型

基本數據類型:
  Hive數據類型    Java數據類型 
  TINYINT       byte    
  SMALINT       short    
  INT            int    
  BIGINT       long    
  BOOLEAN       boolean  
  FLOAT        float    
  DOUBLE        double    
  STRING         string   
  TIMESTAMP      時間類型    
  BINARY         字節數組    
集合
  STRUCT  和c語言中的struct類似,都可以通過“點”符號訪問元素內容。例如,如果某個列的數據類型是STRUCT{first STRING, last STRING},那麼第1個元素可以通過字段.first來引用。    
      struct() 例如struct<street:string, city:string>   MAP MAP是一組鍵-值對元組集合,使用數組表示法可以訪問數據。例如,如果某個列的數據類型是MAP,其中鍵->值對是’first’->’John’和’last’->’Doe’,那麼可以通過字段名[‘last’]獲取最後一個元素
      map() 例如map
<string, int>   ARRAY 數組是一組具有相同類型和名稱的變量的集合。這些變量稱為數組的元素,每個數組元素都有一個編號,編號從零開始。例如,數組值為[‘John’, ‘Doe’],那麼第2個元素可以通過數組名[1]進行引用。
      Array() 例如array
<string>

 

四、管理表和外部表

  默認創建的表為管理表,有時也被稱為內部表。當我們刪除一個管理表時,Hive也會刪除這個表中數據。管理表不適合和其他工具共享數據。  

  添加關鍵字external創建的表是外部表,所以Hive並非認為其完全擁有這份數據。刪除該表並不會刪除掉這份數據,不過描述表的元數據信息會被刪除掉。

#查詢表的類型
desc formatted stu;
#修改內部表stu為外部表
alter table stuset tblproperties('EXTERNAL'='TRUE');
#修改外部表stu為內部表
alter table stuset tblproperties('EXTERNAL'='FALSE');
注意:('EXTERNAL'='TRUE')和('EXTERNAL'='FALSE')為固定寫法,區分大小寫!

清空表數據:

 管理表:truncate table stu;

 外部表:alter table stuset tblproperties(‘EXTERNAL’=’FALSE’); 先改成內部表再執行清空:truncate table stu;

  場景:每天將收集到的網站日誌定期流入HDFS文本文件。在外部表(原始日誌表)的基礎上做大量的統計分析,用到的中間表、結果表使用內部表存儲,數據通過SELECT+INSERT進入內部表。

 

五、查詢常用函數

1,空字段賦值

  NVL( value,default_value)

2,when

  case 字段 when 值 then 替換值 else 替換值 end 同 if(boolean,值,另外值)

3,行轉列

CONCAT(string A/col, string B/col…):返回輸入字符串連接后的結果,支持任意個輸入字符串; 
CONCAT_WS(separator, str1, str2,...):它是一個特殊形式的 CONCAT()。第一個參數剩餘參數間的分隔符。分隔符可以是與剩餘參數一樣的字符串。如果分隔符是 NULL,返回值也將為 NULL。這個函數會跳過分隔符參數后的任何 NULL 和空字符串。分隔符將被加到被連接的字符串之間;
COLLECT_SET(col):函數只接受基本數據類型,它的主要作用是將某字段的值進行去重匯總,產生array類型字段。

4,列轉行

EXPLODE(col):將hive一列中複雜的array或者map結構拆分成多行。
LATERAL VIEW
用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias
解釋:用於和split, explode等UDTF一起使用,它能夠將一列數據拆成多行數據,在此基礎上可以對拆分后的數據進行聚合。

5,窗口函數

OVER():指定分析函數工作的數據窗口大小,這個數據窗口大小可能會隨着行的變而變化。
CURRENT ROW:當前行
n PRECEDING:往前n行數據
n FOLLOWING:往後n行數據
UNBOUNDED:起點,UNBOUNDED PRECEDING 表示從前面的起點, UNBOUNDED FOLLOWING表示到後面的終點
LAG(col,n,default_val):往前第n行數據
LEAD(col,n, default_val):往後第n行數據
NTILE(n):把有序分區中的行分發到指定數據的組中,各個組有編號,編號從1開始,對於每一行,NTILE返回此行所屬的組的編號。注意:n必須為int類型。

6,Rank

RANK() 排序相同時會重複,總數不會變
DENSE_RANK() 排序相同時會重複,總數會減少
ROW_NUMBER() 會根據順序計算

7,時間

#均只適合yyyy-MM-dd HH:mm:ss格式的時間,當然時分秒可以省略
date_format:格式化時間select date_format('2020-06-04','yyyy-MM');2020-06
date_add:   時間跟天數相加select date_add('2020-06-04',5);2020-06-09
date_sub:   時間跟天數相減select date_sub('2020-06-04',5);2020-05-30
datediff:   兩個時間相減select datediff('2020-06-04','2020-05-10');25
year:       獲取年select year('2020-06-04');2020
month:      獲取月份select month('2020-06-04');04
day:        獲取日select day('2020-06-04');04

格式轉換
regexp_replace: 字段,原格式,替換的格式;select regexp_replace('2020/05/04','/','-');2020-05-04

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

波札那數百頭大象離奇死亡 查明是藍綠藻毒素作祟

摘錄自2020年9月22日中央社報導

非洲國家波札那(Botswana)官員今(21日)說,波札那今年數百頭大象死亡讓環境保護人士感到困惑和震驚,最後發現是由水中細菌藍綠藻(cyanobacteria)產生的毒素引起。

路透社報導,波札那野生動物和國家公園部副主任陶洛(Cyril Taolo)在記者會說,死亡大象的數量已從7月的281頭上升到330頭。

野生動物和國家公園部首席獸醫魯賓(Mmadi Reuben)在記者會表示:「我們的最新檢測發現,藍綠藻神經毒素是造成(大象)死亡的原因。這些是在水中發現的細菌。」

他又說:「(大象)死亡是由藍綠藻造成中毒引起,藍綠藻在窪地或水坑中生長。」另外,大象的死亡「在2020年6月底已經停止,正是窪地枯乾的時候」。

由於盜獵,非洲大象整體數量正在下降,但是波札那大象數量已經增加到約13萬頭,是非洲大陸近1/3大象的家。

生物多樣性
國際新聞
波札那
大象
動物死亡
藍綠藻

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準