首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

Kubernetes 学习笔记(一)--- 基本概念及利用kubeadm部署K8S

Kubernetes中文文档http://docs.kubernetes.org.cn/ 目录 一、K8S由来 二、K8S特性 自动装箱 自我修复 水平扩展 服务发现/负载均衡 自动发布和回滚 密钥和配置管理 存储编排 批量处理执行 三、K8S的架构 API Server Scheduler Controller-Manager Kubelet Kube-proxy 四、逻辑概念 Pod Pod控制器 五、K8S网络 六、通过kubeadm部署K8S集群 1. 环境准备(所有节点) 2. kubeadm初始化 3. 在master部署flannel 4. 扩展Pod的副本个数 5. 配置kubectl子命令自动补全 一、K8S由来 Kuberbetes简称K8S,是由Google的几位工程师创立的,2014年6月首次对外公布。K8S的开发深受谷歌内部的一个叫做Borg系统的影响,Borg系统是谷歌内部稳定运行了十几年的大规模容器编排工具。谷歌沿用Borg系统的思路,用GO语言重新构建了这个容器编排工具,并命名为Kubernetes。 二、K8S特性 自动装箱 基于资源依赖,以及其它约束,能够自动完成容器的部署而且不影响可用性。 自我修复 具有自愈能力,考虑到容器非常轻量级的特点,一旦某个应用容器崩溃了,可以很快启动一个新的应用容器替代。 水平扩展 能够实现自动水平扩展,一个容器不够,就再启一个,可以不断的扩展,只要你的物理平台资源足够。 服务发现/负载均衡 自动实现服务发现和负载均衡 自动发布和回滚 密钥和配置管理 存储编排 对存储卷实现动态供给。如某一个容器需要用到存储卷时,会根据容器的需求自动创建符合要求的存储卷 批量处理执行 三、K8S的架构 Kubernetes集群包含有节点代理Kubelet 和Master组件(APIs,scheduler,etc.),下面是K8S的架构图。 image.png Master节点包含API Server、Scheduler(调度器)、ControllerManager(控制器管理器)这三个核心的组件。Node节点包含的核心组件有Kubelet、Docker容器引擎、Kube-proxy API Server 负责接受请求,解析请求,处理请求 Scheduler 调度器,它负责监测每一个Node节点上的可用计算资源,并根据用户创建容器时的最小资源请求量去评估哪个Node节点最合适,就把容器创建在最合适的Node节点上。 Controller-Manager 负责监控每一个Controller(控制器)的健康状态,并确保控制器是健康的。而控制器是确保Pod健康的组件。 Kubelet 是用于与Master节点的API Server进行通信,并接受Master调度过来的各种任务并执行的集群代理。 Kube-proxy 负责管理Service 四、逻辑概念 Pod K8S并不是直接调度容器,K8S中最小的调度单元是Pod。容器是运行在Pod内部的,并且一个Pod内可以运行多个容器,这多个容器共享同一个底层的网络名称空间,它们共享底层的Net、UTS、IPC三个名称空间。另外三个名称空间是互相隔离的:USER、MNT、PID。同一个Pod中的容器通过lo进行通信。同一个Pod中的容器还共享第二种资源:存储卷。通常情况下一个Pod中只放一个容器,除非多个容器间有非常非常紧密的联系。 K8S把Pod分为两类: 自主式Pod,是自我管理的,只在某个节点上运行,一旦节点故障,Pod就会丢失,不支持全局调度; 由控制器管理的Pod,它是由Pod控制器进行管理的。 Pod控制器 常见的Pod控制器: ReplicationController (副本控制器),确保Pod的数量始终保持设定的个数。也支持Pod的滚动更新。 ReplicaSet (副本集),它不直接使用,有一个声明式更新的控制器叫Deployment来负责管理。但是Deployment只能负责管理那些无状态的应用。 StatefulSet (有状态副本集),负责管理有状态的应用。 DaemonSet ,如果需要在每一个Node上只运行一个副本,而不是随意运行,就需要DaemonSet。 Job,运行作业,对于时间不固定的操作,比如:某个应用生成了一大堆数据集,现在需要临时启动一个Pod去清理这些数据集,清理完成后,这个Pod就可以结束了。 这些不需要一直处于运行状态的应用,就用Job这个类型的控制器去控制。如果Pod运行过程中意外中止了,Job负责重启Pod。如果Pod任务执行完了,就不需要再启动了。 Cronjob,周期性作业。 Deployment这种控制器还支持二级控制器,叫HPA (HorizontalPodAutoscaler,水平Pod自动伸缩控制器)。当业务量增长,一组Pod不足以支持业务时,HPA会自动增加Pod数量,业务量下降后,还会自动减少Pod数量。 五、K8S网络 K8S中包含三种网络: 节点网络: 各个节点主机之间的通信网络; Service网络:也被称为集群网络; Pod网络:Pod之间通信的网络。 image.png K8S通过CNI(Container Network Interface,容器网络接口)插件体系来接入外部的网络解决方案。这些插件都应该提供Service网络和Pod网络。 以下三种常用的CNI插件: flannel : 支持网络配置;是叠加网络。简单,但是不支持网络策略;CoreOS开源项目 calico :支持网络配置、网络策略;是三层遂道网络。缺点:功能强大,但网络配置较为复杂。 canel : 是结合了flannel和calico的优点,使用flannel的网络配置、使用calico的网络策略。 。。。最常用的就是这三种,其他网络插件不一一列举。 以上这些网络插件可以做为节点上的守护进程运行,也可以托管在K8S之上做为容器运行。 六、通过kubeadm部署K8S集群 kubeadm是K8S官方提供的集群部署工具。kubeadm将master节点上的apiserver、scheduler、controller-manager、etcd和node节点上的kube-proxy都部署为Pod运行,所以master和node都需要安装kubelet和docker。 image.png image.png 主机准备: 主机名 IP 系统 配置 k8s-master.fhw.com 192.168.100.135 CentOS7.4 4C8G k8s-node1.fhw.com 192.168.100.136 CentOS7.4 2C8G k8s-node2.fhw.com 192.168.100.137 CentOS7.4 2C8G 1. 环境准备(所有节点) 关闭防火墙和禁用selinux systemctl stop firewalld setenforce 0 准备yum源 rm -rf /etc/yum.repos.d/* curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo sed -i '/aliyuncs/d' /etc/yum.repos.d/CentOS-Base.repo yum makecache fast yum install -y vim wget net-tools lrzsz cd /etc/yum.repos.d wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 准备kubernetes的Yum源文件 [root@k8s-master yum.repos.d]# cat kubernetes.repo [kubernetes] name=Kubernetes Repo baaseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg 安装docker yum install docker-ce -y 修改docker启动配置文件,在[Service]下加入如下内容:vim /usr/lib/systemd/system/docker.service Environment="NO_PROXY=127.0.0.0/8" Environment="NO_PROXY=172.20.0.0/16" Environment="NO_PROXY=reg.fhw.com" Environment="NO_PROXY=192.168.100.0/24" # 将自己的镜像仓库地址设置不通过代理访问 ExecStart=/usr/bin/dockerd --insecure-registry=reg.fhw.com --insecure-registry=192.168.100.0/24 ExecStart=/usr/bin/dockerd --insecure-registry=reg.fhw.com --insecure-registry=192.168.100.0/24 ExecStart=/usr/bin/dockerd --insecure-registry=reg.fhw.com --insecure-registry=192.168.100.0/24 启动Docker systemctl enable docker systemctl start docker 确保以下两个内核参数为1: [root@k8s-master ~]# cat /proc/sys/net/bridge/bridge-nf-call-iptables 1 [root@k8s-master ~]# cat /proc/sys/net/bridge/bridge-nf-call-ip6tables 1 kubelet相关文件介绍: [root@k8s-master ~]# rpm -ql kubelet /etc/kubernetes/manifests # 清单目录 /etc/sysconfig/kubelet # kubelet配置文件 /etc/systemd/system/kubelet.service # unit文件 /usr/bin/kubelet # 二进制可执行文件 设置kubelet开机自启 systemctl enable kubelet 此时,因为各种配置还没有初始化完成,初始化完成以后才能启动kubelet。 kubeadm init相关参数: [root@k8s-master ~]# kubeadm init --help Run this command in order to set up the Kubernetes master. Usage: kubeadm init [flags] Flags: --apiserver-advertise-address string API Server监听的IP地址,默认是0.0.0.0 --apiserver-bind-port int32 API Server监听的端口 (default 6443) --apiserver-cert-extra-sans strings Optional extra Subject Alternative Names (SANs) to use for the API Server serving certificate. Can be both IP addresses and DNS names. --cert-dir string 保存和存储证书的路径。(default "/etc/kubernetes/pki") --config string kubeadm配置文件的路径。 警告:配置文件的使用是实验性的。 --cri-socket string 指定要连接的CRI套接字。 (default "/var/run/dockershim.sock") --dry-run Don't apply any changes; just output what would be done. --feature-gates string A set of key=value pairs that describe feature gates for various features. Options are: Auditing=true|false (ALPHA - default=false) CoreDNS=true|false (default=true) DynamicKubeletConfig=true|false (ALPHA - default=false) SelfHosting=true|false (ALPHA - default=false) StoreCertsInSecrets=true|false (ALPHA - default=false) -h, --help help for init --ignore-preflight-errors strings 预检查时忽略哪些错误。Example: 'IsPrivilegedUser,Swap'. 值为“all”时,忽略所有检查到的错误。 --kubernetes-version string 指定Kubernetes版本。 (default "stable-1.11") --node-name string Specify the node name. --pod-network-cidr string 指定Pod网络的IP地址范围。 如果设置,控制平面将自动为每个节点分配CIDR。 --service-cidr string Service网络使用的IP地址范围。 (default "10.96.0.0/12") --service-dns-domain string Use alternative domain for services, e.g. "myorg.internal". (default "cluster.local") --skip-token-print Skip printing of the default bootstrap token generated by 'kubeadm init'. --token string The token to use for establishing bidirectional trust between nodes and masters. The format is [a-z0-9]{6}\.[a-z0-9]{16} - e.g. abcdef.0123456789abcdef --token-ttl duration The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). If set to '0', the token will never expire (default 24h0m0s) Global Flags: -v, --v Level log level for V logs 配置忽略Swap相关报错 [root@k8s-master ~]# cat /etc/sysconfig/kubelet KUBELET_EXTRA_ARGS="--fail-swap-on=false" 先拉取必要镜像并重新打tag (只在master节点操作即可) docker pull gcrxio/kube-apiserver-amd64:v1.11.3 docker pull gcrxio/kube-controller-manager-amd64:v1.11.3 docker pull gcrxio/kube-scheduler-amd64:v1.11.3 docker pull gcrxio/kube-proxy-amd64:v1.11.3 docker pull gcrxio/pause:3.1 docker pull gcrxio/etcd-amd64:3.2.18 docker pull gcrxio/coredns:1.1.3 docker tag gcrxio/kube-apiserver-amd64:v1.11.3 k8s.gcr.io/kube-apiserver-amd64:v1.11.3 docker tag gcrxio/kube-proxy-amd64:v1.11.3 k8s.gcr.io/kube-proxy-amd64:v1.11.3 docker tag gcrxio/kube-controller-manager-amd64:v1.11.3 k8s.gcr.io/kube-controller-manager-amd64:v1.11.3 docker tag gcrxio/kube-scheduler-amd64:v1.11.3 k8s.gcr.io/kube-scheduler-amd64:v1.11.3 docker tag gcrxio/coredns:1.1.3 k8s.gcr.io/coredns:1.1.3 docker tag gcrxio/etcd-amd64:3.2.18 k8s.gcr.io/etcd-amd64:3.2.18 docker tag gcrxio/pause:3.1 k8s.gcr.io/pause:3.1 k8s.gcr.io/pause:3.1是为pod提供底层基础的容器,它仅为pod分配网络名称空间,IP地址、主机名和存储卷。 2. kubeadm初始化 kubeadm init --kubernetes-version=v1.11.3 --pod-network-cidr=10.244.0.0/16 --service-cidr=10.96.0.0/12 --ignore-preflight-errors=Swap 出下如下截图,表示初始化成功 image.png 生产环境中,如图中提示,用一个普通用户配置后操作: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config 测试环境我就直接用root操作了。 查看各组件状态信息: [root@k8s-master ~]# kubectl get cs NAME STATUS MESSAGE ERROR controller-manager Healthy ok scheduler Healthy ok etcd-0 Healthy {"health": "true"} 在node1和node2执行以下命令,以加入k8s集群: kubeadm join 192.168.100.135:6443 --token edsptl.yqqaxnrzg3n09bk8 --discovery-token-ca-cert-hash sha256:ce4ee322a957729cdb0f59ba9d6be15edd585ba1a7a605e6c5a70f54aadc7d2f --ignore-preflight-errors=Swap # 拉镜像和打tag,以下两个镜像是node节点运行起来的必要镜像 docker pull gcrxio/pause:3.1 docker pull gcrxio/kube-proxy-amd64:v1.11.3 docker tag gcrxio/pause:3.1 k8s.gcr.io/pause:3.1 docker tag gcrxio/kube-proxy-amd64:v1.11.3 k8s.gcr.io/kube-proxy-amd64:v1.11.3 在master节点查看节点信息,可以看到node1和node2已经加入集群了: [root@k8s-master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master.fhw.com NotReady master 19m v1.11.3 k8s-node1.fhw.com NotReady <none> 48s v1.11.3 k8s-node2.fhw.com NotReady <none> 7s v1.11.3 之所为STATUS是NotReady (未就绪状态),是因为缺少网络插件flannel或calico。这里我们用flannel做为集群的网络插件。 3. 在master部署flannel # kubernetes 1.7以上的版本,可以直接执行如下命令部署flannel kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 出现下图,并不表示flannel已经部署成功了。 image.png 此时在拉取flannel的镜像,有点慢。。。,等待flannel的pod变为Running状态才是成功了。 image.png 从上图还可以看出coredns的状态为ImagePullBackOff, 这表示镜像拉取失败了。可以通过命令kubectl describe pod coredns-78fcdf6894-dqjzn -n kube-system查看Events : image.png 可以看到确实是拉取镜像k8s.gcr.io/coredns:1.1.3失败了。 再看一下coredns的pod是部署在哪个节点上: image.png 图中是部署在node1节点,再查看另一个coredns 的pod是部署在哪个节点上。然后在对应的节点主机上手动拉取镜像。 解决方法如下: # 拉取coredns的镜像再重新打tag docker pull coredns/coredns:1.1.3 docker tag coredns/coredns:1.1.3 k8s.gcr.io/coredns:1.1.3 镜像拉下来后,过一会再次查看coredns的状态是否为Running image.png 再看看各个节点状态 image.png 三个节点都已经是Ready状态了。 4. 扩展Pod的副本个数 获取deployment [root@k8s-master .kube]# kubectl get deployment -n kube-system NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE coredns 2 2 2 0 14h AVAILABLE为0表示coredns可用的pod个数为0,也就是部署两个coredns pod都没有成功,原因就是第3小节中coredns的镜像拉取失败造成的。 扩展pod数量 [root@k8s-master .kube]# kubectl scale --replicas=3 deployment/coredns -n kube-system deployment.extensions/coredns scaled coredns镜像拉取失败的问题解决后,再次查看deployment: [root@k8s-master ~]# kubectl get deployment -n kube-system NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE coredns 3 3 3 3 14h 此时3个coredns pod都正常运行,AVAILABLE值变为3了。 5. 配置kubectl子命令自动补全 先确认当前shell [root@k8s-master ~]# echo $SHELL /bin/bash 安装必要的包 yum install bash-completion -y # 当前shell为bash: source <(kubectl completion bash) echo "source <(kubectl completion bash)" >> ~/.bashrc # 如果当前shell为zsh: source <(kubectl completion zsh) echo "source <(kubectl completion zsh)" >> ~/.bashrc 6. 部署K8S Dashboard 6.1 部署 在Github上: https://github.com/kubernetes/dashboard 在这个页面上获取部署K8S Dashboard的命令: image.png 先将kubernetes-dashboard.yaml下载下来,修改一些东西 wget https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml 修改镜像为siriuszg/kubernetes-dashboard-amd64:v1.10.0, image.png 修改Service类型为NodePort,这样可以从K8S集群外部访问Dashboard;添加nodePort: 30001, 指定外部访问时用的端口。 image.png 执行如下命令部署Kubernetes Dashboard: kubectl apply -f kubernetes-dashboard.yaml 注意:以上命令依赖谷歌的镜像:k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.0,国内因为网络原因无法正常拉取此镜像。通过如下命令生成所需要的镜像: 如果遇到如下问题,注意查检firewalld服务是否停用。 Error while initializing connection to Kubernetes apiserver. This most likely means that the cluster is misconfigured (e.g., it has invalid apiserver certificates or service account's configuration) or the --apiserver-host param points to a server that does not exist. Reason: Get https://10.96.0.1:443/version: dial tcp 10.96.0.1:443: getsockopt: no route to host 停止firewalld服务:systemctl stop firewalld, systemctl disable firewalld, firewalld服务启动会影响K8S集群中的Pod之间相互访问。 获取kubernetes-dashboard的Service image.png 浏览器访问:https://192.168.100.135:30001/,出现下图界面 image.png 这表明Dashboard已经成功部署,接下来配置认证信息。 6.2 配置认证信息 从上图中可以看出有Kubeconfig和令牌两种认证方法。 认证时的账号必须为ServiceAccount: 被Dashboard Pod拿来由Kubernetes进行认证。 令牌认证(也就是token认证) 创建ServiceAccount,根据其管理目标,使用rolebinding或clusterrolebinding绑定至合理的role或clusterrole。 创建serviceaccount kubectl create serviceaccount dashboard-admin -n kube-system 将dashboard-admin与集群管理员建立绑定关系 [root@k8s-master ~]# kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin clusterrolebinding.rbac.authorization.k8s.io/dashboard-admin created --serviceaccount=NAMESPACE:SERVICEACCOUNT 获取dashboard-admin这个serviceaccount的secret信息: [root@k8s-master ~]# kubectl get secrets -n kube-system | grep dashboard-admin dashboard-admin-token-fg5s8 kubernetes.io/service-account-token 3 7m [root@k8s-master ~]# [root@k8s-master ~]# [root@k8s-master ~]# kubectl describe secrets dashboard-admin-token-fg5s8 -n kube-system Name: dashboard-admin-token-fg5s8 Namespace: kube-system Labels: <none> Annotations: kubernetes.io/service-account.name=dashboard-admin kubernetes.io/service-account.uid=0f74c4f6-d2d7-11e8-8078-000c29eced73 Type: kubernetes.io/service-account-token Data ==== ca.crt: 1025 bytes namespace: 11 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4tZmc1czgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMGY3NGM0ZjYtZDJkNy0xMWU4LTgwNzgtMDAwYzI5ZWNlZDczIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZC1hZG1pbiJ9.RzDorq-mQ8Mwb75LGKKC4vd5SRzxq7d7GxD9pG43xi4WCoftiIIBI38WvWGBn5MXYYFVwDqjkNfLMJStMTXiJomlLd7xJtGphdHJ8PR-jnyIGksDiNFicUOKjG7k6OGkKbCfIPb0cPglcNfceO8aNzRadiTasgoXLyxCpYRQd6kH4rx4UVLUWkEjWYTG4PNkNVjA4duRZ2FuKCdpqhysGwiQo9RMzUF9rTQToCQlu-N-UmsRqsGDAnx6GuZNX5BC97Yrzw1iRiFboekm2-RtGZPYczkVN7N9cP4GQPaNfoPVSpNPDx-y-Go_XqENdiniPQzwq9cUFON1l177hyo_RQ 上述输出结果中的token就是服务账户dashboard-admin的认证令牌。将token的值复制至浏览器登录。 image.png 登录成功。 创建Kubeconfig认证 这里我们创建一个只对default这个名称空间有操作权限的账户。 创建一个serviceaccount [root@k8s-master ~]# kubectl create serviceaccount def-ns-admin -n default serviceaccount/def-ns-admin created 由于我们想让def-ns-admin这个账号只对default名称空间有管理员权限,所以必须用rolebinding去绑定role,这样这个账号才只对这一个名称空间有管理员权限。 [root@k8s-master ~]# kubectl create rolebinding def-ns-admin --clusterrole=admin --serviceaccount=default:def-ns-admin rolebinding.rbac.authorization.k8s.io/def-ns-admin created 这里我把rolebinding和serviceaccount都命名为def-ns-admin,不要搞混淆了。 获取def-ns-admin的secret,利用它的token来生成kubeconfig文件: [root@k8s-master ~]# kubectl get secrets | grep def-ns-admin def-ns-admin-token-6jr4v kubernetes.io/service-account-token 3 9m [root@k8s-master ~]# [root@k8s-master ~]# [root@k8s-master ~]# [root@k8s-master ~]# kubectl describe secrets def-ns-admin-token-6jr4v Name: def-ns-admin-token-6jr4v Namespace: default Labels: <none> Annotations: kubernetes.io/service-account.name=def-ns-admin kubernetes.io/service-account.uid=02d5e211-d2d9-11e8-8078-000c29eced73 Type: kubernetes.io/service-account-token Data ==== ca.crt: 1025 bytes namespace: 7 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZi1ucy1hZG1pbi10b2tlbi02anI0diIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJkZWYtbnMtYWRtaW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIwMmQ1ZTIxMS1kMmQ5LTExZTgtODA3OC0wMDBjMjllY2VkNzMiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpkZWYtbnMtYWRtaW4ifQ.nrNVbuxrJra-cp3BOnWPzfbeQc_0Xe1E0xVzkDkxY6rJ9HexP4fbJIBvUmiqWyoFs5Yq9TT9wRQ_6kaOus0wjcjd7HWx5ZZzui-_UyQzvc3PRqwvWvn11ldLLHAz-7CM37Bcmm-eorj3JOeDvJF4OiloKiAPZ-9xxMQagqckSfa_VB7GGfOzOuRiRSi_fbC5ii81qCvAZxwgqIKFDhUUhXICOQVnZEW5FxMPdZAvYNziug6Ze7iPiAhFysq4ErUHBXzV6D1jghjNYiq01WkAbCmz7uLj7JVatJB9K28da-Wqc6cEBrP80uAfky9f4kz0SglxwqFVrXTd8X_GMqjd2Q set-cluster [root@k8s-master pki]# kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server="https://192.168.100.135:6443" --embed-certs=true --kubeconfig=/root/def-ns-admin.conf Cluster "kubernetes" set. 查看: [root@k8s-master pki]# kubectl config view --kubeconfig=/root/def-ns-admin.conf apiVersion: v1 clusters: - cluster: certificate-authority-data: REDACTED server: https://192.168.100.135:6443 name: kubernetes contexts: [] current-context: "" kind: Config preferences: {} users: [] set-credentials [root@k8s-master ~]# DEF_NS_ADMIN_TOKEN=$(kubectl get secrets def-ns-admin-token-6jr4v -o jsonpath={.data.token} | base64 -d) [root@k8s-master ~]# [root@k8s-master ~]# [root@k8s-master ~]# kubectl config set-credentials def-ns-admin --token=$DEF_NS_ADMIN_TOKEN --kubeconfig=/root/def-ns-admin.conf User "def-ns-admin" set. 查看 image.png set-context kubectl config set-context def-ns-admin@kubernetes --cluster=kubernetes --user=def-ns-admin --kubeconfig=/root/def-ns-admin.conf Context "def-ns-admin@kubernetes" created. 查看 image.png use-context [root@k8s-master ~]# kubectl config use-context def-ns-admin@kubernetes --kubeconfig=/root/def-ns-admin.conf Switched to context "def-ns-admin@kubernetes". image.png 现在/root/def-ns-admin.conf这个文件就可以做为Kubeconfig认证的文件了。 将def-ns-admin.conf这个文件复制至浏览器所在系统上。使用Kubeconfig认证方式: image.png 成功登录,并且只对default名称空间有管理权限: image.png

