OpenShift Route 的实现原理

news2024/9/25 17:22:41

OpenShift Route 的实现原理

    • OpenShift 如何使用 HAProxy 实现 Router 和 Route
      • Router app
      • HAProxy 配置
        • 1. HTTP
        • 2. HTTPS

OpenShift 中的 Route 解决了从集群外部访问服务的需求,与 Kubernetes 中的 Ingress 类似。

OpenShift 中 Route 资源 API 定义如下:

apiVersion: v1
kind: Route
metadata:
  name: route-edge-secured
spec:
  host: www.example.com
  to:
    kind: Service
    name: service-name
  tls:
    termination: edge
    key: |-
      -----BEGIN PRIVATE KEY-----
      [...]
      -----END PRIVATE KEY-----      
    certificate: |-
      -----BEGIN CERTIFICATE-----
      [...]
      -----END CERTIFICATE-----      
    caCertificate: |-
      -----BEGIN CERTIFICATE-----
      [...]
      -----END CERTIFICATE-----      

与 Ingress 类似需要指定:

  • host 也就是服务的域名,也就是主机地址
  • to route 通过该 service 选择器对接至服务背后的 endpoint
  • 如果希望服务更安全可以通过配置 TLS 相关证书来实现 HTTPS

OpenShift 如何使用 HAProxy 实现 Router 和 Route

Router app

首先看一下 Router Pod 的控制器 DeploymentConfig API 资源定义:

apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  creationTimestamp: null
  generation: 1
  labels:
    router: router
  name: router
  selfLink: /apis/apps.openshift.io/v1/namespaces/default/deploymentconfigs/router
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    router: router

OpenShift 集群中的每个 Infra 节点都会启动一个名为 router-n-xxxxx 的 Pod:

$ oc get po -l deploymentconfig=router -n default
NAME             READY     STATUS    RESTARTS   AGE
router-1-8j2pp   1/1       Running   0          8d

我们挑一个 router app 容器进入其中:

$ oc exec -it router-1-8j2pp -n default /bin/bash
bash-4.2$ ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
1000000+      1      0  0 Mar07 ?        01:02:32 /usr/bin/openshift-router
1000000+   3294      1  0 04:26 ?        00:00:21 /usr/sbin/haproxy -f /var/lib/haproxy/conf/haproxy.config -p /var/lib/haproxy/run/haproxy.pid -x /var/lib/haproxy/run/haproxy.so
1000000+   3298      0  0 07:07 ?        00:00:00 /bin/bash
1000000+   3305   3298  0 07:07 ?        00:00:00 ps -ef

Router app 由两个进程组成:openshift-router 和 haproxy。

完整的 OpenShift 高可用集群架构如上图,所有集群上的业务(集群中运行的业务 Pod)流量会通过 DNS 指向右边的负载均衡 IP,然后通过集群前置的负载均衡器分片至集群的 Ingra 节点。在所有 Infra 节点上会以 Hostnetwork 方式运行 HAProxy 容器也就是进程监听着 80 和 443 端口。

$ netstat -lntp | grep haproxy
tcp        0      0 127.0.0.1:10443         0.0.0.0:*               LISTEN      105481/haproxy
tcp        0      0 127.0.0.1:10444         0.0.0.0:*               LISTEN      105481/haproxy
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      105481/haproxy
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      105481/haproxy

这里的 HAProxy 就相当于 Kubernetes 中的 Ingress controller,业务流量经过集群前端的负载均衡器到达 HAProxy 后会根据 HTTP 的 Host 请求头匹配转发规则分发至对应的业务后端。

$ curl https://127.0.0.1 -H 'Host: foo.bar.baz'

HAProxy 配置

查看容器中 haproxy 进程所使用的配置文件:

global
  maxconn 20000

  daemon
  ca-base /etc/ssl
  crt-base /etc/ssl
  # TODO: Check if we can get reload to be faster by saving server state.
  # server-state-file /var/lib/haproxy/run/haproxy.state
  stats socket /var/lib/haproxy/run/haproxy.sock mode 600 level admin expose-fd listeners
  stats timeout 2m

  # Increase the default request size to be comparable to modern cloud load balancers (ALB: 64kb), affects
  # total memory use when large numbers of connections are open.
  tune.maxrewrite 8192
  tune.bufsize 32768

  # Prevent vulnerability to POODLE attacks
  ssl-default-bind-options no-sslv3

