kong笔记——认识kong

kong笔记——认识kong背景最近公司打算重构API网关,给定的硬性条件是支持lua脚本,kubernetes可部署,可解析lua,另外需要支持身份认证,IP黑白名单,限流,负载均衡等一些功能,为此,在技术选型上锁定了kong以及APISIX,最终选择了kong。(原因:稳定性第一,性能第二,拓展性第三,社区未来发展第四)为什么选择kong其实抛开lua的支持,kong的一些功能点还是很吸引我的,比如天生支持API网关的基本特性(权限控制,安全,负载均衡,请求分发,监控等等),即开箱即用。如果我们选择了zuul,当需要为应

大家好,又见面了,我是你们的朋友全栈君。

kong笔记 目录导航

背景

最近公司打算重构API网关,给定的硬性条件是支持lua脚本,kubernetes可部署,可解析lua,另外需要支持身份认证,IP黑白名单,限流,负载均衡等一些功能,为此,在技术选型上锁定了kong以及APISIX,最终选择了kong。

为什么选择kong

其原因如下:

  1. 社区活跃度高,遇到问题可以及时解决(github目前已经30.2k star了);

  2. 成熟度,性能好(kong是基于nginx衍生出的一个openResty应用)

  3. 拓展性好,功能相对齐全,耦合低

    • 以lua插件的方式进行业务功能拓展;
    • 自身支持API网关的基本特性(权限控制,安全,负载均衡,请求分发,监控等等),无需重新开发
    • 一个独立的应用,与框架无耦合

    举一个例子:Zuul,其自身只提供基本的路由功能,如果想使用其他功能,就需自研,而这些成本是很高的,另外,他与spring cloud深度集成,就需要引入其他的框架;

    另外,附一个性能测试链接:Kong1.4 vs SC Gateway2.2 vs Zuul1.3 性能测试

目前市面上的开源的API网关除了kong还有:

  1. Traefik

    Traefik 是一个现代 HTTP 反向代理和负载均衡器,可以轻松部署微服务,Traeffik 可以与您现有的组件(Docker、Swarm,Kubernetes,Marathon,Consul,Etcd,…)集成,并自动动态配置。

  2. Ambassador

    Ambassador 是一个开源的微服务 API 网关,建立在 Envoy 代理之上,为用户的多个团队快速发布,监控和更新提供支持,支持处理 Kubernetes ingress controller 和负载均衡等功能,可以与 Istio 无缝集成。

  3. Tyk

    Tyk是一个开源的、轻量级的、快速可伸缩的 API 网关,支持配额和速度限制,支持认证和数据分析,支持多用户多组织,提供全 RESTful API。基于 go 编写。

  4. Zuul

    Zuul 是一种提供动态路由、监视、弹性、安全性等功能的边缘服务。Zuul 是 Netflix 出品的一个基于 JVM 路由和服务端的负载均衡器。

其对比如下:

Kong Traefik Ambassador Tyk Zuul
基本
主要用途 企业级API管理 微服务网关 微服务网关 微服务网关 微服务网关
学习曲线 适中 simple simple 适中 simple
成本 开源/企业版 开源 开源/pro 开源/企业版 开源
社区star 20742 21194 1719 4299 7186
配置
配置语言 Admin Rest api, Text file(nginx.conf 等) TOML YAML(kubernetes annotation) Tyk REST API REST API,YAML静态配置
配置端点类型 命令式 声明式 声明式 命令式 命令式
拖拽配置 yes no no no no
管理模式 configurable decentralised, self-service decentralised, self-service decentralised, self-service decentralised, self-service
部署
kubernetes 适中(k8s yaml,helm chart) easy easy 适中(k8s yaml,helm chart) 适中(k8s yaml,helm chart)
Cloud IAAS high easy N/A easy easy
Private Data Center high easy N/A easy easy
部署模式 金丝雀(企业版) 金丝雀 金丝雀,shadow 金丝雀 金丝雀
state postgres,cassandra kubernetes kubernetes redis 内存文件
可扩展性
扩展功能 插件 自己实现 插件 插件 自己实现
扩展方法 水平 水平 水平 水平 水平
功能
服务发现 动态 动态 动态 动态 动态
协议 http,https,websocket http,https,grpc,websocket http,https,grpc,websocket http,https,grpc,websocket http,https
基于 kong+nginx traefik envoy tyk zuul
ssl 终止 yes yes yes yes no
websocket yes yes yes yes no
routing host,path,method host,path host,path,header host,path
限流 yes no yes yes 需要开发
熔断 yes yes no yes 需要其他组件
重试 yes yes no yes yes
健康检查 yes no no yes yes
负载均衡算法 轮询,哈希 轮询,加权轮询 加权轮询 轮询 轮询,随机,加权轮询,自定义
权限 Basic Auth, HMAC, JWT, Key, LDAP, OAuth 2.0, PASETO, plus paid Kong Enterprise options like OpenID Connect basic yes HMAC,JWT,Mutual TLS,OpenID Connect,基本身份验证,LDAP,社交OAuth(例如GPlus,Twitter,Github)和传统基本身份验证提供程序 开发实现
tracing yes yes yes yes 需要其他组件
istio集成 no no yes no no
dashboard yes yes grafana,Prometheus yes no

