Spring Cloud Eureka

Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块。而Spring Cloud Netflix项目是Spring Cloud的子项目之一,主要内容是对Netflix公司一系列开源产品的包装,它为Spring Boot应用提供了自配置的Netflix OSS整合。通过一些简单的注解,开发者就可以快速的在应用中配置一下常用模块并构建庞大的分布式系统。它主要提供的模块包括:服务发现(Eureka),断路器(Hystrix),智能路由(Zuul),客户端负载均衡(Ribbon)等。
创建一个项目spring-cloud-eureka
引入子模块需要的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.hu</groupId>
<artifactId>spring-cloud-eureka</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring-cloud-eureka-server</module>
<module>spring-cloud-eureka-provider</module>
<module>spring-cloud-eureka-consumer</module>
<module>spring-cloud-eureka-consumer-feign</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/>
</parent>
<!-- 设置jdk版本等信息 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<!-- dependency management -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

</project>
创建服务注册中心

在Idea中创建一个Eureka服务子模块,命名为spring-cloud-eureka-server,并在pom.xml中引入需要的依赖内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-eureka</artifactId>
<groupId>com.hu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-cloud-eureka-server</artifactId>

<!-- 导入eureka依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>

</dependencies>
</project>

创建 启动类:

1
2
3
4
5
6
7
8
@EnableEurekaServer//表示是一个eurekaserver
@SpringBootApplication
public class EurekaServerApplication {

public static void main(String[] args) {
new SpringApplicationBuilder(EurekaServerApplication.class).web(true).run(args);
}
}

增加配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
security:
basic:
enabled: true
user:
name: user
password: 123456
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://user:123456@localhost:8761/eureka
logging:
level:
创建服务提供方

创建一个服务spring-cloud-eureka-provider,引入依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-eureka</artifactId>
<groupId>com.hu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-cloud-eureka-provider</artifactId>
<dependencies>
<!--boot eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- boot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

</project>

创建启动类:

1
2
3
4
5
6
7
8
@EnableEurekaClient
@SpringBootApplication
public class ProviderApplication {

public static void main(String[] args) {
new SpringApplicationBuilder(ProviderApplication.class).web(true).run(args);
}
}

创建配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server:
port: 7900
spring:
application:
name: provider-hello
eureka:
client:
serviceUrl:
defaultZone: http://user:123456@localhost:8761/eureka
logging:
level:
root: INFO
#通过spring.application.name属性,我们可以指定微服务的名称后续在调用的时候只需要使用该名称就可以进行服务的访问。
#eureka.client.serviceUrl.defaultZone属性对应服务注册中心的配置内容,指定服务注册中心的位置。
#为了在本机上测试区分服务提供方和服务注册中心,使用server.port属性设置不同的端口。

创建服务 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
public class ProviderController {
private static String[] users = {"5","2","3","1"};
@RequestMapping(value = "/users",method = RequestMethod.GET)
public String[] users(){
return users;
}
@RequestMapping(value = "/users/{id}",method = RequestMethod.GET)
public String users(@PathVariable String id){
for (int i=0;i<users.length;i++) {
if (id!=null&&id.equals(users[i])) {
return users[i];
}
}
return null;
}
}

启动项目后,浏览器查看注册中心,可以看到服务已经被注册到注册中心。
同样方式创建服务的消费者

创建服务消费方

创建子模块spring-cloud-eureka-consumer,引入依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-eureka</artifactId>
<groupId>com.hu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-cloud-eureka-consumer</artifactId>

<dependencies>
<!--boot eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- boot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>

创建配置:

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 7902
spring:
application:
name: consumer-hello
eureka:
client:
serviceUrl:
defaultZone: http://user:123456@localhost:8761/eureka
logging:
level:
root: INFO

创建启动类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 创建应用主类
* 初始化RestTemplate,用来真正发起REST请求。
* 注解@EnableDiscoveryClient注解用来将当前应用加入到服务治理体系中。
*/
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApplication {
/**
* 通过注解 @LoadBalanced 使用Ribbon负载均衡
* Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
* 这样就可以在Controller中,去掉通过LoadBalancerClient选取实例和拼接URL的步骤,直接通过RestTemplate发起请求。
* 请求的host位置并没有使用一个具体的IP地址和端口的形式,而是采用了服务名的方式组成,不需要具体的IP和端口。因为:
* Spring Cloud Ribbon有一个拦截器,它能够在这里进行实际调用的时候,自动的去选取服务实例,并将实际要请求的IP地址和端口替换这里的服务名,从而完成服务接口的调用。
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
/*开启多个provider实例,
可以采用修改yml文件 中的端口号重复启动实例,也可以另外新建多个provider,修改其中的yml文件,依次启动。
由于eureka配置类ribbon负载均衡策略,消费者的请求会在客户端被决定好发送到哪台服务提供者进行处理。*/
}