# The default cipher suite can be selected from the three sets recommended by https://wiki.mozilla.org/Security/Server_Side_TLS,
# or the user can provide one using the ROUTER_CIPHERS environment variable.
# By default when a cipher set is not provided, intermediate is used.
  # Intermediate cipher suite (default) from https://wiki.mozilla.org/Security/Server_Side_TLS
  tune.ssl.default-dh-param 2048
  ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS


defaults
  maxconn 20000

  # Add x-forwarded-for header.

  # To configure custom default errors, you can either uncomment the
  # line below (server ... 127.0.0.1:8080) and point it to your custom
  # backend service or alternatively, you can send a custom 503 error.
  #
  # server openshift_backend 127.0.0.1:8080
  errorfile 503 /var/lib/haproxy/conf/error-page-503.http

  timeout connect 5s
  timeout client 30s
  timeout client-fin 1s
  timeout server 30s
  timeout server-fin 1s
  timeout http-request 10s
  timeout http-keep-alive 300s

  # Long timeout for WebSocket connections.
  timeout tunnel 1h

frontend public

  bind :80
  mode http
  tcp-request inspect-delay 5s
  tcp-request content accept if HTTP
  monitor-uri /_______internal_router_healthz

  # Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
  http-request del-header Proxy

  # DNS labels are case insensitive (RFC 4343), we need to convert the hostname into lowercase
  # before matching, or any requests containing uppercase characters will never match.
  http-request set-header Host %[req.hdr(Host),lower]

  # check if we need to redirect/force using https.
  acl secure_redirect base,map_reg(/var/lib/haproxy/conf/os_route_http_redirect.map) -m found
  redirect scheme https if secure_redirect

  use_backend %[base,map_reg(/var/lib/haproxy/conf/os_http_be.map)]

  default_backend openshift_default

# public ssl accepts all connections and isn't checking certificates yet certificates to use will be
# determined by the next backend in the chain which may be an app backend (passthrough termination) or a backend
# that terminates encryption in this router (edge)
frontend public_ssl

  bind :443
  tcp-request  inspect-delay 5s
  tcp-request content accept if { req_ssl_hello_type 1 }

  # if the connection is SNI and the route is a passthrough don't use the termination backend, just use the tcp backend
  # for the SNI case, we also need to compare it in case-insensitive mode (by converting it to lowercase) as RFC 4343 says
  acl sni req.ssl_sni -m found
  acl sni_passthrough req.ssl_sni,lower,map_reg(/var/lib/haproxy/conf/os_sni_passthrough.map) -m found
  use_backend %[req.ssl_sni,lower,map_reg(/var/lib/haproxy/conf/os_tcp_be.map)] if sni sni_passthrough

  # if the route is SNI and NOT passthrough enter the termination flow
  use_backend be_sni if sni

  # non SNI requests should enter a default termination backend rather than the custom cert SNI backend since it
  # will not be able to match a cert to an SNI host
  default_backend be_no_sni

##########################################################################
# TLS SNI
#
# When using SNI we can terminate encryption with custom certificates.
# Certs will be stored in a directory and will be matched with the SNI host header
# which must exist in the CN of the certificate.  Certificates must be concatenated
# as a single file (handled by the plugin writer) per the haproxy documentation.
#
# Finally, check re-encryption settings and re-encrypt or just pass along the unencrypted
# traffic
##########################################################################
backend be_sni
  server fe_sni 127.0.0.1:10444 weight 1 send-proxy

frontend fe_sni
  # terminate ssl on edge
  bind 127.0.0.1:10444 ssl no-sslv3 crt /etc/pki/tls/private/tls.crt crt-list /var/lib/haproxy/conf/cert_config.map accept-proxy
  mode http

  # Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
  http-request del-header Proxy

  # DNS labels are case insensitive (RFC 4343), we need to convert the hostname into lowercase
  # before matching, or any requests containing uppercase characters will never match.
  http-request set-header Host %[req.hdr(Host),lower]

  # map to backend
  # Search from most specific to general path (host case).
  # Note: If no match, haproxy uses the default_backend, no other
  #       use_backend directives below this will be processed.
  use_backend %[base,map_reg(/var/lib/haproxy/conf/os_edge_reencrypt_be.map)]

  default_backend openshift_default

##########################################################################
# END TLS SNI
##########################################################################