引用链接:微服务五种开源API网关实现组件对比

什么是kong

Kong 由Mashape公司开源,是一个在 Nginx 中运行的Lua应用程序,并且可以通过lua-nginx模块实现,Kong不是用这个模块编译Nginx,而是与 OpenResty 一起发布,OpenResty已经包含了 lua-nginx-module, OpenResty 不是 Nginx 的分支,而是一组扩展其功能的模块。

三者的关系如下:

  • Nginx是模块化设计的反向代理软件,C语言开发:
  • OpenResty是以Nginx为核心的Web开发平台,可以解析执行Lua脚本
  • Kong是OpenResty的一个应用,是一个API网关,具有API管理和请求代理的功能

核心思想

实现数据库抽象,路由和插件管理,插件可以存在于单独的代码库中,并且可以在几行代码中注入到请求生命周期的任何位置。
由于Kong是运行在OpenResty,我们先来看一下OpenResty执行流程:
292052-20190628170848889-787127502.png
图出处:OpenResty 执行流程阶段

  • init_by_lua: master进程启动后的回调。
  • init_worker_by_lua:nginx worker进程启动后的回调。
  • rewrite_by_lua:request进来后首先经过rewrite,可以改写URL等。
  • access_by_lua:request路由完成之后,进一步处理之前,可以修改request。
  • header_filter_by_lua:response准备返回之前,可以修改header。
  • body_filter_by_lua:response准备返回之前,可以修改body。
  • log_by_lua:用于请求的日志记录。

Kong通过在 openresty 各个阶段加入了lua代码来实现插件的注入和执行,Kong 的 nginx 配置:

init_by_lua_block {
    kong = require 'kong'
    kong.init() // 完成 Kong 的初始化,路由创建,插件预加载等
}
init_worker_by_lua_block {
    kong.init_worker() // 初始化 Kong 事件, worker 之间的事件,由 worker_events 来处理, cluster 节点之间的事件,由 cluster_events 来处理,缓存机制
}

upstream kong_upstream {
    server 0.0.0.1;
    balancer_by_lua_block {
        kong.balancer() //负载均衡
    }
    keepalive 60;
}
server {
    server_name kong;
    listen 0.0.0.0:8000 reuseport backlog=16384;
    listen 0.0.0.0:8443 ssl http2 reuseport backlog=16384;

    rewrite_by_lua_block {
        kong.rewrite() //插件生效策略的筛选,并执行对应的操作,只能处理全局插件(kong插件级别,全局(作用于所有请求),route(作用于当前路由),service(作用于匹配到当前service的所有请求)),路由匹配未开始。
    }
    access_by_lua_block {
        kong.access() //1.完成路由匹配,2.确认加载的插件(并加入缓存) 3.进入balancer阶段
    }

    header_filter_by_lua_block {
        kong.header_filter() //遍历在缓存中的插件列表,并执行
    }
    body_filter_by_lua_block {
        kong.body_filter() //遍历在缓存中的插件列表,并执行
    }
    log_by_lua_block {
        kong.log() //遍历在缓存中的插件列表,并执行
    }

    location / {
        proxy_pass            $upstream_scheme://kong_upstream$upstream_uri;
    }

}
函数名 LUA-NGINX-MODULE Context 描述
:init_worker() init_worker_by_lua 在每个 Nginx 工作进程启动时执行
:certificate() ssl_certificate_by_lua 在SSL握手阶段的SSL证书服务阶段执行
:rewrite() rewrite_by_lua 从客户端接收作为重写阶段处理程序的每个请求执行。在这个阶段,无论是API还是消费者都没有被识别,因此这个处理器只在插件被配置为全局插件时执行
:access() access_by_lua 为客户的每一个请求而执行,并在它被代理到上游服务之前执行(路由)
:header_filter() header_filter_by_lua 从上游服务接收到所有响应头字节时执行
:body_filter() body_filter_by_lua 从上游服务接收的响应体的每个块时执行。由于响应流回客户端,它可以超过缓冲区大小,因此,如果响应较大,该方法可以被多次调用
:log() log_by_lua 当最后一个响应字节已经发送到客户端时执行

