Istio 入门(三):体验 Istio、微服务部署、可观测性
本教程已加入 Istio 系列:https://istio.whuanle.cn
3,快速入门
在本章中,我们正式迈入学习 Istio 的第一步。因为 Istio 的知识体系是较为庞大的,因此我们可以先通过本章的入门教程快速了解如何使用 Istio 部署一套微服务,以及 Istio 核心功能的使用方法,了解 Istio 可以为微服务解决什么问题。
在本章中,我们将会学习到如何部署一套微服务、如何使用 Istio 暴露服务到集群外,并且如何使用可观测性组件监测流量和系统指标。
在后面的章节中,笔者会针对每个 Istio 组件做单独讲解,而在本章中,我们只需要大概了解使用方法即可。
书店微服务
本章教程示例使用的是 Istio 官方的一套微服务,这套微服务是一个在线书店,打开页面之后会看到一个分类、书的信息以及书的评论,页面的内容由不同的子服务提供。
书店微服务分为四个单独的微服务,在上图中已经使用红色方框画出来了。这四个微服务分别是:
productpage
: 汇集所有服务的数据,生成浏览页面。details
:存储了书籍的信息,如描述、作者、出版社等。reviews
:存储了书籍相关的评论,但是不包含评分打星。ratings
:存储评论中的评分打星。
在这个微服务中,Productpage 服务对外提供 Web 访问页面,而且其它的三个服务只能在集群内部访问。四个服务分别采用了不同的语言开发,Productpage 聚合其它三个服务的信息生成一个页面。
在微服务设计中,我们不要每个子服务都暴露端口到集群外部,应该通过一些应用集中数据后给外部显示。我们可以使用 API 网关,代理子服务一部分接口,然后在 API 网关中实现基于客户端或第三方调用的身份验证。
productpage、details、ratings 都只有一个 v1 版本,而 reviews 有三个版本。
ratings 负责给出用户打分的数据,例如一星、两星。而 reviews 三个版本分别对 ratings 的数据做以下处理:
- reviews v1:屏蔽星级,不显示打分;
- reviews v2:显示星级,使用灰色星星表示,★★★★☆;
- reviews v3:显示星级,使用红色星星表示,★★★★☆;
【图源 istio 官网】
服务依赖图如下所示:
接下来我们将会使用 Kuubernetes Deployment 部署这些服务,这跟常规的 Kubernetes 部署并无差别。
预先准备
给这些示例服务创建一个命名空间。
kubectl create namespace bookinfo
给命名空间添加 Istio 的标签,指示 Istio 在部署应用(只对 Pod 起效)的时候,自动注入 Envoy Sidecar Proxy 容器:
kubectl label namespace bookinfo istio-injection=enabled
开启让 Istio 注入 Sidecar 有很多种方式,其中一种是给命名空间设置下标签,在此命名空间下部署的 Pod,会被自动注入 Sidecar 。
你可以从本系列教程的 git 仓库中找到这些示例,文件位置:https://github.com/whuanle/istio_book/tree/main/3。
仓库拉取后打开 3
目录,执行命令进行部署:
kubectl -n bookinfo apply -f details_deploy.yaml
kubectl -n bookinfo apply -f details_svc.yaml
kubectl -n bookinfo apply -f details_sa.yaml
kubectl -n bookinfo apply -f ratings_deploy.yaml
kubectl -n bookinfo apply -f ratings_svc.yaml
kubectl -n bookinfo apply -f ratings_sa.yaml
kubectl -n bookinfo apply -f reviews_v1_deploy.yaml
kubectl -n bookinfo apply -f reviews_v2_deploy.yaml
kubectl -n bookinfo apply -f reviews_v3_deploy.yaml
kubectl -n bookinfo apply -f reviews_svc.yaml
kubectl -n bookinfo apply -f reviews_sa.yaml
kubectl -n bookinfo apply -f productpage_deploy.yaml
kubectl -n bookinfo apply -f productpage_svc.yaml
kubectl -n bookinfo apply -f productpage_sa.yaml
或者你可以参考下面的四个小节,通过手动的方式部署应用,了解每一个应用是如何定义的。
details 应用
存储了书籍信息的应用。
部署命令:
kubectl -n bookinfo apply -f details_deploy.yaml
kubectl -n bookinfo apply -f details_svc.yaml
kubectl -n bookinfo apply -f details_sa.yaml
使用 Deployment 部署 details 应用。
details_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: details-v1
labels:
app: details
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: details
version: v1
template:
metadata:
labels:
app: details
version: v1
spec:
serviceAccountName: bookinfo-details
containers:
- name: details
image: docker.io/istio/examples-bookinfo-details-v1:1.17.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
securityContext:
runAsUser: 1000
部署 details。
kubectl -n bookinfo apply -f details_deploy.yaml
为 details 服务配置 Kubernetes Service 。
details_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: details
labels:
app: details
service: details
spec:
ports:
- port: 9080
name: http
selector:
app: details
kubectl -n bookinfo apply -f details_svc.yaml
接下来为 details 服务创建一个 ServiceAccount。
Istio 为服务之间的通信提供基于双向 TLS 的认证,这是是通过给每个 ServiceAccount 创建一个证书实现的,可以使用 ServiceAccount 验证对方的身份,不同的应用可以共享同一个 ServiceAccount,但是为每个 Deployment 使用单独的 ServiceAccount 可以更好地组织和管理安全配置。
details_sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-details
labels:
account: details
kubectl -n bookinfo apply -f details_sa.yaml
ratings 应用
提供每条评论的打星数据。
部署命令:
kubectl -n bookinfo apply -f ratings_deploy.yaml
kubectl -n bookinfo apply -f ratings_svc.yaml
kubectl -n bookinfo apply -f ratings_sa.yaml
ratings_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ratings-v1
labels:
app: ratings
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: ratings
version: v1
template:
metadata:
labels:
app: ratings
version: v1
spec:
serviceAccountName: bookinfo-ratings
containers:
- name: ratings
image: docker.io/istio/examples-bookinfo-ratings-v1:1.17.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
securityContext:
runAsUser: 1000
ratings_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: ratings
labels:
app: ratings
service: ratings
spec:
ports:
- port: 9080
name: http
selector:
app: ratings
ratings_sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-ratings
labels:
account: ratings
reviews v1/v2/v3 应用
提供书籍的评论信息。
部署命令:
kubectl -n bookinfo apply -f reviews_v1_deploy.yaml
kubectl -n bookinfo apply -f reviews_v2_deploy.yaml
kubectl -n bookinfo apply -f reviews_v3_deploy.yaml
kubectl -n bookinfo apply -f reviews_svc.yaml
kubectl -n bookinfo apply -f reviews_sa.yaml
为三个版本的 reviews 创建三个 Deployment。
reviews_v1_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v1
labels:
app: reviews
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v1
template:
metadata:
labels:
app: reviews
version: v1
spec:
serviceAccountName: bookinfo-reviews
containers:
- name: reviews
image: docker.io/istio/examples-bookinfo-reviews-v1:1.17.0
imagePullPolicy: IfNotPresent
env:
- name: LOG_DIR
value: "/tmp/logs"
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
- name: wlp-output
mountPath: /opt/ibm/wlp/output
securityContext:
runAsUser: 1000
volumes:
- name: wlp-output
emptyDir: {}
- name: tmp
emptyDir: {}
reviews_v2_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v2
labels:
app: reviews
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v2
template:
metadata:
labels:
app: reviews
version: v2
spec:
serviceAccountName: bookinfo-reviews
containers:
- name: reviews
image: docker.io/istio/examples-bookinfo-reviews-v2:1.17.0
imagePullPolicy: IfNotPresent
env:
- name: LOG_DIR
value: "/tmp/logs"
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
- name: wlp-output
mountPath: /opt/ibm/wlp/output
securityContext:
runAsUser: 1000
volumes:
- name: wlp-output
emptyDir: {}
- name: tmp
emptyDir: {}
reviews_v3_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v3
labels:
app: reviews
version: v3
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v3
template:
metadata:
labels:
app: reviews
version: v3
spec:
serviceAccountName: bookinfo-reviews
containers:
- name: reviews
image: docker.io/istio/examples-bookinfo-reviews-v3:1.17.0
imagePullPolicy: IfNotPresent
env:
- name: LOG_DIR
value: "/tmp/logs"
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
- name: wlp-output
mountPath: /opt/ibm/wlp/output
securityContext:
runAsUser: 1000
volumes:
- name: wlp-output
emptyDir: {}
- name: tmp
emptyDir: {}
给三个 Deployment 创建一个 Service,三个相同应用的不同版本共有同一个 Service。
reviews_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: reviews
labels:
app: reviews
service: reviews
spec:
ports:
- port: 9080
name: http
selector:
app: reviews
reviews_sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-reviews
labels:
account: reviews
productpage 应用
页面聚合服务,供用户浏览书籍信息。
部署命令:
kubectl -n bookinfo apply -f productpage_deploy.yaml
kubectl -n bookinfo apply -f productpage_svc.yaml
kubectl -n bookinfo apply -f productpage_sa.yaml
productpage_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: productpage-v1
labels:
app: productpage
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: productpage
version: v1
template:
metadata:
labels:
app: productpage
version: v1
spec:
serviceAccountName: bookinfo-productpage
containers:
- name: productpage
image: docker.io/istio/examples-bookinfo-productpage-v1:1.17.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
securityContext:
runAsUser: 1000
volumes:
- name: tmp
emptyDir: {}
productpage_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: productpage
labels:
app: productpage
service: productpage
spec:
ports:
- port: 9080
name: http
selector:
app: productpage
productpage_sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-productpage
labels:
account: productpage
检查
执行命令完成后,查看 bookinfo 命名空间下的 Pod。
kubectl get pods -n bookinfo
可以看到,每个 Pod 的 READY 属性都是 2/2
,这表示该 Pod 中有两个容器,并且当前有两个容器已经就绪。
如果我们查看其中一个 Pod 的组成结构,会发现有 Pod 被塞进了一个 istio-proxy 容器。
如果 Kubernetes 中没有安装 Dashbooard ,那么可以使用
kubectl -n bookinfo describe pod {Pod ID}
查看组成结构。
接着使用 kubectl -n bookinfo get svc
查看 Service,四个微服务都已经被注册了 Service。
然后我们访问 productpage 对应的 CLUSTER-IP:
curl 10.233.37.130:9080
默认 Istio 不会开启零信任双向认证模式,因此在集群内可以自己访问应用。如果开启了 mTLS 双向认证模式,则只能在 Pod 中访问应用。
可以看到返回了一堆 html,说明我们的部署是正常的。
临时访问
接着为了查看页面效果,我们在暂未使用 Istio-ingressgateway 之前,临时创建一个 Service 暴露 productpage 的页面。
productpage_tmpsvc.yaml
apiVersion: v1
kind: Service
metadata:
name: productpagetmp
labels:
app: productpage
service: productpage
spec:
ports:
- port: 9080
name: http
selector:
app: productpage
type: NodePort
kubectl -n bookinfo apply -f productpage_tmpsvc.yaml
查看所有 Service:
root@k8smain:/data/learn/book# kubectl -n bookinfo get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
details ClusterIP 10.233.63.247 <none> 9080/TCP 40m
productpage ClusterIP 10.233.37.130 <none> 9080/TCP 23m
productpagetmp NodePort 10.233.47.14 <none> 9080:30258/TCP 77s
ratings ClusterIP 10.233.7.6 <none> 9080/TCP 36m
reviews ClusterIP 10.233.58.219 <none> 9080/TCP 23m
然后在页面中访问 30258 端口(大家的端口不一样,按自己的来)。
接着打开 http://192.168.3.150:30258/productpage?u=normal
。
因为当前使用 Service 绑定 Pod,因此会使用轮询实现负载均衡,你可以多次刷新 http://192.168.3.150:30258/productpage?u=normal
,会查到右侧的评分星星有所变化。
Istio 默认情况下使用轮询负载均衡的方法。
页面右侧评论显示规则是 无星星 => 黑色星星 => 红色星星。
部署入口网关
什么是 Gateway
终于来到体验 Istio 的时刻了,在本小节中,我们将会为 productpage 创建 Istio Gateway,对外提供网页访问。
在第二章中,我们已经部署了 istio-ingressgateway,这个组件起到了类似 nginx、apisix 的效果,对外提供端口访问,然后将流量转发到内部服务中。
但是 istio-ingressgateway 并不能直接转发流量到 Pod,它还需要进行一些配置。我们要为 productpage 创建一个站点,绑定对应的域名,这样外部访问 istio-ingressgateway 的端口时,istio-ingressgateway 才知道该将流量转发给谁。在 Istio 中,定义这种绑定关系的资源叫 Gateway。
后面的章节会解释清楚,这里大概了解即可。
Gateway 类似 Nginx 需要创建一个反向代理时需要绑定的域名配置。
部署 Gateway
创建一个 Gateway,绑定域名入口。
ingress_gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
hosts
表示对外开放的访问路径,你可以绑定域名、IP 等。这里使用*
,表示所有访问都可以进入此网关。
kubectl -n bookinfo apply -f ingress_gateway.yaml
这一步就像 nginx 的监听配置:
server {
listen 80;
server_name example.org www.example.org;
#...
}
当我们创建 Istio Gateway 之后,istio-ingressgateway 会为我们监控流量,检测不同的域名或端口属于哪个 Istio Gateway 。
部署 VirtualService
什么是 VirtualService
虽然创建了 Istio Gateway,但是我们还不能直接通过网关访问到前面部署的微服务,我们还需要创建 Istio VirtualService 将 Istio Gateway 跟对应的 Kubernetes Service 绑定起来,然后流量才能正式流向 Pod。
请一定要注意这里,流量实际并不会经过 Service 中,但是 VirtualService 需要通过 Service 来发现 Pod。
这里类似 nginx 配置反向代理,配置监听之后,还需要指向将请求映射到哪个地址。
server {
listen 80;
server_name example.org www.example.org;
#...
}
location /some/path/ {
proxy_pass http://A:9080;
}
为什么不直接将 Gateway 跟 Service 绑定,而是中间加个 VirtualService 呢?有句话叫做,计算机领域中的问题,都可以通过增加一个层来解决。
VirtualService 的主要目标是为服务提供稳定的入口地址,并通过配置一系列的路由规则来控制流量在网格内的行为。
就以最简单的路由区配来说,Kubernetes Service 是不支持路由规则的,而 Istio 可以通过指定路由后缀中;Service 不支持流量分析,负载均衡只有轮询。而 Istio 利用 Service 来发现 Pod,然后直接将流量转发到 Pod 中,可以实现各种功能。
VirtualService 可以用于实现以下功能:
请求路由:将请求路由到特定的服务或版本,例如将请求分发到不同版本的服务,以实现灰度发布或金丝雀发布。
请求重试:为失败的请求配置重试策略,以提高服务的可用性。
请求超时:设置请求的超时时间,以便在特定时间内没有得到响应时中断请求。
请求镜像:将请求的副本发送到另一个服务,用于测试新版本的服务,而不影响实际的生产流量。
流量分割:将流量按照特定的比例分发到不同的服务或版本,以实现流量控制。
部署 VirtualService
productpage_vs.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- "*"
gateways:
- bookinfo-gateway
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port:
number: 9080
kubectl -n bookinfo apply -f productpage_vs.yaml
关于 VistualService 中各种配置的作用,在 4.1 章节中会有介绍。
这里的 YAML 分为两大部分,第一部分是 http.match
,表示暴露了哪些 API 地址,外部访问时只能访问到这些地址。
可以通过 http.match
限制集群外部访问此地址时所能使用的 URL。
然后通过 http.route
绑定 Kubernetes Service ,通过 Service 中的服务发现,将流量转发到对应的 Pod 中。
host 这里,由于 VirtualService 跟 Service/Pod 在同一个命名空间中,所以只需要配置 Service 的名称即可,如果要跨命名空间访问,则需要加上完整的命名空间名称。
什么是 DestinationRule
在本章中,会提前预告 DestinationRule,下一章才会使用 DestinationRule,这里我们知道还有 DestinationRule 这个东西即可。
Istio VistualService 中可以限制外部能够访问的路由地址,而 DestinationRule 则可以配置访问的 Pod 策略。可以为 Istio VistualService 绑定一个 Istio DestinationRule,通过 DestinationRule 我们还可以定义版本子集等,通过更加丰富的策略转发流量。
由于只暴露了五个地址,所以外部直接访问
/
,是打不开页面的。
检查
为了确保网关没问题,我们需要执行 Istio 命令查看日志:
istioctl analyze
然后我们查看为 productpage 创建的网关。
root@k8smain:/data/learn/book# kubectl get gw -A
NAMESPACE NAME AGE
bookinfo bookinfo-gateway 26m
Kubernetes 本身也有一个 Gateway,因此不能使用
kubectl get gateway
来获取 Istio 的 Gateway,而是使用简写gw
。
然后查看 VistualService。
root@k8smain:/data/learn/book# kubectl get vs -A
NAMESPACE NAME GATEWAYS HOSTS AGE
bookinfo bookinfo ["bookinfo-gateway"] ["*"] 79m
在第二章中,我们通过 Helm 部署了 istio-ingressgateway,其访问端口如下:
在本节部署 bookinfo-gateway 的时候,我们使用了端口 80,因此不需要另外配置 ,直接通过 istio-ingressgateway 的 32309 端口访问即可。
访问时一定需要带
/productpage
,因为我们并没有放通/
。
尝试修改 Gateway 端口
如果需要更换端口,可以修改 istio-ingressgateway 的 Service,增加新的端口映射。
kubectl edit svc istio-ingressgateway -n istio-system
然后修改前面的 ingress_gateway.yaml
,将端口从 80 改成 666 。
即可通过 32666 端口访问到此微服务。
示例:http://192.168.3.150:30666/productpage
Istio 入门(三):体验 Istio、微服务部署、可观测性的更多相关文章
- docker微服务部署之:三,搭建Zuul微服务项目
docker微服务部署之:二.搭建文章微服务项目 一.新增demo_eureka模块,并编写代码 右键demo_parent->new->Module->Maven,选择Module ...
- 解读与部署(三):基于 Kubernetes 的微服务部署即代码
在基于 Kubernetes 的基础设施即代码一文中,我概要地介绍了基于 Kubernetes 的 .NET Core 微服务和 CI/CD 动手实践工作坊使用的基础设施是如何使用代码描述的,以及它的 ...
- 将微服务部署到 Azure Kubernetes 服务 (AKS) 实践
本文是对 <.NET Tutorial - Deploy a microservice to Azure> 的翻译和实践.入门级踩坑实践,k8s 大佬请回避,以免耽误您宝贵的时间. 介绍 ...
- 转载 (三)surging 微服务框架使用系列之我的第一个服务(审计日志)
(三)surging 微服务框架使用系列之我的第一个服务(审计日志) 前言:前面准备了那么久的准备工作,现在终于可以开始构建我们自己的服务了.这篇博客就让我们一起构建自己的第一个服务---审计日志 ...
- docker微服务部署之:七、Rancher进行微服务扩容和缩容
docker微服务部署之:六.Rancher管理部署微服务 Rancher有两个特色用起来很方便,那就是扩容和缩容. 一.扩容前的准备工作 为了能直观的查看效果,需要修改下demo_article项目 ...
- docker微服务部署之:六、Rancher管理部署微服务
docker微服务部署之:五.利用DockerMaven插件自动构建镜像 一. 什么是Rancher Rancher是一个开源的企业级容器管理平台.通过Rancher,企业再也不必自己使用一系列的开源 ...
- docker微服务部署之:五、利用DockerMaven插件自动构建镜像
docker微服务部署之:四.安装docker.docker中安装mysql和jdk1.8.手动构建镜像.部署项目 在上一篇文章中,我们是手动构建镜像,即: 4.1.2.5.1.2.6.1.2中的将d ...
- docker微服务部署之:四、安装docker、docker中安装mysql和jdk1.8、手动构建镜像、部署项目
docker微服务部署之:三,搭建Zuul微服务项目 1.Centos7安装Docker 详见:Centos7安装Docker 2.Docker中安装jdk1.8 详见:使用Docker构建jdk1. ...
- docker微服务部署之:二、搭建文章微服务项目
docker微服务部署之:一,搭建Eureka微服务项目 一.新增demo_article模块,并编写代码 右键demo_parent->new->Module->Maven,选择M ...
- docker微服务部署之:一,搭建Eureka微服务项目
先说明一下docker需要搭建的微服务的基本情况: 项目情况:一个demo_parent项目,下面三个子模块:demo_eureka(eureka服务).demo_article(文章服务).demo ...
随机推荐
- PHP利用 JSON 将XML转换为数组
在很多开发项目中,我们都会遇到将XML文件转换为数组使用,因此在本篇 PHP教程 中,UncleToo和大家一起学习 如何转换XML为数组 . 现在有一个uncletoo.xml的配置文件,格式如下: ...
- API Gateway vs Load Balancer:选择适合你的网络流量管理组件
本文从对比了 API Gateway 和 Load Balancer 的功能区别,帮助读者更好地了解他们在系统架构中扮演的角色. 作者陈泵,API7.ai 技术工程师. 原文链接 由于互联网技术的发展 ...
- 【GPT开发】人人都能用ChatGPT4.0做Avatar虚拟人直播
0 前言 最近朋友圈以及身边很多朋友都在研究GPT开发,做了各种各样的小工具小Demo,AI工具用起来是真的香!在他们的影响下,我也继续捣鼓GPT Demo,希望更多的开发者加入一起多多交流. 上一篇 ...
- [ Docker ] 部署 nps 和 npc 实现内网穿透
https://www.cnblogs.com/yeungchie/ 云主机上运行 nps 创建映射目录 mkdir -p ~/docker/nps/config 拉取镜像 docker pull o ...
- 魔王语言问题c语言实现及思路求解
文章目录 问题描述及需求分析 需求分析 问题分析及实现路线(队列+栈) 结构体核心代码 队列核心代码 入队 出队 栈的核心代码 入栈 出栈 全部代码 单向循环队列和栈(改变B的对应密码) 总结 问题描 ...
- Go坑:time.After可能导致的内存泄露问题分析
Go 中 time.After 可能导致的内存泄露 一.Time 包中定时器函数 go v1.20.4 定时函数:NewTicker,NewTimer 和 time.After 介绍 time 包中有 ...
- 【C#】图片上传并根据长宽大小进行正方形、长方形及等比缩放。
#region 正方型裁剪并缩放 /// <summary> /// 正方型裁剪 /// 以图片中心为轴心,截取正方型,然后等比缩放 /// 用于头像处理 /// </summary ...
- Web进阶LNMP网站部署
Web进阶LNMP网站部署 目录 Web进阶LNMP网站部署 LNMP架构工作流程 部署LNMP架构 1.安装nginx 2.安装php 3.安装数据库 将Nginx和PHP建立连接 1.修改ngin ...
- 2021-04-10:给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null。【要求】如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。
2021-04-10:给定两个可能有环也可能无环的单链表,头节点head1和head2.请实现一个函数,如果两个链表相交,请返回相交的 第一个节点.如果不相交,返回null.[要求]如果两个链表长度之 ...
- 【Vue2.x源码系列08】Diff算法原理
什么是虚拟DOM DOM是很慢的,其元素非常庞大,当我们频繁的去做 DOM更新,会产生一定的性能问题,我们可以直观感受一下 div元素包含的海量属性 在Javascript对象中,虚拟DOM 表现为一 ...