##########################################################################
# TLS NO SNI
#
# When we don't have SNI the only thing we can try to do is terminate the encryption
# using our wild card certificate.  Once that is complete we can either re-encrypt
# the traffic or pass it on to the backends
##########################################################################
# backend for when sni does not exist, or ssl term needs to happen on the edge
backend be_no_sni
  server fe_no_sni 127.0.0.1:10443 weight 1 send-proxy

frontend fe_no_sni
  # terminate ssl on edge
  bind 127.0.0.1:10443 ssl no-sslv3 crt /etc/pki/tls/private/tls.crt accept-proxy
  mode http

  # Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
  http-request del-header Proxy

  # DNS labels are case insensitive (RFC 4343), we need to convert the hostname into lowercase
  # before matching, or any requests containing uppercase characters will never match.
  http-request set-header Host %[req.hdr(Host),lower]

  # map to backend
  # Search from most specific to general path (host case).
  # Note: If no match, haproxy uses the default_backend, no other
  #       use_backend directives below this will be processed.
  use_backend %[base,map_reg(/var/lib/haproxy/conf/os_edge_reencrypt_be.map)]

  default_backend openshift_default

##########################################################################
# END TLS NO SNI
##########################################################################

backend openshift_default
  mode http
  option forwardfor
  #option http-keep-alive
  option http-pretend-keepalive

##-------------- app level backends ----------------

# Secure backend, pass through
backend be_tcp:default:docker-registry
  balance source

  hash-type consistent
  timeout check 5000ms}
  server pod:docker-registry-1-tvrjp:docker-registry:10.128.1.32:5000 10.128.1.32:5000 weight 256

# Secure backend, pass through
backend be_tcp:default:registry-console
  balance source

  hash-type consistent
  timeout check 5000ms}
  server pod:registry-console-1-jspmx:registry-console:10.128.1.10:9090 10.128.1.10:9090 weight 256

# Plain http backend or backend with TLS terminated at the edge or a
# secure backend with re-encryption.
backend be_edge_http:default:route-origin-license-information-provider
  mode http
  option redispatch
  option forwardfor
  balance leastconn

  timeout check 5000ms
  http-request set-header X-Forwarded-Host %[req.hdr(host)]
  http-request set-header X-Forwarded-Port %[dst_port]
  http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
  http-request set-header X-Forwarded-Proto https if { ssl_fc }
  http-request set-header X-Forwarded-Proto-Version h2 if { ssl_fc_alpn -i h2 }
  http-request add-header Forwarded for=%[src];host=%[req.hdr(host)];proto=%[req.hdr(X-Forwarded-Proto)];proto-version=%[req.hdr(X-Forwarded-Proto-Version)]
  cookie f68e01f4fd193411edcdbd2ed2123b1b insert indirect nocache httponly secure
  server pod:deployment-origin-license-information-provider-7b55689655-vcn2j:svc-origin-license-information-provider:10.128.1.1:80 10.128.1.1:80 cookie d000e3ba84d0860ea1f900879572c5fa weight 256

# Secure backend, pass through
backend be_tcp:kube-service-catalog:apiserver
  balance source

  hash-type consistent
  timeout check 5000ms}
  server pod:apiserver-w4szj:apiserver:10.128.1.17:6443 10.128.1.17:6443 weight 256

HAProxy 的配置包括几部分:

  • 全局配置 最大连接数等
  • 前端配置 在 80 端口监听 HTTP 请求和在 443 端口监听 HTTPS 请求
  • 后端配置 包含了协议(mode)、负载均衡方式、请求头改写、后端服务的 OpenShift 集群内部地址

顺着这份配置文件我们可以大致分析出 haproxy 对业务流量的处理:

1. HTTP

frontend public

  bind :80
  mode http
  tcp-request inspect-delay 5s
  tcp-request content accept if HTTP
  monitor-uri /_______internal_router_healthz

  # Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
  http-request del-header Proxy

  # DNS labels are case insensitive (RFC 4343), we need to convert the hostname into lowercase
  # before matching, or any requests containing uppercase characters will never match.
  http-request set-header Host %[req.hdr(Host),lower]

  # check if we need to redirect/force using https.
  acl secure_redirect base,map_reg(/var/lib/haproxy/conf/os_route_http_redirect.map) -m found
  redirect scheme https if secure_redirect

  use_backend %[base,map_reg(/var/lib/haproxy/conf/os_http_be.map)]

  default_backend openshift_default