部署

对于kong的部署,由于它是基于Nginx的,所以只需要添加多台服务器就可以配置成一个集群,通过前置的负载均衡配置把请求均匀地分发到各个 Server,来应对大批量的网络请求。

kong的架构.png

组件

共分为3大块:

  1. kong server : 基于nginx的服务器,用来接收API请求。
  2. Apache Cassandra/PostgreSQL :用来存储操作数据。
  3. Kong dashboard:官方推荐UI管理工具(缺点是仅仅可以集成kong2.0版本一下,不支持最新的kong版本!!),当然,也可以使用 restfull 方式 管理admin api。Konga另一款经典用于UI管理工具(可支持最新的kong版本)。

特性

  • 可扩展: 通过简单地添加更多的机器,可以轻松地平行扩展,这意味着您的平台可以在一个较低负载的情况下处理任何请求。
  • 模块化: 可以通过添加新的插件进行扩展,这些插件可以通过RESTful Admin API轻松配置。
  • 在任何基础架构上运行: Kong可以在任何地方都能运行。您可以在云或内部部署环境中部署Kong,包括单个或多个数据中心设置,以及public,private 或invite-only APIs。

kong的特性.png

  • Kong核心基于OpenResty构建,实现了请求/响应的Lua处理化;
  • Kong插件拦截请求/响应,如果接触过Java Servlet,等价于拦截器,实现请求/响应的AOP处理;
  • Kong Restful 管理API提供了API/API消费者/插件的管理;
  • 数据中心用于存储Kong集群节点信息、API、消费者、插件等信息,目前提供了PostgreSQL和Cassandra支持,如果需要高可用建议使用Cassandra;
  • Kong集群中的节点通过gossip协议自动发现其他节点,当通过一个Kong节点的管理API进行一些变更时也会通知其他节点。每个Kong节点的配置信息是会缓存的,如插件,那么当在某一个Kong节点修改了插件配置时,需要通知其他节点配置的变更。

具体如下:

  • 云原生: 与平台无关,Kong可以从裸机运行到Kubernetes
  • 动态路由:Kong 的背后是 OpenResty+Lua,所以从 OpenResty 继承了动态路由的特性
  • 基于hash的负载均衡:基于hashing/sticky session的负载均衡
  • 断路器:能追踪不健康的upstream services
  • 健康检测:对upstream services进行主动或者被动地监控
  • 服务发现:可结合consul提供服务注册等功能
  • WebSockets:通过WebSockets和upstream service进行通信
  • OAuth2.0:可对API方便地添加OAuth2.0进行授权
  • 日志:通过HTTP/TCP/UDP等方式进行日志相关操作
  • 安全:ACL,机器人检测,黑白名单IP等
  • 系统日志:日志可输入到系统日志中
  • 监控:提供实时监控功能
  • 认证:HMAC/JWT以及基本认证方式
  • 限流(Rate-Limiting):基于多变量对请求进行阻塞或者限制
  • 转换:对TTP请求和相应进行添加/删除/操纵等操作
  • 缓存:在代理层进行缓存和响应处理
  • CLI:通过命令行对kong的集群进行控制
  • REST API:可灵活地通过RESTful API对kong进行操作
  • 失败检测与恢复:Cassandra某一节点停止也不会影响kong的功能
  • 集群:所有的kong节点都能自动加入集群,并保证配置在整个节点间得到更新
  • 可扩展性:kong可以通过添加节点很容易地实现横向扩展
  • 性能:使用nginx作为内核,kong具有nginx的高性能
  • plugin:可以对kong和API进行扩展