创建控制器,控制器中调用了注册中心的提供者服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@RestController
public class ConsumerController {
@Autowired
private RestTemplate resttemplate;
@Autowired
LoadBalancerClient loadBalancerClient;

@RequestMapping(value = "/users",method = RequestMethod.GET)
public String[] users(){
//不使用负载
//ServiceInstance serviceInstance = loadBalancerClient.choose("provider-hello");
//String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/users";

//使用负载
String url="http://provider-hello/users";

//返回值类型和我们的业务返回值一致
return resttemplate.getForObject(url, String[].class);
}
@RequestMapping(value = "/users/{id}",method = RequestMethod.GET)
public String users(@PathVariable String id){
System.out.println("-----------------------"+id);
String url="http://provider-hello/users/"+id;
return resttemplate.getForObject(url, String.class);
}
}

运行,浏览器访问控制器,获取了服务提供者的信息。

Spring Cloud Consul

Spring Cloud Consul项目是针对Consul的服务治理实现。Consul是一个分布式高可用的系统,它包含多个组件,包含了下面几个特性:

  • 服务发现
  • 健康检查
  • Key/Value存储
  • 多数据中心

基于Spring Boot的微服务应用注册到Consul上,实现微服务架构中的服务治理。
以之前实现的基于Eureka的示例为基础,只需要在pom.xml中将eureka的依赖修改为如下依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

再修改配置文件:

1
2
3
4
5
spring: 
cloud:
consul:
host: localhost
port: 8500

这样就把基于Eureka的服务换为基于consul服务治理的服务提供者;
Spring cloud服务发现的接口DiscoveryClient是Spring Cloud对服务治理做的一层抽象,所以可以屏蔽Eureka和Consul服务治理的实现细节,只需要引入不同的服务治理依赖,并配置相关的配置属性即可。
consul是服务端不需要像Eureka那种创建,直接下载consul的服务端程序就可以使用。

Spring Cloud Ribbon

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。它是一个基于HTTP和TCP的客户端负载均衡器。它可以通过在客户端中配置ribbonServerList来设置服务端列表去轮询访问以达到均衡负载的作用。

  • 当Ribbon与Eureka联合使用时,ribbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心中获取服务实例列表。同时它也会用NIWSDiscoveryPing来取代IPing,它将职责委托给Eureka来确定服务端是否已经启动。
  • 当Ribbon与Consul联合使用时,ribbonServerList会被ConsulServerList来扩展成从Consul获取服务实例列表。同时由ConsulPing来作为IPing接口的实现。

不论是与Eureka还是Consul结合,都可以快速在Spring Cloud中实现服务间调用的负载均衡。
在之前的模块中,引入依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>

修改类增加@LoadBalanced注解(其实前面已经使用了):

1
2
3
4
5
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}

去掉通过LoadBalancerClient选取实例和拼接URL的步骤,直接通过RestTemplate发起请求。

1
return resttemplate.getForObject(url, String[].class);

这样就实现了一个简单的Ribbon负载均衡。

Spring Cloud Feign

Spring Cloud Feign是一套基于Netflix Feign实现的声明式服务调用客户端。它使得编写Web服务客户端变得更加简单。我们只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。Spring Cloud Feign还扩展了对Spring MVC注解的支持,同时还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。
新建一个模块spring-cloud-eureka-consumer-feign,引入依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

启动类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Spring Cloud Feign是一套基于Netflix Feign实现的声明式服务调用客户端。它使得编写Web服务客户端变得更加简单。
* 我们只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。
* 它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。
*
* Spring Cloud Feign还扩展了对Spring MVC注解的支持,同时还整合了Ribbon和Eureka来提供均衡负载的HTTP客户端实现。
*
*/
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients //修改应用主类。通过@EnableFeignClients注解开启扫描Spring Cloud Feign客户端的功能
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}

创建一个Feign的客户端接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 创建一个Feign的客户端接口定义。
* 使用@FeignClient注解来指定这个接口所要调用的服务名称,
*
* 接口中定义的各个函数使用Spring MVC的注解就可以来绑定服务提供方的REST接口
*/
@Service
@FeignClient("provider-hello")
public interface HelloFeignClient {
@RequestMapping(value = "/users",method = RequestMethod.GET)
String[] users();
@RequestMapping(value = "/users/{id}",method = RequestMethod.GET)
String users(@PathVariable("id") String id);
}

控制器直接通过接口去掉服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 通过Spring Cloud Feign来实现服务调用的方式更加简单了,通过@FeignClient定义的接口来统一的生命我们需要依赖的微服务接口。
* 而在具体使用的时候就跟调用本地方法一点的进行调用即可。
*
* 由于Feign是基于Ribbon实现的,所以它自带了客户端负载均衡功能,也可以通过Ribbon的IRule进行策略扩展。
* 另外,Feign还整合的Hystrix来实现服务的容错保护,在Dalston版本中,Feign的Hystrix默认是关闭的。
*/
@RestController
public class ConsumerController {
@Autowired
HelloFeignClient helloFeignClient;

@RequestMapping(value = "/users",method = RequestMethod.GET)
public String[] users(){
return helloFeignClient.users();
}
@RequestMapping(value = "/users/{id}",method = RequestMethod.GET)
public String users(@PathVariable("id") String id){
return helloFeignClient.users(id);
}
}

这样就完成了简单的整合调用。