当 haproxy 接收到 HTTP (80 端口)请求,会根据 /var/lib/haproxy/conf/os_http_be.map 文件中的转发规则转发至对应的后端。

^backend-3scale.caas-aio-apps.trystack.cn(:[0-9]+)?(/.*)?$ be_edge_http:mep-apigateway:backend

后端再根据配置中集群内部的 Pod 地址将流量转发至对应业务 Pod,从这里开始走 Kubernetes 内部网络。

# Plain http backend or backend with TLS terminated at the edge or a
# secure backend with re-encryption.
backend be_edge_http:mep-apigateway:backend
  mode http
  option redispatch
  option forwardfor
  balance leastconn

  timeout check 5000ms
  http-request set-header X-Forwarded-Host %[req.hdr(host)]
  http-request set-header X-Forwarded-Port %[dst_port]
  http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
  http-request set-header X-Forwarded-Proto https if { ssl_fc }
  http-request set-header X-Forwarded-Proto-Version h2 if { ssl_fc_alpn -i h2 }
  http-request add-header Forwarded for=%[src];host=%[req.hdr(host)];proto=%[req.hdr(X-Forwarded-Proto)];proto-version=%[req.hdr(X-Forwarded-Proto-Version)]
  cookie dcdcad015a918c4295da5aec7a0daf50 insert indirect nocache httponly
  server pod:backend-listener-1-mwgb6:backend-listener:10.128.1.67:3000 10.128.1.67:3000 cookie 4a624e547781196b55877322edeaec05 weight 256

2. HTTPS

frontend public_ssl

  bind :443
  tcp-request  inspect-delay 5s
  tcp-request content accept if { req_ssl_hello_type 1 }

  # if the connection is SNI and the route is a passthrough don't use the termination backend, just use the tcp backend
  # for the SNI case, we also need to compare it in case-insensitive mode (by converting it to lowercase) as RFC 4343 says
  acl sni req.ssl_sni -m found
  acl sni_passthrough req.ssl_sni,lower,map_reg(/var/lib/haproxy/conf/os_sni_passthrough.map) -m found
  use_backend %[req.ssl_sni,lower,map_reg(/var/lib/haproxy/conf/os_tcp_be.map)] if sni sni_passthrough

  # if the route is SNI and NOT passthrough enter the termination flow
  use_backend be_sni if sni

  # non SNI requests should enter a default termination backend rather than the custom cert SNI backend since it
  # will not be able to match a cert to an SNI host
  default_backend be_no_sni

当 haproxy 接收到 HTTPS (443 端口)请求,会检查 HTTPS 请求支持 sni (TLS Server Name Indication)

这边的处理对应了 Router 对 HTTPS 流量的终结方式:

  • edge HTTPS 流量将在 Router 中解密并以纯 HTTP 转发至后端 Pod
  • passthrough TLS 加密包直接递交给后端 Pod,Router 不做任何 TLS 终结,不需要 TLS 相关证书
  • re-encryption 是 edge 的变种,HTTPS 流量将在 Router 中解密后再使用其他证书加密后转发至后端 Pod

也就是 route API 中的 termination 字段。

当 TLS 终结类型不是 passthrough 时将使用 be_sni 后端:

backend be_sni
  server fe_sni 127.0.0.1:10444 weight 1 send-proxy

名为 fe_sni 的前端与 10444 端口绑定,流量被转向 fe_sni:

frontend fe_sni
  # terminate ssl on edge
  bind 127.0.0.1:10444 ssl no-sslv3 crt /etc/pki/tls/private/tls.crt crt-list /var/lib/haproxy/conf/cert_config.map accept-proxy
  mode http

  # Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
  http-request del-header Proxy

  # DNS labels are case insensitive (RFC 4343), we need to convert the hostname into lowercase
  # before matching, or any requests containing uppercase characters will never match.
  http-request set-header Host %[req.hdr(Host),lower]

  # map to backend
  # Search from most specific to general path (host case).
  # Note: If no match, haproxy uses the default_backend, no other
  #       use_backend directives below this will be processed.
  use_backend %[base,map_reg(/var/lib/haproxy/conf/os_edge_reencrypt_be.map)]

  default_backend openshift_default

HTTPS 流量来到这里后会使用相关 TLS 证书解密,然后根据 /var/lib/haproxy/conf/os_edge_reencrypt_be.map 映射文件中的转发规则转发至对应的后端。

