搭建一个简单的Eureka集群

搭建一个简单的Eureka集群

现在我们稍微把上一篇文章中的Eureka应用扩展一下,扩展成一个简单的Eureka集群。架构图如下所示:

集群部署图

我们要部署两台Eureka服务器,两台ServiceProvider。为此,首先要修改本机的Hosts文件,以便在一台机子上模拟出多台的效果。

修改hosts文件

Eureka Server端

新建maven项目first-cloud-server,其pom依赖如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>

我们通过启动时指定启动哪个server,server1还是server2.这就需要在配置文件中配置不同的profile。

启动类如下所示,启动时需要从输入中读取需要启动的server(server1或server2)

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableEurekaServer
public class FirstCloudServer {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String profiles = scanner.nextLine();
new SpringApplicationBuilder(FirstCloudServer.class).profiles(profiles).run(args);
}
}

配置文件如下所示:

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
server:
port: 8761
spring:
application:
name: first-cloud-server
profiles: server1
eureka:
instance:
hostname: server1
client:
serverUrl:
defaultZone: http://server2:8762/eureka/

---
server:
port: 8762
spring:
application:
name: first-cloud-server
profiles: server2
eureka:
instance:
hostname: server2
client:
serverUrl:
defaultZone: http://server1:8761/eureka/


配置文件中通过profiles声明了两套server配置。它们在启动完之后,会互相注册

Eureka provider

新建Maven项目first-cloud-provider,其pom文件的依赖如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<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>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>

因为我们也是需要启动两台Provider,就通过启动的时候指定不同的端口实现。启动类如下:

1
2
3
4
5
6
7
8
9
10
@SpringBootApplication
@EnableEurekaClient
public class FirstCloudServiceProvider {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String port = scanner.nextLine();
new SpringApplicationBuilder(FirstCloudServiceProvider.class).properties("server.port=" + port).run(args);
}
}

启动完成之后,分别向两台Eureka Server注册,配置文件如下:

1
2
3
4
5
6
7
8
9
spring:
application:
name: first-cloud-provider
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://server1:8761/eureka/,http://server2:8762/eureka/

我们还是对外提供了一个REST服务,代码如下:

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class FirstCloudController {

@RequestMapping(value = "/person/{personId}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Person findPerson(@PathVariable("personId") Integer personId, HttpServletRequest request) {
Person person = new Person(personId, "Crazyit", 30);
person.setMessage(request.getRequestURL().toString());
return person;
}
}

与之前单机部署不同的是,这次在返回中添加了请求的URL信息。

Person Bean如下:

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
44
45
46
47
48
49
50
51
52
53
public class Person {

private Integer id;

private String name;

private Integer age;

private String message;

public Person() {
super();
}

public Person(Integer id, String name, Integer age) {
super();
this.id = id;
this.name = name;
this.age = age;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

Eureka Invoker

同样地,创建first-cloud-invoker maven项目,其pom依赖为:

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
<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>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<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>
</dependencies>

修改其配置文件,使其在启动之后向两个Eureka Server注册:

1
2
3
4
5
6
7
8
9
10
11
server:
port: 9000
spring:
application:
name: first-cloud-invoker
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://server1:8761/eureka/,http://server2:8762/eureka/

同样地,提供一个对外的REST接口,供调用方调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
@Configuration
public class InvokerController {

@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}

@RequestMapping(value = "/router", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String router() {
RestTemplate template = getRestTemplate();
String json = template.getForObject("http://first-cloud-provider/person/1", String.class);
return json;
}
}

启动类:

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient
public class FirstInvoker {

public static void main(String[] args) {
SpringApplication.run(FirstInvoker.class, args);
}

Rest Client

这边简易实现了一个客户端Rest Client,用来调用Invoker提供的rest接口,以检验provider的负载均衡能力。

它也是一个Maven项目,pom依赖为:

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
</dependencies>

编写一个测试类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws Exception {
// 创建默认的HttpClient
CloseableHttpClient httpclient = HttpClients.createDefault();
// 调用6次服务并输出结果
for(int i = 0; i < 6; i++) {
// 调用 GET 方法请求服务
HttpGet httpget = new HttpGet("http://localhost:9000/router");
// 获取响应
HttpResponse response = httpclient.execute(httpget);
// 根据 响应解析出字符串
System.out.println(EntityUtils.toString(response.getEntity()));
}
}

启动

按照

  1. Eureka Server
  2. Eureka provider
  3. Eureka Invoker

的顺序启动。启动完成之后,可以看到Eureka Server1 和 Eureka Server2的注册信息。

Eureka Server1的注册信息

Eureka Server2的注册信息

最后,执行rest client,可以看到rest client 的输出为:

REST Client的输出

从输出中可以看出,Invoker在调用的时候,是调用端口12222一次,端口13333一次,然后又是端口12222一次的。说明Eureka 是做了负载均衡的。

接着再看下Invoker的日志:

1
2
3
4
2020-04-23 17:41:03.095  INFO 16484 --- [nio-9000-exec-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client first-cloud-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=first-cloud-provider,current list of Servers=[localhost:13333, localhost:12222],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;	Instance count:2;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:localhost:12222; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
, [Server:localhost:13333; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@304a4707

从日志中可以看出,Invoker从Server中获取了服务first-cloud-provider的服务列表,分别是localhost:12222和localhost:13333。

参考资料

疯狂Spring Cloud微服务架构实战

0%