基于Istio的高级流量管理二(Envoy流量劫持、Istio架构、高级流量管理)
文章目录
- 一、Envoy流量劫持机制(Iptables规则流转)
- 1、流量出向劫持流程
- (1)envoy怎样劫持入向流量?
- (2)Envoy劫持到流量之后,干什么?(查询目的地)
- (3)获取目的地址之后,怎么到达目的地呢?
- 2、流量入向劫持流程
- 3、流量出入总结图
- 二、Istio、Envoy整体请求流程架构
- 三、Istio高级流量管理实践
- 1、Istio流量控制简介
- 2、Istio基本配置对象解析
- (1)Gateway(一般用IngressGateway,EgressGateway一般不用)
- (2)VirtualService
- 3、istio实际配置场景
- (1)规则匹配的优先(类似location规则匹配的优先级)
- (2)rewrite跳转
- (3)设置HTTPS网站
- (4)金丝雀发布
- (5)两个版本服务之间按比例拆分流量
- (6)超时与重试
- (7)错误注入(混沌测试)
- (8)条件规则
- (9)流量镜像
- (10)规则委托
- (11)熔断器与服务访问请求数限制
- (12)外部服务纳入Istio管控
- (13)遥测(监控收集请求的metrics)
- 4、请求跟踪(服务调用链路的可观测性)
- (1)jaeger的安装
- (2)jaeger的使用
- (3)链路监控的必要条件 Headers 传递
- (4)采集频率控制
在看下面内容之前,先看下这个图,你必须要明白, 什么时候查路由表做路由判决,什么时候去匹配iptables规则。
一、Envoy流量劫持机制(Iptables规则流转)
k create ns sidecar// 设置这个 namespace 要自动注入 istio sidecar
kubectl label namespace sidecar istio-injection=enabled
其实,一个业务pod被创建,不只是会注入一个envoy sidecar,还会注入一个init container
,它的主要作用就是设置一些iptables,使所有请求业务pod的流量和业务pod请求出去的流量都要被envoy抓上来,即下图所描述的那样:
我们这个部分的目的就是讲清楚这些iptables规则,让你对Envoy的流量劫持有个详细的了解!
现在我们要在一个toolbox容器内,访问一个nginx容器,他们都是在sidecar命名空间下,执行下面这个命令:
# 因为在一个命名空间所以可以省去后缀
curl nginx
未使用service mesh
,一般的数据包流转是,kube_DNS将nginx解析成service ip,变成访问service ip,然后因为我们现在在容器中嘛,因此先通过默认路由到主机,后面就是在主机上的操作了,主机发现访问service ip,先做routing decision,因为service没有路由
(因为serivce ip其实就是一个虚拟ip,他的作用就是对一组pod ip的映射,因此iptables postrouting就是完成这个映射的),那么路由判决应该就走主机默认路由所属的网卡出
。再根据主机上的kube-proxy 配置的iptables规则 kube_svc chain 去以1/3、1/2、1的概率分别转到几个pod中。多说一句,kube proxy实际上就是一个控制循环,watch service 、endpoint、node资源的变更,然后通过修改ipvs或者iptables达到转发到后端pod的目的
。
那么用了service mesh又该怎样流转呢?
1、流量出向劫持流程
(1)envoy怎样劫持入向流量?
curl nginx,首先也还是kube_DNS解析成service ip,然后再做route decision(注意这里是在容器上做的路由判决
,上面未使用service mesh的数据包流转
的流程,是在主机上做的路由判决,其实也要在容器上做路由判决,但是由于主要操作都在主机上,因此只说了主机的路由判决,因此就没提容器的,但是istio主要操作是在容器里的),没有service路由,路由判决应该就走容器默认路由所属的网卡出
,然后就到了output chain
、postrouting chain(istio没有postrouting)。下面就是Initcontainer注入的所有Iptables规则
:
docker inspect 5273deaee3a6|grep -i pid
nsenter -t 2777406 -n iptables-save -t nat
15001是什么进程呢?他是envoy监听的端口
,用于接收出向流量的,15006是用于接收入向流量的。这样流量就进入了envoy。
(2)Envoy劫持到流量之后,干什么?(查询目的地)
注意envoy对流量分为outbound(15001端口)、inbound(15006端口),而且用了两个端口进行监听,然后因为我们要访问80端口,因此找outbound 80
//然后ctrl f在页面搜索15001,往下找80监听器, 当然你也可以指定参数--port 80,直接找到listener
// 因为要展示virtualOutbound就没指定参数
[root@vms120 httpbin]# istioctl proxy-config listener httpbin-85d76b4bb6-nrlqv -o json {"name": "virtualOutbound", // 虚拟监听器"address": {"socketAddress": {"address": "0.0.0.0","portValue": 15001}},
..."name": "0.0.0.0_80","address": {"socketAddress": {"address": "0.0.0.0","portValue": 80}},"filterChains": [{"filterChainMatch": {"transportProtocol": "raw_buffer","applicationProtocols": ["http/1.1","h2c"]},"filters": [{"name": "envoy.filters.network.http_connection_manager","typedConfig": {"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager","statPrefix": "outbound_0.0.0.0_80", // outbound 80"rds": {"configSource": {"ads": {},"initialFetchTimeout": "0s","resourceApiVersion": "V3"},"routeConfigName": "80" // 路由配置命名为80
有人可能会纳闷,我80服务这么多,各个服务都要起个outbound 80监听器,这我咋识别,注意这个80只是做个分类,不是监听
,所有80端口的服务都用这一个listener
,然后用一个路由配置(比如上面的“80”)。至于到底那个服务,匹配那个路由还要根据domain来识别
。
然后通过查看80 的RDS,确定自己需要转发的对象,因此我们执行:
// 这里面有所有80服务的路由,可以通过domains分辨那个服务匹配的是那个路由
[root@vms120 httpbin]# istioctl proxy-config route httpbin-85d76b4bb6-nrlqv --name 80 -ojson
{"name": "nginx.sidecar.svc.cluster.local:80","domains": ["nginx.sidecar.svc.cluster.local","nginx.sidecar.svc.cluster.local:80","nginx","nginx:80","nginx.sidecar.svc","nginx.sidecar.svc:80","nginx.sidecar","nginx.sidecar:80","10.99.179.59","10.99.179.59:80"],"routes": [{"name": "default","match": {"prefix": "/" // 不管匹配的URL是什么,都会交给下面的cluster处理},"route": {"cluster": "outbound|80||nginx.sidecar.svc.cluster.local", // cluster name
接下来我们就看"cluster": "outbound|80||nginx.sidecar.svc.cluster.local"
是什么。
[root@vms120 httpbin]# istioctl proxy-config cluster httpbin-85d76b4bb6-nrlqv --fqdn=nginx.sidecar.svc.cluster.local
SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE
nginx.sidecar.svc.cluster.local 80 - outbound EDS
我们可以发现CDS下面又关联了个EDS,那我们查一下这个EDS是干什么的。
[root@vms120 httpbin]# istioctl proxy-config endpoint httpbin-85d76b4bb6-nrlqv --cluster outbound|80||nginx.sidecar.svc.cluster.local
ENDPOINT STATUS OUTLIER CHECK CLUSTER
10.244.216.30:80 HEALTHY OK outbound|80||nginx.sidecar.svc.cluster.local
// 可以看到 CLUSTER nginx.sidecar.svc.cluster.local实际上的终点地址为 10.244.216.30:80
查看 pod 的IP 就是 CLUSTER nginx.sidecar.svc.cluster.local实际上的终点
[root@vms120 httpbin]# kubectl get po -A -o wide|grep 10.244.216.30
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-85b98978db-dbdjl 2/2 Running 2 (5h25m ago) 22h 10.244.216.30 vms121.rhce.cc <none> <none>
上面只是基本的寻址,你可以通过在istio配置各种复杂的流量管理策略,从而改变LDS、RDS配置,从而达到流量管理的效果。
(3)获取目的地址之后,怎么到达目的地呢?
现在我们知道了目的地是10.244.216.30:80,envoy就会设置目的地址为10.244.216.30,然后发出数据包,那么又会经过routing decision,目的是pod ip,在容器上路由判决还是在默认路由网卡,又会经过output,主机上又会进入ISTIO_OUTPUT Chain。
这时候你可以回去看之前的iptables规则,你就会发现这样一条规则:
# 这个的意思就是来源于group id等于1337(Envoy)的数据包直接return放行
-A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN
2、流量入向劫持流程
进来先到prerouting chain,init container iptables规则如下:
如上图我们可以看到,是redirect到了15006端口,这个是envoy监听的端口,专门用作进流量的(inbound),出流量用的是15001。
到了Envoy以后,还是会先去找对应listener,15006是一个virtualInbound虚拟监听器,然后我们要找inbound 80 listener:
# 查看所有listener包括inbound outbound
// 其中输出的0.0.0.0
istioctl proxy-config listener nginx-5c78d859b9-ckd9z -n sidecar --port 80
我们从上图可以看出,outbound和inbound还是有点不一样,毕竟outbound的所有80服务的路由配置都在一个Route里面,但是inbound却是每个都拆开了
,但是内容的确都是一样的。
然后根据Route的名字查询详细信息:
istioctl proxy-config route nginx-5c78d859b9-ckd9z -n sidecar --name music-quiz.music.svc.cluster.local:80 -o json
根据上面cluster中的fqdn,查看cluster信息:
istioctl proxy-config cluster nginx-5c78d859b9-ckd9z -n sidecar --fqdn music-quiz.music.svc.cluster.local
发现是个EDS,那就查endpoint信息:
# 根据cluster名字查endpoint
istioctl proxy-config endpoint nginx-5c78d859b9-ckd9z -n sidecar --cluster "outbound|80||music-quiz.music.svc.cluster.local" -o json
这里就可以得出ip了。其实上面这些路由配置、cluster、ep这些都和outbound是一样的。拿到pod ip发现是本地,然后envoy又发出去,又走output,gid 1337,因此return掉了,然后根据ip就路由到了业务pod。
3、流量出入总结图
二、Istio、Envoy整体请求流程架构
架构图如下(红方框代表istio watch哪些对象
,红圆框是代码解读)
三、Istio高级流量管理实践
上面这个图是流量管理的常见业务场景,上图是金丝雀发布
,下图是按照客户端的设备类型进行分流
。
1、Istio流量控制简介
重点:Istio统一了入向流量(ingress)
、mesh流量(envoy)
、出向流量(egress)
。
istio 引入了服务版本的概念
,可以通过版本(v1、v2)或环境(staging、prod)对服务进一步的细分。这些版本不一定是不同的API版本,他们可能是部署在不同环境(staging、prod或者dev 等)中的同一服务的不同迭代(使用这种方式的常见场景包括A/B 测试或者金丝雀部署
)。
istio 的流量路由规则可以根据服务版本来对服务之间流量进行附加控制
。
- 故障处理
- 微调
Istio流量管理规则允许运维人员为每个服务/版本设置故障恢复的全局默认值
。然而,服务的消费者也可以通过特殊的HTTP头提供的请求级别值覆盖超时和重试的默认值。在Envoy代理的实现中,对应的header分别是x-envoy-upstream-rq-timeout-ms和x-envoy-max-retries
。 - 故障注入(混沌测试)
(1)为什么需要错误注入:微服务架构下,需要测试端到端的故障恢复能力
。
(2)Istio允许在网络层面按协议注入错误来模拟错误
,无需通过应用层面删除pod,或者人为在TCP层造成网络故障来模拟。
(3)注入的错误可以基于特定的条件,可以设置出现错误的比例:Delay
- 提高网络延时、Aborts
- 直接返回特定的错误码 - Envoy 在负载均衡池中的实例之间分发流量
(1)istio 目前仅允许三种负载均衡模式:轮询
、随机
、和带权重的最少请求
。
(2)除了负载均衡外,Envoy 还会定期检查池中每个实例的运行状况。Envoy遵循熔断器风格模式, 根据健康检查API调用的失败率将实例分类为不健康和健康两种
。当给定实例的健康检查失败次数超过预定阈值时,将会被从负载均衡池中弹出。类似地,当通过的健康检查数超过预定國值时,该实例将被添加回负载均衡池。您可以在处理故障中了解更多有关 Envoy 的故障处理功能。
(3)服务可以通过使用 HTTP 503 响应健康检查来主动减轻负担
。在这种情况下,服务实例将立即从调用者的负载均衡池中删除。
2、Istio基本配置对象解析
之前分析了在pod中访问service的mesh流量的流程,是必须要经过envoy的。接下来我们要实践的是用户从外部访问域名的流量控制流程,这个是从IngressGateway进来的
。因为金丝雀发布这些都是面向用户的这种外部流量嘛。
(1)Gateway(一般用IngressGateway,EgressGateway一般不用)
IngressGateway这其实对应的就是Envoy Listener
。
[root@vms120 networking]# cat bookinfo-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:name: bookinfo-gateway
spec:selector:istio: ingressgateway // 这筛选的就是IngressGateway podservers:- port:number: 80name: httpprotocol: HTTPhosts:- "bookinfo.bianmc.com"
上面这个的语义就是向istio: ingressgateway这个selector筛选的pod的envoy插入一个监听80端口、http协议、domain为bookinfo.bianmc.com 的listener
(2)VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:name: bookinfo
spec:hosts:- "bookinfo.bianmc.com"gateways:- bookinfo-gateway // 指定gateways 转发规则,要与这个listener关联http:- match:- port: 80 // 默认,可以不写uri:exact: /productpageroute:- destination:host: productpageport:number: 9080
解读:访问域名 bookinfo.bianmc.com 如果 match 端口80,路径 /productpage ,就 destination 转发到目的地名为 productpage 的svc,svc的端口为9080,所以 VirtualService 对应的是Envoy中的路由 route 转发规则
。
除了这两个基本对象之外,还有DestinationRule(金丝雀发布)、ServiceEntry和WorkloadEntry(外部服务纳入istio管控)三个扩展对象
,这个就在后面讲解。
3、istio实际配置场景
(1)规则匹配的优先(类似location规则匹配的优先级)
重点看红色部分的字,规则匹配是从上到下匹配,当匹配到之后就不再往后匹配,因此我们应该把精确匹配,范围较小的匹配放在前面,像匹配所有这种或者范围大的应该放在后面
。
(2)rewrite跳转
rewrite跳转规则,只用修改VirtualService即可。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:name: bookinfo
spec:hosts:- "bookinfo.bianmc.com"gateways:- bookinfo-gatewayhttp:- match:- uri:exact: /productpage/hellorewrite: // 跳转到那个urluri: /productpageroute:- destination:host: productpageport:number: 9080
服务版本更新,原来的/productpage/hello接口被废弃了,现在变成了/productpage接口,但是用户不知道这个呀,用户还是去访问/productpage/hello,因此我们要将访问uri: /productpage/hello跳转到uri: /productpage的访问。
(3)设置HTTPS网站
- 生成 https 证书
[root@vms120 ~]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=bianmc Inc./CN=*.bianmc.io' -keyout bianmc.io.key -out bianmc.io.crt
Generating a 2048 bit RSA private key
..................................+++
...........................................................................+++
writing new private key to 'bianmc.io.key'
-----
- 创建存放证书的 secret ,也可以在 VirtualService 中直接调用证书文件
[root@vms120 ~]# kubectl create secret tls bianmc-credential --key=bianmc.io.key --cert=bianmc.io.crt -n istio-system
secret/bianmc-credential created
- 创建 Gateway 和 VirtualService
[root@vms120 networking]# cat bookinfo-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:name: bookinfo-gateway
spec:selector:istio: ingressgateway # use istio default controllerservers:- port:number: 443 // https 端口name: https protocol: HTTPS // 协议HTTPStls: // 指定 存放证书的 secretmode: SIMPLEcredentialName: bianmc-credential // 也可以在 VirtualService 中直接调用证书文件 // serverCertificate: /etc/istio/ingressgateway-certs/tls.crt// privateKey: /etc/istio/ingressgateway-certs/tls.keyhosts:- "bookinfo.bianmc.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:name: bookinfo
spec:hosts:- "bookinfo.bianmc.com"gateways:- bookinfo-gatewayhttp:- match:- uri: exact: /productpageport: 443 // 监听端口修改route:- destination:host: productpageport:number: 9080
测试:
[root@vms120 ~]# curl --resolve bookinfo.bianmc.com:443:10.101.89.49 https://bookinfo.bianmc.com/productpage -v -k
(4)金丝雀发布
现在我们有两个版本的Service,一个version: v1,一个version: v2,现在我们就要对这两个版本做精细的流量控制,配置如下:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:name: canary
spec:hosts:- canaryhttp:- match:- headers:user:exact: jesseroute:- destination:host: canarysubset: v2- route:- destination:host: canarysubset: v1
我们先来看上面这个配置,匹配host = canary,匹配headers中 user = jesse这个用户的请求路由会发送subset: v2去,subset v2这是什么,这就引出DestinationRule这个对象:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:name: canary
spec:host: canarytrafficPolicy:loadBalancer:simple: RANDOMsubsets:- name: v1labels:version: v1- name: v2labels:version: v2trafficPolicy:loadBalancer:simple: ROUND_ROBIN
观察这个对象我们可以看出,首先匹配host = canary,然后定义了一个默认的后端pod负载均衡策略是RANDOM,然后又定义了两个subset v1和v2,v1匹配Service中有label version: v1的,且采用默认的RANDOM策略,v2匹配Service中有label version: v2的,采用ROUND_ROBIN 轮询策略
。
合在一起,就是jesse这个用户的请求会发送给version: v2 Service,且Service到后端pod的负载均衡策略是轮询。其他用户的请求会发送给version: v1 Service,且负载均衡策略是随机的
。这样就做到了金丝雀发布。像什么新版本尝鲜申请,就是给你的用户请求,打了个header标记newversion = true,所有带着这个的请求都发到新版本,这样你就得到新版本的页面。
(5)两个版本服务之间按比例拆分流量
注意两个加起来weight值必须要达到100%。
(6)超时与重试
(7)错误注入(混沌测试)
(8)条件规则
(9)流量镜像
mirror规则可以使Envoy截取所有request,并在转发请求的同时,将request转发至Mirror版本中
,同时在Header的Host/Authority加上-shadow。注意这些请求会工作在 fire and forget模式,所有response都会被丢弃
。
(10)规则委托
主要是减少主配置的复杂度,各个子业务线可以灵活配置自己的下面各个更小服务的路由规则。
(11)熔断器与服务访问请求数限制
注意熔断器是outlierDetection里面定义的,熔断器服务访问请求数限制这个是为了保护服务做的设置
(12)外部服务纳入Istio管控
Istio内部会维护一个服务注册表,可以用ServiceEntry向其中加入额外的条目。通常这个对象用来启用对istio服务网格之外的服务发出请求
。
ServiceEntry中使用hosts字段来指定目标,字段值可以是一个完全限定名,也可以时个通配符域名
。
这样就把一个外部的服务,变成了集群内部的一个Service了。就可以纳入istio管理。
(13)遥测(监控收集请求的metrics)
4、请求跟踪(服务调用链路的可观测性)
在微服务中往往一次请求会尽力N多服务,那么每个服务的响应状态这个业务经过哪些服务对开发或问题排查就显得额外重要,链路监控是其中的一种解决方案,把微服务中的调用链进行记录并且通过可视化的方式进行展示,行业中相对成熟的解决方案就是zipkin,但是因为zipkin的界面并不是那么友好一般我们配合着jaeger进行使用
,istio也对它进行了整合.
(1)jaeger的安装
- 部署jaeger
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.17/samples/addons/jaeger.yaml
- Jaeger 部署完毕后,需要指定 Istio 代理向 jaeger Deployment 发送流量。在安装时,可以使用以下命令进行配置。
istioctl upgrade --set values.global.tracer.zipkin.address=<jaeger-collector-address>:9411
- jaeger service采用nodeport暴露出去,这样我们就可以通过浏览器访问jaeger的web页面了
> kubectl edit svc jaeger-query -n istio-systemports:- name: query-httpport: 16686protocol: TCPtargetPort: 16686nodePort: 30686selector:app: jaegersessionAffinity: Nonetype: NodePort
(2)jaeger的使用
在 Jaeger dashboard里从Service下选择productpage,点击Find Traces 按钮,可以看到跟踪信息:
进到下一层可以看到每个服务的调用层次以及总体消耗时间的分布:
再展开可以看到更多的相关内容:
(3)链路监控的必要条件 Headers 传递
为什么使用服务网格之后还需要传递指定的Headers呢? 这里就要从链路监控的机制来说了,在服务网格之前需要链路监控每个程序都需要向链路监控服务器发送消息,由第一个程序找链接监控发起ID获取,接下来的每个程序被调用的时候都需要告知链路监控系统我是在这个链路ID之中,此时才能关联整个链路.
虽然 Istio 代理能够自动发送 Span 信息,但还是需要一些辅助手段来把整个跟踪过程统一起来。应用程序应该自行传播跟踪相关的 HTTP Header,这样在代理发送 Span 信息的时候,才能正确的把同一个跟踪过程统一起来。
为了完成跟踪的传播过程,应用应该从请求源头中收集下列的 HTTP Header,并传播给外发请求:
x-request-id
x-b3-traceid
x-b3-spanid
x-b3-parentspanid
x-b3-sampled
x-b3-flags
x-ot-span-context
如果查看示例服务,可以看到productpage服务(Python)从HTTP请求中提取所需的标头:
def getForwardHeaders(request):headers = {}incoming_headers = [ 'x-request-id','x-b3-traceid','x-b3-spanid','x-b3-parentspanid','x-b3-sampled','x-b3-flags','x-ot-span-context']for ihdr in incoming_headers:val = request.headers.get(ihdr)if val is not None:headers[ihdr] = val#print "incoming: "+ihdr+":"+valreturn headers
上面是python的写法,go语言的写法有点不一样:
func rootHandler(w http.ResponseWriter, r *http.Request) {// 我现在是service0,然后去调用service1,我在调用service1的时候需要把header传过去req, err := http.NewRequest("GET", "http://service1", nil)if err != nil {fmt.Printf("%s", err)}lowerCaseHeader := make(http.Header)// go和python不一样的地方在于,把header拿出来的时候会把首字母变成大写,我们需要转成小写for key, value := range r.Header {lowerCaseHeader[strings.ToLower(key)] = value}req.Header = lowerCaseHeaderclient := &http.Client{}resp, err := client.Do(req)if err != nil {glog.Info("HTTP get failed with error: ", "error", err)} else {glog.Info("HTTP get succeeded")}if resp != nil {resp.Write(w)}glog.V(4).Infof("Respond in %d ms", delay)
}
(4)采集频率控制
Istio 默认捕获所有请求的跟踪。例如,何时每次访问时都使用上面的 Bookinfo 示例应用程序 / productpage你在 Jaeger 看到了相应的痕迹仪表板。链路监控每次和链路服务器通讯也是有性能消耗的,在一个每天千万pv的业务下把所有链路全部采集下来是不合适的
,无论从CPU还是磁盘空间都很容易出现瓶颈,并且链路监控并不是日志是一种排查手段,所以我们需要在生产环境下进行采集频率的限制
:
找到pilot中PILOT_TRACE_SAMPLING
环境变量从100%修改成10%的采集率:
> kubectl -n istio-system edit deploy istio-pilot
...- name: PILOT_TRACE_SAMPLINGvalue: "10"
...
再去刷新页面10次在JaegerUI只会看到一次调用,这边最小精度是0.01%有效值是0.0~100.0(不需要此功能可以完全不开启)
相关文章:
基于Istio的高级流量管理二(Envoy流量劫持、Istio架构、高级流量管理)
文章目录一、Envoy流量劫持机制(Iptables规则流转)1、流量出向劫持流程(1)envoy怎样劫持入向流量?(2)Envoy劫持到流量之后,干什么?(查询目的地)&a…...
Sharding-Springboot-mybatis-plus整合(三)-inline策略
Sharding-Springboot-mybatis-plus整合(三) 1.简介 本节目标,使用SpringBoot整合Sharding和Mybatis-Plus验证上节分片策略 从配置文件上看策略包括( inline、standard、complex、hint) 环境搭建以inline策略演示 …...
编码的基本概念
本专栏包含信息论与编码的核心知识,按知识点组织,可作为教学或学习的参考。markdown版本已归档至【Github仓库:information-theory】,需要的朋友们自取。或者公众号【AIShareLab】回复 信息论 也可获取。 文章目录信源编码分类前缀…...
函数指针与指针函数的区别
目录:一、函数指针1 函数类型2 函数指针(指向函数的指针)3 函数指针数组二.函数指针和指针函数比较1 定义不同2 写法不同3.用法不同三.函数指针做函数参数(回调函数)1 利用回调函数实现打印任意类型数据2 提供能够打印任意类型数组函数3 利用回调函数 提供查找功能四…...
死锁的四个必要条件以及如何避免死锁
死锁的四个必要条件以及如何避免死锁 一.什么是死锁?二.死锁的四个必要条件 1.互斥条件:2.请求与保持条件:3.不剥夺条件:4.循环等待条件: 三.如何避免死锁 1.破坏请求保持条件2.破坏不剥夺条件3.破坏循环等待条件 死锁的四个必要条件以及如…...
浏览器多线程到事件循环机制
浏览器与js运行机制 进程与线程 进程 进程是CPU分配资源的最小单位,它是一个可以自己独立运行且拥有自己资源空间的任务程序;包括程序以及程序所使用的内存及系统资源 线程 线程是CPU调度的最小单位,它就是程序中的一个执行流࿱…...
Lambda表达式的本质
一直想写一篇文章,来总结lambda表达式,但是之前感觉总结的不是特别到位,现在看了几篇文章和视频后,感觉对lambda表达式有了比较深刻的认识,现在进行记录总结如下: lambda表达式又叫做匿名函数,…...
类的加载过程(生命周期)
类的加载过程(生命周期) 一、装载:通过一个类的全限定名获取定义此类的二进制字节流将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构在内存中生成一个代表这个类的java.lang.Class对象(将字节码加载到内存中),作为…...
2023最新谷粒商城笔记之MQ消息队列篇(全文总共13万字,超详细)
MQ消息队列 其实队列JDK中本身就有,不过这种队列也只能单体服务可能会使用,一旦项目使用的分布式架构,那么一定还是需要用到一个消息中间件的。我们引入消息队列的原因就是对我们的页面相应速度再优化,让用户的体验更好ÿ…...
多变量线性回归模型
多变量线性回归模型 模型参数为n1维向量,此时模型公式为 hθ(x)θ0x0θ1x1θ2x2...θnxnh_{\theta}(x)\theta_{0}x_{0}\theta_{1}x_{1}\theta_{2}x_{2}...\theta_{n}x_{n} hθ(x)θ0x0θ1x1θ2x2...θnxn 可以简化为 hθ(x)θTXh_{\theta}(x)\th…...
php 基于ICMP协议实现一个ping命令
php 基于ICMP协议实现一个ping命令 网络协议是什么ICMP 协议什么是ICMP?ICMP 的主要功能ICMP 在 IPv4 和 IPv6 的封装Wireshark抓包ICMP 请求包分析PHP构建 ICMP 数据包php中的 pack & unpack 函数字节和字符packunpackICMP计算校验和步骤总结网络协议是什么 网络协议&…...
Java基本数据类型
1.概述 佛说,大千世界,无奇不有。在这个世界里,物种的多样性,遍地开花,同样,在Java的世界里,也有着异曲同工之妙,Java秉承面向对象的特性,必然少不了区分对象的类型&…...
English Learning - L2 语音作业打卡 Day2 2023.2.22 周三
English Learning - L2 语音作业打卡 Day2 2023.2.22 周三💌 发音小贴士:💌 当日目标音发音规则/技巧:🍭 Part 1【热身练习】🍭 Part2【练习内容】🍭【练习感受】🍓元音[ ɑː ]&…...
45. 跳跃游戏 II
题目: 45. 跳跃游戏 II难度中等1974收藏分享切换为英文接收动态反馈给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 num…...
应届生Java面试50题线程篇(含解析)
什么是线程? 答:线程是操作系统能够进行运算调度的最小单位,是程序执行流的最小单元。在Java中,可以通过实现Runnable接口或继承Thread类来创建线程。 创建线程的方式有哪些?各自的优缺点是什么? 继承 Thread 类&…...
【数据库】第七章 数据库设计
第七章数据库设计 数据库设计概述 数据库设计的基本步骤 需求分析概念结构设计逻辑结构设计物理结构设计数据库实施数据库运行和维护 需求分析 收集需求,理解需求 收集各个角色的需求 概念数据库设计 建立概念模型 ,E-R图/IDEF1x图 消除冲突&…...
Burp Suite 常用模块简介
Burp Suite 常用模块分为 目标站点(target)模块 代理(proxy)模块 攻击(Intruder)模块 重放(Repeater) 模块 Target模块是对站点资源的收集,与站点各资源包发出和相应包的记录 Proxy模块是核心模块,可以拦截数据包发送往浏览器,进行修改后再…...
QML Item和Rectangle详解
1.Item和Rectangle Item类型是Qt Quick中所有可视项的基本类型。 Qt Quick中的所有可视项都继承Item。尽管Item对象没有视觉外观,但它定义了视觉项中常见的所有属性,例如x和y位置、宽度和高度、锚定和键处理支持。 Rectangle继承自Item,多…...
常见前端基础面试题(HTML,CSS,JS)(六)
GET 和 POST 的区别 从 http 协议的角度来说,GET 和 POST 它们都只是请求行中的第一个单词,除了语义不同,其实没有本质的区别。 之所以在实际开发中会产生各种区别,主要是因为浏览器的默认行为造成的。 受浏览器的影响…...
深度学习 李沐报错
3.6. softmax回归的从零开始实现 — 动手学深度学习 2.0.0 documentation softmax从0开始实现 函数执行需要加main指定 改成这样 if __name__"__main__":print(evaluate_accuracy(net, test_iter)) 不然会这样出错 RuntimeError: An attempt has been m…...
【JAVA程序设计】(C00104)基于Springboot的家庭理财管理系统——有文档
基于Springboot的家庭理财管理系统项目简介项目获取开发环境项目技术运行截图运行视频项目简介 基于Springboot开发的家庭理财管理系统设计与实现共分为三个角色:系统管理员、家庭管理员、家庭用户 管理员角色包含以下功能: 用户管理、修改密码、角色管…...
【第五章 AOP概述,底层原理,AOP术语,切入点表达式,AOP操作(基于注解方式,基于xml配置文件)】
第五章 AOP概述,底层原理,AOP术语,切入点表达式,AOP操作(基于注解方式,基于xml配置文件) 1.AOP概述: (1)什么是AOP: ①面向切面编程(…...
面试官: 你知道 JWT、JWE、JWS 、JWK嘛?
想起了 之前做过的 很多 登录授权 的项目 它相比原先的session、cookie来说,更快更安全,跨域也不再是问题,更关键的是更加优雅 ,所以今天总结了一篇文章来介绍他 JWT 指JSON Web Token,如果在项目中通过 jjwt 来支持 J…...
基于企业微信应用消息的每日早安推送
基于企业微信应用消息的每日早安推送 第一步:注册企业微信 企业微信注册地址:https://work.weixin.qq.com/wework_admin/register_wx 按照正常流程填写信息即可,个人也可以注册企业微信,不需要公司 注册完成后,登录…...
【数字IC基础】黑盒验证、白盒验证、 灰盒验证
文章目录 一、黑盒验证二、白盒验证三、灰盒验证一、黑盒验证 1、黑盒验证:大多数基于仿真的验证环境都是黑盒验证;2、不需要知道设计的内部结构和特性,只需要在输入端口打激励,观察输出即可;3、验证工程师学习设计的规格,然后编写验证环境中的 drivers, monitors, check…...
管理的本质是达成目标
“没有目标,其实就没有管理学存在的意义。要有效地使用管理学的智慧,首先要建立清晰的目标。” - 《宁向东的管理学课》 起源 最近开始刷很久之前就在得到上买了的已经起灰了的课程,看到这句话觉得很有道理。 思考 这里面有一个很重要的词…...
【数字IC基础】IC(Integrated Circuit,集成电路)常用缩写
文章目录 1、集成电路:2、数字IC设计相关步骤:3、数字设计相关概念:4、验证相关:5、语言类:6、IC设计相关工具:7、存储器相关:8、总线协议类:9、文件格式类:10、标准和规范:11、其它:1、集成电路: 缩写全称中文翻译LSILarge-scale intergrated circuit大规模集成电…...
JavaScript 高级1 :面向对象
JavaScript 高级1 :面向对象 Date: January 16, 2023 Text: 面向对象、ES6中类和对象、类的继承、面向对象案例 目标: 能够说出什么是面向对象 能够说出类和对象的关系 能够使用 class 创建自定义类型 能够说出什么是继承 面向对象编程介绍 面向过…...
C语言结构体对齐
1. 结构体对齐 要点 变量只能存储在他的长度的整数倍地址上结构体整体对齐跟他的最长的字段整数倍对齐 栗子1 struct Example1 {char a; //1个字节int c; //4个字节short b; //2个字节 };std::cout << sizeof(Example1 ) << std::endl; // 12 std::cout &…...
Bootstrap系列之导航
Bootstrap导航 可以在 ul 元素上添加 .nav类,在每个 li 选项上添加 .nav-item 类,在每个链接上添加 .nav-link 类: 基本的导航 <div class"container mt-3"><h2>导航</h2><p>简单的水平导航:</p><ul class&…...
垫江网站建设djrckj/站长工具关键词排名怎么查
对于给出的一组数据,要生成列表有以下方法:(1):>>>l []>>>for x in range(10):l.append(x) //追加元素到列表末尾print l[0,1,2,3,4,5,6,7,8,9](2):>>>l [x for x in range(10) if x%20] //我理解为先执…...
网站 加域名/四川百度推广排名查询
http://www.ctshk.com/passport/talent_bookrep.htm 換領往來港澳通行證和申請簽注延期須知為方便持《往來港澳通行證》以內地逗留簽注在香港工作、就學、接受培訓的內地居民及其隨行家屬,以及勞務人員在香港逗留標籤的有效期間內,可以在香港換領 &#…...
wordpress url绝对路径/win10系统优化软件哪个好
1、判空函数 说明:使用指定的替换值替换 NULL。 语法:ISNULL ( check_expression , replacement_value ) 参数: check_expression:将被检查是否为 NULL 的表达式。check_expression 可以为任何类型。 replacement_value࿱…...
企业网站管理系统手机版教程/怎样进行seo优化
题目:原题链接(简单) 标签:动态规划 解法时间复杂度空间复杂度执行用时Ans 1 (Python)O(N)O(N)O(N)O(N)O(N)O(N)28ms (98.81%)Ans 2 (Python)Ans 3 (Python) 解法一: class Solution:def numWays(self, n: int, k: …...
中国住房和城乡建设部网站6/品牌设计公司排名前十强
朱利安莫茨 ( Julian Motz )对使用Web Notification API显示动态消息进行了同行评审。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态! 我们生活在一个世界中,在您的智能手机范围内,不再存在来自您喜爱…...
徐州旅游的网站建设/有没有永久免费crm
今天突然想在windows下利用cygwin执行一个脚本判断输入的文件名是目录还是文件,代码很简单,如下 #!/bin/shread -p enter file name: filenamepath$filenameif [ -d $path ] then echo $filename is the directoryelif [ -f $path ] then echo $path is …...