一、如果只想加入feign,不要载入hystrix,则在引包时排除掉hystrix的包。
org.springframework.cloud spring-cloud-starter-feignio.github.openfeign feign-hystrix
并且去掉//@EnableHystrix和
@FeignClient(name = "ACCOUNT-SERVICE")
public interface AccountFeignClient {
的配置,以及application中的hystrix配置
feign.hystrix.enabled=false #任务执行超时时间 #hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=400 # #hystrix.command.default.circuitBreaker.requestVolumeThreshold=2 # ##设置统计的时间窗口值的毫秒值 #hystrix.command.default.metrics.rollingStats.timeInMilliseconds=5000 # #hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000 # #hystrix.command.default.circuitBreaker.enabled=true # ## 错误比率阀值,如果错误率>=该值,circuit会被打开,并短路所有请求触发fallback。默认50 #hystrix.command.default.circuitBreaker.errorThresholdPercentage=50 # ## 线程池大小 #hystrix.threadpool.default.coreSize=3 ## 缓冲区大小, 如果为-1,则不缓冲,直接进行降级 fallback #hystrix.threadpool.default.maxQueueSize=5 ## 缓冲区大小超限的阈值,超限就直接降级 #hystrix.threadpool.default.queueSizeRejectionThreshold=2 # ## fallback执行并发量 #hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=2 ribbon.ReadTimeout=30000 ribbon.ConnectTimeout=30000
4.0.0 com.simplemall.micro.serv simplemall-proj0.0.1-SNAPSHOT com.simplemall.micro.serv.page front-appfrontPage 前端页面及服务调用 UTF-8 1.7 de.codecentric spring-boot-admin-starter-client1.4.6 org.springframework.cloud spring-cloud-starter-eurekaorg.springframework.boot spring-boot-starter-testtest org.springframework.boot spring-boot-starter-weborg.springframework.cloud spring-cloud-starter-zipkinorg.springframework.boot spring-boot-starter-aoporg.springframework.boot spring-boot-starter-freemarkerorg.springframework.boot spring-boot-starter-testcom.fasterxml.jackson.core jackson-corecom.fasterxml.jackson.core jackson-databindcom.fasterxml.jackson.datatype jackson-datatype-jodacom.fasterxml.jackson.module jackson-module-parameter-namesio.springfox springfox-swagger22.6.1 io.springfox springfox-swagger-ui2.6.1 com.simplemall.micro.serv.common common-module0.0.1-SNAPSHOT org.springframework.cloud spring-cloud-starter-feignio.github.openfeign feign-hystrixorg.springframework.cloud spring-cloud-starter-ribbonio.jsonwebtoken jjwt0.7.0 commons-codec commons-codec1.10 org.springframework.cloud spring-cloud-dependenciesCamden.SR5 pom import org.springframework.boot spring-boot-maven-plugin
二、调试接口,查看代码
package com.simplemall.micro.serv.page.client;
import java.util.List;
//import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
//import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
//import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import com.simplemall.micro.serv.page.client.hystrix.AccountFeignClientHystrix;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.simplemall.micro.serv.common.bean.account.AccAddress;
import com.simplemall.micro.serv.common.bean.account.Account;
@FeignClient(name = "ACCOUNT-SERVICE")
public interface AccountFeignClient {
@RequestMapping("/acc/login")
public Account login(@RequestParam("phone") String phone, @RequestParam("password") String password);
@RequestMapping("/acc/signup")
// @HystrixCommand(groupKey="test-thread-quarantine",
// commandKey = "testThreadQuarantine",
// threadPoolKey="test-thread-quarantine",
// threadPoolProperties = {
// @HystrixProperty(name="coreSize", value="30"),
// @HystrixProperty(name="maxQueueSize", value="100"),
// @HystrixProperty(name="keepAliveTimeMinutes", value="2"),
// @HystrixProperty(name="queueSizeRejectionThreshold", value="15")
// },
// fallbackMethod = "threadQuarantineFallback")
public String signup(@RequestParam("phone") String phone, @RequestParam("password") String password);
@RequestMapping("/address/list/{accountTid}")
// @HystrixCommand(fallbackMethod="semaphoreQuarantineFallback",
// commandProperties={
// @HystrixProperty(
// name= HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY,
// value="SEMAPHORE"), // 信号量隔离
// @HystrixProperty(
// name=HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS,
// value="100") // 信号量最大并发数
// })
public List getList(
@RequestParam(value = "accountTid", required = true) @PathVariable("accountTid") String accountTid);
}
@Slf4j
@Api(value = "用户服务", tags = "用户服务接口")
@RestController
@RefreshScope // 使用该注解的类,会在接到SpringCloud配置中心配置刷新的时候,自动将新的配置更新到该类对应的字段中。需要重新触发加载动作可以使用POST方式请求/refresh接口,该接口位于spring-boot-starter-actuator依赖,调用前需添加否则404。
public class APIAccountController {
private Logger logger = LoggerFactory.getLogger(APIAccountController.class);
// @Value("${switch.sms}")
private boolean switchSMS = true;
@Autowired
private AccountFeignClient accountFeignClient;
@ApiOperation(value = "用户登陆")
@RequestMapping(value = "acc/login", method = { RequestMethod.POST,RequestMethod.GET })
public RestAPIResult login(@ApiParam(value = "手机号") @RequestParam(required = true) String phone,
@ApiParam(value = "密码") @RequestParam(required = true) String password, HttpSession session) {
RestAPIResult restAPIResult = new RestAPIResult<>();
try{
Account account = accountFeignClient.login(phone, password);
log.info(" account:{}", JSONObject.toJSonString(account));
if (StringUtils.isEmpty(account.getTid())) {
restAPIResult = new RestAPIResult("登陆失败,用户名或密码不正确!");
restAPIResult.setRespData("kkk");
} else {
if (account.getTid().equalsIgnoreCase("hystrix")){
restAPIResult = new RestAPIResult("hystrix!");
restAPIResult.setRespData("触发熔断");
}else{
try {
// 正常情况返回jwt
JSonObject subject = new JSonObject(true);
subject.put("tid", account.getTid());
// token此处定义12小时有效,据实际应用场景确定有效性,也可以定义刷新机制,保持用户token的使用时限
String accessToken = JWTUtils.createJWT(UUIDUtils.getUUID(), subject.toJSonString(),
12 * 60 * 60 * 1000);
restAPIResult.setRespData(accessToken);
} catch (Exception e) {
logger.error("生成jwt异常{}", e);
}
}
}
logger.info("login result = {}", restAPIResult.getRespData());
}catch (Exception e){
restAPIResult = new RestAPIResult("登陆异常!");
restAPIResult.setRespData(e.getMessage());
}
return restAPIResult;
}
查看调用堆栈,我们可以看到feignClient的代理对象为FeignInvocationHandler,真正的处理类为
SynchronousMethodHandler
然后client为LoadBalancerFeignClient, 这里通过LoadBalancerContext获取负载均衡器的健康实例
ILoadBalancer lb = getLoadBalancer(); Server svc = lb.chooseServer(loadBalancerKey);
然后在AbstractLoadBalancerAwareClient.executeWithLoadBalancer进行URL中的HOST服务名替换为真实IP端口。
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
LoadBalancerCommand command = LoadBalancerCommand.builder()
.withLoadBalancerContext(this)
.withRetryHandler(handler)
.withLoadBalancerURI(request.getUri())
.build();
try {
return command.submit(
new ServerOperation() {
@Override
public Observable call(Server server) {
URI finalUri = reconstructURIWithServer(server, request.getUri());
S requestForServer = (S) request.replaceUri(finalUri);
try {
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
}
catch (Exception e) {
return Observable.error(e);
}
}
})
.toBlocking()
.single();
} catch (Exception e) {
Throwable t = e.getCause();
if (t instanceof ClientException) {
throw (ClientException) t;
} else {
throw new ClientException(e);
}
}
}
替换完是这样
接着跳到FeignLoadBalancer.execute
这里配置超时,就是ribbon的超时,然后通过RetryTemplate进行一个重试框架的调用
。
现在进到真正的client调用
这里有一个TraceFeignClient,用来做链路追踪的。
最终调用这个代理client对象真正完成HTTP请求。
Response response = this.delegate.execute(modifiedRequest, options);
代理对象源码
package feign;
public interface Client {
Response execute(Request request, Options options) throws IOException;
public static class Default implements Client {
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection).toBuilder().request(request).build();
}
执行堆栈
这里已经返回结果了
HttpResponse
最后外层返回结果



