Eureka注册中心的服务,有2中调用方式,RestTemplate+Ribbon和Feign。
首先,我们有一个Eureka注册中心集群(2台机器),这2台机器相互注册,机器上的服务相互同步,保证服务的高可用。
然后有一个服务提供方集群(2台机器),将服务注册到Eureka注册中心集群。
1.eurekaServer模块,是注册中心集群,分8771,8772端口,分別启动。
#客户端默认连接8761这个端口,如果不指定,则默认启动是8080端口,则客户端也得改为8080端口。 server: port: 8771 spring: application: name: eurekaserver profiles: node1 eureka: instance: hostname: node1 # preferIpAddress: true //集群是,不要加这个。 server: enableSelfPreservation: false #关闭自我保护机制,将停掉的服务移除 eviction-interval-timer-in-ms: 1000 #每1秒清除一次 client: #自身是否注册到eureka服务器 # registerWithEureka: true # 是否从eureka服务器获取注册信息 # fetchRegistry: true #8771这个机器上的服务同步到8772,注册中心上的服务信息是相互同步的 serviceUrl: defaultZone: http://node2:8772/eureka/ --- server: port: 8772 spring: application: name: eurekaserver profiles: node2 eureka: instance: hostname: node2 # preferIpAddress: true server: enableSelfPreservation: false #关闭自我保护机制,将停掉的服务移除 eviction-interval-timer-in-ms: 1000 #每5秒清除一次 client: #自身是否注册到eureka服务器serviceUrl # registerWithEureka: true # 是否从eureka服务器获取注册信息 # fetchRegistry: true #8772这个机器上的服务同步到8771,注册中心上的服务信息是相互同步的 serviceUrl: defaultZone: http://node1:8771/eureka/
添加依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency>
启动类加注解:
@EnableEurekaServer
在模块根目录,运行mvn clean package,进入target目录,分别启动不同配置项的项目。
2.serviceProvider模块,服务提供方,是服务提供者集群,分8762,8763端口,分別启动。
#默认注册地址 #eureka: # client: # serviceUrl: # defaultZone: http://localhost:8761/eureka/ #management: # contextPath: /eurekaclient1 #传给注册中心的实例信息 eureka: instance: hostname: localhost # preferIpAddress: true #心跳时间,客户端注册服务元数据注册中心 leaseRenewalIntervalInSeconds: 1 leaseExpirationDurationInSeconds: 1 homePageUrlPath: ${server.servletPath} statusPageUrlPath: ${server.servletPath}/info healthCheckUrlPath: ${server.servletPath}/health #对应"metadata":{"instanceId":"eurekaclient:56929cfd113d3fe33d3d3df34715d780" metadataMap: instanceId: ${spring.application.name}:${random.value} client: # fetchRegistry: true #从注册中心获取最新服务,更新本地缓存 # healthcheck: # enabled: true serviceUrl: defaultZone: http://node1:8771/eureka/,http://node2:8772/eureka/ server: port: 8762 # servletPath: / spring: application: name: serviceProvider profiles: serviceProvider-1 --- eureka: instance: hostname: localhost # preferIpAddress: true #心跳时间,客户端注册服务元数据注册中心 leaseRenewalIntervalInSeconds: 1 leaseExpirationDurationInSeconds: 1 homePageUrlPath: ${server.servletPath} statusPageUrlPath: ${server.servletPath}/info healthCheckUrlPath: ${server.servletPath}/health #对应"metadata":{"instanceId":"eurekaclient:56929cfd113d3fe33d3d3df34715d780" metadataMap: instanceId: ${spring.application.name}:${random.value} client: # fetchRegistry: true #从注册中心获取最新服务,更新本地缓存 # healthcheck: # enabled: true serviceUrl: defaultZone: http://node1:8771/eureka/,http://node2:8772/eureka/ server: port: 8763 # servletPath: / spring: application: name: serviceProvider profiles: serviceProvider-2
添加依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
启动类加注解:
@EnableEurekaClient
跟上面一样的,打包,分别启动2个端口配置项的项目。
3.服务消费方
3.1 RestTemplate+Ribbon,serviceConsumerByRibbon模块
启动类:
@SpringBootApplication @EnableDiscoveryClient //发现服务和注册服务 @EnableHystrix //断路器,对服务的延迟和容错进行兜底处理 @EnableHystrixDashboard //断路器仪表盘,对方法进行监控 @RestController public class RibbonApplication { public static void main(String[] args) { SpringApplication.run(RibbonApplication.class, args); } @Bean @LoadBalanced //必须要加上,它不仅进行负载均衡,默认是轮询调用,还要从注册中心获取服务列表地址 RestTemplate restTemplate() { return new RestTemplate(); } @Autowired HelloService helloService; @RequestMapping(value = "/hello") public String hello(@RequestParam String name) { return helloService.hello1(name); } }
断路器处理类:
@Service public class HelloService { @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "helloError") //断路器命令 public String hello1(String name) { return restTemplate.getForObject("http://serviceProvider/hello?name=" + name, String.class); //对服务进行动态调用。 } public String helloError(String name) { //服务调用失败的默认处理。 return "hello," + name + ",error!"; } }application.yml
eureka: client: serviceUrl: defaultZone: http://node1:8771/eureka/,http://node2:8772/eureka/ //注册中心集群 server: port: 8764 spring: application: name: serviceConsumerByRibbon
添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency>
场景1:服务提供方未启动或者启动了,但服务还没有注册到注册中心,发起调用,断路器做了默认处理
场景2:不同端口的服务提供方启动了,我们不停刷新页面。
可以发现,他们默认是轮询方式调用服务提供方。
场景3:手动停止端口8762的服务提供方,刷新页面,由于注册中心上的服务还没及时更新清除,调用失败,断路器默认处理
过一会,在刷新,就可以正常访问8763的服务了。
断路器监控:输入地址:http://localhost:8764/hystrix,进入页面后,输入http://localhost:8764/hystrix.stream。
PS1:搜狗浏览器可能回出现如下问题,用谷歌就没问题。
PS2.当RestTemplate实例化时,如果没有加@LoadBalanced注解,也会访问失败。因为它获取不到服务地址列表。
3.2 Feign,serviceConsumerByFeign模块
Feign是只带Ribbon负载均衡特性的。这里我们不用默认的轮询方式,改为按响应时间的权重方式。
启动类:
@SpringBootApplication @EnableDiscoveryClient //改注解包含断路器功能,不包含断路器仪表盘功能 @EnableFeignClients @RestController //以下2个注解,是启用仪表盘功能 @EnableHystrixDashboard //@EnableCircuitBreaker //和下面注解一样,都可以用 @EnableHystrix public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } @Autowired private HelloService helloService; @Bean public IRule weightedResponseTimeRule() { return new WeightedResponseTimeRule();//这里必须要配置实例化,和配置文件类型对应 } @RequestMapping(value = "/hello", method = RequestMethod.GET) public String hello(@RequestParam String name) { return helloService.hello(name); } }
application.yml:
eureka: client: serviceUrl: defaultZone: http://node1:8771/eureka/,http://node2:8772/eureka/ //注册中心集群 server: port: 8765 spring: application: name: serviceConsumerByFeign feign: hystrix: enabled: true //必须要手动打开断路器 serviceprovider: #服务名 ribbon: #按响应时间的权重分配请求,时间越短,权重越大,分配到请求的几率越大 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
断路器配置:
@FeignClient(value = "serviceProvider",fallback = HelloServiceImpl.class) public interface HelloService { @RequestMapping(value = "/hello",method = RequestMethod.GET) String hello(@RequestParam(value = "name") String name); }
serviceProvider为服务名,HelloServiceImpl实现了HelloService接口,对应的方法是断路器方法。
依赖除了上面要添加的外,还要加上:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
测试场景与上面一样,只不过这里用的是按响应时间权重方式来请求服务。
跟上面一样,进入断路器监控页面:
4.hystrixTurbine断路器聚合监控
断路器聚合监控也就是将多个应用的断路器监控统一起来监控。
启动类:
@SpringBootApplication @EnableHystrixDashboard @EnableTurbine public class TurbineApplication { public static void main(String[] args) { SpringApplication.run(TurbineApplication.class, args); } }
application.yml:
eureka: client: serviceUrl: defaultZone: http://node1:8771/eureka/,http://node2:8772/eureka/ server: port: 8001 spring: application: name: hystrixTurbine turbine: aggregator: clusterConfig: default clusterNameExpression: new String("default") # 要监控的应用名,也是注册到注册中心的服务名 appConfig: serviceConsumerByFeign,serviceConsumerByRibbon
添加依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-turbine</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-turbine</artifactId> </dependency>
访问地址:localhost:8001/hystrix,进入页面后,输入:localhost:8001/turbine.stream。
就这么进去,是看不到任何监控信息的。一直显示loading。我们需要分别访问serviceConsumerByFeign,serviceConsumerByRibbon这2个应用的服务才行。
注意:这里spring cloud依赖的版本是Dalston.RC1,不同版本可能有些区别和问题。