附022.Kubernetes_v1.18.3高可用部署架構一

kubeadm介紹

kubeadm概述

參考附003.Kubeadm部署Kubernetes。

kubeadm功能

參考附003.Kubeadm部署Kubernetes。

本方案描述

  • 本方案採用kubeadm部署Kubernetes 1.18.3版本;
  • etcd採用混部方式;
  • Keepalived:實現VIP高可用;
  • Nginx:以Pod形式運行與Kubernetes之上,即in Kubernetes模式,提供反向代理至3個master 6443端口;
  • 其他主要部署組件包括:
    • Metrics:度量;
    • Dashboard:Kubernetes 圖形UI界面;
    • Helm:Kubernetes Helm包管理工具;
    • Ingress:Kubernetes 服務暴露;
    • Longhorn:Kubernetes 動態存儲組件。

部署規劃

節點規劃

節點主機名 IP 類型 運行服務
master01 172.24.8.71 Kubernetes master節點 docker、etcd、kube-apiserver、kube-scheduler、kube-controller-manager、kubectl、kubelet、metrics、calico
master02 172.24.8.72 Kubernetes master節點 docker、etcd、kube-apiserver、kube-scheduler、kube-controller-manager、kubectl、kubelet、metrics、calico
master03 172.24.8.73 Kubernetes master節點 docker、etcd、kube-apiserver、kube-scheduler、kube-controller-manager、kubectl、kubelet、metrics、calico
worker01 172.24.8.74 Kubernetes worker節點 docker、kubelet、proxy、calico
worker02 172.24.8.75 Kubernetes worker節點 docker、kubelet、proxy、calico
worker03 172.24.8.76 Kubernetes worker節點 docker、kubelet、proxy、calico

Kubernetes的高可用主要指的是控制平面的高可用,即指多套Master節點組件和Etcd組件,工作節點通過負載均衡連接到各Master。

Kubernetes高可用架構中etcd與Master節點組件混布方式特點:

  • Etcd混布方式
  • 所需機器資源少
  • 部署簡單,利於管理
  • 容易進行橫向擴展
  • 風險大,一台宿主機掛了,master和etcd就都少了一套,集群冗餘度受到的影響比較大。

提示:本實驗使用Keepalived+Nginx架構實現Kubernetes的高可用。

初始準備

[root@master01 ~]# hostnamectl set-hostname master01 #其他節點依次修改

[root@master01 ~]# cat >> /etc/hosts << EOF
172.24.8.71 master01··
172.24.8.72 master02
172.24.8.73 master03
172.24.8.74 worker01
172.24.8.75 worker02
172.24.8.76 worker03
EOF

[root@master01 ~]# vi k8sinit.sh

#!/bin/sh
#****************************************************************#
# ScriptName: k8sinit.sh
# Author: xhy
# Create Date: 2020-05-30 16:30
# Modify Author: xhy
# Modify Date: 2020-05-30 16:30
# Version: 
#***************************************************************#
# Initialize the machine. This needs to be executed on every machine.

# Add docker user
useradd -m docker

# Disable the SELinux.
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

# Turn off and disable the firewalld.
systemctl stop firewalld
systemctl disable firewalld

# Modify related kernel parameters & Disable the swap.
cat > /etc/sysctl.d/k8s.conf << EOF
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.tcp_tw_recycle = 0
vm.swappiness = 0
vm.overcommit_memory = 1
vm.panic_on_oom = 0
net.ipv6.conf.all.disable_ipv6 = 1
EOF
sysctl -p /etc/sysctl.d/k8s.conf >&/dev/null
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
modprobe br_netfilter

# Add ipvs modules
cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
modprobe -- nf_conntrack
EOF

chmod 755 /etc/sysconfig/modules/ipvs.modules
bash /etc/sysconfig/modules/ipvs.modules

# Install rpm
yum install -y conntrack ntpdate ntp ipvsadm ipset jq iptables curl sysstat libseccomp wget

# Install Docker Compose
sudo curl -L "http://down.linuxsb.com:8888/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# Update kernel
rpm --import http://down.linuxsb.com:8888/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://down.linuxsb.com:8888/elrepo-release-7.0-4.el7.elrepo.noarch.rpm
yum --disablerepo="*" --enablerepo="elrepo-kernel" install -y kernel-ml
sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT=0/' /etc/default/grub
grub2-mkconfig -o /boot/grub2/grub.cfg
yum update -y

# Reboot the machine.
# reboot

提示:對於某些特性,可能需要升級內核,內核升級操作見《018.Linux升級內核》。4.19版及以上內核nf_conntrack_ipv4已經改為nf_conntrack。

互信配置

為了更方便遠程分發文件和執行命令,本實驗配置master節點到其它節點的 ssh 信任關係。

[root@master01 ~]# ssh-keygen -f ~/.ssh/id_rsa -N ''
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@master01
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@master02
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@master03
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@worker01
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@worker02
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@worker03

提示:此操作僅需要在master節點操作。

其他準備

[root@master01 ~]# vi environment.sh

#!/bin/sh
#****************************************************************#
# ScriptName: environment.sh
# Author: xhy
# Create Date: 2020-05-30 16:30
# Modify Author: xhy
# Modify Date: 2020-05-30 16:30
# Version: 
#***************************************************************#
# 集群 MASTER 機器 IP 數組
export MASTER_IPS=(172.24.8.71 172.24.8.72 172.24.8.73)

# 集群 MASTER IP 對應的主機名數組
export MASTER_NAMES=(master01 master02 master03)

# 集群 NODE 機器 IP 數組
export NODE_IPS=(172.24.8.74 172.24.8.75 172.24.8.76)

# 集群 NODE IP 對應的主機名數組
export NODE_NAMES=(worker01 worker02 worker03)

# 集群所有機器 IP 數組
export ALL_IPS=(172.24.8.71 172.24.8.72 172.24.8.73 172.24.8.74 172.24.8.75 172.24.8.76)

# 集群所有IP 對應的主機名數組
export ALL_NAMES=(master01 master02 master03 worker01 worker02 worker03)
[root@master01 ~]# source environment.sh
[root@master01 ~]# chmod +x *.sh
[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
  do
    echo ">>> ${all_ip}"
    scp -rp /etc/hosts root@${all_ip}:/etc/hosts
    scp -rp k8sinit.sh root@${all_ip}:/root/
    ssh root@${all_ip} "bash /root/k8sinit.sh"
  done

集群部署

Docker安裝

[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
  do
    echo ">>> ${all_ip}"
    ssh root@${all_ip} "yum -y install yum-utils device-mapper-persistent-data lvm2"
    ssh root@${all_ip} "yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo"
    ssh root@${all_ip} "yum -y install docker-ce"
    ssh root@${all_ip} "mkdir /etc/docker"
    ssh root@${all_ip} "cat > /etc/docker/daemon.json <<EOF
{
  \"registry-mirrors\": [\"https://dbzucv6w.mirror.aliyuncs.com\"],
  \"exec-opts\": [\"native.cgroupdriver=systemd\"],
  \"log-driver\": \"json-file\",
  \"log-opts\": {
    \"max-size\": \"100m\"
  },
  \"storage-driver\": \"overlay2\",
  \"storage-opts\": [
    \"overlay2.override_kernel_check=true\"
  ]
}
EOF"
    ssh root@${all_ip} "systemctl restart docker"
    ssh root@${all_ip} "systemctl enable docker"
    ssh root@${all_ip} "systemctl status docker"
    ssh root@${all_ip} "iptables -nvL"
  done

提示:如上僅需Master01節點操作,從而實現所有節點自動化安裝。

相關組件包

需要在每台機器上都安裝以下的軟件包:

  • kubeadm: 用來初始化集群的指令;
  • kubelet: 在集群中的每個節點上用來啟動 pod 和 container 等;
  • kubectl: 用來與集群通信的命令行工具。

kubeadm不能安裝或管理 kubelet 或 kubectl ,所以得保證他們滿足通過 kubeadm 安裝的 Kubernetes控制層對版本的要求。如果版本沒有滿足要求,可能導致一些意外錯誤或問題。
具體相關組件安裝見;附001.kubectl介紹及使用書

提示:Kubernetes 1.18版本所有兼容相應組件的版本參考:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.18.md。

正式安裝

