1. 单体架构时代
服务自成一体,依赖的少数外部服务会采用配置域名的方式访问
使用外部短信供应商的短信发送接口,会使用 appId 和 appKey 调用该供应商的域名接口即可。
2. SOA 时代
Service Oriented Ambiguity 即面向服务架构, 简称 SOA。
一种粗粒度、松耦合服务架构,服务之间通过简单、精确定义接口进行通讯,不涉及底层编程接口和通讯模型。
A 服务部署在 3 台虚拟机上,这三个服务实例都有自己独立的内网 IP,假设 B 服务要调用A服务的接口服务,可以采用 Nginx 策略
B 服务 Nginx 配置如下:
upstream servicea_api_servers {
server 192.168.12.01:80 weight=3 max_fails=3 fail_timeout=20s;
server 192.168.12.02:80 weight=3 max_fails=3 fail_timeout=20s;
server 192.168.12.03:80 weight=3 max_fails=3 fail_timeout=20s;
}
server {
listen 8080;
listen 80;
server_name serviceb.com.cn;
if ($request_method !~* (GET|POST)) {return 503;}
proxy_http_version 1.1;
location /api/servicea {
proxy_http_version 1.0;
proxy_pass http://servicea_api_servers/api/;
}
}
问题描述:
采用 Nginx 方式调用,但是以上问题无法解决
在传统应用中,组件之间的调用,通过有规范的约束的接口来实现,从而实现不同模块间良好的协作。但是被拆分成微服务后,每个微服务实例的网络地址都可能动态变化,数量也会变化,使得原来硬编码的地址失去了作用。需要一个中心化的组件来进行服务的登记和管理,为了解决上面的问题,于是出现了服务治理,就是管理所有的服务信息和状态,也就是我们所说的注册中心
Eureka is a REST (Representational State Transfer) based service that is primarily used in the AWS cloud for locating services for the purpose of load balancing and failover of middle-tier servers.
AWS 即 Amazon Web Services,是亚马逊公司旗下云计算服务平台,为全世界范围内的客户提供云解决方案。AWS 面向用户提供包括弹性计算、存储、数据库、应用程序在内的一整套云计算服务,帮助企业降低 IT 投入成本和维护成本。
首先,Netflix 是一家做视频的网站,可以这么说该网站上的美剧应该是最火的。
Netflix 是一家没有 CTO 的公司,正是这样的组织架构能使产品与技术无缝的沟通,从而能快速迭代出更优秀的产品。在当时软件敏捷开发中,Netflix 的更新速度不亚于当年的微信后台变更,虽然微信比 Netflix 迟发展,但是当年微信的灰度发布和敏捷开发应该算是业界最猛的。
Netflix 由于做视频的原因,访问量非常的大,从而促使其技术快速的发展在背后支撑着,也正是如此,Netflix 开始把整体的系统往微服务上迁移。
Netflix 的微服务做的不是最早的,但是确是最大规模的在生产级别微服务的尝试。也正是这种大规模的生产级别尝试,在服务器运维上依托AWS云。当然 AWS 云同样受益于 Netflix 的大规模业务不断的壮大。
Netflix 的微服务大规模的应用,在技术上毫无保留的把一整套微服务架构核心技术栈开源了出来,叫做 Netflix OSS,也正是如此,在技术上依靠开源社区的力量不断的壮大。
Spring Cloud 是构建微服务的核心,而 Spring Cloud 是基于 Spring Boot 来开发的。
Pivotal 在 Netflix 开源的一整套核心技术产品线的同时,做了一系列的封装,就变成了 Spring Cloud;虽然 Spring Cloud 到现在为止不只有 Netflix 提供的方案可以集成,还有很多方案,但 Netflix 是最成熟的。
Eureka 包含两个组件:Eureka Server 和 Eureka Client。
Eureka Server 提供服务注册服务,各个节点启动后,会在 Eureka Server 中进行注册,这样 Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Server 本身也是一个服务,默认情况下会自动注册到 Eureka 注册中心。
Eureka Client 是一个 java 客户端,用于简化与 Eureka Server 的交互,客户端同时也具备一个内置的、使用轮询 (round-robin) 负载算法的负载均衡器。在应用启动后,将会向 Eureka Server 发送心跳,默认周期为30秒,如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka Client 分为两个角色,分别是:Application Service(Service Provider) 和 Application Client(Service Consumer)
流程说明:
注册:每个微服务启动时,将自己的网络地址等信息注册到注册中心,注册中心会存储(内存中)这些信息。
获取服务注册表:服务消费者从注册中心,查询服务提供者的网络地址,并使用该地址调用服务提供者,为了避免每次都查注册表信息,所以 client 会定时去 server 拉取注册表信息到缓存到 client 本地。
心跳:各个微服务与注册中心通过某种机制(心跳)通信,若注册中心长时间和服务间没有通信,就会注销该实例。
调用:实际的服务调用,通过注册表,解析服务名和具体地址的对应关系,找到具体服务的地址,进行实际调用。
服务注册表:记录各个微服务信息,例如服务名称,ip,端口等。注册表提供查询 API(查询可用的微服务实例)和管理 API(用于服务的注册和注销)。
服务注册与发现:注册:将微服务信息注册到注册中心。发现:查询可用微服务列表及其网络地址。
服务检查:定时检测已注册的服务,如发现某实例长时间无法访问,就从注册表中移除。
服务提供者: 是一个 Eureka Client,向 Eureka Server 注册和更新自己的信息,同时能从 Eureka Server 注册表中获取到其他服务的信息。
服务注册中心: 提供服务注册和发现的功能。每个 Eureka Cient 向 Eureka Server 注册自己的信息,也可以通过 Eureka Server 获取到其他服务的信息达到发现和调用其他服务的目的。
服务消费者: 是一个 Eureka Client,通过 Eureka Server 获取注册到其上其他服务的信息,从而根据信息找到所需的服务发起远程调用。
同步复制: Eureka Server 之间注册表信息的同步复制,使 Eureka Server 集群中不同注册表中服务实例信息保持一致。
远程调用: 服务客户端之间的远程调用。
注册: Client 端向 Server 端注册自身的元数据以供服务发现。
续约: 通过发送心跳到 Server 以维持和更新注册表中服务实例元数据的有效性。当在一定时长内,Server 没有收到 Client 的心跳信息,将默认服务下线,会把服务实例的信息从注册表中删除。
下线: Client 在关闭时主动向 Server 注销服务实例元数据,这时 Client 的服务实例数据将从 Server 的注册表中删除。
获取注册表: Client 向 Server 请求注册表信息,用于服务发现,从而发起服务间远程调用。
本质上就是在服务启动的时候,需要调用 Eureka Server 的 REST API 的 register 方法,去注册该应用的实例信息。
对于 Spring Cloud 应用,可以使用 spring-cloud-starter-netflix-eureka-client,基于 Spring Boot 自动配置,自动实现服务信息注册。
正常情况下,服务应用在关闭应用的时候,应通过钩子方法或其他生命周期回调方法去调用 Eureka Server 的 REST API 的 de-register 方法,实现删除自身服务实例信息。
异常情况下因服务实例挂掉没有及时删除自身信息的问题,Eureka Server 要求 Client 端定时进行续约,也就是发送心跳来证明服务实例依旧是存活的。若租约超过一定时间没有进行续约操作,Eureka Server 端主动剔除。
服务注册及发现中心不可能是单点的,其自身势必有个集群,服务实例注册信息如何在这个集群中保持一致与 Eureka Server 的架构有关,这需要通过后面几个设计理念分析。
一般,分布式系统的数据在多个副本之间的复制方式分为主从复制和对等复制。
Master-Slave 模式,有一个主副本,其他副本为从副本。主副本变化更新到从副本的具体更新情况又分为同步更新、异步更新、同步及异步混合。
对于主从复制模式来讲,写操作的压力都在主副本上,其成为了整个系统的瓶颈,但从副本可帮主副本分担读请求。
Peer to Peer 模式,副本之间无主从概念,任何副本都可以接收写操作,不存在写操作压力瓶颈,然后每个副本之间都可以相互进行数据更新,但是,各副本之间的数据同步与冲突处理就变为了一个棘手的问题。
Eureka Server 采用的就是 Peer to Peer 复制模式。站在客户端和服务端两个角度分析:
1. 客户端
eureka:
client:
serviceUrl:
defaultZone:http://192.168.8.220:8082/eureka/,http://192.168.8.226:8082/eureka/
实际代码里支持 preferSameZoneEureka ,即有多个分区的话,优先选择与应用所在分区一样的其他服务的实例,如果没有找到则默认使用 defaultZone。
客户端使用 quarantineSet 维护了一个不可用的 Eureka Server 列表,进行请求的时候,优先从可用列表中进行选择,如果请求失败则切换到下一个 Eureka Server 进行重试,重试次数默认为 3。
为防止每个 Clent 端都按配置文件指定的顺序进行请求造成 Eureka Server 节点请求分布不均衡的情况,Client 端有个定时任务(默认为 5 分钟执行一次)来刷新并随机化 Eureka Server 的列表。
2. 服务端
Eureka Server 本身也依赖 Eureka Client,也就是每个 Eureka Server 作为了其他 Eureka Server 的 Client。
在单个 Eureka Server 启动的时候,会有一个 syncUp 的操作,通过 Eureka Client 请求其他 Eureka Server 节点中的一个节点获取注册的应用实例信息,然后复制到其他 peer 节点。
Eureka Server 每当自己的信息变更后,例如 Client 向自己发起注册、续约、注销请求, 就会把自己的最新信息通知给其他 Eureka Server,保持数据同步。
问题:如果自己的信息变更是另一个Eureka Server同步过来的,这是再同步回去的话就出现数据同步死循环了。
解决:Eureka Server 在执行复制操作的时候,使用 HEADER_REPLICATION 这个 http header 来区分普通应用实例的正常请求,说明这是一个复制请求,这样其他 peer 节点收到请求时,就不会再对其进行复制操作,从而避免死循环。
Eureka Server 采用以下两种方式来解决 Peer to Peer 模式中数据复制的冲突问题:lastDirtyTimestamp+heartbeat。
问题:比如 server A 向 server B 发起同步请求,如果 A 的数据比 B 的还旧,B 不可能接受 A 的数据,那么 B 是如何知道 A 的数据是旧的呢?这时 A 又应该怎么办呢?
解决:数据的新旧一般是通过版本号来定义的,Eureka 是通过 lastDirtyTimestamp 这个类似版本号的属性来实现的。
lastDirtyTimestamp 是注册中心里面服务实例的一个属性,表示此服务实例最近一次变更时间。
如果开启 SyncWhenTimestampDiffers 配置(默认开启),当 lastDirtyTimestamp 不为空的时候,有以下处理:
场景:
A 的数据比 B 的新,B 返回 404,A 重新把这个应用实例注册到 B。
A 的数据比 B 的旧,B 返回 409,要求 A 同步 B 的数据。
续约操作,来进行数据的最终修复,因为节点间的复制可能会出错,通过心跳就可以发现错误,进行弥补。
peer 节点之间的相互复制并不保证所有操作都能成功,故 Eureka 需通过应用实例与 Server 之间的 heartbeat 也就是 renewLease 操作来进行数据的最终修复,即发现数据不一致,Server 返回 404,应用实例重新进行 register 操作。
Eureka Server 采用的是对等通信(P2P),无中心化的架构,无 master/slave 区分,每一个 server 都是对等的,既是 Server 又是 Client,所以其集群方式可以自由发挥,可以各点互连,也可以接力互连。Eureka Server 通过运行多个实例以及彼此之间互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向另一个节点。
Netflix 的服务大部分是在 Amazon 上,故 Eureka 的设计有一部分也是基于 Amazon 的 Zone 及 Region 的基础设施上。
Eureka 提供了 region 和 zone 两个概念来进行分区,这两个概念均来自于亚马逊的 AWS(AWS即Amazon Web Services ,是亚马逊公司旗下云计算服务平台,为全世界范围内的客户提供云解决方案。AWS面向用户提供包括弹性计算、存储、数据库、应用程序在内的一整套云计算服务,帮助企业降低IT投入成本和维护成本。)
region:可以简单理解为地理上的分区,比如亚洲地区,或者华北地区,再或者北京等等,没有具体大小的限制。根据项目具体的情况,可以自行合理划分 region。
zone:可以简单理解为 region 内的具体机房,比如说 region 划分为北京,然后北京有两个机房,就可以在此 region 之下划分出 zone1,zone2 两个 zone。
Eureka Server 原始支持了 Region 及 AvailabilityZone,由于资源在 Region 之间默认是不会复制的,因此 Eureka Server 的高可用主要就在于 Region 下面的 AvailabilityZone。
Eureka Client 支持 preferSameZone,也就是获取 Eureka Server 的 serviceUrl 优先拉取和应用实例同处于一个 AvailabilityZone 的 Eureka Server 地址列表。
一个 AvailabilityZone 可以设置多个 Eureka Server 实例,他们之间构成 peer 节点,然后采用 Peer to Peer 复制模式。
有时候我们会看到这样的提示信息
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
这是因为默认情况下,Eureka Server 在一定时间内,没有接收到某个微服务心跳,会将某个微服务注销(90S)。但是当网络故障时,微服务与 Server 之间无法正常通信,上述行为就非常危险,因为微服务正常,不应该注销,它的指导思想就是 宁可保留健康的和不健康的,也不盲目注销任何健康的服务
我们也可以通过命令去关闭自我保护的功能:
eureka:
server:
enable-self-preservation: false
自我保护机制的触发条件是:当每分钟心跳次数(renewsLastMin)小于 numberOfRenewsPerMinThreshold 时,并且开启自动保护模式开关(eureka.server.enable-self-preservation = true) 时,触发自我保护机制,不再自动过期租约
上面我们所有的小于 numberOfRenewsPerMinThreshold,到底是怎么计算的呢,我们在eureka源码中可以得知
numberOfRenewsPerMinThreshold = expectedNumberOfRenewsPerMin * 续租百分比(默认为0.85)
expectedNumberOfRenewsPerMin = 当前注册的应用实例数 x 2
当前注册的应用实例数 x 2 是因为,在默认情况下,注册的应用实例每半分钟续租一次,那么一分钟心跳两次,因此 x 2
例如:我们有10个服务,期望每分钟续约数:10 * 2=20,期望阈值:20*0.85=17,当少于17时,就会触发自我保护机制
由于 Server 和 Client 通过心跳保持服务状态,而只有状态为 UP 的服务才能被访问。看 Eureka 界面中的 status。
在 Client 端配置:将自己的健康状态传播到 Server ,配置如下
eureka:
client:
healthcheck:
enabled: true
Eureka Client 会每隔 30 秒发送一次心跳来续约。 通过续约来告知 Eureka Server 该 Eureka Client 运行正常,没有出现问题。 默认情况下,如果 Eureka Server 在 90 秒内没有收到 Eureka Client 的续约,Server 端会将实例从其注册表中删除,此时间可配置,一般情况不建议更改。
如果 Eureka Client 在注册后,既没有续约,也没有下线(服务崩溃或者网络异常等原因),那么服务的状态就处于不可知的状态,不能保证能够从该服务实例中获取到回馈,所以需要服务剔除此方法定时清理这些不稳定的服务,该方法会批量将注册表中所有过期租约剔除,剔除是定时任务,默认60秒执行一次。延时60秒,间隔60秒
剔除的限制:
Eureka 通过增加 region 和 zone 的定义可以快速时间跨机房灾备( region 和 zone 的概念均来自于亚马逊的 AWS)
一般 region 概念指代区域,如亚洲地区、华北地区、北京等。而 zone 概念指代具体机房。
灾备原则是一个机房内的 Consumer 优先调用同机房的 Service,当同一机房的 Service 或者 Eureka 不可用时,再去调用其他机房的服务。
如果 prefer-same-zone-eureka 为 false,按照 service-url 下的 list 取第一个注册中心来注册,并和其维持心跳检测。不会再向 list 内的其它的注册中心注册和维持心跳。只有在第一个注册失败的情况下,才会依次向其它的注册中心注册,总共重试3次,如果3个 service-url 都没有注册成功,则注册失败。每隔一个心跳时间,会再次尝试。
如果 prefer-same-zone-eureka 为 true ,先通过 region 取 availability-zones 内的第一个 zone ,然后通过这个 zone 取 service-url 下的 list ,并向 list 内的第一个注册中心进行注册和维持心跳,不会再向 list 内的其它的注册中心注册和维持心跳。只有在第一个注册失败的情况下,才会依次向其它的注册中心注册,总共重试3次,如果3个 service-url 都没有注册成功,则注册失败。每隔一个心跳时间,会再次尝试。
所以说,为了保证服务注册到同一个 zone 的注册中心,一定要注意 availability-zones 的顺序,必须把同一 zone 写在前面
对于服务调用者和服务提供者,都是通过 eureka.instance.metadata-map.zone 来设置属于哪个 zone
服务消费者会先通过 ribbon 去注册中心拉取一份服务提供者的列表,然后通过 eureka.instance.metadata-map.zone 指定的 zone 进行过滤,过滤之后如果同一个 zone 内的服务提供者有多个实例,则会轮流调用。
只有在同一个 zone 内的所有服务提供者都不可用时,才会调用其它 zone 内的服务提供者。
Zone:代表当前 eureka 所在区域,分区
Eureka Server 的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,形成一组互相注册的服务中心,以实现服务列表的相互同步,达到高可用的效果。
准备三个节点node1,node2,node3。
在每个实例的 application.xml 文件里加入
eureka.client.service-url.defaultZone: {address},address是其他节点的地址。如果是node1,address就是http://node2/eureka,http://node3/eureka,其他节点依次类推。
eg: defaultZone: http://127.0.0.1:10001/eureka/,http://127.0.0.1:10002/eureka/,http://127.0.0.1:10003/eureka/
Eureka放弃了一致性,保证的是AP。Spring Cloud 的开发者认为注册服务更重要的是可用性,可以接受短期内达不到一致性的状况。
客户端在向某个 Eureka 注册发现连接失败,则会自动切换至其它节点,只要有一台 Eureka 还在,就能保证注册服务可用,只不过查到的信息可能不是最新的。
如果在15分钟内超过85%的节点都没有正常的心跳,那么 Eureka 就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)。
当网络稳定时,当前实例新的注册信息会被同步到其它节点中。
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
2.2.6.RELEASE
程序入口添加注解:@EnableEurekaServer
修改配置文件
spring:
application:
name: eureka-server
server:
port: 10001
servlet:
context-path: /
eureka:
instance:
hostname: dev
instance-id: dev
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://127.0.0.1:10001/eureka/
server:
wait-time-in-ms-when-sync-empty: 0
enable-self-preservation: true
peer-eureka-nodes-update-interval-ms: 10000
eureka.client.registry-fetch-interval-seconds:表示服务间隔多久去 Eureka 中获取注册信息,默认为30s。
eureka.instance.lease-renewal-interval-in-seconds:表示服务给 Eureka 发送心跳间隔,默认为30s。如果该instance实现了HealthCheckCallback,并决定让自己unavailable的话,则该instance也不会接收到流量。
eureka.instance.lease-expiration-duration-in-seconds表示Eureka:上次收到服务的心跳后,等待下一次心跳的时间,如果超时则移除实例,默认为90s。
eureka.server.enable-self-preservation:表示是否开启自我保护模式,默认为true。默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。
Eureka通过“自我保护模式”来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
>综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
eureka.server.eviction-interval-timer-in-ms:表示 Eureka 清理无效节点的时间间隔,默认为60,000ms。
eureka.client.register-with-eureka:表示是否将Eureka注册到自身,多实例中一边选择true。
eureka.client.fetch-registry:表示是否拉去注册的服务。假设,服务A只注册到master节点的Eureka,但是开启该选项,所有的Eureka节点都会注册该服务。
eureka.client.defaultZone:表示希望注册到Eureka的地址,格式为http://ip:port/eureka/,如果部署环境有dns,也可以将ip换成域名,如果有是ha模式,配置多个地址用逗号隔开。
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
3.修改配置文件
server:
port: 8010
spring:
application:
name: eureka-client-a
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10001/eureka/
Eureka | Consul | Nacos | |
---|---|---|---|
配置中心 | 不支持 | 支持 但用起来偏麻烦,不太符合springBoot框架的命名风格,支持动态刷新 | 支持 用起来简单,符合springBoot的命名风格,支持动态刷新 |
应用内/外 | 直接集成到应用中,依赖于应用自身完成服务的注册与发现 | 属于外部应用,侵入性小 | 属于外部应用,侵入性小 |
ACP原则 | 遵循AP(可用性+分离容忍)原则,有较强的可用性,服务注册快,但牺牲了一定的一致性。 | 遵循CP原则(一致性+分离容忍) 服务注册稍慢,由于其一致性导致了在Leader挂掉时重新选举期间真个consul不可用。 | 通知遵循CP原则(一致性+分离容忍) 和AP原则(可用性+分离容忍) |
版本迭代 | 目前已经不进行升级 | 目前仍然进行版本迭代 | 目前仍然进行版本迭代 |
集成支持 | 只支持SpringCloud集成 | 支持SpringCloud K8S集成 | 支持Dubbo 、SpringCloud、K8S集成 |
访问协议 | HTTP | HTTP/DNS | HTTP/动态DNS/UDP |
雪崩保护 | 支持雪崩保护 | 不支持雪崩保护 | 支持雪崩保护 |
界面 | 英文界面,不符合国人习惯 | 英文界面,不符合国人习惯 | 中文界面,符合国人习惯 |
上手 | 容易 | 复杂一点 | 极易,中文文档,案例,社区活跃 |
Nacos | Eureka | Consul | CoreDNS | Zookeeper | |
---|---|---|---|---|---|
一致性协议 | CP+AP | AP | CP | - | CP |
健康检查 | TCP/HTTP/MySQL/Client Beat | Client Beat | TCP/HTTP/gRPC/Cmd | - | Keep Alive |
负载均衡策略 | 权重/metadata/Selector | Ribbon | Fabio | RoundRobin | - |
雪崩保护 | 有 | 有 | 无 | 无 | 无 |
自动注销实例 | 支持 | 支持 | 不支持 | 不支持 | 支持 |
访问协议 | HTTP/DNS | HTTP | HTTP/DNS | DNS | TCP |
监听支持 | 支持 | 支持 | 支持 | 不支持 | 支持 |
多数据中心 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
跨注册中心同步 | 支持 | 不支持 | 支持 | 不支持 | 不支持 |
Spring Cloud | 支持 | 支持 | 支持 | 不支持 | 不支持 |
Dubbo集成 | 支持 | 不支持 | 不支持 | 不支持 | 支持 |
K8S集成 | 支持 | 不支持 | 支持 | 支持 | 不支持 |