插件

目前已经提供的插件有:

  1. 身份认证插件:Kong提供了Basic Authentication、Key authentication、OAuth2.0 authentication、HMAC authentication、JWT、LDAP authentication认证实现。
  2. 安全控制插件:ACL(访问控制)、CORS(跨域资源共享)、动态SSL、IP限制、爬虫检测实现。
  3. 流量控制插件:请求限流(基于请求计数限流)、上游响应限流(根据upstream响应计数限流)、请求大小限制。限流支持本地、Redis和集群限流模式。
  4. 分析监控插件:Galileo(记录请求和响应数据,实现API分析)、Datadog(记录API Metric如请求次数、请求大小、响应状态和延迟,可视化API Metric)、Runscope(记录请求和响应数据,实现API性能测试和监控)。
  5. 协议转换插件:请求转换(在转发到upstream之前修改请求)、响应转换(在upstream响应返回给客户端之前修改响应)。
  6. 日志应用插件:TCP、UDP、HTTP、File、Syslog、StatsD、Loggly等。

kong目前提供了37个插件,其中商业收费7个,30个开源免费的插件,可以设定到api/服务/路由粒度上,详情如下:

类别 免费/收费 name 插件名 使用场景
认证 免费 basic-auth Basic Authentication 对于服务或者路由提供用户名/密码基本认证机制
认证 免费 key-auth Key Authentication 对于服务或者路由提供用关键字认证机制
认证 收费 OpenID Connect 提供与三方OpenID的集成方式
认证 免费 oauth2 OAuth 2.0 Authentication 添加OAuth 2.0认证
认证 收费 OAuth 2.0 Introspection 提供与三方OAuth 2.0认证服务器的集成方式
认证 免费 hmac-auth HMAC Authentication 提供HMAC(Hashed Message Authentication Code)签名认证方式
认证 免费 jwt JWT 提供JWT(JSON WEB Token)的认证方式
认证 免费 ldap-auth LDAP Authentication 提供了与LDAP认证服务器的集成方式
安全 免费 acl ACL 通过ACL(Access Control List)的组名称对服务或者路由进行黑白名单的访问控制
安全 免费 cors CORS 对服务或者路由提供CORS支持
安全 免费 ip-restriction IP Restriction 通过IP地址对服务或者路由进行黑白名单的访问控制
安全 免费 bot-detection Bot Detection 对服务或者路由提供机器人检出并能进行黑白名单的设定
安全 免费 ssl Dynamic SSL 对路由或者API提供ssl支持
流量控制 收费 EE Canary Release 提供金丝雀发布支持
流量控制 收费 EE Forward Proxy 提供企业内网中前向代理的支持
流量控制 收费 EE Proxy Caching 提供代理缓存的加速功能
流量控制 免费 request-size-limiting Request Size Limiting 可以对请求的body的size进行限制
流量控制 免费 rate-limiting Rate Limiting 提供对给定时间段HTTP请求数目的控制功能
流量控制 收费 EE Rate Limiting CE版本Rate Limiting的强化
流量控制 免费 response-ratelimiting Response Rate Limiting 基于用户响应Header的信息,提供对HTTP请求数目的控制功能
流量控制 免费 request-termination Request Termination 根据响应的状态码和信息,可停止对某个服务或者路由的流量
Serverless 免费 aws-lambda AWS Lambda 调用和管理AWS Lambda函数
Serverless 免费 openwhisk Apache OpenWhisk 调用和管理OpenWhisk
分析&监控 免费 zipkin Zipkin 提供兼容zipkin的方式对用户请求进行追踪
分析&监控 免费 datadog Datadog 可将API的指标信息在datadog上进行可视化地展示
分析&监控 免费 runscope Runscope 结合runscope进行API性能测试和监控
转换 免费 request-transformer Request Transformer 通过kong对请求进行转换
转换 收费 EE request Transformer request Transformer的强化版
转换 免费 response-transformer Response Transformer 通过kong对响应进行转换
转换 免费 correlation-id Correlation ID 通过kong实现请求和响应之间的关联
日志 免费 tcp-log TCP 发送请求和响应日志到TCP服务器
日志 免费 udp-log UDP 发送请求和响应日志到UDP服务器
日志 免费 http-log HTTP 发送请求和响应日志到HTTP服务器
日志 免费 file-log File 发送请求和响应日志到磁盘的文件中
日志 免费 statsd StatsD 发送请求和响应日志到StatsD服务器
日志 免费 syslog Syslog 发送请求和响应日志到syslog中
日志 免费 syslog Loggly 发送请求和响应日志到Loggly服务器