[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
  do
    echo ">>> ${all_ip}"
    ssh root@${all_ip} "cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF"
    ssh root@${all_ip} "yum install -y kubeadm-1.18.3-0.x86_64 kubelet-1.18.3-0.x86_64 kubectl-1.18.3-0.x86_64 --disableexcludes=kubernetes"
    ssh root@${all_ip} "systemctl enable kubelet"
done

[root@master01 ~]# yum search -y kubelet --showduplicates #查看相應版本

提示:如上僅需Master01節點操作,從而實現所有節點自動化安裝,同時此時不需要啟動kubelet,初始化的過程中會自動啟動的,如果此時啟動了會出現報錯,忽略即可。

說明:同時安裝了cri-tools, kubernetes-cni, socat三個依賴: socat:kubelet的依賴; cri-tools:即CRI(Container Runtime Interface)容器運行時接口的命令行工具。

部署高可用組件I

Keepalived安裝

[root@master01 ~]# for master_ip in ${MASTER_IPS[@]}
  do
    echo ">>> ${master_ip}"
    ssh root@${master_ip} "yum -y install gcc gcc-c++ make libnl libnl-devel libnfnetlink-devel openssl-devel"
    ssh root@${master_ip} "wget http://down.linuxsb.com:8888/software/keepalived-2.0.20.tar.gz"
    ssh root@${master_ip} "tar -zxvf keepalived-2.0.20.tar.gz"
    ssh root@${master_ip} "cd keepalived-2.0.20/ && ./configure --sysconf=/etc --prefix=/usr/local/keepalived && make && make install"
    ssh root@${master_ip} "systemctl enable keepalived && systemctl start keepalived"
  done

提示:如上僅需Master01節點操作,從而實現所有節點自動化安裝。

創建配置文件

[root@master01 ~]# wget http://down.linuxsb.com:8888/ngkek8s.sh		#拉取自動部署腳本
[root@master01 ~]# chmod u+x ngkek8s.sh
[root@master01 ~]# vi ngkek8s.sh
#!/bin/sh
#****************************************************************#
# ScriptName: k8s_ha.sh
# Author: xhy
# Create Date: 2020-05-13 16:32
# Modify Author: xhy
# Modify Date: 2020-06-12 12:53
# Version: v2
#***************************************************************#

#######################################
# set variables below to create the config files, all files will create at ./config directory
#######################################

# master keepalived virtual ip address
export K8SHA_VIP=172.24.8.100

# master01 ip address
export K8SHA_IP1=172.24.8.71

# master02 ip address
export K8SHA_IP2=172.24.8.72

# master03 ip address
export K8SHA_IP3=172.24.8.73

# master01 hostname
export K8SHA_HOST1=master01

# master02 hostname
export K8SHA_HOST2=master02

# master03 hostname
export K8SHA_HOST3=master03

# master01 network interface name
export K8SHA_NETINF1=eth0

# master02 network interface name
export K8SHA_NETINF2=eth0

# master03 network interface name
export K8SHA_NETINF3=eth0

# keepalived auth_pass config
export K8SHA_KEEPALIVED_AUTH=412f7dc3bfed32194d1600c483e10ad1d

# kubernetes CIDR pod subnet
export K8SHA_PODCIDR=10.10.0.0

# kubernetes CIDR svc subnet
export K8SHA_SVCCIDR=10.20.0.0

[root@master01 ~]# ./ngkek8s.sh

解釋:如上僅需Master01節點操作。執行ngkek8s.sh腳本后,會自動生成以下配置文件:

  • kubeadm-config.yaml:kubeadm初始化配置文件,位於當前目錄
  • keepalived:keepalived配置文件,位於各個master節點的/etc/keepalived目錄
  • nginx-lb:nginx-lb負載均衡配置文件,位於各個master節點的/etc/kubernetes/nginx-lb/目錄
  • calico.yaml:calico網絡組件部署文件,位於config/calico/目錄
[root@master01 ~]# cat kubeadm-config.yaml		#檢查集群初始化配置
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
networking:
  serviceSubnet: "10.20.0.0/16"			     	#設置svc網段
  podSubnet: "10.10.0.0/16"			        #設置Pod網段
  dnsDomain: "cluster.local"
kubernetesVersion: "v1.18.3"			    	#設置安裝版本
controlPlaneEndpoint: "172.24.8.100:16443"		#設置相關API VIP地址
apiServer:
  certSANs:
  - master01
  - master02
  - master03
  - 127.0.0.1
  - 172.24.8.71
  - 172.24.8.72
  - 172.24.8.73
  - 172.24.8.100
  timeoutForControlPlane: 4m0s
certificatesDir: "/etc/kubernetes/pki"
imageRepository: "k8s.gcr.io"
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
featureGates:
  SupportIPVSProxyMode: true
mode: ipvs

提示:如上僅需Master01節點操作,更多config文件參考:https://godoc.org/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2。
此kubeadm部署初始化配置更多參考:https://pkg.go.dev/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2?tab=doc。

啟動Keepalived

[root@master01 ~]# cat /etc/keepalived/check_apiserver.sh	#確認Keepalived配置
[root@master01 ~]# for master_ip in ${MASTER_IPS[@]}
  do
    echo ">>> ${master_ip}"
    ssh root@${master_ip} "systemctl start keepalived.service && systemctl enable keepalived.service"
    ssh root@${master_ip} "systemctl status keepalived.service | grep Active"
  done
[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
  do
    echo ">>> ${all_ip}"
    ssh root@${all_ip} "ping -c1 172.24.8.100"
  done								#等待10s左右執行檢查

提示:如上僅需Master01節點操作,從而實現所有節點自動啟動服務。

啟動Nginx

執行ngkek8s.sh腳本后,nginx-lb的配置文件會自動複製到各個master的節點的/etc/kubernetes/nginx-lb目錄。

[root@master01 ~]# for master_ip in ${MASTER_IPS[@]}
  do
    echo ">>> ${master_ip}"
    ssh root@${master_ip} "cd /etc/kubernetes/nginx-lb/ && docker-compose up -d"
    ssh root@${master_ip} "docker-compose ps"
  done

提示:如上僅需Master01節點操作,從而實現所有節點自動啟動服務。

初始化集群-Master

拉取鏡像

[root@master01 ~]# kubeadm --kubernetes-version=v1.18.3 config images list #列出所需鏡像

[root@master01 ~]# cat config/downimage.sh			#確認版本,提前下載鏡像
#!/bin/sh
#****************************************************************#
# ScriptName: downimage.sh
# Author: xhy
# Create Date: 2020-05-29 19:55
# Modify Author: xhy
# Modify Date: 2020-05-30 16:07
# Version: v2
#***************************************************************#

KUBE_VERSION=v1.18.3
CALICO_VERSION=v3.14.1
CALICO_URL=calico
KUBE_PAUSE_VERSION=3.2
ETCD_VERSION=3.4.3-0
CORE_DNS_VERSION=1.6.7
GCR_URL=k8s.gcr.io
METRICS_SERVER_VERSION=v0.3.6
INGRESS_VERSION=0.32.0
CSI_PROVISIONER_VERSION=v1.4.0
CSI_NODE_DRIVER_VERSION=v1.2.0
CSI_ATTACHER_VERSION=v2.0.0
CSI_RESIZER_VERSION=v0.3.0 
ALIYUN_URL=registry.cn-hangzhou.aliyuncs.com/google_containers
UCLOUD_URL=uhub.service.ucloud.cn/uxhy
QUAY_URL=quay.io

kubeimages=(kube-proxy:${KUBE_VERSION}
kube-scheduler:${KUBE_VERSION}
kube-controller-manager:${KUBE_VERSION}
kube-apiserver:${KUBE_VERSION}
pause:${KUBE_PAUSE_VERSION}
etcd:${ETCD_VERSION}
coredns:${CORE_DNS_VERSION}
metrics-server-amd64:${METRICS_SERVER_VERSION}
)

for kubeimageName in ${kubeimages[@]} ; do
docker pull $UCLOUD_URL/$kubeimageName
docker tag $UCLOUD_URL/$kubeimageName $GCR_URL/$kubeimageName
docker rmi $UCLOUD_URL/$kubeimageName
done

calimages=(cni:${CALICO_VERSION}
pod2daemon-flexvol:${CALICO_VERSION}
node:${CALICO_VERSION}
kube-controllers:${CALICO_VERSION})

for calimageName in ${calimages[@]} ; do
docker pull $UCLOUD_URL/$calimageName
docker tag $UCLOUD_URL/$calimageName $CALICO_URL/$calimageName
docker rmi $UCLOUD_URL/$calimageName
done

ingressimages=(nginx-ingress-controller:${INGRESS_VERSION})

for ingressimageName in ${ingressimages[@]} ; do
docker pull $UCLOUD_URL/$ingressimageName
docker tag $UCLOUD_URL/$ingressimageName $QUAY_URL/kubernetes-ingress-controller/$ingressimageName
docker rmi $UCLOUD_URL/$ingressimageName
done

csiimages=(csi-provisioner:${CSI_PROVISIONER_VERSION}
csi-node-driver-registrar:${CSI_NODE_DRIVER_VERSION}
csi-attacher:${CSI_ATTACHER_VERSION}
csi-resizer:${CSI_RESIZER_VERSION}
)

for csiimageName in ${csiimages[@]} ; do
docker pull $UCLOUD_URL/$csiimageName
docker tag $UCLOUD_URL/$csiimageName $QUAY_URL/k8scsi/$csiimageName
docker rmi $UCLOUD_URL/$csiimageName
done
[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
  do
    echo ">>> ${all_ip}"
    scp -rp config/downimage.sh root@${all_ip}:/root/
    ssh root@${all_ip} "bash downimage.sh &"
  done

提示:如上僅需Master01節點操作,從而實現所有節點自動拉取鏡像。
[root@master01 ~]# docker images #確認驗證

Master上初始化

[root@master01 ~]# kubeadm init --config=kubeadm-config.yaml --upload-certs  

保留如下命令用於後續節點添加:
You can now join any number of the control-plane node running the following command on each as root:

  kubeadm join 172.24.8.100:16443 --token xb9wda.v0yf7tlsgo8mdrhk \
    --discovery-token-ca-cert-hash sha256:249884d81a23bd821e38d3345866a99e6d55e443b545825c3c448f30f8e52c3b \
    --control-plane --certificate-key e30428776a47ed2c7e18c9e2951d9e40e068c9ecec5a4858457f1475f1a2a39a

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 172.24.8.100:16443 --token xb9wda.v0yf7tlsgo8mdrhk \
    --discovery-token-ca-cert-hash sha256:249884d81a23bd821e38d3345866a99e6d55e443b545825c3c448f30f8e52c3b

注意:如上token具有默認24小時的有效期,token和hash值可通過如下方式獲取: kubeadm token list 如果 Token 過期以後,可以輸入以下命令,生成新的 Token:

kubeadm token create
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'***

[root@master01 ~]# mkdir -p $HOME/.kube [root@master01 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config [root@master01 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config

[root@master01 ~]# cat << EOF >> ~/.bashrc
export KUBECONFIG=$HOME/.kube/config
EOF							#設置KUBECONFIG環境變量
[root@master01 ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc
[root@master01 ~]# source ~/.bashrc

附加:初始化過程大致步驟如下:

  1. [kubelet-start] 生成kubelet的配置文件”/var/lib/kubelet/config.yaml”
  2. [certificates]生成相關的各種證書
  3. [kubeconfig]生成相關的kubeconfig文件
  4. [bootstraptoken]生成token記錄下來,後邊使用kubeadm join往集群中添加節點時會用到

提示:初始化僅需要在master01上執行,若初始化異常可通過kubeadm reset && rm -rf $HOME/.kube重置。

添加其他master節點

[root@master02 ~]# kubeadm join 172.24.8.100:16443 --token xb9wda.v0yf7tlsgo8mdrhk \
    --discovery-token-ca-cert-hash sha256:249884d81a23bd821e38d3345866a99e6d55e443b545825c3c448f30f8e52c3b \
    --control-plane --certificate-key e30428776a47ed2c7e18c9e2951d9e40e068c9ecec5a4858457f1475f1a2a39a
[root@master02 ~]# mkdir -p $HOME/.kube
[root@master02 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master02 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
 [root@master02 ~]# cat << EOF >> ~/.bashrc`
export KUBECONFIG=$HOME/.kube/config
EOF						               	#設置KUBECONFIG環境變量
[root@master02 ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc
[root@master02 ~]# source ~/.bashrc

提示:master03也如上執行添加至集群的controlplane。
提示:若添加異常可通過kubeadm reset && rm -rf $HOME/.kube重置。

安裝NIC插件

NIC插件介紹

  • Calico 是一個安全的 L3 網絡和網絡策略提供者。
  • Canal 結合 Flannel 和 Calico, 提供網絡和網絡策略。
  • Cilium 是一個 L3 網絡和網絡策略插件, 能夠透明的實施 HTTP/API/L7 策略。 同時支持路由(routing)和疊加/封裝( overlay/encapsulation)模式。
  • Contiv 為多種用例提供可配置網絡(使用 BGP 的原生 L3,使用 vxlan 的 overlay,經典 L2 和 Cisco-SDN/ACI)和豐富的策略框架。Contiv 項目完全開源。安裝工具同時提供基於和不基於 kubeadm 的安裝選項。
  • Flannel 是一個可以用於 Kubernetes 的 overlay 網絡提供者。 +Romana 是一個 pod 網絡的層 3 解決方案,並且支持 NetworkPolicy API。Kubeadm add-on 安裝細節可以在這裏找到。
  • Weave Net 提供了在網絡分組兩端參与工作的網絡和網絡策略,並且不需要額外的數據庫。
  • CNI-Genie 使 Kubernetes 無縫連接到一種 CNI 插件,例如:Flannel、Calico、Canal、Romana 或者 Weave。
    提示:本方案使用Calico插件。

設置標籤

[root@master01 ~]# kubectl taint nodes --all node-role.kubernetes.io/master- #允許master部署應用

提示:部署完內部應用后可使用kubectl taint node master01 node-role.kubernetes.io/master=””:NoSchedule重新設置Master為Master Only 狀態。

部署calico

[root@master01 ~]# cat config/calico/calico.yaml	#檢查配置
……
            - name: CALICO_IPV4POOL_CIDR
              value: "10.10.0.0/16"		        #檢查Pod網段
……
            - name: IP_AUTODETECTION_METHOD
              value: "interface=eth.*"		     	#檢查節點之間的網卡
# Auto-detect the BGP IP address.
            - name: IP
              value: "autodetect"
……
[root@master01 ~]# kubectl apply -f config/calico/calico.yaml
[root@master01 ~]# kubectl get pods --all-namespaces -o wide		#查看部署
[root@master01 ~]# kubectl get nodes

修改node端口範圍

[root@master01 ~]# vi /etc/kubernetes/manifests/kube-apiserver.yaml
……
    - --service-node-port-range=1-65535
……

部署高可用組件II

高可用說明

高可用kubernetes集群步驟三已完成配置,但是使用docker-compose方式啟動nginx-lb由於無法提供kubernetes集群的健康檢查和自動重啟功能,nginx-lb作為高可用kubernetes集群的核心組件建議也作為kubernetes集群中的一個pod來進行管理。

污點和標籤

[root@master01 ~]# kubectl taint node master01 node-`role.kubernetes.io/master="":NoSchedule
[root@master01 ~]# kubectl taint node master02 node-role.kubernetes.io/master="":NoSchedule
[root@master01 ~]# kubectl taint node master03 node-role.kubernetes.io/master="":NoSchedule
[root@master01 ~]# kubectl label nodes master01 node-role.kubernetes.io/master="true" --overwrite
[root@master01 ~]# kubectl label nodes master02 node-role.kubernetes.io/master="true" --overwrite
[root@master01 ~]# kubectl label nodes master02 node-role.kubernetes.io/master="true" --overwrite

容器化實現高可用

[root@master01 ~]# for master_ip in ${MASTER_IPS[@]}
  do
    echo ">>> ${master_ip}"
    ssh root@${master_ip} "systemctl stop kubelet"
    ssh root@${master_ip} "docker stop nginx-lb && docker rm nginx-lb"
    scp -rp /root/config/k8s-nginx-lb.yaml root@${master_ip}:/etc/kubernetes/manifests/
    ssh root@${master_ip} "systemctl restart kubelet docker"
  done

提示:如上僅需Master01節點操作,從而實現所有Master節點自動啟動服務。

[root@master01 ~]# kubectl -n kube-system get pods -o wide | grep -E 'NAME|nginx'

添加Worker節點

添加Worker節點

[root@master01 ~]# source environment.sh

[root@master01 ~]# for node_ip in ${NODE_IPS[@]}
  do
    echo ">>> ${node_ip}"
    ssh root@${node_ip} "kubeadm join 172.24.8.100:16443 --token xb9wda.v0yf7tlsgo8mdrhk \
    --discovery-token-ca-cert-hash sha256:249884d81a23bd821e38d3345866a99e6d55e443b545825c3c448f30f8e52c3b"
    ssh root@${node_ip} "systemctl enable kubelet.service"
  done

提示:如上僅需Master01節點操作,從而實現所有Worker節點添加至集群,若添加異常可通過如下方式重置:

[root@node01 ~]# kubeadm reset
[root@node01 ~]# ifconfig cni0 down
[root@node01 ~]# ip link delete cni0
[root@node01 ~]# ifconfig flannel.1 down
[root@node01 ~]# ip link delete flannel.1
[root@node01 ~]# rm -rf /var/lib/cni/

確認驗證

[root@master01 ~]# kubectl get nodes			         	#節點狀態
[root@master01 ~]# kubectl get cs			             	#組件狀態
[root@master01 ~]# kubectl get serviceaccount		     	        #服務賬戶
[root@master01 ~]# kubectl cluster-info			         	#集群信息
[root@master01 ~]# kubectl get pod -n kube-system -o wide	        #所有服務狀態

提示:更多Kubetcl使用參考:https://kubernetes.io/docs/reference/kubectl/kubectl/ https://kubernetes.io/docs/reference/kubectl/overview/
更多kubeadm使用參考:https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/

Metrics部署

Metrics

Kubernetes的早期版本依靠Heapster來實現完整的性能數據採集和監控功能,Kubernetes從1.8版本開始,性能數據開始以Metrics API的方式提供標準化接口,並且從1.10版本開始將Heapster替換為Metrics Server。在Kubernetes新的監控體系中,Metrics Server用於提供核心指標(Core Metrics),包括Node、Pod的CPU和內存使用指標。 對其他自定義指標(Custom Metrics)的監控則由Prometheus等組件來完成。

開啟聚合層

有關聚合層知識參考:https://blog.csdn.net/liukuan73/article/details/81352637 kubeadm方式部署默認已開啟。

獲取部署文件

[root@master01 ~]# mkdir metrics
[root@master01 ~]# cd metrics/
[root@master01 metrics]# wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.6/components.yaml
[root@master01 metrics]# vi components.yaml
……
apiVersion: apps/v1
kind: Deployment
……
spec:
  replicas: 3						        	#根據集群規模調整副本數
……
    spec:
      hostNetwork: true
……
      - name: metrics-server
        image: k8s.gcr.io/metrics-server-amd64:v0.3.6
        imagePullPolicy: IfNotPresent
        args:
          - --cert-dir=/tmp
          - --secure-port=4443
          - --kubelet-insecure-tls				        #追加此args
          - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP	#追加此args
……

正式部署

[root@master01 metrics]# kubectl apply -f components.yaml
[root@master01 metrics]# kubectl -n kube-system get pods -l k8s-app=metrics-server
NAME                              READY   STATUS    RESTARTS   AGE
metrics-server-7b97647899-ghnxw   1/1     Running   0          11s
metrics-server-7b97647899-nqwvq   1/1     Running   0          10s
metrics-server-7b97647899-zkmxs   1/1     Running   0          10s

查看資源監控

[root@k8smaster01 ~]# kubectl top nodes
[root@k8smaster01 ~]# kubectl top pods --all-namespaces

提示:Metrics Server提供的數據也可以供HPA控制器使用,以實現基於CPU使用率或內存使用值的Pod自動擴縮容功能。
部署參考:https://linux48.com/container/2019-11-13-metrics-server.html 有關metrics更多部署參考: https://kubernetes.io/docs/tasks/debug-application-cluster/resource-metrics-pipeline/
開啟開啟API Aggregation參考: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/
API Aggregation介紹參考: https://kubernetes.io/docs/tasks/access-kubernetes-api/configure-aggregation-layer/

Nginx ingress部署

參考附020.Nginx-ingress部署及使用,建議採用社區版。

Dashboard部署

設置標籤

[root@master01 ~]# kubectl label nodes master01 dashboard=yes
[root@master01 ~]# kubectl label nodes master02 dashboard=yes
[root@master01 ~]# kubectl label nodes master03 dashboard=yes

創建證書

本實驗已獲取免費一年的證書,免費證書獲取可參考:https://freessl.cn。

[root@master01 ~]# mkdir -p /root/dashboard/certs
[root@master01 ~]# cd /root/dashboard/certs
[root@master01 certs]# mv k8s.odocker.com tls.crt
[root@master01 certs]# mv k8s.odocker.com tls.crt
[root@master01 certs]# ll
total 8.0K
-rw-r--r-- 1 root root 1.9K Jun  8 11:46 tls.crt
-rw-r--r-- 1 root root 1.7K Jun  8 11:46 tls.ke

提示:也可手動如下操作創建自簽證書:

[root@master01 ~]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=ZheJiang/L=HangZhou/O=Xianghy/OU=Xianghy/CN=k8s.odocker.com"

手動創建secret

[root@master01 ~]# kubectl create ns kubernetes-dashboard	#v2版本dashboard獨立ns
[root@master01 ~]# kubectl create secret generic kubernetes-dashboard-certs --from-file=$HOME/dashboard/certs/ -n kubernetes-dashboard
[root@master01 ~]# kubectl get secret kubernetes-dashboard-certs -n kubernetes-dashboard -o yaml		#查看新證書`

下載yaml

[root@master01 ~]# cd /root/dashboard
[root@master01 dashboard]# wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.1/aio/deploy/recommended.yaml

修改yaml

[root@master01 dashboard]# vi recommended.yaml
……
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  type: NodePort			       	#新增
  ports:
    - port: 443
      targetPort: 8443
      nodePort: 30001				#新增
  selector:
    k8s-app: kubernetes-dashboard
---
……						#如下全部註釋
#apiVersion: v1
#kind: Secret
#metadata:
#  labels:
#    k8s-app: kubernetes-dashboard
#  name: kubernetes-dashboard-certs
#  namespace: kubernetes-dashboard
#type: Opaque
……
kind: Deployment
……
  replicas: 3					        #適當調整為3副本
……
          imagePullPolicy: IfNotPresent		        #修改鏡像下載策略
          ports:
            - containerPort: 8443
              protocol: TCP
          args:
            - --auto-generate-certificates
            - --namespace=kubernetes-dashboard
            - --tls-key-file=tls.key
            - --tls-cert-file=tls.crt
            - --token-ttl=3600	       		        #追加如上args
……
      nodeSelector:
        "beta.kubernetes.io/os": linux
        "dashboard": "yes"	        		#部署在master節點
……
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: dashboard-metrics-scraper
  name: dashboard-metrics-scraper
  namespace: kubernetes-dashboard
spec:
  type: NodePort	             			#新增
  ports:
    - port: 8000
      nodePort: 30000		         		#新增
      targetPort: 8000
  selector:                                                                                  
    k8s-app: dashboard-metrics-scraper
……
   replicas: 3			            		#適當調整為3副本
……
      nodeSelector:
        "beta.kubernetes.io/os": linux
        "dashboard": "yes"	        		#部署在master節點
……

正式部署

[root@master01 dashboard]# kubectl apply -f recommended.yaml
[root@master01 dashboard]# kubectl get deployment kubernetes-dashboard -n kubernetes-dashboard
[root@master01 dashboard]# kubectl get services -n kubernetes-dashboard
[root@master01 dashboard]# kubectl get pods -o wide -n kubernetes-dashboard

提示:master01 NodePort 30001/TCP映射到 dashboard pod 443 端口。

創建管理員賬戶

提示:dashboard v2版本默認沒有創建具有管理員權限的賬戶,可如下操作創建。

[root@master01 dashboard]# vi dashboard-admin.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard

[root@master01 dashboard]# kubectl apply -f dashboard-admin.yaml

ingress暴露dashboard

創建ingress tls

[root@master01 ~]# cd /root/dashboard/certs
[root@master01 certs]# kubectl -n kubernetes-dashboard create secret tls kubernetes-dashboard-tls --cert=tls.crt --key=tls.key
[root@master01 certs]# kubectl -n kubernetes-dashboard describe secrets kubernetes-dashboard-tls

創建ingress策略

[root@master01 ~]# cd /root/dashboard/
[root@master01 dashboard]# vi dashboard-ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: kubernetes-dashboard-ingress
  namespace: kubernetes-dashboard
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    #nginx.ingress.kubernetes.io/secure-backends: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      proxy_ssl_session_reuse off;
spec:
  rules:
  - host: k8s.odocker.com
    http:
      paths:
      - path: /
        backend:
          serviceName: kubernetes-dashboard
          servicePort: 443
  tls:
  - hosts:
    - k8s.odocker.com
    secretName: kubernetes-dashboard-tls
[root@master01 dashboard]# kubectl apply -f dashboard-ingress.yaml
[root@master01 dashboard]# kubectl -n kubernetes-dashboard get ingress

訪問dashboard

導入證書

將k8s.odocker.com導入瀏覽器,並設置為信任,導入操作略。

創建kubeconfig文件

使用token相對複雜,可將token添加至kubeconfig文件中,使用KubeConfig文件訪問dashboard。

[root@master01 dashboard]# ADMIN_SECRET=$(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}') 
[root@master01 dashboard]# DASHBOARD_LOGIN_TOKEN=$(kubectl describe secret -n kubernetes-dashboard ${ADMIN_SECRET} | grep -E '^token' | awk '{print $2}') 
[root@master01 dashboard]# kubectl config set-cluster kubernetes \
  --certificate-authority=/etc/kubernetes/pki/ca.crt \
  --embed-certs=true \
  --server=172.24.8.100:16443 \
  --kubeconfig=local-ngkek8s-dashboard-admin.kubeconfig		# 設置集群參數
 [root@master01 dashboard]# kubectl config set-credentials dashboard_user \
  --token=${DASHBOARD_LOGIN_TOKEN} \
  --kubeconfig=local-ngkek8s-dashboard-admin.kubeconfig		# 設置客戶端認證參數,使用上面創建的 Token
[root@master01 dashboard]# kubectl config set-context default \
  --cluster=kubernetes \
  --user=dashboard_user \
  --kubeconfig=local-ngkek8s-dashboard-admin.kubeconfig		# 設置上下文參數
[root@master01 dashboard]# kubectl config use-context default --kubeconfig=local-ngkek8s-dashboard-admin.kubeconfig		# 設置默認上下文

將local-ngkek8s-dashboard-admin.kubeconfig文件導入,以便於瀏覽器使用該文件登錄。

測試訪問dashboard

本實驗採用ingress所暴露的域名:https://k8s.odocker.com 方式訪問。使用local-ngkek8s-dashboard-admin.kubeconfig文件訪問。

提示: 更多dashboard訪問方式及認證可參考附004.Kubernetes Dashboard簡介及使用。 dashboard登錄整個流程可參考:https://www.cnadn.net/post/2613.html

Longhorn存儲部署

Longhorn概述

Longhorn是用於Kubernetes的開源分佈式塊存儲系統。
提示:更多介紹參考:https://github.com/longhorn/longhorn。

Longhorn部署

[root@master01 ~]# source environment.sh
[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
  do
    echo ">>> ${all_ip}"
    ssh root@${all_ip} "yum -y install iscsi-initiator-utils &"
  done

提示:所有節點都需要安裝。

[root@master01 ~]# mkdir longhorn
[root@master01 ~]# cd longhorn/
[root@master01 longhorn]# wget \
https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
[root@master01 longhorn]# vi longhorn.yaml
#……
---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: longhorn-ui
  name: longhorn-frontend
  namespace: longhorn-system
spec:
  type: NodePort			#修改為nodeport
  selector:
    app: longhorn-ui
  ports:
  - port: 80
    targetPort: 8000
    nodePort: 30002
---
……
kind: DaemonSet
……
        imagePullPolicy: IfNotPresent
……
#……
[root@master01 longhorn]# kubectl apply -f longhorn.yaml
[root@master01 longhorn]# kubectl -n longhorn-system get pods -o wide

提示:若部署異常可刪除重建,若出現無法刪除namespace,可通過如下操作進行刪除:

wget https://github.com/longhorn/longhorn/blob/master/uninstall/uninstall.yaml
rm -rf /var/lib/longhorn/
kubectl apply -f uninstall.yaml
kubectl delete -f longhorn.yaml

動態sc創建

提示:默認longhorn部署完成已創建一個sc,也可通過如下手動編寫yaml創建。

 [root@master01 longhorn]# kubectl get sc
NAME                   PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
……
longhorn               driver.longhorn.io      Delete          Immediate              true                   15m
[root@master01 longhorn]# vi longhornsc.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: longhornsc
provisioner: rancher.io/longhorn
parameters:
  numberOfReplicas: "3"
  staleReplicaTimeout: "30"
  fromBackup: "" 

[root@master01 longhorn]# kubectl create -f longhornsc.yaml

測試PV及PVC

[root@master01 longhorn]# vi longhornpod.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: longhorn-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn
  resources:
    requests:
      storage: 2Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: longhorn-pod
  namespace: default
spec:
  containers:
  - name: volume-test
    image: nginx:stable-alpine
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: volv
      mountPath: /data
    ports:
    - containerPort: 80
  volumes:
  - name: volv
    persistentVolumeClaim:
      claimName: longhorn-pvc
[root@master01 longhorn]# kubectl apply -f longhornpod.yaml
[root@master01 longhorn]# kubectl get pods
[root@master01 longhorn]# kubectl get pvc
[root@master01 longhorn]# kubectl get pv

創建ingress訪問UI

[root@master01 longhorn]# yum -y install httpd-tools
[root@master01 longhorn]# htpasswd -c auth xhy			#創建用戶名和密碼

提示:也可通過如下命令創建:
USER=xhy; PASSWORD=x120952576; echo "${USER}:$(openssl passwd -stdin -apr1 <<< ${PASSWORD})" >> auth

[root@master01 longhorn]# kubectl -n longhorn-system create secret generic longhorn-basic-auth --from-file=auth
[root@master01 longhorn]# vi longhorn-ingress.yaml		#創建ingress規則
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: longhorn-ingress
  namespace: longhorn-system
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: longhorn-basic-auth
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required '
spec:
  rules:
  - host: longhorn.odocker.com
    http:
      paths:
      - path: /
        backend:
          serviceName: longhorn-frontend
          servicePort: 80

[root@master01 longhorn]# kubectl apply -f longhorn-ingress.yaml

確認驗證

瀏覽器訪問:longhorn.odocker.com,並輸入賬號和密碼。

登錄查看。

Helm安裝

參考053.集群管理-Helm工具

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

【其他文章推薦】

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

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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

ThreadLocal的使用場景分析

目錄

一.ThreadLocal介紹

二.使用場景1——數據庫事務問題

  2.1 問題背景

  2.2 方案1-修改接口傳參

  2.3 方案2-使用ThreadLocal

三.使用場景2——日誌追蹤問題

四.其他使用場景

 

 

一.ThreadLocal介紹

  我們知道,變量從作用域範圍進行分類,可以分為“全局變量”、“局部變量”兩種:

  1.全局變量(global variable),比如類的靜態屬性(加static關鍵字),在類的整個生命周期都有效;

  2.局部變量(local variable),比如在一個方法中定義的變量,作用域只是在當前方法內,方法執行完畢后,變量就銷毀(釋放)了;

  使用全局變量,當多個線程同時修改靜態屬性,就容易出現併發問題,導致臟數據;而局部變量一般來說不會出現併發問題(在方法中開啟多線程併發修改局部變量,仍可能引起併發問題);

  再看ThreadLocal,從名稱上就能知道,它可以用來保存局部變量,只不過這個“局部”是指“線程”作用域,也就是說,該變量在該線程的整個生命周期中有效。

 

二.使用場景1——數據庫事務問題

2.1問題背景

  下面介紹示例,UserService調用UserDao刪除用戶信息,涉及到兩張表的操作,所以用到了數據庫事務:

  數據庫封裝類DbUtils

public class DbUtils {

    // 使用C3P0連接池
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource("dev");

    public static Connection getConnectionFromPool() throws SQLException {
        return dataSource.getConnection();
    }

    // 省略其他方法.....
}

  UserService代碼如下:  

public class UserService {

    private UserDao userDao;

    public void deleteUserInfo(Integer id, String operator) {
        Connection connection = null;
        try {
            // 從連接池中獲取一個連接
            connection = DbUtils.getConnectionFromPool();
            // 因為涉及事務操作,所以需要關閉自動提交
            connection.setAutoCommit(false);

            // 事務涉及兩步操作,刪除用戶表,增加操作日誌
            userDao.deleteUserById(id);
            userDao.addOperateLog(id, operator);

            connection.commit();
        } catch (SQLException e) {
            // 回滾操作
            try {
                if (connection != null) {
                    connection.rollback();
                }
            } catch (SQLException ex) {
            }
        } finally {
            DbUtils.freeConnection(connection);
        }
    }
}

  下面是UserDao,省略了部分代碼:

package cn.ganlixin.dao;
import cn.ganlixin.util.DbUtils;
import java.sql.Connection;

/**
 * @author ganlixin
 * @create 2020-06-12
 */
public class UserDao {

    public void deleteUserById(Integer id) {
        // 從連接池中獲取一個數據連接
        Connection connection = DbUtils.getConnectionFromPool();

        // 利用獲取的數據庫連接,執行sql...........刪除用戶表的一條數據

        // 歸還連接給連接池
        DbUtils.freeConnection(connection);
    }

    public void addOperateLog(Integer id, String operator) {
        // 從連接池中獲取一個數據連接
        Connection connection = DbUtils.getConnectionFromPool();

        // 利用獲取的數據庫連接,執行sql...........插入一條記錄到操作日誌表

        // 歸還連接給連接池
        DbUtils.freeConnection(connection);
    }
}

  上面的代碼乍一看,好像沒啥問題,但是仔細看,其實是存在問題的!!問題出在哪兒呢?就出在從數據庫連接池獲取連接哪個位置。

  1.UserService會從數據庫連接池獲取一個連接,關閉該連接的自動提交;

  2.UserService然後調用UserDao的兩個接口進行數據庫操作;

  3.UserDao的兩個接口,都會從數據庫連接池獲取一個連接,然後執行sql;

  注意,第1步和第3步獲得的連接不一定是同一個!!!!這才是關鍵。

  如果UserService和UserDao獲取的數據庫連接不是同一個,那麼UserService中關閉自動提交的數據庫連接,並不是UserDao接口中執行sql的數據庫連接,當userService中捕獲異常,即使執行rollback,userDao中的sql已經執行完了,並不會回滾,所以數據已經出現不一致!!!

 

2.2方案1-修改接口傳參

  上面的例子中,因為UserService和UserDao獲取的連接不是同一個,所以並不能保證事務原子性;那麼只要能夠解決這個問題,就可以保證了

  可以修改userDao中的代碼,不要每次在UserDao中從數據庫連接池獲取連接,而是增加一個參數,該參數就是數據庫連接,有UserService傳入,這樣就能保證UserService和UserDao使用同一個數據庫連接了

public class UserDao {

    public void deleteUserById(Connection connection, Integer id) {
        // 利用傳入的數據庫連接,執行sql...........刪除用戶表的一條數據
    }

    public void addOperateLog(Connection connection, Integer id, String operator) {
        // 利用傳入的數據庫連接,執行sql...........插入一條記錄到操作日誌表
    }
}

  UserService調用接口時,傳入數據庫連接,修改代碼后如下:

// 事務涉及兩步操作,刪除用戶表,增加操作日誌
// 新增參數傳入數據庫連接,保證UserService和UserDao使用同一個連接
userDao.deleteUserById(connection, id);
userDao.addOperateLog(connection, id, operator);

  這樣做,的確是能解決數據庫事務的問題,但是並不推薦這樣做,耦合度太高,不利於維護,修改起來也不方便;

 

2.3使用ThreadLocal解決

  ThreadLocal可以保存當前線程有效的變量,正好適合解決這個問題,而且改動的點也特別小,只需要在DbUtils獲取連接的時候,將獲取到的連接存到ThreadLocal中即可:

public class DbUtils {

    // 使用C3P0連接池
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource("dev");

    // 創建threadLocal對象,保存每個線程的數據庫連接對象
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

    public static Connection getConnectionFromPool() throws SQLException {
        if (threadLocal.get() == null) {
            threadLocal.set(dataSource.getConnection());
        }

        return threadLocal.get();
    }

    // 省略其他方法.....
}

  然後UserService和UserDao中,恢復最初的版本,UserService和UserDao中都調用DbUtils獲取數據庫連接,此時他們獲取到的連接則是同一個Connection對象,就可以解決數據庫事務問題了。

 

三.使用場景2——日誌追蹤問題

  如果理解了場景1的數據庫事務問題,那麼對於本小節的日誌追蹤,光看標題就知道是怎麼回事了;

  開發過程時,會在項目中打很多的日誌,一般來說,查看日誌的時候,都是通過關鍵字去找日誌,這就需要我們在打日誌的時候明確的寫入某些標識,比如用戶ID、訂單號、流水號…

  如果業務比較複雜,那麼一個請求的處理流程就會比較長,如果將這麼一長串的流程給串起來,也可以通過前面說的用戶ID、訂單號、流水號來串,但有個問題,某些接口沒有用戶ID或者訂單號作為參數!!!!這個時候,雖然可以像場景1中給接口增加用戶ID或者訂單號作為參數,但是並不推薦這麼做。

  此時可以就可以使用ThreadLocal,封裝一個工具類,提供唯一標識(可以是用戶ID、訂單號、或者是分佈式全局ID),示例如下:

package cn.ganlixin.util;

/**
 * 描述:
 * 日誌追蹤工具類,設置和獲取traceId,
 * 此處的traceId使用snowFlake雪花數算法,詳情可以參考:https://www.cnblogs.com/-beyond/p/12452632.html
 *
 * @author ganlixin
 * @create 2020-06-12
 */
public class TraceUtils {
    // 創建ThreadLocal靜態屬性,存Long類型的uuid
    private static final ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    // 全局id生成器(雪花數算法)
    private static final SnowFlakeIdGenerator generator = new SnowFlakeIdGenerator(1, 1);

    public static void setUuid(String uuid) {
        // 雪花數算法
        threadLocal.set(generator.nextId());
    }

    public static Long getUuid() {
        if (threadLocal.get() == null) {
            threadLocal.set(generator.nextId());
        }
        return threadLocal.get();
    }
}

  

  使用示例:

@Slf4j
public class UserService {

    private UserDao userDao;

    public void deleteUserInfo(Integer id, String operator) {
        log.info("traceId:{}, id:{}, operator:{}", TraceUtils.getUuid(), id, operator);
        
        //.....
    }
}

@Slf4j
public class UserDao {

    public void deleteUserById(Connection connection, Integer id) {
        log.info("traceId:{}, id:{}", TraceUtils.getUuid(), id);
    }
}

  

 四.其他場景

  其他場景,其實就是利用ThreadLocal“線程私有且線程間互不影響”特性,除了上面的兩個場景,常見的還有用來記錄用戶的登錄狀態(當然也可以用session或者cookie實現)。

 

  原文地址:https://www.cnblogs.com/-beyond/p/13111015.html 

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

【其他文章推薦】

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

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

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

※南投搬家公司費用需注意的眉眉角角,別等搬了再說!

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

SSH原理常見應用升級及端口轉發

SSH介紹

SSH是Secure Shell Protocol的簡寫,由IETF網絡工作小組(Network working Group)指定;在進行數據傳輸之前,SSH先對聯機數據包通過加密技術進行加密處理,加密后在進行數據傳輸。確保了傳遞的數據安全.

SSH是專為遠程登錄會話和其他網絡服務提供的安全性協議。利用SSH協議可以有效的放置遠程管理過程中的信息泄露問題,在當前的生產環境運維工作中,絕大多數企業普遍採用SSH協議服務來代替傳統的不安全的遠程聯機服務軟件,如telnet(23端口,非加密)

在默認狀態下,SSH服務主要提供了兩個服務功能,一個是提供類似Telnet遠程聯機服務器的服務,即上面提到的SSH服務;另一個是類似FTP服務的sftp-server,藉助SSH協議來傳輸數據的,提供更安全的SFTP服務(vsftp,proftp)

ssh 客戶端(ssh命令)還包含一個遠程安全拷貝命令scp,也是通過ssh協議工作.

小結

# 1、SSH是安全的加密協議,用於遠程連接linux服務器
# 2、SSH默認端口是22,安全協議版本SSH2,除了2之外還有SSH1(漏洞)
# 3、SSH服務端主要包含兩個服務協議SSH遠程連接,SFTP服務
# 4、Linux SSH客戶端包含ssh遠程連接命令,以及遠程拷貝scp命令

SSH結構

# SSH服務由服務端軟件OpenSSH (openssl)
# 客戶端(常見的有SSH(linux),SecureCRT,Putty,Xshell)組成

# SSH服務默認使用22端口提供服務,它有兩個不兼容的SSH協議版本分別是1.x和2.x

rpm -qa openssh
openssh-6.6.1p1-31.el7.x86_64		# 遠程連接安裝包

rpm -qa openssl
openssl-1.0.2k-19.el7.x86_64		# 加密安裝包

OpenSSH同時支持SSH 1.x和2.x 用SSH 2.x的客戶端程序不能鏈接到SSH1.x的服務程序上

SSH服務是一個守護進程(daemon),他在後台運行並響應來自客戶端的連接請求,SSH服務端的進程名為sshd,負責實時監聽遠程SSH客戶端的連接請求,並進行處理,一般包括公共密鑰認證、密鑰交換、對稱密鑰加密和非安全連接等。

SSH客戶端包含ssh以及像scp(遠程拷貝)slogin(遠程登錄)sftp(安全FTP文件傳輸)等應用程序

SSH的工作機制大致是本地的ssh客戶端發送一個連接請求到遠程的ssh服務器,服務器檢查連接的客戶端發送的數據包和ip地址,如果確認合法,就會發送密鑰給SSH的客戶端,此時,客戶端本地再將密鑰發回給服務端,自己建立連接。SSH1.x和SSH2.x在連接協議上有一些安全方面的差異

SSH加密技術

SSH加密技術是將人類可以看得懂的數據,通過一定的特殊的程序算法,把這些數據變成雜亂的無意義的信息,然後,通過網絡進行傳輸,二擋到了目的地后,在通過對應的解密算法,把傳過來的加密的數據信息解密成加密前的可讀取的正常數據。因此,當數據在互聯網上傳輸時即使被有心的黑客監聽竊取了,也很難獲取到真正需要的數據

網絡上的數據包加密技術一般是通過所謂的一對公鑰和私鑰(Public key and Pivate key)組合撐的密鑰對進行加密與解密操作。

SSH 1.x

每一台SSH服務器主機都可以使用RSA加密方式來產生一個1024-bit的RSA Key 這個RSA的加密方式就是用來產生公鑰和私鑰的算法之一。

當服務啟動時,就會產生一個768 bit的臨時公鑰存放在Server中

grep ServerKey /etc/ssh/sshd_config 
#ServerKeyBits 1024
SSH 2.x

在SSH 1.x的聯機過程中,當Server接收Clinet端的Private Key后,就不再針對該次聯機的Key pair進行檢驗。若此時有而已黑客對該聯機key pair插入而已的程序代碼時,由於服務端你不會在檢驗聯機的正確性,因此可能會接收該程序代碼,從而導致系統被黑.

為了改正這個缺點,SSH version2多加了一個確認聯機正確性的Diffie-Hellman機制

在每次數據傳輸中,Server都會以該機制檢查數據的來源是否正確,這樣,可以避免聯機過程中被插入而已程序代碼的問題

另外,SSH2同時支持RSA和DSA密鑰,但是SSH1僅支持RSA密鑰

由於SSH1協議本身存在較大問題,建議使用SSH2的聯機模式

當Client端SSH聯機請求傳送過來時,Server就會將這個768-bit的公鑰傳給Client端

此時Client會將此公鑰與先前存儲的公鑰進行對比,看是否一致,判斷標準是Client端聯機用戶目錄下~/.ssh/known_hosts文件的內容(linux-客戶端)

不加用戶默認是root 不加-p 指定端口 默認是22`

ssh -p22222 root@39.108.140.0
The authenticity of host '39.108.140.0 (39.108.140.0)' can't be established.
ECDSA key fingerprint is 17:33:ef:9b:05:b3:69:d3:20:48:49:e1:28:9b:7c:c8.
Are you sure you want to continue connecting (yes/no)? 

# 連接密碼文件存放路徑
cat /root/.ssh/known_hosts 
121.36.43.223 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDgQ7H6KDIPTzOklwMSOxgFI0Xc3rgvwPnCLIuXIuzaCfYQBouM6owCArpj2CXEyk40lSn96ktW1vETbP1JmjEY=

# 第一次SH連接的時候,本地會產生一個密鑰文件~/.ssh/known_hosts
如何防止SSH登錄入侵
# 1、用密鑰登錄,不用密碼登錄
# 2、牤牛陣法:解決SSH安全問題
# 3、防火牆封閉SSH,指定源限制(局域網,信任公網)
# 4、開啟SSH只監聽本地內網IP(ListenAddress 10.0.0.8)
# 5、盡量不給服務器外網IP

SSH客戶端附帶的scp命令

scp的基本語法使用:scp -sercure copy (remote file copy program)

推push

scp -P22 -rp /root/test.txt root@39.108.140.0:/root/

拉pull

scp -p22 -rp root@39.108.140.0:/root/test.txt ./

# -P 指定端口,默認22,可忽略
# -p 表示拷貝前後保持文件或目錄屬性
# -r 遞歸,表示拷貝目錄
# -l 限制速度

# 小結
# 1、scp是加密的遠程拷貝,而cp僅為本地拷貝
# 2、可以把數據從一台機器推送到另一台機器,也可以從其他服務器把數據拉回到本地執行命令的服務器
# 3、每次都是全量完成拷貝,因此效率不高,適合第一次拷貝用,如果需要增量拷貝,用rsync
ssh服務附帶的sftp功能服務
# 1. rz,sz(lrzsz)
# 2. winscp WinSCP-v4.0.5 基於SSH,sftp
# 3. SFX(xshell) 4) SFTP 基於SSH加密傳輸
# 4. samba,http,ftp,nfs
# FTP工具:vsftp、proftpd、SFTP

# linux sftp客戶端登錄sftp服務方法
# 登錄FTP的方法就是
sftp -oPort=22 root@39.108.140.0:
sftp> put test.txt
Uploading test.txt to /root/test.txt
test.txt                                          100%    0     0.0KB/s   00:00                                   
sftp> ls test.txt 
test.txt

SSH服務認證類型介紹

基於口令安全認證

基於口令的安全驗證的方式就是大家現在一直在用的,只要知道服務器的SSH端口號和口令,應服務器的IP及開放的端口,默認都為22,就可以通過ssh客戶端登錄到主機,此時聯機過程中所有傳輸都是加密的

基於密鑰的安全驗證

基於密鑰的安全驗證方式是指,需要依靠密鑰,也就是必須事先建立一對密鑰,然後把公用密鑰(Publickey)放在需要訪問的目標服務器上,另外,個還需要把私有密鑰(Private key)放到SSH客戶端或對應的客戶端服務器上

此時,如果要想連接到這個帶有公用密鑰的SSH服務器,客戶端SSH軟件或者客戶端端服務就會想SSH服務端發出請求,請求用聯機用戶密鑰進行安全連接。SSH服務會在收到請求之後,會現在改SSH服務器上連接的用戶的加密路下 放上去的對應用戶密鑰,然後把它和連接的SSH客戶端發來進行密鑰,如果兩個密鑰一直SSH服務就會用公用密鑰加密“質詢”(challenge)並把它發送給SSH客戶端

更改ssh 默認登錄配置

修改SSH服務的運行參數,是通過修改配置文件/etc/ssh/sshd_config實現的

一般來說SSH服務使用默認的配置已經夠很好的工作,如果對安全要求不高,僅僅提供SSH服務的情況不需要修改任何配置

sshd_config配置文件說明:

優化SSH配置文件選項
cp /etc/ssh/sshd_config{,.bak}
vim /etc/ssh/sshd_config
Port 52113              # ssh連接端口默認為22,修改端口號可以提高級別
PermitRootLogin no      # 禁止root遠程登錄
PermitEmptyPasswords no # 禁止空密碼的用戶登錄
UseDNS no               # 不使用DNS進行解析
GSSAPIAuthentication no # 會導致SSH連接慢

# ssh遠程連接服務滿解決方法
sed -ri '13 iPort 52113\nPermitRootLogin no\nPermitEmptyPasswords no\nUseDNS no\nGSSAPIAuthentication no' /etc/ssh/sshd_config

SSH優化

優化sshd_config
sed -i '13 iPort 52113\nPermitRootLogin no   \n   禁止root登錄
PermitEmptyPasswords no \n      #        禁止使用密碼
UseDNS no\n                     # 禁用DNS
GSSAPIAuthentication no'        #        禁用GSSAPI
sshd_config
檢查hosts解析
cat >>/etc/hosts <<EOF
39.108.140.0 blog
149.129.38.117 blog2
121.36.43.223 huawei 
49.233.69.195 tenxun 
116.196.83.113 jd 
EOF

useradd oldboy
echo "123456"|passwd --stdin youmen
su – youmen
ssh-keygen -t dsa   //非交互式創建密鑰  

#ssh-keygen是生產密鑰的工具 -t參數是指定密鑰的類型,這裡是建立dsa類型密鑰
#也可以使用ssh-keygen -t rsa來建立rsa類型密鑰

#RSA與DSA加密算法的區別
#RSA:是一種加密算法(PS:RSA也可以進行数字簽名的)它的簡寫的來由是RonRivest、Adi Shamir和LeonAdleman 這三個姓氏加在一起就是RSA

#DSA就是数字簽名算法的英文全稱的簡寫,即Digital Sigenature Algorithm=DSA

# RSA既可以進行加密,也可以進行数字簽名實現認證,而DSA只能用於数字簽名
分發密鑰
yum -y install sshpass
ssh-keygen -t rsa -P '' -f ~/.ssh/id_dsa &>/dev/null
sshpass -p youmen ssh -o StrictHostKeyChecking=no  root@192.168.43.159


# 一鍵生成密鑰對
ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa >/dev/null 2>&1

ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.43.159
# -i 代表要發送的文件

# ssh-copy-id 只能發公鑰,不能發私鑰

# 1 免密碼登錄是單向的,方向從私鑰(鑰匙)==》公鑰(鎖)
# 2 SSH免密碼登錄基於用戶的,最好不要跨不同的用戶
# 3 ssh連接慢的問題解決
ssh批量管理步驟
# 1、ssh優化和hosts解析
# 2、創建用戶
# 3、生成密鑰對
# 4、分發公鑰到所有服務器 ssh-copy-id
# 5、測試 遠程連接ssh 遠程執行命令ssh ifconfig
# 遠程拷貝文件scp rsync-e隧道模式

ssh常見操作

將本地hosts發送指定IP

1. sudo提權實現沒有權限用戶拷貝

echo“youmenALL= NOPASSWD:/usr/bin/rsync ”>>/etc/sudoers

visudo -c 

scp -P52113 hosts oldboy@192.168.43.159:~	# 最好發送到家目錄下,直接發送到/下是沒有權限

ssh -p22 -t youmen@192.168.43.159 sudo rsync ~/hosts /etc/hosts
# 需要授權sudo權限

2. 使用suid實現沒有權限用戶拷貝

chmod u+s `which rsync`
scp -P22 hosts oldboy@192.168.43.159:~
ssh -p22 youmen@192.168.43.159 rsync ~/hosts  /etc/hosts

3. 使用root進行操作

# rsync使用
rsync -avz hosts -e ‘ssh -p 22’youmen@192.168.43.159:~
# 可以增量備份
查看hosts主機系統版本
 cat view.sh 
#!/bin/sh
for n in blog2 tenxun jd huawei 
do
  echo -n "====$n===="	
  ssh -p 22 $n $1		
done
[root@nginx_test ~]# sh view.sh "cat /etc/redhat-release"
====blog2====CentOS Linux release 7.3.1611 (Core) 
====tenxun====CentOS Linux release 7.6.1810 (Core) 
====jd====CentOS Linux release 7.6.1810 (Core) 
====huawei====CentOS Linux release 7.4.1708 (Core) 
分發文件
cat file.sh 
#!/bin/sh
. /etc/init.d/functions
if [ $# -ne 1 ];then 
    echo "USAGE:/bin/sh $0 FILENAME"	$
    exit 1    
fi
for n in blog2 tenxun jd huawei 
do
  echo -n "====$n===="	
  scp -P22 $1  $n: &>/dev/null
  if [ $? -eq 0 ];then
      action "dis $1 to $n" /bin/true
else
      action "$n" /bin/false
fi
done

sh file.sh /etc/hosts
====blog2====dis /etc/hosts to blog2                       [  OK  ]
====tenxun====dis /etc/hosts to tenxun                     [  OK  ]
====jd====dis /etc/hosts to jd                             [  OK  ]
====huawei====dis /etc/hosts to huawei                     [  OK  ]

SSH端口轉發簡介

SSH會自動加密和解密所有SSH客戶端與服務端之間的網絡數據。但是,SSH還能夠將其他TCP端口的網絡數據通SSH鏈接來轉發,並且自動提供了相應的加密及解密服務。這一過程也被叫做”隧道“(tunneling),這是因為SSH為其他TCP鏈接提供了一個安全的通道來進行傳輸而得名。例如,Telnet ,SMTP ,LDAP這些TCP應用均能夠從中得益,避免了用戶名,密碼以及隱私信息的明文傳輸。而與此同時,如果工作環境許中的防火牆限制了一些網絡端口的使用,但是允許SSH的連接,也能夠將通過將TCP用端口轉發來使用SSH進行通訊。

SSH端口轉發兩大功能
# 1. 加密SSH Client端至SSH Server端之間的通訊數據。
# 2. 突破防火牆的簡直完成一些之前無法建立的TCP連接。

本地轉發

命令 -L localport:remotehost:remotehostport sshserver

說明

localport      #  本機開啟的端口號
remotehost      # 最終連接機器的IP地址
remotehostport     #  轉發機器的端口號
sshserver      #  轉發機器的IP地址

# -L 本機端口
# -f 後台啟用,可以在本機直接執行命令,無需另開新終端
# -N 不打開遠程shell,處於等待狀態,不跳到遠程主機,還在主機上,只是搭好了隧道,橋搭好,不ssh上去
# -g 啟用網關功能
# -R 服務端口

# 舉例:
ssh –L 9527:telnetsrv:23 -N sshsrv
telnet 127.0.0.1 9527
# 當訪問本機的9527的端口時,被加密後轉發到sshsrv的ssh服務,再解密被轉發到telnetsrv:23
data < >localhost:9527 < > localhost:XXXXX < > sshsrv:22 < > sshrv:yyyyy < > telnetsrv:23
環境背景

背景:企業內部C服務器只允許telnet連接(23端口)訪問,不允許外部直接訪問,B服務器是一個ssh服務器;有一個用戶需要從外部連接到企業內部的C服務器。

前提:防火牆允許22端口進來(或者企業內部有一個堡壘機,ssh -t通過堡壘機進去)。

原理: 數據一旦telnet以後,數據會發送到本機9527端口,再在本機開一個隨機端口,充當ssh客戶端,再把數據流量發送到22端口的ssh服務端,收到數據以後,解密數據,臨時開一個隨機端口充當客戶端,再把流量發送到23端口telnet服務端

機器: blogA用戶,huawei模擬B機器,tenxun模擬C機器

節點名 IP 軟件版本 硬件 網絡 說明
CentOS7-A 39.108.140.0 1C2G 公有雲 阿里雲
CentOS6-B 121.36.43.223 1C2G 公有雲 華為雲
CentOS6-C 49.233.69.195 1C2G 公有雲 騰訊雲
# ssh協議裏面封裝了telnet,一旦A連接了B主機,立即使用telnet連接C主機,此過程可以突破防火牆的限制
# 實驗
#   A->C    訪問被限制
#    A-B->C  使用B主機作為跳板突破訪問限制
配置步驟

C機器通過iptables拒絕A機器登錄

[root@C ~]# iptables -A INPUT -s 39.108.140.0 -j REJECT
[root@C ~]# yum -y install telnet-server xinetd
[root@C ~]# systemctl start telnet.socket
[root@C ~]# systemctl start xinetd
[root@C ~]# ss -tnl |grep 23
LISTEN     0      128       [::]:23                    [::]:*   

# 此時我們A機器是直接連接不上C機器的
[root@A ~]# ssh 49.233.69.195
ssh: connect to host 49.233.69.195 port 22: Connection refused

# 開啟端口轉發(telnet隧道)
[root@A ~]# ssh -L 10000:49.233.69.195:23 -Nf 121.36.43.223
# 通過本地9527端口訪問centos6-1服務器IP地址使用telnet協議23端口,跳板機ip地址

# 隧道已經搭建好了,此時A主機可以通過telnet訪問C主機
telnet 127.0.0.1 10000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

Kernel 3.10.0-1062.9.1.el7.x86_64 on an x86_64
c login: youmen
Password: 
Last login: Fri Jun 12 22:32:04 from centos-b
[root@C ~]$

# 如何需要刪除這個連接使用killall ssh即可
killall ssh   # 刪除搭建的橋
telnet 127.0.0.1 10000
Trying 127.0.0.1...
telnet: connect to address 127.0.0.1: Connection refused

遠程轉發

遠程轉發機制

# -R sshserverport:remotehost:remotehostportsshserver

# 舉例
ssh–R 9527:telnetsrv:23 –N sshsrv
# 讓sshsrv偵聽9527端口的訪問,如有訪問,就加密后通過ssh服務轉發請求到本機ssh客戶端,再由本機解密後轉發到telnetsrv:23
Data < > sshsrv:9527 < > sshsrv:22 < > localhost:XXXXX < > localhost:YYYYY< >telnetsrv:23

# 需求:
# 在A(Centos7)上開啟smtp服務(postfix),B(Centos6)做跳板,C(Centos6-1)客戶端給Centos7發送郵件

# 流程解釋
C-x->A    (拒絕訪問)
C-B->A      (通過遠程代理,接受訪問)  

環境依然是上面三台機器,只是C服務器換成116.196.83.113 (JD)這台服務器

配置步驟
[root@A ~]# vim /etc/postfix/main.cf 
# inet_interfaces = localhost  # 註釋此行,不讓接口直接綁定在127.0.0.1上
[root@A ~]# systemctl restart postfix
[root@A ~]# ss -tnl |grep 25
LISTEN     0      100          *:25                       *:*                  
LISTEN     0      100         :::25                      :::*  

[root@A ~]# yum -y install telnet-server
[root@A ~]# systemctl start telnet.socket

# 如果B上面有一些影響的防火牆規則就將它刪掉
[root@B ~]# iptables -nL --line-number
[root@B ~]# iptables -D INPUT 1

# 測試下C能不能telnet 25端口連接A
[root@C ~]# 39.108.140.0 25
Trying 39.108.140.0...
Connected to 39.108.140.0.
Escape character is '^]'.
220 nginx_test.localdomain ESMTP Postfix

# 設置防火牆策略,使A不接受C的一切請求
[root@A ~]# iptables -A INPUT -s 39.108.140.0 -j REJECT
[root@C ~]# telnet 39.108.140.0 25
Trying 39.108.140.0...
telnet: connect to address 39.108.140.0: Connection refused

# 使用B遠程轉發,發送郵件到
[root@B ~]# ssh-copy-id root@116.196.83.113:
# 使用B遠程轉發,發送郵件到A,最好免密,否則需要手動口令驗證
[root@B ~]#  ssh -R 50000:39.108.140.0:25 -fN 121.36.43.223

# 此時可以到C跳板機看到已經有端口在監聽了
[root@C ~]# ss -tnl |grep 50000
LISTEN     0      128    127.0.0.1:50000                    *:*                

[root@C ~]# telnet 127.0.0.1 5000
Trying 127.0.0.1...
telnet: connect to address 127.0.0.1: Connection refused
[root@zabbix ~]# telnet 127.0.0.1 50000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
220 nginx_test.localdomain ESMTP Postfix
mail from:youmen@163.com      
250 2.1.0 Ok

openssh升級

下載地址

http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/

具體升級步驟
# 記錄sshd.pid路徑
find / -name sshd.pid

# 查看openssh現有版本
ssh -V

# 使用rpm刪除現有的openssh
rpm -e --nodeps $(rpm -qa | grep openssh)

# 刪除舊的配置文件
rm -rf /etc/ssh/*

# 安裝openssl-devel
yum -y install openssl-devel

# 下載tar.gz包,配置編譯,安裝
wget http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-8.0p1.tar.gz

# 編譯並安裝
tar xvf openssh-8.0p1.tar.gz 
cd openssh-8.0p1
./configure --prefix=/usr/ --sysconfdir=/etc/ssh/ --with-ssl-dir=/etc/ssl --with-md5-passwords --mandir=/usr/share/man/
make && make install

# 設置ssh服務開機自啟動
# 複製啟動文件至/etc/init.d/
cp -a contrib/redhat/sshd.init /etc/init.d/sshd
     
# 編輯/etc/init.d/sshd文件, 將PID_FILE改為之前記下的sshd.pid路徑
    sed -i "s/PID_FILE=\/var\/run\/sshd.pid/PID_FILE=\/run\/sshd.pid/" /etc/init.d/sshd
     
# 設置開機啟動sshd
    chkconfig sshd on
    chkconfig --list sshd
    
    
# 編輯/etc/ssh/sshd_config, 設置一些常用參數
sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/" /etc/ssh/sshd_config
sed -i "s/#PubkeyAuthentication yes/PubkeyAuthentication yes/" /etc/ssh/sshd_config
sed -i "s/#PasswordAuthentication yes/PasswordAuthentication yes/" /etc/ssh/sshd_config
sed -i "s/#AllowTcpForwarding yes/AllowTcpForwarding yes/" /etc/ssh/sshd_config
sed -i "s/#X11Forwarding no/X11Forwarding yes/" /etc/ssh/sshd_config
sed -i "s/#PidFile \/var\/run\/sshd.pid/PidFile \/run\/sshd.pid/" /etc/ssh/sshd_config

# 加入系統服務
cat > /usr/lib/systemd/system/sshd.service  << EOF
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
#After=network.target sshd-keygen.service
#Wants=sshd-keygen.service
After=network.target
[Service]
ExecStart=/usr/sbin/sshd
[Install]
WantedBy=multi-user.target
EOF

# 啟用sshd服務
systemctl enable sshd
# 重啟服務
systemctl restart sshd
# 查看服務狀態
systemctl status sshd

# 驗證
ssh -V
OpenSSH_8.0p1, OpenSSL 1.0.2k-fips  26 Jan 2017

SSH設置登錄歡迎信息

到/etc/motd裏面編寫內容,看個人愛好

[root@nginx_test ~]# cat /etc/motd 
  /\/\
  (_人|人_)
   /‥\
  ミ(_Y_)ミ
   > <
  (/ \)
  _(   )_
 (_>―<_)

# 關閉當前會話再登錄
  /\/\
  (_人|人_)
   /‥\
  ミ(_Y_)ミ
   > <
  (/ \)
  _(   )_
 (_>―<_)
[root@nginx_test ~]#
小熊圖案
┴┬┴┬/ ̄\_/ ̄\   
┬┴┬┴▏  ▏▔▔▔▔\   
┴┬┴/\ /      ﹨   
┬┴∕       /   )   
┴┬▏        ●  ▏   
┬┴▏           ▔█    
┴◢██◣     \___/   
┬█████◣       /     
┴█████████████◣   
◢██████████████▆▄   
█◤◢██◣◥█████████◤\   
◥◢████ ████████◤   \   
┴█████ ██████◤      ﹨   
┬│   │█████◤        ▏   
┴│   │              ▏   
┬ ∕    ∕    /▔▔▔\     ∕   
┴/___/﹨   ∕     ﹨  /\   
┬┴┬┴┬┴\    \      ﹨/   ﹨   
┴┬┴┬┴┬┴ \___\     ﹨/▔\﹨ ▔\   
▲△▲▲╓╥╥╥╥╥╥╥╥\   ∕  /▔﹨/▔﹨   
   **╠╬╬╬╬╬╬╬╬*﹨  /  //

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

【其他文章推薦】

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

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

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

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

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

redis 主從複製

redis 主從複製

master 節點提供數據,也就是寫。slave 節點負責讀。

不是說master 分支不能讀數據,也能只是我們希望將讀寫進行分離。

slave 是不能寫數據的,只能處理讀請求

主從實現

客戶端 127.0.0.1:6379 服務器 212.64.89.173:6379

方式一

客戶端發送請求同步命令

slaveof masterip masterport

slaveof 212.64.89.173 6379

方式二

客戶端啟動服務器參數

redis-server --slaveof masterip masterport

redis-server --slaveof 212.64.89.173 6379

方式三

在客戶端的配置文件中寫入 slaveof 信息

redis.conf

slaveof 212.64.89.173 6379

注意 斷開主從鏈接方式: 客戶端執行 slaveof no one

設置鏈接密碼

server 端

服務啟動後設置

config set requierpass <password>

配置文件添加密碼

# redis.conf
requirepass <password>

client 端

命令設置密碼

auth <password>

配置文件設置密碼

masterauth <password>

啟動客戶端設置密碼

redis-cli -a <password>

建立連接

建立鏈接的過程就是希望 master 和 slave 都保有對方的 IP 和 Port。

數據同步

數據的同步分兩部分,全量同步和增量同步,在增量同步結束后,master 應當保存Slave 同步數據的位置。

複製(積壓)緩衝區

它有兩部分組成 偏移量 + 字節值

結構

創建

1 當啟動AOF 時就會創建 複製積壓緩衝區

2 當被選為 master 節點,必須創建積壓緩衝區

作用

保存所有的對數據修改或數據庫修改的指令,查詢指令不會被記錄。

數據源

所有的進入master 的對數據修改或數據庫修改的指令都會被填充到積壓緩衝區中。

偏移量

1 Master 和 Slave 都會記錄 offset 值, 每次複製都會對比offset 是否一致。如果一致,Master直接從 offset 處開始傳緩衝區數據,如果不一致,那麼Master將遵循 Slave 的offset 來傳。當然會保證命令是完整的。

2 Master 保存有多個 offset 而 Slave 僅保存自己的。

3 Master發送一次,記錄一次, Slave 接受一次記錄一次。

關於Master注意

1如果master 數據量過大,應該避免業務高峰期進行數據同步。避免造成 master 阻塞

2 數據緩衝區滿, 此時將會丟棄最早的記錄(FIFO),如果全量複製的時間開銷過大,則可能在開增量複製時候已經存在數據丟失,這會導致Master 和 Slave 數據不一致,為了保證一致性,必須開始新一輪的全量複製,完成后緩衝區又被填滿並存在丟棄,則會讓Slave進入死循環。

因此數據緩衝區要設置的大小合適(依具體情況而定)。

repl-backlog-size 1mb   # 默認的大小為 1MB

3 master 單機內存不應該佔用主機內存過多。一般的 50 ~ 70% 預留下 30% ~ 50%來進行bgsave 、 創建複製緩衝區、執行其他業務等。

關於Slave注意

1 為避免slave進行全量複製、部分複製時服務器響應阻塞或數據不同步,建議關閉此期間的對外服務。

slave-server-stale-data yes|no

2 數據同步階段,master 發送給 slave 信息可以理解 master 是 slave 的一個客戶端,主動向slave發送命令。

3 多個slave同時對master請求數據同步,master發送的RDB文件增多,會對帶寬造成巨大衝擊,如果master寬帶不足,因此數據同步需要根據業務需求,適量錯峰。

4 slave過多時,應該對拓撲結構進行調整,由一主多從結構變為樹狀結構,中間結點即是master,也是slave。但是使用樹狀結構時,因為層級越深,數據同步時延越大,因此將強一致性的數據放在頂層節點,一致性稍弱的數據放在靠底層的節點。

命令傳播

當master數據庫狀態被修改后,導致主從服務器數據庫狀態不一致,此時需要讓主從數據同步到一致的狀態,同步的動作成為命令傳播
master將接受到的數據變更命令發送給slave,slave接受命令后執行命令。

網絡閃斷閃連 忽略
短時間網絡中斷 部分複製
長時間網絡中斷 全量複製

服務器運行ID(runid)

每台服務器每次運行都會產生的身份識別碼,同一個服務器多次運行產生的runid是不一樣的。

形式:runid 由 40 個字符組成 一般是16進制的字符串

info server
run_id:409b6e9ea2e5c32958de8f365711598c98489f13

心跳機制

master

指令 PING

周期 repl-ping-slave-period 默認是 10s

作用 判斷 slave 是否在線

查詢 INFO replication 獲取最後一次 slave 連接時間間隔 lag = 0 / 1 屬於正常

slave

指令 REPLCONF{offset}

周期: 1s

作用1: 彙報自己的複製偏移量 獲取最新的數據變更指令

作用2: 判斷 master 是否存活

心跳注意事項

當 salve多數掉線 或者網絡延時過高時,master 會拒絕所有的同步信息。

min-slaves-to-write 2   # 最小的 slave 數量

min-slaves-max-lag 8  # 最長的

當 slave 的數量小於2 ,或者所有的時延都大於等於 8 時,會強 關閉 master 的血功能來停止數據同步。

Slave 的數量和延時由REPLCONF{offset} 命令確認。

完整的主從複製流程

讀寫分離

在redis主從架構中,Master節點負責處理寫請求,Slave節點只處理讀請求。對於寫請求少,讀請求多的場景,例如電商詳情頁,通過這種讀寫分離的操作可以大幅提高併發量,通過增加redis從節點的數量可以使得redis的QPS達到10W+。

負載均衡

基於主從結構,配合讀寫分離,由slave分擔master負載,並根據需求的變化,改變slave的數量,通過多個從節點分擔數據讀取負載,大大提高Redis服務器併發量與數據吞吐量

故障恢復

當master出現問題時,由slave提供服務,實現快速的故障恢復

數據冗餘

實現數據熱備份,時持久化之外的一種數據冗餘方式

高可用基石

基於主從複製,構建哨兵模式與集群,實現redis 的高可用方案。

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

【JAVA進階架構師指南】之五:JVM性能調優

前言

  首先給大家說聲對不起,最近屬實太忙了,白天上班,晚上加班,回家還要收拾家裡,基本每天做完所有事兒都是凌晨一兩點了,沒有精力再搞其他的了.
  好了,進入正題,讓我們來聊聊JVM篇最後一個章節—-JVM性能調優.童鞋們隨便打開一個大廠的招聘崗位JD,應該都會有JVM調優相關的描述,其實招聘方不一定要求候選人真的對JVM調優有實際調優經驗,但是至少得有思路,知道應該怎樣進行JVM層面的性能調優,說實話,知道如何進行JVM層面的性能調優的人,在面試中確實是有加分的.
  筆者在公司擔任面試官的時候,經常會看到候選人簡歷描述有JVM性能調優經驗,每當這個時候我都會問候選人一個問題,你是如何進行JVM性能調優的,很多童鞋的回答就是:噢,就是調整一下初始堆大小,新生代大小.這明顯不是筆者想要的答案,因為這根本就不叫JVM性能調優.童鞋們對號入座一下,對JVM調優僅僅是我上述說的那樣的,趕緊改一下簡歷,不要說自己會JVM性能調優.說實話,對JVM進行性能調優是對架構師的要求,甚至我敢說很多架構師都不一定有實際的JVM性能調優經驗.話不多說,讓我們進入正題,我們將從以下幾點來講解如何進行性能調優:

  • JVM性能調優的前提
  • JVM性能調優的預備知識
  • STW現象–Stop-The-World
  • 垃圾回收器的種類
  • 性能調優的目的
  • 如何進行性能調優

JVM性能調優的前提

  所有有經驗的架構師一定會有一個共識,JVM層面的性能調優一定是作為最後的調優手段,在此之前,一定要確保系統其它方面都已經做到了極致,無法再進行調優了,在這個前提下,才會考慮JVM性能調優.這裏的其他方面包括從前端到架構到代碼層面,我舉一些例子
從瀏覽器/APP角度可進行的優化有:

  • 減少HTTP請求次數.
  • 使用客戶端緩存.
  • 瀏覽器啟用壓縮
  • 使用CDN加速
  • 動態資源和靜態資源分離

從系統層面可進行的優化有:

  • 啟用緩存,比如redis緩存數據
  • 使用集群
  • 異步處理,比如引入消息隊列
  • 對代碼的優化

  以上列出的每一點,都能單獨拎出來講很久,但是博文篇幅問題,就不細說了,有興趣的童鞋可以自行下去了解,我們只着重說一下代碼優化,相信大家都知道,業界比較公認的編碼規範是阿里巴巴發布的<java開發手冊>,裏面的內容都是阿里集團多年來血的教訓積累的精華,阿里集團內部有一個組織專門負責手冊的編寫和推廣,並且在不斷進行優化,最新版發布到泰山版了(我看的時候還是華山版,哈哈).如果有童鞋還不知道的,我建議去下載下來看一看.另外阿里雲上有<java開發手冊>的考試,如果通過了這個考試,說明你的編碼規範還是不錯的,有興趣的童鞋可以去試一下.
  除此之外,童鞋們還應該了解JVM本身為我們悄悄做的各種優化,其中最重要的是JIT編譯器的優化.我舉幾個例子,比如:方法內聯,逃逸分析等.默認情況下,這些都是開啟的,如果不開啟這些功能,JVM性能會下降50%以上.除此之外,還有一些比如:棧上分配,TLAB等優化.這些內容由於平時我們開發中不會用到,是JVM在背後悄悄為我們做了優化,因此可能很多童鞋都不知道,但是如果想成為一個合格的架構師,這些內容都是必須要知道的,畢竟架構師的知識廣度和深度決定了架構師的高度.
  除此之外,性能調優一定是基於性能測試的,空口說進行性能調優的都是耍流氓,只有在經過了實際的性能測試后,我們才知道系統的瓶頸在哪裡,才知道那些方面需要進行調優,如何進行調優.常用的性能測試指標有TPS/QPS/吞吐量.並且默認所有的接口訪問都遵循二八原則(接口每天80%的訪問量集中在20%的時間內).

JVM性能調優的預備知識

  在進行JVM性能調優之前,我們還得了解JVM,比如我前面的幾篇有關JVM的博文,都是需要掌握的,比如JVM內存模型,垃圾回收機制等等.另外我們還需要掌握一些進行JVM分析的工具.其實在我們安裝JDK的時候,JDK已經為我們準備了許多有用的性能調優監控工具,我們可以看一下JDK安裝目錄下的bin目錄:

  • jps: 主要用來輸出JVM中運行的進程狀態信息
  • jstack: 主要用來查看某個Java進程內的線程堆棧信息
  • jmap: 用來查看堆內存使用狀況,一般結合jhat使用
  • jstat: JVM統計監測工具
  • jconsole: 圖形化的統計工具
  • jvisualvm: 比jconsole功能更強的圖形化的監控工具

  另外我們需要知道,當JVM發生OOM異常時,可以使用命令生成一個.hprof後綴的dump文件,該文件是某個時間節點的heap的快照,我們可以使用VisualVM來查看,也可以用第三方提供的一些工具,比如eclipse的MAT來查看內存溢出的原因.

STW現象

  所謂STW現象(Stop-The-World)是指在執行垃圾收集算法時,Java應用程序的其他所有線程(除了垃圾收集線程之外的線程)都被掛起.此時,系統只允許GC線程繼續運行,其他線程全部暫停,等待GC線程執行完畢后才能繼續執行.這些工作都是由虛擬機在後台自動發起和自動完成的,是在用戶不可見的情況下把用戶正常工作的線程全部停掉,舉個例子,某個接口平時可能只需要50ms的RT,忽然某次調用花費了200ms.因此STW對實時性要求很高的系統來說是難以接受的.

垃圾回收器的種類

  既然有STW現象,那麼有沒有解決方案呢?這就是我們接下來要講的,垃圾回收器的種類,目前為止,JVM一共為我們提供了七種垃圾回收器,其中年輕代有三種,老年代有三種,另外還有一種特殊的G1:

  當然,在更新版本的JDK中還有一種ZGC,為未來垃圾回收器提供了一種趨勢,有興趣的童鞋可以自行了解.

性能調優的目的和具體過程

  有了前面的鋪墊,終於來到了我們最重要的正題:如何進行JVM性能調優?在我看來JVM調優的具體步驟分為如下幾步:

  • 確定調優的目的,選擇合適的GC collector
  • 調整JVM heap的大小
  • 調整young generation在整個JVM heap中所佔的比重.

確定調優的目的,選擇合適的GC collector

  由於每種垃圾回收器的特性並不相同,因此我們需要根據我們的調優目的選擇合適的垃圾回收器,比如,我們需要降低STW的停頓時間,那我們就不能選用串行和并行的垃圾回收器,而應該選用併發的垃圾回收器,即CMS,與之搭配的新生代垃圾回收器就應該選用ParNew.目前一般主流互聯網公司都是用CMS垃圾回收器.

調整JVM heap的大小

  確定了垃圾回收器的類型,就需要調整JVM heap的大小,在這一步的時候,首先我們需要了解JVM相關的一些指令,比如可以在啟動java程序時加上
-XX:+HeapDumpOnOutOfMemoryError,當發生OOM時,JVM會自動為我們生成DUMP文件
-XX:+PrintGCDetails -Xloggc:D:\gclogger\gc.log -XX:+PrintGCDateStamps 生成GC日誌
-Xms2g -Xmx2g 當然,還應該要根據實際情況設置heap的最大最小值,童鞋們要知道,默認情況下,java程序啟動的最小heap大小為1/64物理內存,最大值為1/4物理內存,一般要求我們最大最小值保持一致,避免JVM頻繁擴容和縮容導致不必要的性能浪費.

調整young generation在整個JVM heap中所佔的比重.

  確定了heap的大小,還需要確定新生代的比重
–Xmn1500m -XX:MetaspaceSize=150M

  當然,再厲害的架構師也不可能一次就調整得出最佳的JVM配置參數,而是應該多設置幾組不同的值,放到生產環境(或者和生產環境一樣的環境,比如阿里內部有預發環境,和生產環境保持一致)進行性能測試,通過對比結果得出最佳的JVM性能調優參數,完成JVM性能調優.

總結

  讀完了本篇文章,我相信童鞋們應該或多或少會有些收穫.掌握JVM調整的核心步驟:

  • 確定調優的目的,選擇合適的GC collector
  • 調整JVM heap的大小
  • 調整young generation在整個JVM heap中所佔的比重.
      不了解如何進行JVM的調優的人,把本文內容好好理解后,能讓面試官刮目相看;了解JVM調優,但是條理不清晰的童鞋,可能會對JVM調優有更清晰的認識.總而言之,筆者認為本文是一篇滿滿的乾貨,網上許多講JVM調優的博文,並沒有這麼系統的講解過真正應該如何進行JVM調優.當然筆者能力有限,如文章有錯誤,歡迎指正,畢竟是人就會犯錯.
      PS:一入JVM深似海,從此再也出不來.對JVM有興趣的童鞋,可以鑽研,但是在經驗不足之前,不建議太過深入了解JVM,否則會耽誤自己.當然,立志要成為一名牛逼的架構師,這些都是必須要會的.
      本文我們講完了JVM,下一篇開始,讓我們繼續學習JAVA鎖相關的內容.
      如果覺得博主寫的不錯,歡迎關注博主微信公眾號,博主會不定期分享技術乾貨!

本文由博客一文多發平台 OpenWrite 發布!

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

小師妹學JavaIO之:NIO中那些奇怪的Buffer

目錄

  • 簡介
  • Buffer的分類
  • Big Endian 和 Little Endian
  • aligned內存對齊
  • 總結

簡介

妖魔鬼怪快快顯形,今天F師兄幫助小師妹來斬妖除魔啦,什麼BufferB,BufferL,BufferRB,BufferRL,BufferS,BufferU,BufferRS,BufferRU統統給你剖析個清清楚楚明明白白。

Buffer的分類

小師妹:F師兄不都說JDK源碼是最好的java老師嗎?為程不識源碼,就稱牛人也枉然。但是我最近在學習NIO的時候竟然發現有些Buffer類居然沒有註釋,就那麼突兀的寫在哪裡,讓人好生心煩。

更多內容請訪問www.flydean.com

居然還有這樣的事情?快帶F師兄去看看。

小師妹:F師兄你看,以ShortBuffer為例,它的子類怎麼後面都帶一些奇奇怪怪的字符:

什麼什麼BufferB,BufferL,BufferRB,BufferRL,BufferS,BufferU,BufferRS,BufferRU都來了,點進去看他們的源碼也沒有說明這些類到底是做什麼的。

還真有這種事情,給我一個小時,讓我仔細研究研究。

一個小時后,小師妹,經過我一個小時的辛苦勘察,結果發現,確實沒有官方文檔介紹這幾個類到底是什麼含義,但是師兄我掐指一算,好像發現了這些類之間的小秘密,且聽為兄娓娓道來。

之前的文章,我們講到Buffer根據類型可以分為ShortBuffer,LongBuffer,DoubleBuffer等等。

但是根據本質和使用習慣,我們又可以分為三類,分別是:ByteBufferAsXXXBuffer,DirectXXXBuffer和HeapXXXBuffer。

ByteBufferAsXXXBuffer主要將ByteBuffer轉換成為特定類型的Buffer,比如CharBuffer,IntBuffer等等。

而DirectXXXBuffer則是和虛擬內存映射打交道的Buffer。

最後HeapXXXBuffer是在堆空間上面創建的Buffer。

Big Endian 和 Little Endian

小師妹,F師兄,你剛剛講的都不重要,我就想知道類後面的B,L,R,S,U是做什麼的。

好吧,在給你講解這些內容之前,師兄我給你講一個故事。

話說在明末浙江才女吳絳雪寫過一首詩:《春 景 詩》

鶯啼岸柳弄春晴,
柳弄春晴夜月明。
明月夜晴春弄柳,
晴春弄柳岸啼鶯。

小師妹,可有看出什麼特異之處?最好是多讀幾遍,讀出聲來。

小師妹:哇,F師兄,這首詩從頭到尾和從尾到頭讀起來是一樣的呀,又對稱又有意境!

不錯,這就是中文的魅力啦,根據讀的方式不同,得出的結果也不同,其實在計算機世界也存在這樣的問題。

我們知道在java中底層的最小存儲單元是Byte,一個Byte是8bits,用16進製表示就是Ox00-OxFF。

java中除了byte,boolean是佔一個字節以外,好像其他的類型都會佔用多個字節。

如果以int來舉例,int佔用4個字節,其範圍是從Ox00000000-OxFFFFFFFF,假如我們有一個int=Ox12345678,存到內存地址裏面就有這樣兩種方式。

第一種Big Endian將高位的字節存儲在起始地址

第二種Little Endian將地位的字節存儲在起始地址

其實Big Endian更加符合人類的讀寫習慣,而Little Endian更加符合機器的讀寫習慣。

目前主流的兩大CPU陣營中,PowerPC系列採用big endian方式存儲數據,而x86系列則採用little endian方式存儲數據。

如果不同的CPU架構直接進行通信,就由可能因為讀取順序的不同而產生問題。

java的設計初衷就是一次編寫處處運行,所以自然也做了設計。

所以BufferB表示的是Big Endian的buffer,BufferL表示的是Little endian的Buffer。

而BufferRB,BufferRL表示的是兩種只讀Buffer。

aligned內存對齊

小師妹:F師兄,那這幾個又是做什麼用的呢? BufferS,BufferU,BufferRS,BufferRU。

在講解這幾個類之前,我們先要回顧一下JVM中對象的存儲方式。

還記得我們是怎麼使用JOL來分析JVM的信息的嗎?代碼非常非常簡單:

log.info("{}", VM.current().details());

輸出結果:

# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

上面的輸出中,我們可以看到:Objects are 8 bytes aligned,這意味着所有的對象分配的字節都是8的整數倍。

再注意上面輸出的一個關鍵字aligned,確認過眼神,是對的那個人。

aligned對齊的意思,表示JVM中的對象都是以8字節對齊的,如果對象本身佔用的空間不足8字節或者不是8字節的倍數,則補齊。

還是用JOL來分析String對象:

[main] INFO com.flydean.JolUsage - java.lang.String object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0    12           (object header)                           N/A
     12     4    byte[] String.value                              N/A
     16     4       int String.hash                               N/A
     20     1      byte String.coder                              N/A
     21     1   boolean String.hashIsZero                         N/A
     22     2           (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 2 bytes external = 2 bytes total

可以看到一個String對象佔用24字節,但是真正有意義的是22字節,有兩個2字節是補齊用的。

對齊的好處顯而易見,就是CPU在讀取數據的時候更加方便和快捷,因為CPU設定是一次讀取多少字節來的,如果你存儲是沒有對齊的,則CPU讀取起來效率會比較低。

現在可以回答部分問題:BufferU表示是unaligned,BufferRU表示是只讀的unaligned。

小師妹:那BufferS和BufferRS呢?

這個問題其實還是很難回答的,但是經過師兄我的不斷研究和探索,終於找到了答案:

先看下DirectShortBufferRU和DirectShortBufferRS的區別,兩者的區別在兩個地方,先看第一個Order:

DirectShortBufferRU:

public ByteOrder order() {
        return ((ByteOrder.nativeOrder() != ByteOrder.BIG_ENDIAN)
                ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
    }
DirectShortBufferRS:

public ByteOrder order() {
        return ((ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN)
                ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
    }

可以看到DirectShortBufferRU的Order是跟nativeOrder是一致的。而DirectShortBufferRS的Order跟nativeOrder是相反的。

為什麼相反?再看兩者get方法的不同:

DirectShortBufferU:

public short get() {
        try {
            checkSegment();
            return ((UNSAFE.getShort(ix(nextGetIndex()))));
        } finally {
            Reference.reachabilityFence(this);
        }
    }
DirectShortBufferS:

public short get() {
        try {
            checkSegment();
            return (Bits.swap(UNSAFE.getShort(ix(nextGetIndex()))));
        } finally {
            Reference.reachabilityFence(this);
        }
    }

區別出來了,DirectShortBufferS在返回的時候做了一個bits的swap操作。

所以BufferS表示的是swap過後的Buffer,和BufferRS表示的是只讀的swap過後的Buffer。

總結

不寫註釋實在是害死人啊!尤其是JDK自己也不寫註釋的情況下!

更多精彩內容且看:

  • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
  • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
  • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
  • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/java-io-nio-kinds-of-buffer/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

計算機網絡之傳輸層

傳輸層概述

  從信息處理得角度上去看,傳輸層主要是給上面得應用層提供通信服務得。我們平時再對網絡進行編程得時候,我們很多時候都是直接對接得傳輸層,也就是我們使用傳輸層所提供得接口來進行網絡編程,所以我們常說傳輸層是用戶功能得最底層,是面向通信部份得最高層。

  傳輸層負責將上層數據分段並提供端到端的、可靠的或不可靠的傳輸以及端到端的差錯控制和流量控制問題;包含的主要協議:TCP協議(Transmission Control Protocol,傳輸控制協議)、UDP協議(User Datagram Protocol,用戶數據報協議)。

UDP協議

  UDP(User Datagram Protocol: 用戶數據報協議),是一個非常簡單的協議,它對接收到的數據報不合併也不拆分,如下圖:

UDP協議格式:

16位源端口號 16位目的端口號
16位UDP長度 16位UDP校驗和
UDP數據

  UDP協議的特點:UDP是無連接協議;UDP不能保證可靠的交付數據,即UDP協議不會感知網絡是否擁塞,UDP協議不管網絡是否擁塞,都會把數據交付出去,給這個網絡就完了。無法保證數據在網絡中是否丟失;UDP是面向報文傳輸的;UDP沒有擁塞控制,而且UDP首部開銷很小。

TCP協議

  TCP(Transmission Control Protocol: 傳輸控制協議),是計算機網絡中非常複雜的一個協議。

  TCP協議特點:TCP是面向連接的協議;TCP的一個連接有兩端(點對點通信);TCP提供可靠的傳輸服務;TCP協議提供全雙工的通信;TCP是面向字節流的協議。

TCP協議格式:

16位源端口 16位目的端口
序號
確認號
數據偏移 保留字段 TCP標記 窗口
校驗和 緊急指針
TCP選項 填充

序號:0~\(2^{32}\)-1,一個字節一個序號, 數據首字節序號
確認號:0~\(2^{32}\)-1,一個字節一個序號, 期望收到數據的首字節序號。確認號為N:則表示N-1序號的數據都已經收到。
數據偏移:佔4位:0~15,單位為:32位字,數據偏離首部的距離。
TCP標記:佔6位,每位各有不同意義

標記 含義
URG Urgent: 緊急位,URG=1,表示緊急數據
ACK Acknowledgement: 確認位,ACK=1,確認號才生效
PSH Push: 推送位,PSH=1,儘快地把數據交付給應用層
RST Reset: 重置位,RST=1,重新建立連接
SYN Synchronization: 同步位,SYN=1 表示連接請求報文
FIN Finish: 終止位,FIN=1 表示釋放連接

窗口:佔16位:0~\(2^{16}\)-1,窗口指明允許對方發送的數據量
緊急指針:緊急數據(URG=1),指定緊急數據在報文的位置
TCP選項:最多40字節,支持未來的拓展

可靠傳輸的基本原理

  • 停止等待協議:發送方生成新的消息,發送給接收方,並且此時不會產生新的消息,需要收到接受方的確認消息后,才會產生新的消息。超時重傳:如果發送方的消息在傳輸的過程種丟失了,接收方沒有收到消息,就會進行超時重傳;如果接收方發送的確認消息,在傳輸的過程中丟失,也會進行超時重傳,因此 每發送一個消息,都需要設置一個定時器。停止等待協議是最簡單的可靠傳輸協議,但停止等待協議對信道的利用效率不高。
  • 連續ARQ協議:ARQ(Automatic Repeat reQuest:自動重傳請求),由於單個發送和確認效率低,我們可以通過批量發送和確認來提升效率。

  TCP的可靠傳輸基於連續ARQ協議,TCP的滑動窗口以字節為單位,窗口滑動過程如下圖:

如果接收到的序號沒有按序收到確認號,在超時時間內就會進行重新傳送,如下圖:

為了避免對整個窗口中的字節進行重傳,因此TCP協議使用了選擇重傳來提高傳輸效率。選擇重傳:重傳的是一段字節流,而不是某個字節,在TCP選項里存儲的是需要重傳的字節流的邊界。選擇重傳需要指定需要重傳的字節,每一個字節都有唯一的32位序號。

TCP協議的流量控制

   流量控制指讓發送方發送速率不要太快,是使用滑動窗口來實現的,即通過窗口大小控制對方發送速率。當接收到窗口為0的消息,則啟動堅持定時器,堅持定時器每隔一段時間發送一個窗口探測報文。

TCP協議的擁塞控制

  一條數據鏈路經過非常多的設備,數據鏈路中各個部分都有可能成為網路傳輸的瓶頸。流量控制考慮點對點的通信量的控制,擁塞控制考慮整個網絡,是全局性的考慮。如何判斷是否發生了網絡擁塞?根據報文超時來判斷髮生了擁塞是不成立的,如果我們在傳輸的過程中,把光纖或者網絡斷了,這個時候也會導致報文超時,但這是因為網絡故障造成的
  慢啟動算法: 由小到大逐漸增加發送數據量,每收到一個報文確認,就加一。例如:發送的數據量以此為:1 2 4 8 16…,是指數增長的。當使用慢啟動算法增長到慢啟動閾值時,就會使用擁塞避免算法;擁塞避免算法:維護一個擁塞窗口的變量,只要網絡不擁塞,就試探着擁塞窗口調大,如1 2 4 8 16 17 18 19。

TCP連接的建立

標記 含義
URG Urgent: 緊急位,URG=1,表示緊急數據
ACK Acknowledgement: 確認位,ACK=1,確認號才生效
PSH Push: 推送位,PSH=1,儘快地把數據交付給應用層
RST Reset: 重置位,RST=1,重新建立連接
SYN Synchronization: 同步位,SYN=1 表示連接請求報文
FIN Finish: 終止位,FIN=1 表示釋放連接

TCP三次握手的過程:

  1. 第一次握手:建立連接時,客戶端(發送方)發送syn包(seq=j)到服務器,並進入SYN_SENT狀態,等待服務器(接收方)確認;
  2. 第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(seq=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態。
  3. 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手

  在客戶端與服務器端傳輸的TCP報文中,雙方的確認號Ack和序號Seq的值,都是在彼此Ack和Seq值的基礎上進行計算的,這樣做保證了TCP報文傳輸的連貫性。一旦出現某一方發出的TCP報文丟失,便無法繼續”握手”,以此確保了”三次握手”的順利完成。此後客戶端和服務器端進行正常的數據傳輸。這就是“三次握手”的過程。

為什麼發送方要發出第三個確認報文呢?

  • 為了避免已經失效的連接請求報文傳送到對方,引起錯誤

TCP連接的釋放

TCP四次揮手的過程:TCP連接斷開過程:假設Client端發起中斷連接請求,也就是發送FIN報文。Server端接到FIN報文後,意思是說”我Client端沒有數據要發給你了”,但是如果你還有數據沒有發送完成,則不必急着關閉Socket,可以繼續發送數據。所以你先發送ACK,”告訴Client端,你的請求我收到了,但是我還沒準備好,請繼續你等我的消息”。這個時候Client端就進入FIN_WAIT狀態,繼續等待Server端的FIN報文。當Server端確定數據已發送完成,則向Client端發送FIN報文,”告訴Client端,好了,我這邊數據發完了,準備好關閉連接了”。Client端收到FIN報文後,”就知道可以關閉連接了,但是他還是不相信網絡,怕Server端不知道要關閉,所以發送ACK後進入TIME_WAIT狀態,如果Server端沒有收到ACK則可以重傳。”,Server端收到ACK后,”就知道可以斷開連接了”。Client端等待了2MSL后依然沒有收到回復,則證明Server端已正常關閉,那好,我Client端也可以關閉連接了。Ok,TCP連接就這樣關閉了!

  MSL(Max Segment Lifetime): 最長報文段壽命,MSL建議設置為2分鐘。為什麼需要等待2MSL?其實在釋放連接的過程中,客戶端最後一次發送的報文,服務端是沒有確認的,為了確保發送方的ACK可以達到接收方,如果2MSL時間內沒有收到,則接收方會重發。這也是等待計時器的作用,主要是為了確保發送方發送的第四次揮手報文可以正確的到達接收方,如果沒有到達的話,接收方就會重新放鬆第三次揮手的報文,以正確得到釋放這次連接。等待計時器的另一個作用就是確保當前連接的所有報文都已經過期。

為什麼關閉連接需要四次揮手呢?
  這是因為服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求后,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一個報文里來發送。但關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了,所以你可能未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以它這裏的ACK報文和FIN報文多數情況下都是分開發送的。

傳輸層總結:第一個端到端,即主機到主機的層次。傳輸層負責將上層數據分段並提供端到端的、可靠的或不可靠的傳輸。此外,傳輸層還要處理端到端的差錯控制和流量控制問題。 傳輸層的任務是根據通信子網的特性,最佳的利用網絡資源,為兩個端系統的會話層之間,提供建立、維護和取消傳輸連接的功能,負責端到端的可靠數據傳輸。在這一層,信息傳送的協議數據單元稱為段或報文。 網絡層只是根據網絡地址將源結點發出的數據包傳送到目的結點,而傳輸層則負責將數據可靠地傳送到相應的端口。 有關網絡層的重點:

  • 傳輸層負責將上層數據分段並提供端到端的、可靠的或不可靠的傳輸以及端到端的差錯控制和流量控制問題;
  • 包含的主要協議:TCP協議(Transmission Control Protocol,傳輸控制協議)、UDP協議(User Datagram Protocol,用戶數據報協議);
  • 重要設備:網關。

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

這不是紀念品!法遊客欲帶走1.8公斤沙子 挨罰逾3萬

摘錄自2020年9月8日自由時報報導

法國1名遊客日前疑將4磅重(約1.8公斤)的當地海灘沙子裝在行李中的瓶子,試圖帶離薩丁尼亞島,被查獲後於卡利亞里埃爾馬斯機場被捕,據稱被罰1000歐元(約新台幣3萬4558元)。

《CNN》報導,薩丁尼亞島2017年起規定,從島上取沙屬非法行為,若帶走恐面臨罰款甚至入獄。當地森林保護員發言人表示,該遊客裝紗的瓶子已沒收,並指祭出罰則是因類似情形層出不窮,尤其是針對粉紅、白色等顏色特殊的沙子。據稱,有網站將島上沙子作為紀念品出售。

報導指出,該發言人提及,罰金落在500至3000歐元(約新台幣1萬7275至10萬3649元)區間,視取走來源與數量決定,過去3年,管制、裁罰變得更加嚴格。除了會與警方合作,也接受民眾檢舉。

海洋
環境新聞
國際新聞
法國
海灘
保護海洋

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

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

神奇夢幻的藍色光芒 點亮愛爾蘭海灘

摘錄自2020年9月8日大紀元報導

在愛爾蘭南部的一座海灘上出現一種奇特景象,攜帶著發光物質的海洋生物照亮了拍打過來的海浪,呈現一片夢幻的螢光藍色。

其實,芳泉鎮海灘以生物發光這一獨特的自然奇觀而聞名於世。生物發光是諸如細菌、藻類、水母以及浮游生物等海洋生物發光現象的一種說法。這種現象可以發生在任何深度的海水中,但當它發生在淺水區時,就會產生特別超現實的景象。較溫暖的天氣使水溫大面積上升,能充分給予海洋生物所需的能量,從而使其發光更加明亮。

根據愛爾蘭海洋研究與保護組織的說法,在愛爾蘭水域,生物發光是由稱為夜光藻(Noctiluca scintillans)的單細胞鞭毛藻發出的,這種藻類通常被稱為「星光海」(Sea Sparkle)。

海洋
環境新聞
國際新聞
愛爾蘭
海灘

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

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

餐桌上的減碳計畫:智庫研究改善糧食系統 可實現2050年減碳目標20%

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

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

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

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

※回頭車貨運收費標準