^user-management.caas-aio-apps.trystack.cn(:[0-9]+)?(/.*)?$ be_edge_http:user-management:user-management
^test-groupcbc37148-3scale-apicast-staging.caas-aio-apps.trystack.cn(:[0-9]+)?(/.*)?$ be_edge_http:mep-apigateway:zync-3scale-api-sp8kr
^test-groupcbc37148-3scale-apicast-production.caas-aio-apps.trystack.cn(:[0-9]+)?(/.*)?$ be_edge_http:mep-apigateway:zync-3scale-api-twdpk
^test-group26a9a65d-3scale-apicast-staging.caas-aio-apps.trystack.cn(:[0-9]+)?(/.*)?$ be_edge_http:mep-apigateway:zync-3scale-api-r7rwm
^test-group26a9a65d-3scale-apicast-production.caas-aio-apps.trystack.cn(:[0-9]+)?(/.*)?$

与 HTTP 完全一样,后端再根据配置中集群内部的 Pod 地址将流量转发至对应业务 Pod,从这里开始走 Kubernetes 内部网络。

而我们创建/删除 Route API 资源的本质就是修改 HAProxy 的配置来控制流量的走向,OpenShift Router 的七层负载均衡能力也正是 Infra 节点上的 haproxy 提供的。

与 Ingress 类似,OpenShift Route 的 service 选择器只是用来向 HAProxy 配置中添加相关后端 Pod,而实际使用中跳过了 Kubernetes Service 这一层而避免 iptables 带来的性能影响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/638194.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解剖学关键点检测方向论文翻译和精读:基于热力图回归的CNN融入空间配置实现关键点定位

解剖学关键点检测方向论文翻译和精读:Integrating spatial configuration into heatmap regression based CNNs for landmark localization Abstract: In many medical image analysis applications, only a limited amount of training data is available due to t…

Pyinstaller 打包 paddle出现的问题

目录 问题解决方法 1.找到自己项目所需要的包 2.使用【auto-py-to-exe】库,添加对应项目对应的包 --add-data 需要添加的文件 --hidden-import 需要添加的文件 问题来源 问题解决思路 问题解决方法 1.找到自己项目所需要的包 这里可以参考博主的另外一篇文章&…

大模型当私人法律顾问?北大团队推出法律大模型,数据与模型全部开源!

作者 | 小戏、Python “要是我有一个学法的朋友就好了” 法律作为我们社会生活的最高指引,其重要性毋庸多言。然而类似于医学,法律又是一个入行门槛非常高的专业领域,想想通过率不到 10% 的法考,显然遇到法律相关的专业问题不太支…

mysql中SQL语句查询字段相加的和,以及更改小数精确位。和查询SQL语句结果相减的SQL查询

最近在工作中碰到了SQL进行查询,用户缴费以及退费信息的工作,以及查询完后需要修改SQL结果后小数位的需求,整理结果如下: 首先,看内容: 如图,operate_type代表缴费与退费,分别用0和1区别。 #…

快速教你如何使用postman工具进行接口测试?(配置全局token、JWT可用)

现在很多人都在使用前后端分离进行项目开发,那在后端开发过程中使用postman测试工具可快速便捷完成接口测试。但是在测试过程中,很多人发现因为JWT的鉴权,导致半个小时就需要更新一下token,让我们的测试变得复杂。 若是将token作…

【JUC中的 “8锁现象” 】 —— 每天一点小知识