kong的架构

kong的目录.png

从技术的角度讲,Kong 可以认为是一个 OpenResty 应用程序。 OpenResty 运行在 Nginx 之上,使用 Lua 扩展了 Nginx。 Lua 是一种非常容易使用的脚本语言,可以让你在 Nginx 中编写一些逻辑操作。之前我们提到过一个概念 Kong = OpenResty + Nginx + Lua,但想要从全局视角了解 Kong 的工作原理,还是直接看源码比较直接。我们定位到本地的 Kong 文件夹,按照上图中的目录层级来识识 Kong 的庐山真面目。

  1. Kong 文件下包含了全部源码和必要组件,分析他们,我们便得到了 Kong 的架构。0.13.x 是目前 Kong 的最新版本。
  2. 从 2 号块中可以看到 nginx.conf ,这其实便是一个标准的 Nginx 目录结构,这也揭示了 Kong 其实就是运行在 Nginx 的基础之上,而进行的二次封装。由 share 文件夹向下展开下一次分析。
  3. share 文件夹中包含了 OpenResty 的相关内容,其实背后就是一堆 Lua 脚本,例如 lapis 包含了数据库操作,Nginx 生命周期,缓存控制等必要的 Lua 脚本,logging 包含了日志相关的 Lua 脚本,resty 包含了 dns,健康检查等相关功能的 Lua 脚本…而其中的 kong 目录值得我们重点分析,他包含了 Kong 的核心对象。
  4. api 和 core 文件夹,封装了 Kong 对 service,route,upstream,target 等核心对象的操作代码(这四个核心对象将会在下面的小节重点介绍),而 plugins 文件夹则是 Kong 高可扩展性的根源,存放了 kong 的诸多扩展功能。
  5. plugins 文件夹包含了上一节提到的 Kong 的诸多插件功能,如权限控制插件,跨域插件,jwt 插件,oauth2 插件…如果需要自定义插件,则需要将代码置于此处。

从上述文件夹浏览下来,大概可以看到它和 Nginx 的相似之处,并在此基础之上借助于 Lua 对自身的功能进行了拓展,除了 nginx.conf 中的配置,和相对固定的文件层级,Kong 还需要连接一个数据库来管理路由配置,服务配置,upstream 配置等信息,是的,由于 Kong 支持动态路由的特性,所以几乎所有动态的配置都不是配置在文件中,而是借助于 Postgres 或者 Cassandra 进行管理。

kong的数据库结构.png

Kong 对外暴露了 Restful API,最终的配置便是落地在了数据库之中。

引用链接:https://mp.weixin.qq.com/s/CWGy6cwy6QGLvwByurEGbw

kong的应用

API 网关可以通过实现一些中间件来解决一些问题,这些中间件的功能你就不用再到每个service中实现了。你也是知道的,不同的团队使用不同的方式来实现了不同的微服务。

如果你不去做一些中心化和抽象化的事情,你将会死于不同的认证方式以及不同的速率限制实现,五花八门。你肯定希望避免这样的糟糕局面。

API网关不仅可以帮你解决API的管理部分,而且还可以解决下面两件事情:

  • 分析(Analytics) – API网关可以和你的分析基础设施保持透明的交互和通信,因为API网关是每个请求(request)的入口,必经地。API网关可以看到所有的数据,可以知道经过你的service的流量。这就相当于你有一个集中的地方,你可以把所有的这些信息push到你的监控或分析工具,比如Kibana或者Splunk。
  • 自动化(Automation) –网关还有助于自动化部署,还可以帮助你实现自动化登入验证(on boarding)。 什么是登入(on boarding)呢? 如果你有API,并且你希望有身份验证,你可能需要一些功能可以允许用户为该API创建登入凭据(credentials)然后开始使用(消费)API。 开发人员门户网站或你的文档中心等都可以与网关集成来配置这些凭据(credentials),这样你就不用从头开始构建一些功能了。

