SpringCloud(三)服务消费
常见问题
EnableEurekaClient与EnableDiscoveryClient的区别
选用的注册中心是eureka,那么就推荐@EnableEurekaClient,如果是其他的注册中心,那么推荐使用@EnableDiscoveryClient。这个和“Slf4j与Log4j”的道理一样,Log4j可以直接用,也可以通过Slf4j调用Log4j。@EnableDiscoveryClient是通用注解,适用范围更广。
异常:com.netflix.client.ClientException: Load balancer does not have available server for client
如果出现异常:com.netflix.client.ClientException: Load balancer does not have available server for client。
可以关闭wifi后重试,也可以尝试重启Client,或者是适当增加Feign的超时时间。
(一般是因为Maven依赖不够,导致的注册失败问题,如果和我的配置相同,那么,很可能是因为默认的超时时间过短)
正文
前面的文章,我们已经实现了服务中心,并且注册了第一个服务,这里,我们开始调用我们的服务。
相当于Spring中的@Resource注解,Cloud中用的是Feign,下面开始尝试使用它。
最直接的调用方式,办法就是写 “服务名” 和 “地址” (如下),这种做法不符合系统架构的设计,不可能调用一个接口写一个这样的代码,
但是或许能成为以后解决问题的一种办法,这里也做个笔记。
@FeignClient(name = "client-a", url = "xxxxx") public interface PassportServiceApi { }
源码
工程目录(单纯的服务消费,内容太少,索性把服务降级的一些内容加进来)。
Maven依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.13.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.seaboot</groupId> <artifactId>feign</artifactId> <version>0.0.1-SNAPSHOT</version> <name>feign</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR5</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--hystrix 仪表盘--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
程序入口
@EnableHystrixDashboard:Hystrix 仪表盘注解;
@EnableDiscoveryClient:把Feign工程也注册成一个Client,A调用B,B调用C,C回头还可能调用A,我们的服务,在调用别人的时候偶,显然也可能被其它系统调用;
@EnableFeignClients:Feign调用其他服务所必需的注解。
package cn.seaboot.feign; import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; @EnableHystrixDashboard @EnableFeignClients @SpringBootApplication @EnableDiscoveryClient public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } /** * Hystrix仪表盘配置 */ @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(streamServlet); registrationBean.setLoadOnStartup(1); //仪表盘url是可配置的 registrationBean.addUrlMappings("/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; } }
Yml配置
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8765 spring: application: name: service-feign feign: #适当的增加超时时间 client: config: default: connect-timeout: 10000 read-timeout: 20000 client-a: connect-timeout: 10000 read-timeout: 20000 #开启熔断 hystrix: enabled: true
FeignClient
@FeignClient,通过这个注解,说明你要调用的是哪个服务。
name(或者value):指定要调用的服务;
fallback:指定服务降级之后的一些应对信息;
@HystrixCommand:可以根据各种业务需求,定制自己的服务降级策略
package cn.seaboot.feign.services; import cn.seaboot.feign.def.HelloServiceHystric; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; /** * @author Mr.css * @date 2020-03-05 */ @FeignClient(value = "client-a", fallback = HelloServiceHystric.class) public interface HelloService { /** * 这里正常执行,关闭client后,降级服务后返回:sorry hi */ @RequestMapping(value = "/hi", method = RequestMethod.GET) String sayHi(@RequestParam(value = "name") String name); /** * 命令执行超时时间,默认1000ms,这里指定3000 * client中Thread.sleep(5000); 连接必定超时,降级服务后返回:sorry hello */ @HystrixCommand(commandKey="hello",commandProperties={ @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000") }) @RequestMapping(value = "/hello", method = RequestMethod.GET) String sayHello(@RequestParam(value = "name") String name); }
服务降级
需要继承实现Service的接口,如果网络不通或是其它原因,则直接调用这些实现类。
package cn.seaboot.feign.def; import cn.seaboot.feign.services.HelloService; import org.springframework.stereotype.Component; /** * 降级服务 * @author Mr.css * @date 2020-03-05 */ @Component public class HelloServiceHystric implements HelloService { @Override public String sayHi(String name) { return "sorry hi"; } @Override public String sayHello(String name) { return "sorry hello"; } }
Controller
与常规代码一致,重点在Service层。
package cn.seaboot.feign.ctrl; import cn.seaboot.feign.services.HelloService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @author Mr.css * @date 2020-03-05 */ @RestController public class HiController { @Resource HelloService helloService; @GetMapping(value = "/hi") public String sayHi(@RequestParam String name) { return helloService.sayHi( name ); } @GetMapping(value = "/hello") public String sayHello(@RequestParam String name) { return helloService.sayHello(name); } }
结果
hi接口正常使用,如果client关闭,就会出现服务降级后的数据
http://localhost:8765/hi?name=forezp
hello接口因为肯定会超时,必定出现服务降级之后的数据
http://localhost:8765/hello?name=forezp
访问下列地址,可以使用Hystrix的管理界面
在上面的管理页面中,填入http://localhost:8765/hystrix.stream,点击按钮,即可跳转下列页面