模块A→模块B。
而,模块B→请求模块C。
如果A和B本身又有其它的请求如:
模块D->模块A,模块E→模块B;
此时,对于:
4.0.0
org.sky.demo.timeout
TimeoutServiceDemo
0.0.1-SNAPSHOT
TimeoutUser
org.aspectj
aspectjweaver
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-configuration-processor
true
org.springframework.boot
spring-boot-starter-log4j2
commons-lang
commons-lang
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-logging
org.slf4j
slf4j-log4j12
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
spring:
application:
name: TimeoutUser
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
package org.sky.demo.timeout;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = { "org.sky" })
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
@EnableDiscoveryClient
public class TimeoutUserApp {
public static void main(String[] args) {
SpringApplication.run(TimeoutUserApp.class);
}
}
就一个@EnableDiscoveryClient搞定了。
package org.sky.demo.timeout.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RestController
@RequestMapping("demo")
public class TimeoutUserController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@GetMapping(value = "/timeout/liveinfor")
@ResponseBody
public String getLiveInfor() {
try {
Thread.sleep(8000);
return "code:0";
} catch (Exception e) {
logger.error(">>>>>>giveLiveInfor error: " + e.getMessage(), e);
return "code: -1";
}
}
@GetMapping(value = "/timeout/getUserInfo")
@ResponseBody
public String getUserInfo() {
return "code: 0";
}
}
好了,provider端搞定。
4.0.0
org.sky.demo.timeout
TimeoutServiceDemo
0.0.1-SNAPSHOT
AdvancedIndex
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
commons-lang
commons-lang
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.cloud
spring-cloud-starter-hystrix
org.springframework.cloud
spring-cloud-starter-hystrix-dashboard
org.springframework.boot
spring-boot-starter-log4j2
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-logging
org.slf4j
slf4j-log4j12
com.squareup.okhttp3
okhttp
org.springframework.boot
spring-boot-starter-test
test
org.ow2.asm
asm
org.aspectj
aspectjweaver
com.google.guava
guava
com.alibaba
fastjson
org.sky.demo.timeout
TimeoutUserFacade
此处的org.sky.demo.timeout.TmeoutUserFacade是微服务的feign+hystrix的熔断出错处理作为子jar包引入到consumer/client端用的后面会给出代码。
为什么这大一陀,比网上的丰富多了(网上很多复制粘贴都是错的)
server:
port: 9080
spring:
application:
name: AdvancedIndex
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
ribbon:
eager-load:
enabled: true
clients: TimeoutUser
feign:
okhttp:
enabled: true
httpclient:
enabled: false
max-connections: 1000
max-connections-per-route: 100
compression:
request:
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
enabled: true
response:
enabled: true
client:
config:
default:
connect-timeout: 2000
read-timeout: 2000
hystrix:
enabled: true
hystrix:
threadpool:
default:
coreSize: 800 #每个微服务容器内可以支持的最大并发
maximumSize: 2000
maxQueueSize: -1
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000
其中的:
ribbon: eager-load: enabled: true clients: TimeoutUser
处的clients指向provider的spring.application.name后所指的值。这是因为Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,很容易就会出现上面所描述的显现。这个问题和ESClient的第一次加载的梗是一样的。
此处,我们把这个proxy+熔断器作为了一个工程的子jar包,引入到我们的consumer端主体工程内了。
4.0.0
org.sky.demo.timeout
TimeoutServiceDemo
0.0.1-SNAPSHOT
TimeoutUserFacade
org.springframework.cloud
spring-cloud-starter-openfeign
provided
org.springframework.boot
spring-boot-starter-log4j2
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-logging
org.slf4j
slf4j-log4j12
package org.sky.demo.timeout.advancedindex.proxy;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@FeignClient(value = "TimeoutUser", fallbackFactory = TimeoutUserServiceProxyFallback.class)
public interface TimeoutUserServiceProxy {
@GetMapping(value = "/demo/timeout/liveinfor")
@ResponseBody
public String getLiveInfor();
@GetMapping(value = "/demo/timeout/getUserInfo")
@ResponseBody
public String getUserInfo();
}
package org.sky.demo.timeout.advancedindex.proxy;
import feign.hystrix.FallbackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class TimeoutUserServiceProxyFallback implements FallbackFactory {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public TimeoutUserServiceProxy create(Throwable throwable) {
return new TimeoutUserServiceProxy() {
@Override
public String getLiveInfor() {
StringBuffer msg = new StringBuffer();
msg.append("code: -1");
msg.append("服务报错导致服务降级: " + throwable.getMessage());
logger.error(">>>>>>服务报错导致服务降级: " + throwable.getMessage(), throwable);
return new String(msg.toString());
}
@Override
public String getUserInfo() {
StringBuffer msg = new StringBuffer();
msg.append("code: -1");
msg.append("服务报错导致服务降级: " + throwable.getMessage());
logger.error(">>>>>>服务报错导致服务降级: " + throwable.getMessage(), throwable);
return new String(msg.toString());
}
};
}
}
package org.sky.demo.timeout.advancedindex.controller;
import javax.annotation.Resource;
import org.sky.demo.timeout.advancedindex.proxy.TimeoutUserServiceProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("demo")
public class IndexController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private TimeoutUserServiceProxy timeoutUserServiceProxy;
@GetMapping(value = "/timeout/advancedIndexWithLive")
@ResponseBody
public String indexWithLive() {
try {
logger.info(">>>>>>into indexWithLive");
String liveResult = timeoutUserServiceProxy.getLiveInfor();
String userInfoResult = timeoutUserServiceProxy.getUserInfo();
logger.info(">>>>>>live room result->{} user info result->{}", liveResult, userInfoResult);
return userInfoResult;
} catch (Exception e) {
logger.error(">>>>>>into AdvancedIndex error: " + e.getMessage(), e);
return "code: -1";
}
}
@GetMapping(value = "/timeout/advancedIndexNoLive")
@ResponseBody
public String indexNoLive() {
try {
logger.info(">>>>>>into indexNoLive");
String userInfoResult = timeoutUserServiceProxy.getUserInfo();
logger.info(">>>>>>user info result->{}", userInfoResult);
return userInfoResult;
} catch (Exception e) {
logger.error(">>>>>>into AdvancedIndex error: " + e.getMessage(), e);
return "code: -1";
}
}
}
package org.sky.demo.timeout.advancedindex;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class AdvancedIndexApp {
public static void main(String[] args) {
SpringApplication.run(AdvancedIndexApp.class, args);
}
}
此处有两个annotation:
@EnableFeignClients
@EnableDiscoveryClient
把3个项目启动起来,然后通过http://localhost:9080/demo//timeout/advancedIndexWithLive记问。
然后你会看到因为该微服务连着的实际业务服务里有一个Thread.sleep(8000);而我们的微服务超时为:2秒。
因此你会看到AdvancedIndex工程内抛出一个熔断信息为“服务报错导致服务降级”的exception,进而打断服务。