Kong作为API网关提供了API管理功能,及围绕API管理实现了一些默认的插件,另外还具备集群水平扩展能力,从而提升整体吞吐量。Kong本身是基于OpenResty,可以在现有Kong的基础上进行一些扩展,从而实现更复杂的特性。

虽然有一些特性Kong默认是缺失的,如API级别的超时、重试、fallback策略、缓存、API聚合、AB测试等,这些功能插件需要企业开发人员通过Lua语言进行定制和扩展。

拓展

为什么需要API网关

传统方式的四大痛点:

痛点 说明
重复多 在多个微服务中,共通的功能重复,比如认证或者日志相关共通模块
巨石化 单个服务仍然后变成尾大不掉的巨石应用的趋势
影响大 影响较大,很难做到扩展功能而能不影响其他服务
效率低 由于系统限制,导致生产性低下

加了网关之后:

为什么需要API网关.png

如何选择微服务 API 网关:对比 Kong、APISIX、Tyk、Apigee 和其他网关: 主要说明了API网关的作用,以及API网关在选型上的注意点是什么

总结

kong作为一款API网关的应用实现,其优点很明显:

  1. 自带一些API网关的基本特性,无需再次开发,即开箱即用;
  2. 基于nginx开发,性能好;
  3. 拓展性好,如果自带的插件不满足生产,可自行开发插件,不过需要会lua语法;
  4. 有可视化的dashboard,方便查看。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/153188.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • window.location.href的用法

    window.location.href的用法javascript中的location.href有很多种用法,主要如下。self.location.href="/url"当前页面打开URL页面location.href=&

  • Sql语句里的递归查询(转)

    Sql语句里的递归查询(转)

  • java反编译工具Java Decompiler

    java反编译工具Java Decompiler我想看一下jsp编译后生成的java文件,用记事本这些看的话要不就乱码,要不就看起来很乱,可读性很低百度了一下java反编译工具JavaDecompiler:这个工具不仅能反编译.class文件,将.class文件转换为可读的.java文件,而且还可以对整个jar包进行反编译。该工具不仅有自己的图形化界面工具JD-GUI,而且还有eclipse和IntelliJIDEA的…

  • 企业版php自动发卡平台,企业版PHP自动发卡平台源码V6.3版

    企业版php自动发卡平台,企业版PHP自动发卡平台源码V6.3版系统环境;php5.2+mysql修复内容:1.解密一律源码,无需zend环境可运行2.免受权即用一律功能3.去除源码内的后门(已删除usr/dir.php列目录后门和a8tg/auth.php无需密码登录后端的后门),另外还去除了几个XSS跨站后门4.修复支付宝、财付通、微信支付接口,新添加集成6钱包支付接口5.添加彩虹易支付接口,行云支付,爱玩支付,去除默认的永纯支付接口6.修复Linux主机…

  • 处理高并发的六种方法

    处理高并发的六种方法处理高并发的六种方法1:系统拆分,将一个系统拆分为多个子系统,用dubbo来搞。然后每个系统连一个数据库,这样本来就一个库,现在多个数据库,这样就可以抗高并发。2:缓存,必须得用缓存。大部分的高并发场景,都是读多写少,那你完全可以在数据库和缓存里都写一份,然后读的时候大量走缓存不就得了。毕竟人家redis轻轻松松单机几万的并发啊。没问题的。所以你可以考的虑考虑你的项目里,那些承载主要请求读场景…

  • php三个数从大到小排列_php常用的流程控制语句

    php三个数从大到小排列_php常用的流程控制语句<?php$a = rand(100,999);$b = rand(100,999);$c = rand(100,999);echo “a=”.”$a”.”<br>”;echo “b=”.”$b”.”<br>”;echo “c=”.”$c”.”<br>”;if(($a > $b ) && ($a > …

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号