主要就是配置一下通用依赖和基本插件
1buildscript {
2 ext {
3 springBootVersion = '2.2.2.RELEASE'
4 springBootManagementVersion = '1.0.8.RELEASE'
5 springCloudVersion = 'Hoxton.SR1'
6 }
7 repositories {
8 maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
9 mavenCentral()
10 maven { url 'https://repo.spring.io/snapshot' }
11 maven { url 'https://repo.spring.io/milestone' }
12 }
13 dependencies {
14 classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
15 classpath("io.spring.gradle:dependency-management-plugin:${springBootManagementVersion}")
16 }
17}
18
19allprojects {
20 group "com.github.rep3.cloud"
21 version "1.0.0"
22}
23
24subprojects {
25 apply plugin: 'java'
26 apply plugin: 'application'
27 apply plugin: 'idea'
28 apply plugin: 'eclipse'
29 apply plugin: 'war'
30 apply plugin: 'org.springframework.boot'
31 apply plugin: 'io.spring.dependency-management'
32 sourceCompatibility = JavaVersion.VERSION_1_8
33 targetCompatibility = JavaVersion.VERSION_1_8
34 jar {
35 enabled = true
36 }
37 bootJar {
38 classifier = 'boot'
39 }
40 repositories {
41 maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
42 mavenCentral()
43 maven { url 'https://repo.spring.io/snapshot' }
44 maven { url 'https://repo.spring.io/milestone' }
45 }
46 dependencies {
47 compile(
48 'org.springframework.boot:spring-boot-starter-web',
49 'org.springframework.boot:spring-boot-starter-tomcat',
50 'org.springframework.boot:spring-boot-starter-actuator',
51 'org.springframework.cloud:spring-cloud-starter',
52 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client',
53 'com.google.guava:guava:23.0'
54
55 )
56 testCompile(
57 "org.springframework:spring-test",
58 "junit:junit:4.12"
59 )
60 }
61 dependencyManagement {
62 imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}") }
63 imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" }
64 }
65}
配置一下 EurekaServer 端依赖
1mainClassName = 'com.github.rep3.cloud.eureka.EurekaApplication'
2springBoot {
3 mainClassName = 'com.github.rep3.cloud.eureka.EurekaApplication'
4 buildInfo {
5 properties {
6 artifact = 'eureka'
7 version = '1.0.0'
8 group = 'com.github.rep3.cloud'
9 name = 'eureka'
10 }
11 }
12}
13bootJar {
14 classifier = 'boot'
15}
16dependencies {
17 compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
18}
配置 zuul 依赖
1mainClassName = 'com.github.rep3.cloud.zuul.Rep3ZuulApplication'
2springBoot {
3 mainClassName = 'com.github.rep3.cloud.zuul.Rep3ZuulApplication'
4 buildInfo {
5 properties {
6 artifact = 'zuul'
7 version = '1.0.0'
8 group = 'com.github.rep3.cloud'
9 name = 'zuul'
10 }
11 }
12}
13bootJar {
14 classifier = 'boot'
15}
16dependencies {
17 compile('org.springframework.cloud:spring-cloud-starter-netflix-zuul')
18}
Cloud 基本的依赖就配置完成了,接下来就配置配置 YAML 跑起来试试看
SpringBoot 不做过多赘述,主要看一下配置文件加载的优先级
配置加载顺序 1->12 越小优先级越高
11.命令行传入参数
22.SPRING_APPLICATION_JSON中的属性
33.java:comp/env中的JNDI属性
44.Java的系统属性:System.getProperties()
55.操作系统环境变量
66.random*配置的随机属性
77.位于当前应用jar包之外的application-profile.yaml配置文件
88.位于当前应用jar包之内的application-profile.yaml配置文件
99.位于jar包之外的appication.yml配置
1010.位于jar包之内的application.yml配置
1111.@Configuration配置类中
1212.应用默认属性比如port默认8080
微服务的监控和管理,将依赖配置在根模块中,所有的子模块 SpringBoot 就可以使用了
1'org.springframework.boot:spring-boot-starter-actuator'
启动以后查看 /health 就可以看到监控信息了
放开所有监控点
1management:
2 endpoints:
3 web:
4 exposure:
5 include: '*'
查看所有监控点
1http://192.168.0.101:8002/actuator
返回了所有监控点信息
1{"_links":{"self":{"href":"http://192.168.0.101:8002/actuator","templated":false},"archaius":{"href":"http://192.168.0.101:8002/actuator/archaius","templated":false},"beans":{"href":"http://192.168.0.101:8002/actuator/beans","templated":false},"caches-cache":{"href":"http://192.168.0.101:8002/actuator/caches/{cache}","templated":true},"caches":{"href":"http://192.168.0.101:8002/actuator/caches","templated":false},"health-path":{"href":"http://192.168.0.101:8002/actuator/health/{*path}","templated":true},"health":{"href":"http://192.168.0.101:8002/actuator/health","templated":false},"info":{"href":"http://192.168.0.101:8002/actuator/info","templated":false},"conditions":{"href":"http://192.168.0.101:8002/actuator/conditions","templated":false},"configprops":{"href":"http://192.168.0.101:8002/actuator/configprops","templated":false},"env":{"href":"http://192.168.0.101:8002/actuator/env","templated":false},"env-toMatch":{"href":"http://192.168.0.101:8002/actuator/env/{toMatch}","templated":true},"loggers-name":{"href":"http://192.168.0.101:8002/actuator/loggers/{name}","templated":true},"loggers":{"href":"http://192.168.0.101:8002/actuator/loggers","templated":false},"heapdump":{"href":"http://192.168.0.101:8002/actuator/heapdump","templated":false},"threaddump":{"href":"http://192.168.0.101:8002/actuator/threaddump","templated":false},"metrics-requiredMetricName":{"href":"http://192.168.0.101:8002/actuator/metrics/{requiredMetricName}","templated":true},"metrics":{"href":"http://192.168.0.101:8002/actuator/metrics","templated":false},"scheduledtasks":{"href":"http://192.168.0.101:8002/actuator/scheduledtasks","templated":false},"mappings":{"href":"http://192.168.0.101:8002/actuator/mappings","templated":false},"refresh":{"href":"http://192.168.0.101:8002/actuator/refresh","templated":false},"features":{"href":"http://192.168.0.101:8002/actuator/features","templated":false},"service-registry":{"href":"http://192.168.0.101:8002/actuator/service-registry","templated":false}}}
11./beans: 所有bean信息
22./caches:缓存信息
33./health:服务状态
44./info:服务自定义信息
55./configprops:应用配置的属性信息
66./env:应用所有可用环境信息
77./mappings:所有SpringMvc映射关系信息
88./metrics:内存信息,线程信息,垃圾回收信息等
99./dump:运行中的线程信息
1010./trace:http跟踪信息
11...
服务注册
:主要用来开微服务架构中有多少服务都部署在哪里
服务发现
:服务之间调用关系的处理,比如 A 服务调用 C 服务,会直接像注册中心发出咨询请求,然后注册中心将 C服务的位置清单
返回给 A 服务,A 便获得了 多个C服务的位置
,然后就可以调用其一。
SpringCloudEureka 使用 Netflix Eureka
来实现服务注册与发现,既包括了注册端也包括了服务端
服务端依赖
1compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
2
客户端依赖
1compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client',
服务端 Application 开启 @EnableEurekaServer
注解标识为服务中心
同时要配置配置文件,给出 hostname 地址,客户端策略等
1eureka:
2 instance:
3 hostname: localhost
4 client:
5 register-with-eureka: false
6 fetch-registry: false
客户端需要开启 @EnableEurekaClient
标示为 Eureka 客户端,同时配置出服务中心的地址
1eureka:
2 client:
3 serviceUrl:
4 defaultZone: http://localhost:8000/eureka/
5
上面搭建的只有一个服务注册中心 1 Eureka : N Springboot
,当这一台 Eureka 崩掉以后,所有服务将不能正常访问
在 Eureka 中,所有服务既是服务提供者,也是服务消费者,Eureka 本身也不例外,现在放开一下配置,并新建一个 Eureka 服务,两者互相注册成服务
1 client:
2 register-with-eureka: false
3 fetch-registry: false
EurekaA
1server:
2 port:8001
3spring:
4 application:
5 name:eureka1
6eureka:
7 instance:
8 hostname: peer1
9 client:
10 serviceUrl:
11 defaultZone: http://peer2:8000/eureka
EurekaB
1server:
2 port:8000
3spring:
4 application:
5 name:eureka2
6eureka:
7 instance:
8 hostname: peer2
9 client:
10 serviceUrl:
11 defaultZone: http://peer1:8001/eureka
最后将 peer 的 ip 地址解析在/etc/hosts 中即可完成 eureka 高可用部署
高可用服务注册
1spring:
2 profiles: dev
3 application:
4 name: auth
5server:
6 port: 8002
7eureka:
8 client:
9 serviceUrl:
10 defaultZone: http://peer1/eureka/,http://peer2/eureka/
11management:
12 endpoints:
13 web:
14 exposure:
15 include: '*'
16
ribbon 已经在 zuul 里了所以不必添加依赖
ribbon 和 eureka 联合使用时,在 eureka 的服务发现基础上实现了一套 服务选择
的策略,从而达到每个服务负载均衡
将 @EnableEurekaClient
替换为 @EnableDiscoveryClient
当使用服务时,ribbon 会轮询服务从而走负载较低的服务达到负载均衡的效果
1服务注册中心: Eureka提供的服务端,提供服务注册与发现功能
2服务提供: Eureka提供的客户端,将自己的服务注册到Eureka中
3服务消费者: 消费者从应用中心获取服务列表,从而使消费者知道到何处调用服务,ribbon来实现服务消费,另外还有Feign消费方式
1服务提供者
21.服务注册
3 服务提供者在启动的时候`发送REST`请求到Eureka,其中包含了自身服务的信息
4Eureka接收到请求后,将元数据信息存储在`双层Map`中,第一层key为服务名称,第二层是具体服务实例名称.
5
62.服务同步
7服务提供者可能注册到了不同的注册中心,他们的信息分别被多个eureka维护
8所以访问其中一台eureka是无法调用到其余服务的,那么将eureka构建成集群模式
9当服务提供者发送`REST请求`到其中一个注册中心时
10eureka将请求转发给集群中其他的eureka中,从而实现同步
11
123.服务续约
13心跳检测来观测哪些服务死掉,死掉的服务会被剔除出注册中心集群中去
14其中有两个配置项
15服务续约任务调用时间间隔,默认为30秒
16eureka.instance.lease-renewal-interval-in-seconds=30
17服务失效时间默认为90秒
18eureka.instance.lease-expiration-duration-in-seconds=90
19
20服务消费者
211.获取服务
22消费者访问Eureka中的服务时,Eureka会提供一份只读的服务清单,该清单被eureka缓存并每30秒更新一次
23eureka.client.registry-fetch-interval-seconds配置缓存服务清单时间
242.服务调用
25服务消费者获取服务清单后,通过服务名可以获得具体的提供服务的实例名称和服务元数据信息
26客户端决定需要调用哪个服务实例,如果使用ribbon,则使用了轮询方式调用,从而实现客户端的负载均衡
273.服务下线
28正常关闭服务实例时,服务实例发送`REST请求给EurekaServer`告诉服务中心我要下线了,服务中心收到后将服务状态设置为`Down`并传播下线事件
29
30服务注册中心
311.失效剔除
32发生不可预料的错误时导致服务崩溃但eureka没有收到下线消息的时候,由于服务没有及时续约,所以被剔除
332.自我保护
34请求重试,断路器等机制
部分源码解析
1//DiscoveryClient 为接口,抽象了一个服务中心基本需要实现的功能
2// 所以注册中心可以换位任一实现了该接口的类,但不用修改代码
3public class EurekaDiscoveryClient implements DiscoveryClient {
4
5 // 一段描述
6 public static final String DESCRIPTION = "Spring Cloud Eureka Discovery Client";
7 // 客户端
8 private final EurekaClient eurekaClient;
9 // 客户端配置
10 private final EurekaClientConfig clientConfig;
11
12 // 根据服务ID获取服务实例列表
13 @Override
14 public List<ServiceInstance> getInstances(String serviceId) {
15 List<InstanceInfo> infos = this.eurekaClient.getInstancesByVipAddress(serviceId,
16 false);
17 List<ServiceInstance> instances = new ArrayList<>();
18 for (InstanceInfo info : infos) {
19 instances.add(new EurekaServiceInstance(info));
20 }
21 return instances;
22 }
23
24 //获取服务名称
25 public List<String> getServices() {
26 Applications applications = this.eurekaClient.getApplications();
27 if (applications == null) {
28 return Collections.emptyList();
29 }
30 List<Application> registered = applications.getRegisteredApplications();
31 List<String> names = new ArrayList<>();
32 for (Application app : registered) {
33 if (app.getInstances().isEmpty()) {
34 continue;
35 }
36 names.add(app.getName().toLowerCase());
37
38 }
39 return names;
40 }
41}
42// EurekaClient抽象了许多接口来定义Client的行为
43public interface EurekaClient extends LookupService {
44 // 通过Region获取Applications,Region为yaml中设置的字符串
45 // Applicaions中包含了appNameApplicationMap等
46 // Applications 包装eureka服务器返回的所有注册表信息的类
47 public Applications getApplicationsForARegion(@Nullable String region);
48 // 通过地址获取 服务注册表
49 public Applications getApplications(String serviceUrl);
50 // 注册监听
51 public void registerEventListener(EurekaEventListener eventListener);
52 // 取消监听
53 public boolean unregisterEventListener(EurekaEventListener eventListener);
54 // 心跳检测
55 public HealthCheckHandler getHealthCheckHandler();
56 // 停止服务
57 public void shutdown();
58 // 获取Client的配置
59 public EurekaClientConfig getEurekaClientConfig();
60 public ApplicationInfoManager getApplicationInfoManager();
61}
62........
EndpointUtils.java
1// 从属性文件中获取要与eureka客户端对话的所有eureka服务URL的列表。
2 public static Map<String, List<String>> getServiceUrlsMapFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
3 Map<String, List<String>> orderedUrls = new LinkedHashMap<>();
4 // 从配置文件中读取Region返回
5 // 通过eureka.client.region属性来定义
6 String region = getRegion(clientConfig);
7 // 读取 eureka.client.defaultZone
8 String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
9 if (availZones == null || availZones.length == 0) {
10 availZones = new String[1];
11 availZones[0] = DEFAULT_ZONE;
12 }
13 logger.debug("The availability zone for the given region {} are {}", region, availZones);
14 int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);
15
16 String zone = availZones[myZoneOffset];
17 // 获取zone中所有配置的eurekaServer地址
18 List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
19 if (serviceUrls != null) {
20 orderedUrls.put(zone, serviceUrls);
21 }
22 int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1);
23 while (currentOffset != myZoneOffset) {
24 zone = availZones[currentOffset];
25 serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
26 if (serviceUrls != null) {
27 // 最后放入 服务列表中
28 orderedUrls.put(zone, serviceUrls);
29 }
30 if (currentOffset == (availZones.length - 1)) {
31 currentOffset = 0;
32 } else {
33 currentOffset++;
34 }
35 }
36 if (orderedUrls.size() < 1) {
37 throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!");
38 }
39 return orderedUrls;
40 }
服务注册
在 DiscoveryClient 的构造函数中调用了 initScheduledTasks
函数,初始化一些任务
函数中实力化了一个 InstanceInfoReplicator
该类中的 run 函数,这句就是调用注册了,
在往下就是 http 层了,层层专递的 InstanceInfo 就是实例的一些配置信息了
服务获取与续约
还在 initScheduledTasks
中
registryFetchIntervalSeconds:eureka.client.registry-fetch-interval-seconds
renewalIntervalInSecs:eureka.instance.lease-renewal-interval-in-seconds
其最后也是通过 RestTemplate 去请求服务端
负载均衡设备/模块 都会维护一个下挂可用的服务清单,发送心跳检测
剔除不可用设备,当客户端发送请求到负载均衡设备时,该设备按照某种
算法,从维护的清单中抽取出一台服务器,并进行转发。
SpringCloudRibbon 客户端使用负载均衡
11.服务提供者向服务中心注册
22.服务消费者通过@LoadBalanced注解修饰过的RestTemplate访问服务
@LoadBalanced 注解给 RestTemplate 做标记,以使用负载均衡的客户端来配置它 LoadBalancerClient
ServiceInstance choose(String serviceId) 根据传入的服务名,从负载均衡器中挑选一个对应的服务实例
T execute(String serviceId,LoadBalancerRequest request): 从负载均衡器中提取服务来 执行 request
URI reconstructURI(ServiceInstance instance,URI original): 拼接请求地址
LoadBalancerInterceptor 的 Bean,用于对客户端发起的请求实时拦截
RestTemplateCstomizer 的 Bean,用于给 RestTemplate 增加 LoadBalancerInterceptor
维护了一个 RestTemplate 列表,然后给每一个 RestTemplate 实例去初始化拦截器
拦截器中注入了 LoadBalancerClient
和 LoadBalancerRequestFactory
最后交给 LoadBalancerClient
去执行 request
最后走到了 RibbonLoadBalancerClient
Server server = getServer(loadBalacer,hint);
getLoadBalancer通过Factory构造了默认的ZoneAwaerLoadBalaner
然后调用了 ZoneAwaerLoadBalaner 中的 chooseServer 来选择一个服务,继而执行 request,选择策略在 ZoneAwaerLoadBalaner的chooseServer中
负载均衡器
1AbstractLoadBalancer:
2ILoadBalancer的抽象实现,定义了
3分组枚举:ALL所有服务实例,STATUS_UP正常服务实例,STATUS_NOT_UP停止服务实例.
4实现了一个chooseServer函数,key为null,表示在选择具体服务实例时忽略key的条件判断.
5getServerList(ServerGroup goup): 根据不同的分组选择不同的服务实例列表.
6getLoadBalancerStats():获取LoadBalancerStats对象,储存负载均衡器中各个服务实例当前的属性和统计信息.
1BaseLoadBalancer:Ribbon负载均衡器的基础实现类
2定义并维护了两个存储服务实例Server的对象列表,分别存储了所有服务实例和所有正常运行服务实例
3定义了LoadBalancerStats
4定义了IPing检查服务是否运行正常
5定义了IPingStrategy Iping的执行策略对象
6定义了IRule负载均衡的处理规则
7...服务实例列表相关函数
1DynamicServerListLoadBalancer继承BaseLoadBalancer,扩展了基础负载均衡器
负载均衡策略
1AbstractLoadBalancerRule
2RandomRule:随机选择策略
3RoundRobinRule:线性轮询策略
4RetryRule:重试机制策略
5WeightedResponseTimeRule:权重计算策略
单个服务出错断路
1'org.springframework.cloud:spring-cloud-starterhystrix:1.4.7.RELEASE',
SpringCloudApplication 注解聚合了,服务注册负载均衡熔断机制等注解,可以直接标示到 Application 上面
1@SpringBootApplication
2@EnableDiscoveryClient
3@EnableCircuitBreaker
1@Service
2public class DemoService {
3
4 @Autowired
5 RestTemplate restTemplate;
6 // 如果函数出现错误,或者请求超时,那么将会出发熔断请求,并调用callback,返回结果
7 @HystrixCommand(fallbackMethod = "helloFallback")
8 public String toAuth() {
9 return restTemplate.getForEntity("http://auth/home", String.class).getBody();
10 }
11
12 public String helloFallback(){
13 return "error";
14 }
15
16}
工作流程
11.创建HystrixCommand/HystrixObservbleCommand Bean,用来表示对依赖服务的操作请求,同时传递所有需要的参数.
22.命令执行,execute同步执行,queue异步执行,observe()HotOb返回Ob对象,toObServable返回Ob对象ColdOb
33.结果是否被缓存
44.断路器是否打开
55.线程池/请求队列是否被占满
66.HystrixCommand.run()返回单一结果或跑一场
7HystrixObservableCommand.construct()返回一个Ob来发射多个结果/通过onError发送错误通知
87.计算断路器健康程度,根据健康程度熔断/断路服务
98.fallback处理
109.返回成功的Response
断路器原理
HystrixCircuitBreaker 维护了三个抽象函数和三个类
1allowRequest():每个Hystrix命令的请求通过该函数判断是否被执行
2
3isOpen():断路器是否打开
4
5markSuccess():闭合断路器
6
7Factory:维护了一个Hystrix命令和HystrixCircuitBreaker的关系集合,key
8通过HystrixCommandKey定义,每一个Hystrix命令都有一个key标识,同时一个Hystrix命令也会在该集合中找到它对应的断路器HystrixCirucuitBreaker
9
10NoOpCirutiBreaker定义了一个什么都不做的断路器实现,允许所有请求,断路器始终闭合
11
12HystrixCircuitBreakerImpl是本接口实现,定义了4个核心对象
13HystrixCommandProperties:对应实例对象
14HystrixCommandMetrics:记录度量指标对象
15circuitOpen:是否打开标志
16circuitOpenedOrLastTestedTime:打开或是上一次测试的时间
HystrixCircuitBreakerImpl 的 isOpen
HystrixCircuitBreakerImpl 的 allowRequest,通过 isOpen 判断是否允许被请求
!isOpen()||allowSingleTest()
通过配置文件中的 circuiBreakerSleepWindowInMilliseconds 属性休眠时间,对比时间戳判断请求是否可以访问,如果休眠时间到达后请求访问失败,则断路器再次进入打开状态,如果成功,断路器关闭,这句是切换断路器打开关闭状态的切换的实现。
单独使用可以进行路由转发,类似 nginx
1zuul:
2 routes:
3 {serviceId}:
4 path: /user/**
5 url: http://www.baidu.com/user/
上面将 /user 的路由全部转发到下面对应的 url 中
多实例配置
1zuul:
2 routes:
3 {serviceId}:
4 path: /user/**
5 serviceId: {serviceId}
6// 没有整合eureka这里应该关闭
7ribbon:
8 eureka:
9 enabled: false
10{serviceId}:
11 ribbon:
12 listOfServers: http://www.baidu1.com/,http://www.baidu2.com/
忽略路由
1zuul.ignored.parttens=/**/hello/**
本地跳转
1zuul.routes.{serviceId}.url=forward:/local
传递 Http Header 信息
1zuul:
2 routes:
3 {router}:
4 // 对制定路由开启自定义敏感头信息
5 customSenstiveHeaders:true
6 // 将制定路由敏感信息头设置为空
7 senstiveHeaders
当遇到 302 跳转到具体微服务而不是网关时
1zuul:
2 // 跳转时设置Host为zuul
3 addHostHeader : true
Pre: 路由转发之前
name | index | desc |
---|---|---|
ServletDetectionFilter | -3 | 检测当前请求为 DispatcherServelet/ZuulServlet 处理运行 |
Servlet30WrapperFilter | -2 | HttpServletRequest->Servlet30RequestWrapper |
FormBodyWrapperFillter | -1 | 将 ContentType 为 x-www-form-urlencoded/multipart-form-data 请求->FormBodyRequestWrapper |
DebugFilter | 1 | debug 信息 |
PreDecorationFilter | 5 | 设置请求信息,进行路由匹配 |
route:路由转发中
name | index | desc |
---|---|---|
RibbonRouterFilter | 10 | 通过 serviceId 和 Ribbon/Hystrix 对应用实例发起请求并将结果返回 |
SimpleHostRoutingFiler | 100 | 通过 routesHost 和路由规则向物理机器发送请求 |
SendForwardFillter | 500 | 对 forward.to 参数的请求进行处理,forward 跳转 |
post route 和 error 过滤器调用之后进入
name | index | desc |
---|---|---|
SendErrorFillter | 0 | 包装错误信息组成 forward 发送给网关来报错 |
SendResponseFiller | 1000 | 返回响应发送给客户端 |