💧 J U C 中的“ 8 锁现象” \color{#FF1493}{JUC中的 “8锁现象” } JUC中的“8锁现象”💧 🌷 仰望天空,妳我亦是行人.✨ 🦄 个人主页——微风撞见云的博客🎐 🐳 《数据结构与算法》…

python基础学习3【NumPy矩阵与通用函数【矩阵相乘+平方+广播机制+转置】+ save、load、sort、repeat、unique、鸢尾花1】

NumPy矩阵与通用函数 a np.mat([[1,2],[3,4]])#生成矩阵b np.matrix([[1,7],[6,4]])np.bmat("a b") 矩阵的运算 矩阵特有属性: 属性 说明 T自身转置H共轭转置I逆矩阵A自身数据的二维数据视图 例如: np.matrix(a).T 矩阵相乘:…

【Map集合的基本功能】

Map集合的基本功能 V put(K key,V value):添加元素 V remove(K key,V value):根据键删除键值对元素 package com.gather.map; //Map集合常用的基本功能 import java.util.HashMap; import java.util.Map; public class MapDemo02 {public static void m…

Flutter:路由、拦截返回事件、自定义独立路由

路由堆栈 Flutter 路由管理中有两个非常重要的概念: Route:路由是应用程序页面的抽象,对应 Android 中 Activity 和 iOS 中的 ViewController,由 Navigator 管理。Navigator:Navigator 是一个组件,管理和…

哈希表--想彻底了解哈希表,看这一篇文章就可以了

为了一次存储便能得到所查记录,在记录的存储位置和它的关键字之间建立一个确定的对应关系H,已H(key)作为关键字为key的记录在表中的位置,这个对应关系H为哈希(Hash)函数, 按这个思路建立的表为哈…

【Leetcode】 1071. 字符串的最大公因子

For two strings s and t, we say “t divides s” if and only if s t ... t (i.e., t is concatenated with itself one or more times). Given two strings str1 and str2, return the largest string x such that x divides both str1 and str2. Example 1: Input: s…

开发改了接口,经常忘通知测试的解决方案!

目录 前言: Apifox解决方案 Apifox对此给出的解决方案是: 用Apifox怎么处理接口变更 接口代码实现逻辑修改 接口参数修改 前言: 在开发过程中,接口变动十分频繁,测试人员没有及时获得相关通知的情况也很普遍。这…

这一次AI应该是真正的已经到来

渐渐感觉这一次AI的变革能真正的突破迷雾,迎来真正的人工智能时代的到来。所以写篇博文学习一下。经过半年的发酵与发展,不得不说AI已经成为了不可逆转的趋势。之所以说这一次AI应该是真正的已经到来,是因为人工智能的发展其实已经经历了几十…

【Python】一文带你了解 正则表达式 + 简单操作

作者主页:爱笑的男孩。的博客_CSDN博客-深度学习,活动,python领域博主爱笑的男孩。擅长深度学习,活动,python,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typeblog个…

通过第三方软件修改 MacOS 的键盘映射

通过第三方软件修改 MacOS 的键盘映射 由于文本编辑时大量使用word level的左移、右移,其中: OSX的单词级左右移为option Left/Right Arrow,整行级左右移为command Left/Right Arrow 单词级移动与进行编辑常用的command不同键位&#xff0c…

Chat2DB:阿里巴巴开源的聊天数据管理工具--实践

Chat2DB:阿里巴巴开源的聊天数据管理工具–实践 简介 ​ Chat2DB 是一款有开源免费的多数据库客户端工具,支持windows、mac本地安装,也支持服务器端部署,web网页访问。和传统的数据库客户端软件Navicat、DBeaver 相比Chat2DB集成了…

Jetpack Compose ——Row

当我们构建界面时,经常需要在Compose中使用Row布局来水平排列多个组件。Row提供了一种方便的方式来管理和定位子组件,使它们按照我们期望的方式呈现。 在Compose中,Row可以接受多个子组件作为参数,并根据指定的布局规则进行排列。…

桥梁监测方案-智慧桥梁监测管理系统

桥梁作为现代交通基础设施的重要组成部分,承载着大量的车辆和行人交通。然而,随着时间的推移,桥梁结构可能会受到自然环境、交通负荷和物质疲劳等因素的影响,从而导致潜在的风险和结构损坏。桥梁监测能够通过实时监测桥梁的结构状…

git一定要学会,加油

gitgit文档http://file:///F:/%E8%B5%84%E6%96%99%E5%A4%8D%E4%B9%A0/Git%E4%BC%98%E7%A7%80%E5%BC%80%E6%BA%90%E4%B9%A6%E7%B1%8D/Git%E5%BC%80%E6%BA%90%E4%B9%A6%E7%B1%8D/Pro%20Git%E4%B8%AD%E6%96%87PDF%E7%89%88.pdf init 初始化仓库 这个命令在当前目录下初始化一个 G…

安装Django 3.2的过程记录~

首先看下Django 3.2目前更新到哪些版本,用下面这条命令: pip install Django100.0.5与3.2有关的版本号有: 3.2a1, 3.2b1, 3.2rc1, 3.2, 3.2.1, 3.2.2, 3.2.3, 3.2.4, 3.2.5, 3.2.6, 3.2.7, 3.2.8, 3.2.9, 3.2.10, 3.2.11, 3.2.12, 3.2.13, …