优秀的个人博客,低调大师

深入学习Java虚拟机——垃圾收集器与内存分配策略

垃圾回收操作的步骤:首先确定对象是否死亡,然后进行回收 1. 如何判断对象是否死亡 1.1 引用计数法 1.引用计数法:给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时就减1,任何时刻,计数器为0的对象是不可能在被使用的。 2.优缺点:优点是实现简单,判定效率高;缺点是很难解决对象间相互循环引用的问题,所以如今的主流Java虚拟机都没使用该方法进行管理内存。比如以下代码 /** * * @ClassName:ReferenceCountGC * @Description:引用计数法无法解决的对象间互相循环引用的问题 * @author: * @date:2018年7月29日 */ public class ReferenceCountGC { public Object obj; public static void main(String[] args) { ReferenceCountGC a=new ReferenceCountGC(); ReferenceCountGC b=new ReferenceCountGC(); a.obj=b; b.obj=a; a=null; b=null; //假设此处进行GC,若虚拟机采用引用计数法,则无法回收a,b两个对象 System.gc(); } } 1.2 可达性分析算法(根追踪算法) 1. 可达性分析算法:通过一系列的“GC Roots”的对象为起点,从这些节点开始往下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连,则证明该对象时不可用的。以下图为例, 对象obj1,2,3,4到GC Roots都是可达的,所以这四个对象都是不可回收的,而obj5,6,7虽然有引用关系,但无法到达GC Roots,所以他们将会被判定为可回收对象。 2. 可作为GC Roots的对象包括以下几种: (1)虚拟机栈中的引用的对象 (2)方法区中静态属性引用的对象 (3)方法区中常量引用的对象 (4)本地方法栈引用的对象 1.3 再谈引用 1. 引用分类: (1)强引用:类似于 Object obj=new Object() 这类的引用,只要强引用还存在,垃圾收集器永远不会回收该类引用的对象。 (2)软引用:用来描述一些好有用但并非必需的对象,在系统将要发生内存溢出异常之前,会把这些对象进行回收,如果这次回收之后还没有足够的内存就会发生内存溢出异常。JDK提供了SoftReference类来实现软引用。 (3)弱引用:用来描述非必需对象,但它的强度比弱引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前,无论内存是否足够,都会回收掉只被弱引用关联的对象。JDK使用WeakReference类来实现弱引用。 (4)虚引用:他是最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间产生影响,也无法通过一个虚引用来取得一个对象实例,一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。Jdk中使用PhantomReference类来实现虚引用。 1.4 对象是否死亡 1. 即使在可达性算法分析中不可达的对象,也并不是直接被判定为死亡,而是进行一次标记,要真正确定一个对象是否死亡,至少需要两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链那么他将会被第一次标记并进行第一次筛选,筛选的条件是该对象是否有必要执行finalize()方法,对象没有覆盖finalize()方法或finalize()方法已经被调用过,则都没有必要执行;如果被判定为有必要执行,则该对象会被放置在一个队列中,并在稍后由一个由虚拟机自动建立的、低优先级的finalizer线程去执行该队列中所有对象的finalize()方法,虚拟机会执行该对象的finalize()方法,但不会保证等待它执行结束,因为如果执行对象的finalize()方法时非常缓慢或发生死循环就有可能导致该队列中的其他对象处于等待中,甚至导致虚拟机崩溃。finalize()方法的执行是对象逃脱死亡的最后一次机会,在执行finalize()方法后,GC将对队列中的对象进行第二次小规模标记。如果在finalize()方法执行时重新建立与引用链上的任意一个对象建立关联即可避免回收,否则如果在此次finalize()执行后仍没有逃脱标记队列,那么基本就会被回收。对象自我拯救实例代码如下 public class FinalizeEscapeGC { public static FinalizeEscapeGC SAVE_HOOK=null; public void isAlive(){ System.out.println("Object is alive"); } //重写finalize()方法,第一次标记时使虚拟机判定该对象需要执行finalize()方法 @Override protected void finalize() throws Throwable { // TODO Auto-generated method stub super.finalize(); System.out.println("finalize() excute"); FinalizeEscapeGC.SAVE_HOOK=this;//此行代码对该对象进行的拯救 } public static void main(String[] args) throws InterruptedException { SAVE_HOOK=new FinalizeEscapeGC(); //初次拯救:成功 SAVE_HOOK=null; System.gc(); Thread.sleep(1000); if(SAVE_HOOK!=null){ SAVE_HOOK.isAlive(); }else{ System.out.println("Object is dead"); } //第二次拯救:失败 SAVE_HOOK=null; System.gc(); Thread.sleep(1000); if(SAVE_HOOK!=null){ SAVE_HOOK.isAlive(); }else{ System.out.println("Object is dead"); } } } 运行结果: finalize() excute Object is alive Object is dead 注意:每个对象的finalize() 方法只会被自动调用一次,在下一次回收时不会被执行,所以在上述代码中第一次拯救成功,第二次拯救失败。对于finalize()这个方法不推荐使用,或者说禁止使用,更推荐使用try-finally或者其他方式。 1.5 回收方法区 1. 方法区(在某些虚拟机中被称之为永久代)的垃圾收集主要回收两部分内容:废弃常量与无用的类。 (1)回收废弃常量与回收堆中的普通对象类似,以常量池中字面量的回收为例,假如一个字符串“abc”进入了常量池,但当前系统中没有任何一个String对象是叫做“abc”的,换句话说,也就是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用这个字面量,如果此时发生内存回收,而且有必要的话,这个“abc”就会被清理出常量池。常量池中其他的类(接口),方法,字段的符合引用也与此类似。 扩展:关于String的创建 (1)String str = "abc"创建对象的过程 1 首先在常量池中查找是否存在内容为"abc"字符串对象 2 如果不存在则在常量池中创建"abc",并让str引用该对象 3 如果存在则直接让str引用该对象 至 于"abc"是怎么保存,保存在哪?常量池属于类信息的一部分,而类信息反映到JVM内存模型中是对应存在于JVM内存模型的方法区,也就是说这个类信息 中的常量池概念是存在于在方法区中,而方法区是在JVM内存模型中的堆中由JVM来分配的,所以"abc"可以说存在于堆中。一般这种情况下,"abc"在编译时就被写入字节码中,所以class被加载时,JVM就为"abc"在常量池中 分配内存,所以和静态区差不多。 (2)String str = new String("abc")创建实例的过程 1 首先在堆中(不是常量池)创建一个指定的对象"abc",并让str引用指向该对象 2 在字符串常量池中查看,是否存在内容为"abc"字符串对象 3 若存在,则将new出来的字符串对象与字符串常量池中的对象联系起来 4 若不存在,则在字符串常量池中创建一个内容为"abc"的字符串对象,并将堆中的对象与之联系起来 (3)String str1 = "abc"; String str2 = "ab" + "c"; str1==str2是ture 是因为String str2 = "ab" + "c"会查找常量池中时候存在内容为"abc"字符串对象,如存在则直接让str2引用该对象,显然String str1 = "abc"的时候,上面说了,会在常量池中创建"abc"对象,所以str1引用该对象,str2也引用该对象,所以str1==str2 (4)String str1 = "abc"; String str2 = "ab"; String str3 = str2 + "c"; str1==str3是false 是因为String str3 = str2 + "c"涉及到变量(不全是常量)的相加,所以会生成新的对象,其内部实现是先new一个StringBuilder,然后 append(str2),append("c");然后让str3引用toString()返回的对象 (2)回收无用类:类需要满足3个条件才能算是无用类, 该类的所有的实例都已经被回收,也就是说堆中不存在该类以及其子类的任何对象 加载该类的ClassLoader已经被回收 该类对应的java.lang.Class对象没有在任何地方被引用,没有在任何地方通过反射来访问该类 满足以上三个条件就是无用类,此时虚拟机可以对其进行回收,但不是必须回收。在大量使用反射、动态代理、CGLib等频繁定义ClassLoader的场景都需要虚拟机具备类卸载功能,保证永久代不会溢出。 2. 对象的回收——垃圾收集算法 2.1 标记-清除算法(Mark-Sweep) 1. 算法思想:分为两个阶段,标记和清除;首先对要进行回收的对象进行标记,然后清除。 2. 缺点: (1)效率低,无论是标记过程还是清除过程效率都很低。 (2)浪费内存空间,标记清除后会造成大量的不连续的内存碎片,导致无法为后续分配较大内存的对象时无法分配,从而引起又一次的垃圾清理动作。 2.2 复制算法(Copying) 1. 算法思想:将内存分为大小相等的两块,每次只使用其中一块。当正在使用的这块内存即将用完时,就将所有存货的对象复制到另一块内存中,然后将使用过的上一块内存全部清空。 2. 优缺点:优点是效率相较于标记-清除算法较高,也不会存在大量内存碎片的情况,只需移动堆顶指针,顺序分配内存即可,实现简单。 缺点是对内存空间消耗大,可使用内存仅为原来的一半,内存代价高。 3. 应用:商业虚拟机大多选用该算法对堆中的新生代中的对象进行回收。对于新生代区域中的对象,几乎98%都是“朝生夕死”的,所以不需要按照1比1划分内存空间,而是将新生代的内存空间划分为较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden区和Survivor区存活的对象一次性复制到另一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认的Eden和Survivor区的大小比例时8:1,也就是说新生代中可用内存空间为整个新生代的90%。如果另一块Survivor空间没有足够的内存空间存放上一次垃圾回收从新生代中存活的对象时,这些对象将直接通过分配担保机制进入老年代。 2.3 标记-整理算法(Mark-Compact) 对于新生代所采用的的复制算法,在对象存活率较高时因为需要大量的复制操作而导致效率变低,并且还需要额外的空间进行分配担保避免浪费50%的新生代内存空间。而为了应对老年代区域存活率较高的特点,甚至是被使用内存中所有对象都全部存活的极端情况,所以不采用此算法。 1. 算法思想:首先是标记所有的可回收对象,然后将所有的存活的对象向同一端移动,保证所有的存活对象所占内存空间都是连续的时候,直接清理边界以外的内存。 2.4 分代收集算法 1. 算法思想:也就是依据对象的存活周期将内存分为几块。一般是吧Java堆分为新生代、老年代和永久代,永久代不做讨论。对于新生代和老年代分别采用不同的收集算法,以此保证垃圾收集的高效性。比如新生代中每次垃圾收集时都会有大量的对象死去,只有少量对象存活,那么就用复制算法。而老年代中对象存活率高而且没有额外空间对它进行分配担保,所以就必须使用标记-整理或标记-清除算法。 3. 垃圾收集器——对GC相关算法的实现 3.1 可达性分析算法中枚举根节点 1. 在可达性分析算法中需要从GC Roots节点找引用链,而可以作为GC Roots的节点主要是在全局性的引用(常量,静态属性等)以及执行上下文(栈桢中的本地变量表)中。但如果有方法区达到几百兆内存,此时在逐个检查引用,那么将会消耗大量时间。 另外,GC耗时的另一个体现为GC停顿,在GC工作正在进行时,Java虚拟机必须终止其他所有的Java执行线程,随着堆的扩大这个暂停时间也会越久,因为可达性分析工作必须在一个能确保一致性的快照中进行,“一致性”指的是在整个分析期间不可以出现对象引用关系处于不断变化的情况。所以对于 System.gc()方法时禁止在程序中使用的,因为显式声明是做堆内存全扫描,也就是 Full GC,是需要停止所有的活动的,也就是上面所说的终止其他所有线程,对于程序是无法接受的。 2. HotSpot虚拟机使用一组称为OopMap的数据结构来直接得知哪些地方存放对象引用,在类加载完成的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定位置记录下栈和寄存器中哪些位置是引用。 3.2 垃圾收集器 1. 收集算法是内存回收的方法论,而垃圾收集器就是内存回收的具体实现。对于不同的虚拟机可能会有不同的收集器实现,对于HotSpot虚拟机,其总共有6种垃圾收集器用于不同分代的垃圾收集。如下图所示,两个收集器之间的连线表示可以搭配使用,所处区域表示用于老年代或新生代。 没有最好的收集器,也没有万能的收集器,只有最合适的收集器。 并行垃圾收集器:指多条垃圾收集线程并行工作,但用户线程处于等待状态 并发垃圾收集器:指用户线程与垃圾收集线程并发执行或并行执行,用户程序继续执行,而垃圾收集线程运行在另一个CPU上 2. Serial收集器:单线程收集器,也就是说当它在进行垃圾收集时必须暂停其他所有线程,直到该收集器的线程执行结束,该动作由虚拟机后台自动执行,用户不可见。 (1)采用算法:新生代使用复制算法,老年代使用标记-整理算法。 (2)优缺点:在单线程情况下(也就是只有垃圾收集器线程执行),该收集器具有简单高效的特点,但问题就是GC时导致所有应用程序的暂停,所以在后来的收集器就出现了并发收集器,使暂停时间尽量缩短,但无法完全消除。 3. ParNew收集器:Serial收集器的并行版,可以使用多条线程收集垃圾,其余行为与Serial收集器完全相同,比如GC暂停,控制参数设置,应用的收集算法,对象分配策略和回收策略等。只有此收集器可以与CMS收集器配合工作。 (1)优缺点:与Serial收集器相比,其最大的优点就是可以使用多条线程进行垃圾回收,在单线程中其效率不会比Serial收集器更好,由于线程交互的开销,在CPU较少的情况下,都无法保证可以超越erial收集器。但是随着CPU数量的增加,其效率肯定要更好。缺点与Serial收集器相同,会发生GC时暂停现象。 4. Parallel Scavenge收集器:专用于新生代的收集器,使用复制算法,并且是并行的多线程收集器,用于达到一个可控制的 用户代码运行时间/(垃圾收集时间+用户程序运行时间),即吞吐量。虚拟机会依据当前系统运行状态自动调整Parallel Scavenge收集器的控制参数,比如停顿时间或最大的吞吐量,这种调节被称为GC自适应调节策略,而该策略也是Parallel Scavenge收集器与PreNew收集器的区别。 5. Serial Old收集器:该收集器是Serial收集器的老年代版本,即针对老年代进行收集。同样为单线程收集器,使用标记-整理算法。可以与Parallel Scavenge收集器搭配使用或作为CMS的后备方案。 6.Parallel Old收集器:Parallel Scavenge收集器的老年代版本,使用标记-整理算法,并行收集器。 7. CMS收集器:该收集器以获取最短停顿时间为目标的收集器,是并发收集器,使用标记-清除算法,分为4个步骤,初始标记,并发标记,重新标记,并发清除,其中初始标记和重新标记仍会发生GC暂停。初始标记是进行标记GC Roots直接关联的对象,并发标记是进行GC Roots根追踪,重新标记是修正并发标记期间用户程序继续运行而导致的标记变动的对象的标记记录。 (1)优缺点: 缺点有 CMS对CPU资源非常敏感 无法处理浮动垃圾导致可能出现“Concurrent Model Failure”失败而导致另一次Full GC的发生。由于用户程序的不断运行,那么就会有垃圾产生,但如果部分垃圾出现在标记之后就会导致CMS无法处理,只有在下一次GC时进行处理,这一部分垃圾就被称为浮动垃圾。 标记-清除算法导致的内存空间碎片化。 优点是响应速度快,系统停顿时间短。 8. 理解GC日志: 最前方的数字(比如 “ 88.11:”)表示GC活动发生的时间,即从虚拟机启动以来经过得秒数; 紧跟的“GC”或“Full GC”表示此处垃圾回收的停顿类型,“Full GC”表示会暂停其他所有用户程序的线程活动,而用户程序中显示调用System.gc()方法也会导致Full GC,所以不建议调用该方法; 而接下来的 [DefNew,[Tenured,[Perm分别表示在新生代,老年代或持久代进行的垃圾回收,但对于不同的收集器,对于对象分带的名称也可能不同;对于具体年代方括号内的如“333k->3k”表示GC前该内存区域使用量—>GC后该内存区域使用量,后再跟在这个区域GC所用时间; 而在方括号之外的表示GC前堆已使用空间—>GC后堆已使用空间。 4 内存分配与垃圾回收策略 4.1 内存分配 对象的内存分配,绝大部分在堆上分配,主要分配与新生代的Eden区,如果启动了T本地线程分配缓冲,按线程优先分配在TLAB上。少数情况下分配在老年代,没有绝对确定的分配规则。其细节取决于当前使用的是哪一种垃圾收集器组合。 1. 以下有几种较为普遍的对象分配策略: 绝大部分新对象优先在新生代中的Eden区分配,当Eden没有足够空间进行分配时,虚拟机则会进行一次新生代GC。 大对象直接进入老年代,大对象指大量连续内存空间的Java对象,比如极长的字符串或数组,在程序中更应该避免大量的”朝生夕死”的大对象。 虚拟机为每个对象定义了一个对象年龄计数器,如果对象在Eden区出生并经过第一次新生代GC后仍然存活,则对象年龄就会加1,并且该对象能被Survivor区容纳的话,就将还存活的对象将被复制到 Survivor 区(两个中的一个),当对象每熬过一次新生代GC后,年龄就会加1,当对象年龄超过15(默认,可以通过控制参数设置)时,将被复制“年老区(Tenured)”。 动态对象年龄判断,即虚拟机并不一定要求对象年龄必须达到最大年龄才能晋升老年代,当新生代中的相同年龄的存活对象的大小总和大于Survivor区的空间的一半时(也就是说正在使用的Survivor1区或Survivor2区内存空间不足时),年龄大于或等于该年龄的可以直接进入老年代。 空间分配担保,在进行新生代GC之前,虚拟机会检查老年代最大可用连续内存空间是否大于新生代所有对象总空间,如果成立那么新生代GC就是安全的。但是可能会出现新生代GC后新生代中有大量的对象存活,导致Survivor区无法容纳,此时就需要老年代分配担保,把Survivor区无法容纳的对象直接进入老年代,但前提是老年代本身具有足够的空间,而是否采用这种方式承担风险是可以通过控制参数设置的。

优秀的个人博客,低调大师

论文 | 用迁移学习解释:电商网站的用户评论应如何优化排序?

小蚂蚁说: 2018年6月初,自然语言处理三大顶会之一NAACL (Annual Conference of the North American Chapter of the Association for Computational Linguistics)在美国新奥尔良举行,吸引了世界各地的自然语言处理等领域的学者参加。 据统计,NAACL今年共收到了长短论文1092篇,蚂蚁金服人工智能部也有一篇论文被录用《Cross-Domain Review Helpfulness Prediction based on Convolutional Neural Networks with Auxiliary Domain Discriminators》,作者:陈岑、杨寅飞、周俊、李小龙、鲍盛。论文链接:http://aclweb.org/

优秀的个人博客,低调大师

(5)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 熔断降级(Polly)

一、 什么是熔断降级 熔断就是“保险丝”。当出现某些状况时,切断服务,从而防止应用程序不断地尝试执行可能会失败的操作给系统造成“雪崩”,或者大量的超时等待导致系统卡死。 降级的目的是当某个服务提供者发生故障的时候,向调用方返回一个错误响应或者替代响应。举例子:调用联通接口服务器发送短信失败之后,改用移动短信服务器发送,如果移动短信服务器也失败,则改用电信短信服务器,如果还失败,则返回“失败”响应;在从推荐商品服务器加载数据的时候,如果失败,则改用从缓存中加载,如果缓存中也加载失败,则返回一些本地替代数据。 二、 Polly 简介 .Net Core 中有一个被.Net 基金会认可的库 Polly,可以用来简化熔断降级的处理。主要功能:重试(Retry);断路器(Circuit-breaker);超时检测(Timeout);缓存(Cache);降级(FallBack); 官网:https://github.com/App-vNext/Polly 介绍文章:https://www.cnblogs.com/CreateMyself/p/7589397.html Nuget安装指令:Install-Package Polly -Version 6.0.1 Polly 的策略由“故障”和“动作”两部分组成,“故障”包括异常、超时、返回值错误等情况,“动作”包括 降级(FallBack)、重试(Retry)、熔断(Circuit-breaker)等。 策略用来执行可能会有有故障的业务代码,当业务代码出现“故障”中情况的时候就执行“动作”。 由于实际业务代码中故障情况很难重现出来,所以 Polly 这一些都是用一些无意义的代码模拟出来。 Polly 也支持请求缓存“数据不变化则不重复自行代码”,但是和新版本兼容不好,而且功能局限性很大,因此这里不讲。 由于调试器存在,看不清楚 Polly 的执行过程,因此本节都用【开始执行(不调试)】 三、Polly简单使用 使用Policy的静态方法创建ISyncPolicy实现类对象,创建方法既有同步方法也有异步方法,根据自己的需要选择。下面先演示同步的,异步的用法类似。 举例:当发生ArgumentException异常的时候,执行Fallback代码。 Policy policy = Policy .Handle<ArgumentException>() //故障 .Fallback(() =>//动作 { Console.WriteLine("执行出错"); }); policy.Execute(() => {//在策略中执行业务代码 //这里是可能会产生问题的业务系统代码 Console.WriteLine("开始任务"); throw new ArgumentException("Hello world!"); Console.WriteLine("完成任务"); }); Console.ReadKey(); 如果没有被Handle处理的异常,则会导致未处理异常被抛出。 还可以用Fallback的其他重载获取异常信息: Policy policy = Policy .Handle<ArgumentException>() //故障 .Fallback(() =>//动作 { Console.WriteLine("执行出错"); },ex=> { Console.WriteLine(ex); }); policy.Execute(() => { //在策略中执行业务代码 //这里是可能会产生问题的业务系统代码 Console.WriteLine("开始任务1"); throw new ArgumentException("Hello1 world!"); Console.WriteLine("完成任务"); }); 如果Execute中的代码是带返回值的,那么只要使用带泛型的Policy<T>类即可: Policy<string> policy = Policy<string> .Handle<Exception>() //故障 .Fallback(() =>//动作 { Console.WriteLine("执行出错"); return "降级的值"; }); string value = policy.Execute(() => { Console.WriteLine("开始任务"); throw new Exception("Hello world!"); Console.WriteLine("完成任务"); return "正常的值"; }); Console.WriteLine("返回值:"+value); FallBack的重载方法也非常多,有的异常可以直接提供降级后的值。 (*)异常中还可以通过lambda表达式对异常判断“满足***条件的异常我才处理”,简单看看试试重载即可。还可以多个Or处理各种不同的异常。 (*)还可以用HandleResult等判断返回值进行故障判断等,我感觉没太大必要。 四、重试处理 Policy policy = Policy .Handle<Exception>() .RetryForever(); policy.Execute(() => { Console.WriteLine("开始任务"); if (DateTime.Now.Second % 10 != 0) { throw new Exception("出错"); } Console.WriteLine("完成任务"); }); RetryForever()是一直重试直到成功 Retry()是重试最多一次; Retry(n) 是重试最多n次; WaitAndRetry()可以实现“如果出错等待100ms再试还不行再等150ms秒。。。。”,重载方法很多,不再一一介绍。还有WaitAndRetryForever。 五、 短路保护 Circuit Breaker 出现N次连续错误,则把“熔断器”(保险丝)熔断,等待一段时间,等待这段时间内如果再Execute 则直接抛出BrokenCircuitException异常,根本不会再去尝试调用业务代码。等待时间过去之后,再执行Execute的时候如果又错了(一次就够了),那么继续熔断一段时间,否则就恢复正常。 这样就避免一个服务已经不可用了,还是使劲的请求给系统造成更大压力。 Policy policy = Policy .Handle<Exception>() .CircuitBreaker(6,TimeSpan.FromSeconds(5));//连续出错6次之后熔断5秒(不会再去尝试执行业务代码)。 while(true) { Console.WriteLine("开始Execute"); try { policy.Execute(() => { Console.WriteLine("开始任务"); throw new Exception("出错"); Console.WriteLine("完成任务"); }); } catch(Exception ex) { Console.WriteLine("execute出错"+ex); } Thread.Sleep(500); } 其计数的范围是policy对象,所以如果想整个服务器全局对于一段代码做短路保护,则需要共用一个policy对象。 六、策略封装,包裹Warp 可以把多个ISyncPolicy合并到一起执行: policy3= policy1.Wrap(policy2); 执行policy3就会把policy1、policy2封装到一起执行。 Policy的静态方法Wrap可以把更多的policy一起封装: policy9=Policy.Wrap(policy1, policy2, policy3, policy4, policy5); 七、超时处理 这些处理不能简单的链式调用,要用到Wrap。例如下面实现“出现异常则重试三次,如果还出错就FallBack”这样是不行的 Policy policy = Policy.Handle<Exception>().Retry(3).Fallback(()=> { Console.WriteLine("执行出错"); });//这样不行,系统会直接报错 注意Wrap是有包裹顺序的,内层的故障如果没有被处理则会抛出到外层。 下面代码实现了“出现异常则重试三次,如果还出错就FallBack” Policy policyRetry = Policy.Handle<Exception>().Retry(3); //出现异常重试三次 Policy policyFallback = Policy .Handle<Exception>() .Fallback(()=> { Console.WriteLine("降级"); }); //Wrap:包裹。policyRetry在里面,policyFallback裹在外面。 //如果里面出现了故障,则把故障抛出来给外面 Policy policy = policyFallback.Wrap(policyRetry); policy.Execute(()=> { Console.WriteLine("开始任务"); if (DateTime.Now.Second % 10 != 0) { throw new Exception("出错"); } Console.WriteLine("完成任务"); }); 运行结果: Timeout是定义超时故障,如果超时会抛出TimeoutRejectedException异常。 Policy policy = Policy.Timeout(3, TimeoutStrategy.Pessimistic);// 创建一个3秒钟(注意单位)的超时策略。 Timeout生成的Policy要和其他Policy一起Wrap使用。 超时策略一般不能直接用,而是和其他封装到一起用: Policy policy = Policy .Handle<TimeoutRejectedException>() //定义所处理的故障 .Fallback(() => { Console.WriteLine("降级"); }); policy = policy.Wrap(Policy.Timeout(2,TimeoutStrategy.Pessimistic)); policy.Execute(()=> { Console.WriteLine("开始任务"); Thread.Sleep(5000); Console.WriteLine("完成任务"); }); 执行结果: 上面的代码就是如果执行超过2秒钟,则直接Fallback。 这个的用途:请求网络接口,避免接口长期没有响应造成系统卡死。 八、Polly 的异步用法 所有方法都用Async方法即可,Handle由于只是定义异常,所以不需要异常方法: 带返回值的例子: Policy<byte[]> policy = Policy<byte[]> .Handle<Exception>() .FallbackAsync(async c => { Console.WriteLine("降级"); return new byte[0]; },async r=> { Console.WriteLine(r.Exception); }); policy = policy.WrapAsync( Policy.TimeoutAsync(2, TimeoutStrategy.Pessimistic, async(context, timespan, task) => { Console.WriteLine("timeout"); })); var bytes = await policy.ExecuteAsync(async () => { Console.WriteLine("开始任务"); HttpClient httpClient = new HttpClient(); var result = await httpClient.GetByteArrayAsync("http://static.rupeng.com/upload/chatimage/20183/07EB793A4C247A654B31B4D14EC64BCA.png"); Console.WriteLine("完成任务"); return result; }); Console.WriteLine("bytes长度"+bytes.Length); 执行结果: 没返回值的例子: Policy policy = Policy .Handle<Exception>() .FallbackAsync(async c => { Console.WriteLine("降级"); },async ex=> {//对于没有返回值的,这个参数直接是异常 Console.WriteLine(ex); }); policy = policy.WrapAsync(Policy.TimeoutAsync(3, TimeoutStrategy.Pessimistic, async(context, timespan, task) => { Console.WriteLine("timeout"); })); await policy.ExecuteAsync(async () => { Console.WriteLine("开始任务"); await Task.Delay(5000);//注意不能用Thread.Sleep(5000); Console.WriteLine("完成任务"); }); 执行结果: 注:此文章是我看杨中科老师的.Net Core微服务第二版和.Net Core微服务第二版课件整理出来的 现在的努力只是为了更好的将来,将来你一定不会后悔你现在的努力。一起加油吧!!! C#/.NetCore技术交流群:608188505欢迎加群交流 如果您认为这篇文章还不错或者有所收获,您可以点击右下角的【推荐】按钮精神支持,因为这种支持是我继续写作,分享的最大动力!

优秀的个人博客,低调大师

(3)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Consul服务治理

Consul是注册中心,服务提供者、服务提供者、服务消费者等都要注册到Consul中,这样就可以实现服务提供者、服务消费者的隔离。 除了Consul之外,还有Eureka、Zookeeper等类似软件。 Consul就是来存储服务器名称与IP和端口对应关系的服务器 一、 consul 服务器安装 consul 下载地址 https://www.consul.io/ 墙外的网站 需要科学上网 cmd运行 consul.exe agent -dev 这是开发环境测试,生产环境要建集群,要至少一台 Server,多台 Agent (因为如果只有一台的话,如果服务死了 整个网站就出问题了) 开发环境中 consul 重启后数据就会丢失。 consul 的监控页面 http://127.0.0.1:850 consult 主要做三件事:提供服务到 ip 地址的注册;提供服务到 ip 地址列表的查询;对提供服务方的健康检查(HealthCheck); 二、 .Net Core 连接 consul 1. Nuget - > Install-Package Consul 安装Consul 2. 提供一个HealthCheck API接口 用于Consul做健康检查调用,只要返回个结果就可以 不论是什么结果。如 [Route("api/[controller]")] public class HealthController : Controller { [HttpGet] public IActionResult Get() { return Ok("ok"); } } 3.服务注册 Consul 及注销 public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); string ip = Configuration["ip"]; int port = Convert.ToInt32(Configuration["port"]); string serviceName = "MsgService"; string serviceId = serviceName + Guid.NewGuid(); using (var client = new ConsulClient(ConsulConfig)) { //注册服务到 Consul ServiceRegister是一个异步方法 client.Agent.ServiceRegister(new AgentServiceRegistration() { ID = serviceId,//服务编号,不能重复,用 Guid 最简单 Name = serviceName,//服务的名字 Address = ip,//服务提供者的能被消费者访问的 ip 地址(可以被其他应用访问的地址,本地测试可以用 127.0.0.1,机房环境中一定要写自己的内网 ip 地址) Port = port,// 服务提供者的能被消费者访问的端口 Check = new AgentServiceCheck { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务停止多久后反注册(注销) Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔,或者称为心跳间隔 HTTP = $"http://{ip}:{port}/api/health",//健康检查地址 Timeout = TimeSpan.FromSeconds(5) } }).Wait();//Consult 客户端的所有方法几乎都是异步方法,但是都没按照规范加上Async 后缀,所以容易误导。记得调用后要 Wait()或者 await } //程序正常退出的时候从 Consul 注销服务 //要通过方法参数注入 IApplicationLifetime //程序结束的时候会调用这个方法 applicationLifetime.ApplicationStopped.Register(()=> { using (var client = new ConsulClient(ConsulConfig)) { client.Agent.ServiceDeregister(serviceId).Wait(); } }); } private void ConsulConfig(ConsulClientConfiguration c) { c.Address = new Uri("http://127.0.0.1:8500"); c.Datacenter = "dc1"; } 小哥哥 小姐姐们 如果本篇文章对你们有帮助的话 就点点右下角的推荐吧 0.0 现在的努力只是为了更好的将来,将来你一定不会后悔你现在的努力。一起加油吧!!! C#/.NetCore技术交流群:608188505欢迎加群交流 如果您认为这篇文章还不错或者有所收获,您可以点击右下角的【推荐】按钮精神支持,因为这种支持是我继续写作,分享的最大动力!

优秀的个人博客,低调大师

Mysql HA方案之MySQL半复制+MHA+Keepalived+Atlas+LVS(学习笔记十九)

http://hugnew.com/?p=749&utm_source=tuicool&utm_medium=referral 架构图 角色 ip地址 主机名 MHA Manger 209 Mysql Master 226 Mysql slave1 227 Mysql slave2 228 Candicate master 230 Atlas1 210 Atlas2 214 LVS Master 231 LVS Backup 232 MHA 1、所有服务器:yum install perl-DBD-MySQL -y unzipmha4mysql-node-master.zip mvmha4mysql-node-master /usr/local/ cd/usr/local/mha4mysql-node-master yum install -y perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRescpanperl-ExtUtils-CBuilder perl-ExtUtils-MakeMakerperl-ExtUtils-Embed cpan ExtUtils::Install cpan Module::Install perl Makefile.PL make&&make install

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册