日本政府的太陽能紓困方針 幫助日企回流國內_貨運

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

文:宋瑞文(加州能源特約撰述)

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

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

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

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地址來測試

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

※回頭車貨運收費標準

「從零單排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…(歷史文章查閱非常方便)

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

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

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

時常看到日系車車尾貼着壁虎,難道在庇護不要被…。(大家自行腦補吧)。第三、放鞭炮放鞭炮寓意着有好事到來,在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/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

※回頭車貨運收費標準

逾70頭鯨魚擱淺澳洲海灣沙洲 海洋專家設法救援

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

澳洲官員今(21日)表示,有超過70頭鯨魚擱淺在澳洲島嶼塔斯馬尼亞的海灣,海洋專家現正動員,評估救援的可能性。

那些鯨魚擱淺在麥夸利港(Macquarie Harbour),據信受困在沙洲上。麥夸利港位於塔斯馬尼亞(Tasmania)地形崎嶇且人煙稀少的西岸地區。

塔斯馬尼亞掌管環境的部門表示,警方已趕赴事發現場,海洋專家也正在評估狀況。

擱淺的鯨魚據信為領航鯨,但實際物種尚未獲得確認。塔斯馬尼亞相對常發生大批鯨魚擱淺事件,然而擱淺鯨魚為數眾多,使救援情況不甚樂觀。

海洋
國際新聞
澳洲
沙洲
鯨魚擱淺

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

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

空巴發表3款氫動力零碳排概念機 目標2035年問世

摘錄自2020年9月21日中央社巴黎報導

航空業造成的碳排放占全球總量的3%,法國等歐洲國家投資數以10億計歐元發展環保氫,主要希望應用在高度污染的運輸業。歐洲飛機製造商空中巴士(Airbus)21日發表3款以氫氣為動力的零排放概念機,宣示要在2035年前,讓全球第一架氫動力商用客機投入使用。

路透社與美國有線電視新聞網(CNN)報導,空巴零排放概念機計畫ZEROe共包括3款飛機,其中兩款外型較為傳統,包括一款使用渦輪扇噴射發動機的客機,可搭載120至200名乘客,航程超過2000浬(3700公里),另一款則使用渦輪螺旋槳發動機,最多可搭載100名乘客,航程可達1000浬。第3款概念機外型較為前衛,採「翼身融合」(blended wing body)設計,將機身和機翼融為一體,類似2月間發表的機型。

空巴執行長佛喜(Guillaume Faury)表示,脫碳氫燃料的研發是空巴研發的優先軸心。空巴早已在該公司研發的衛星和阿利安火箭(Ariane rocket)上使用了氫氣科技。生產技術仍需大約5年才能成熟,此外,還需再多花2年時間,才能讓供應商和工業場所做好準備,因此可能會在2028年執行這項計畫。

氣候變遷
能源轉型
國際新聞
碳排放量
氫燃料
航空

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

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