Eureka服务发现和注册_服务容错hystrix
Eureka Server: 服务注册中心,负责服务列表的注册、维护和查询等功能;
Service Provider: 服务提供方,同时也是一个Eureka Client,负责将所提供的服务向Eureka Server进行注册、续约和注销等操作。
注册时所提供的主要数据包括:服务名、机器ip、端口号、域名等,从而能够使服务消费方能够找到。
Service Consumer: 服务消费方,同时也是一个Eureka Client,同样也会向Eureka Server注册本身所提供的服务。
在本示例工程中更多的是从Eureka Server中获取相应的服务列表,以便能够发起服务调用。
Service Provider(服务提供方)和Service Consumer(服务消费方)并不是一个严格的概念,往往服务消费方也是一个服务提供方,同时服务提供方也可能会调用其它服务方所提供的服务。
当然在我们进行微服务构建时还是需要遵守业务层级之间的划分,尽量避免服务之间的循环依赖。
断路器熔断机制:当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务.
断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN).
第一部分:创建服务注册cloud-eureka-server 工程
1,创建一个基础的Spring Boot工程,名字叫cloud-eureka-server,并在pom.xml中引入需要的依赖内容,如下:
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.abc.cloud</groupId>
<artifactId>cloud-eureka-server</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
<relativePath />
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.RELEASE</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>
注意:这里的版本号需要对应,随便写版本号可能会报错,导致注解不能引入。
2,创建一个启动类AppEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class AppEurekaServer {
public static void main(String[] args) {
SpringApplication.run(AppEurekaServer.class, args);
}
}
通过@EnableEurekaServer注解启动一个服务注册中心提供给其他应用进行对话。
3,创建一个配置文件 application.properties,注意不要出现空格,否启动报错。
server.port=8000
# if register self to eureka server ? default value is true. 是否注册自己到eureka server,因为自己就是eureka server,所以设为false.
eureka.client.register-with-eureka=false
# if get register info from other eureka server ? default=true, single eureka server set false. 是否从其他eureka server获取注册信息?
eureka.client.fetch-registry=false
security.basic.enabled=true
security.user.name=admin
security.user.password=123
# set exchange address, search and register sevice all need this address. 服务提供者或消费者 用来注册或发现服务的url地址。
eureka.client.serviceUrl.defaultZone=http://admin:123@localhost:8000/eureka/
4,编译打包,查看注册中心:
http://127.0.0.1:8000/ 输入用户admin 密码123 进入管理界面。
第二部分:创建服务提供者cloud-service-provider工程
1,pom文件和cloud-eureka-server 工程的pom文件的区别。
a,cloud-starter-eureka依赖有区别:(没有-server)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
b,需要把 spring-boot-starter-security 这个依赖删掉,否则访问服务时,总是弹出“需要进行身份验证”的小窗口,而且输入用户和密码后,小窗口还不消失。
2,application.properties文件配置:
spring.application.name=eureka.client.provider.test
server.port=8762
eureka.client.serviceUrl.defaultZone=http://admin:123@localhost:8000/eureka/
3,写服务接口类:
public interface TestIntf {
@RequestMapping(value = "/testHello", method = RequestMethod.GET)
public String testHello(@RequestParam("name") String name);
@RequestMapping(value = "/testRibbon", method = RequestMethod.GET)
public String testRibbon(@RequestParam("version") String version, @RequestParam("date") Date date);
@RequestMapping(value = "/testRibbon2", method = RequestMethod.POST)
public String testRibbon2(@RequestParam("version") String version);
}
4,写服务接口实现类:
@RestController
public class TestImpl implements TestIntf {
public String testHello(String name) {
return "Hello world,"+name;
}
public String testRibbon(@RequestParam("version") String version, @RequestParam("date") Date date) {
return "test ribbon ok. version = "+version + ", date = "+date;
}
public String testRibbon2(String version) {
return "test ribbon2 ok. version = "+version;
}
}
5,写工程启动类:
如果选用的注册中心是eureka,那么就推荐@EnableEurekaClient,如果是其他的注册中心,那么推荐使用@EnableDiscoveryClient。
//@EnableDiscoveryClient
@EnableEurekaClient
@EnableAutoConfiguration
@SpringBootApplication
public class AppServiceProvider {
public static void main(String[] args) {
new SpringApplicationBuilder(AppServiceProvider.class).web(true).run(args);
}
}
6,启动cloud-service-provider 工程
会在http://127.0.0.1:8000/ 管理界面 看到注册了新的服务:EUREKA.CLIENT.TEST01
然后用http://127.0.0.1:8762/testHello?name=longe 测试,结果如下:
Hello world,longe
第三部分:创建一个cloud-service-consumer工程
在调用cloud远程服务时必须使用HTTP客户端,这一部分采用Spring的RestTemplate的方式来实现,但是,用起来最方便、最优雅的还是要属Feign了。
关联web前端(与web, android, ios交互)的服务消费者工程。
1,pom文件 <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!--以下两项需要如果不配置,解析themleaft 会有问题-->
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.5</thymeleaf-layout-dialect.version>
</properties>
<dependencies>
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</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-tomcat</artifactId>
</dependency>
<!-- spring cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
</dependencies>
说明:加了spring-cloud-starter-ribbon依赖,该子项目提供客户端负载均衡功能。
引入 spring-cloud-starter-hystrix 依赖,提供服务容错功能,Hystrix 默认超时时间为 2000 毫秒。
2,编写启动类:
@EnableEurekaClient
@EnableCircuitBreaker
@SpringBootApplication
public class AppServiceConsumer {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(AppServiceConsumer.class, args);
}
}
说明:@EnableCircuitBreaker 注解开启断路器功能,Hystrix 默认超时时间为 2000 毫秒。
3,编写服务调用类:
/** @RestController注解相当于@ResponseBody + @Controller合在一起的作用。 */
@Controller
@RequestMapping("/")
public class HelloController {
@Autowired
private RestTemplate restTemplate;
@Autowired HelloService helloService;
@RequestMapping(value="/index", method=RequestMethod.GET)
public String index(HttpServletRequest request) {
return "index";
}
@ResponseBody
@RequestMapping(value = "/helloRibbon", method = RequestMethod.GET)
public String helloRibbon(@RequestParam("version") String version) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("version", version);
map.put("date", new Date());
return restTemplate.getForEntity("http://eureka.client.provider.test/testRibbon?version={version}&date={date}",
String.class, map).getBody();
}
// 测试服务容错保护hystrix
@ResponseBody
@RequestMapping(value = "/helloRibbon2", method = RequestMethod.GET)
public String helloRibbon2(@RequestParam("version") String version) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("version", version);
map.put("date", new Date());
return helloService.helloService(map);
}
@ResponseBody
@RequestMapping(value = "/postForObject_test", method = RequestMethod.POST)
public String postForObject_test(@RequestParam("version") String version) {
MultiValueMap<String, String> requestParam = new LinkedMultiValueMap<>();
requestParam.set("version", version);
return restTemplate.postForObject("http://eureka.client.provider.test/testRibbon2", requestParam, String.class);
}
}
32,新建HelloService类:用来测试服务容错保护hystrix
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "helloFallback")
public String helloService(Map<String, Object> map) {
return restTemplate.getForEntity("http://eureka.client.provider.test/testRibbon?version={version}&date={date}", String.class, map).getBody();
}
// 如果方法helloFallback和helloService方法参数不同,则会报错误:allback method wasn't found: helloFallback([interface java.util.Map])
public String helloFallback(Map<String, Object> map) {
return "error hhl.";
}
}
4,application.properties文件配置:
server.port=8763
spring.application.name=eureka.client.consumer.test
eureka.client.service-url.defaultZone=http://admin:123@localhost:8000/eureka
5,测试:
51,测试GET方法:
在浏览器中输入:http://192.168.20.211:8763/helloRibbon?version="3.0.4" 会得到结果:
test ribbon ok. version = "3.0.4", date = Tue Jan 30 07:36:33 CST 2018
52,测试POST方法:
新增一个页面,然后页面用POST方法请求,如:
<form action="/postForObject_test" method="POST">
<label for="version">version:</label> <input type="text" name="version" size="50"/><br />
<input type="submit" value="submit"/>
</form>
然后在页面输入version字段的字符串,如:1.32 ,点提交,这时页面会变成:
test ribbon2 ok. version = 1.32
至此,spring cloud的服务注册中心,服务提供者注册服务,服务消费者用restful方式调用远程服务 都已实现,并测试OK。
第四部分:创建一个cloud-consumer-feign工程:采用feign的方式来实现cloud的远程服务调用
第三部分是采用Spring的RestTemplate的方式来实现,但是,用起来最方便、最优雅的还是要属Feign了。
1,在pom.xml文件引入feign依赖的jar包:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2,application.yml 配置文件如下:
eureka:
client:
serviceUrl:
defaultZone: http://admin:123@localhost:8000/eureka/
server:
port: 8764
spring:
application:
name: cloud-consumer-feign-test01
feign:
hystrix:
enabled: true
3,应用程序启动类AppConsumerFeign
/**
* Feign 采用的是基于接口的注解,Feign 整合了ribbon,默认实现了负载均衡。
* Feign能干Ribbon和Hystrix的事情
*/
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
public class AppConsumerFeign {
public static void main(String[] args) {
SpringApplication.run(AppConsumerFeign.class, args);
}
}
4,和web界面交互的controller类:
@RestController
public class HiController {
@Autowired
HiService hiService;
@RequestMapping(value = "/testHello",method = RequestMethod.GET)
public String testHello(@RequestParam String name){
return hiService.testHello(name);
}
}
5,调用cloud远程服务的service类:
// 下面的FeignClient value是服务提供者的spring.application.name,调用的对应方法是testHello
//@FeignClient(value = "eureka.client.provider.test")
// 添加指定fallback类,在服务熔断的时候返回fallback类中的内容。
@FeignClient(name = "eureka.client.provider.test", fallback = HiServiceHystrix.class)
public interface HiService {
@RequestMapping(value = "/testHello",method = RequestMethod.GET)
String testHello(@RequestParam(value = "name") String name);
}
6,启动项目,测试
报错:Spring Boot :: (v1.4.3.RELEASE) FeignClient] must be declared as an @AliasFor [serviceId], not [name].
原因是spring cloud的版本和spring boot的版本造成的,cloud Brixton版本的feign使用的是spring boot 1.3.x的版本。
所以,我们在选择Spring Boot与Spring Cloud版本的时候,还是需要尽可能的按照Spring Cloud官方版本依赖关系来使用:
cloud Brixton版本对应Spring Boot 1.3.x cloud Camden版本对应Spring Boot 1.4.x
因为我们上面用的cloud Brixton版本,所以需要把feign的pom.xml 改为如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.8.RELEASE</version>
</parent>
测试:在浏览器中输入:http://192.168.20.211:8764/testHello?name="dragon",在界面会输出:
Hello world,"dragon"
测试成功。
7,写一个类用Hystrix来对feign的接口类HiService实现熔断保护:
// 用Hystrix 对HiService接口类的方法做熔断保护,上面的HiService接口类也会标记用此类来做熔断保护。
@Component
public class HiServiceHystrix implements HiService {
@Override
public String testHello(String name) {
return "hello" +name+", this messge send failed.";
}
}
测试:停掉cloud provider服务,然后在浏览器中输入:http://192.168.20.211:8764/testHello?name="dragon2",在界面会输出:
hello"dragon2", this messge send failed.
测试成功。
浙公网安备 33010602011771号