02、Eureka搭建

98 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

02、Eureka搭建

一、单节点Eureka

Eureka 提供基于 REST 的服务,在集群中主要用于服务管理 Eureka 提供了基于 Java语言 的客户端组件,客户端组件实现了负载均衡的功能,为业务组件的集群部署创造了条件。 使用该框架,可以将业务组件注册到Eureka 容器中,这些组件可进行集群部署,Eureka主要维护这些服务的列表并自动检查它们的状态.

Eureka架构

一个简单的 Eureka 集群,需要一个Eureka 务器、若干个服务提供者。我们可以将业务组件注册到 Eureka 服务器中, 其他客户端组件可以向服务器获取服务并且进行远程调用。下图为Eureka 的架构图。 图中有两个服务器,服务器支持集群部署,每个服务器也可以作为对方服务器的客户端进行相互注册与复制。图 3- 中所示的 三个Eureka 客户端,两个用于发布服务,另一个用于调用服务 不管是服务器还是客户端,都可以部署多个实例,如此一来,就很容构建高可用的服务集群。

image.png

服务器端

对于注册到服务器端的服务组件, Eureka 服务器并没有提供后台的存储,这些注册的服务实例被保存在内存的注册中心,它们通过心跳来保持其最新状态,这些操作都可以在内存中完成。客户端存在着相同的机制,同样在内存中保存了注册表信息,这样的机制提升了 Eureka 组件的性能,每次服务的请求都不必经过服务器端的注册中心

服务提供者

作为 Eureka 客户端存在的服务提供者,主要进行以下工作: 第一,向服务器注册服务:发送心跳给服务器;第三 向服务器端获取注册列表。当客户端注册到服务器时,它将会提供一些关于自己的信息给服务器端,例如自己的主机、端口、健康检测连接等

服务调用者

  对于发布到Eureka服务器的服务,服务调用者可对其进行服务查找与调用,服务调用者也是作为客户端存在的,但其职责主要是发现与调用服务。在实际情况中,有可能出现本身既是服务提供者,又是服务调用者的情况,例如在传统的企业应用三层架构中,服务层会调用数据访问层的接口进行数据操作,它本身也会提供服务给控制层使用。

搭建单节点 Eureka 应用

POM文件如下:

<dependencies>
	<!--spring-cloud-starter-eureka-server会自动引入spring-boot-starter-web,因此项目具有Web容器的功能-->
	<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
	</dependency>
</dependencies>

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

启动类上加上@EnableEurekaServer注解

@SpringBootApplication
@EnableEurekaServer
public class FirstEkServerApplication {
	public static void main(String[] args) {
		SpringApplication.run(FirstEkServerApplication.class, args);
	}
}

配置文件配置端口:

server.port=8761

可直接启动,会报如下异常,不影响:

在启动 ureka 服务器时,会在控制台看到以下两个异常信息

java.net.ConnectException: Connection refused : connect 
com.netflix.discovery .shared.transport .TransportException : Cannot execute request on any known server 

这是由于在服务器启动时,服务器会把自己当作 个客户端,去注册 Eureka 服务器, 且会到 Eureka 服务器抓取注册信息,它自己本身只是个服务器,而不是服务的提供者(客户端),因此可以修改 p配置文件,修改以下两个 配置 eureka.client.registerWithEureka声明是否将自己的信息注册到 Eureka 服务器,默认值为 true 。 属性 eureka.client.fetchRegistry 表示是否到 Eureka 器中抓取注册信息。将这两个属性设置为 false ,启动时不会出现异常信息。

声明是否将自己的信息注册到Eureka 服务器,默认值为trueeureka.client.registerWithEureka=false 是否到Eureka服务器中抓取注册信息eureka.client.fetchRegistry=false

编写服务提供者

POM主要依赖:

<dependencies>
<!-- 需要手动添加该web模块 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
	</dependency>
</dependencies>

配置文件:

#端口
server.port=9999
#应用名称
spring.application.name=provider-server
#配置本服务示例的主机名称
eureka.instance.hostname=localhost
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

Controller:

@RestController
public class MyController {

	@GetMapping("/hello")
	@ResponseBody
	public String hello() {
		return "Hello World";
	}
	@RequestMapping(value = "person/{id}", method = RequestMethod.GET)
	public Person select(@PathVariable("id") Integer id, HttpServletRequest request) {
		Person person = new Person();
		person.setId(id);
		person.setAge("张三");
		person.setName("克罗斯");
		//为了查看结果 将请求的 URL 设置到 Person 实例中
		person.setMessage(request.getRequestURL().toString());
		return person;
	}
}

启动类:

@SpringBootApplication
@EnableEurekaClient
public class SrlProviderServerApplication {
public static void main(String[] args) {
SpringApplication.run(SrlProviderServerApplication.class, args);
}
}

编写服务调用者:

POM主要依赖:

<dependencies>
	<!-- 使用httpclient的依赖 -->
	<dependency>
		<groupId>org.apache.httpcomponents</groupId>
		<artifactId>httpclient</artifactId>
		<version>4.5.2</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
	</dependency>
	<dependency>
		<groupId>com.google.guava</groupId>
		<artifactId>guava</artifactId>
		<version>21.0</version>
		<scope>compile</scope>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.11</version>
		<scope>compile </scope>
	</dependency>
</dependencies>

配置文件:

#端口
server.port=9000
#应用名称
spring.application.name=invoker-server
#配置本服务示例的主机名称
eureka.instance.hostname=localhost
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

Controller:

@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
/**
* http://provider-server/person/1 根据名称调用,服务名区分大小写
* 如果只是resttemplate 不区分大小写,如果要整合 Ribbon 区分大小写,不然自定义的Rule Ping不起作用 *********
* @return
*/
@RequestMapping(value = "/router", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String router() {
	RestTemplate restTpl = getRestTemplate();
	// 根据名称调用服务 ********* 区分大小写 provider-server 区分大小写
	String json = restTpl.getForObject("http://provider-server/person/1",
	String.class);
	return json;
}

启动类:

/**
* @EnableEurekaClient 注解己经包含了@EnableDiscoveryClient 的功能,也就是说Eureka客户端本身就有发现服能力
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableEurekaClient
public class SrlInvokerServerApplication {
	public static void main(String[] args) {
	     SpringApplication.run(SrlInvokerServerApplication.class, args);
	}
}

访问:localhost:9000/router

程序结构:

image.png

二、Eureka集群搭建

Eureka架构图

image.png

##服务器端 Eureka-server POM文件:

 <dependencies>
        <!--spring-cloud-starter-eureka-server会自动引入spring-boot-starter-web-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </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>

配置文件    application.properties

#激活方式也可通过program arguments启动参数指定 --spring.profiles.active=slave1,slave2
spring.profiles.active=slave1
#配置应用名称
spring.application.name=eureka-server
#剔除失效节点 -- 注册表的清理间隔
eureka.server.eviction-interval-timer-in-ms=6000
#是否启用自我保护,不启用 自我保护:如果心跳的失败率超过一定比例,服务会将这些实例保护起来,并不会马上将其从注册表中剔除。结合后面容错机制
eureka.server.enable-self-preservation=false

application-slave1.properties

#端口
server.port=8761
#声明是否将自己的信息注册到Eureka 服务器,默认值为true
eureka.client.registerWithEureka=true
#是否到Eureka服务器中抓取注册信息
eureka.client.fetchRegistry=true
#当前实例主机名称
eureka.instance.hostname=slave1
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://slave2:8762/eureka/

application-slave2.properties

#端口
server.port=8762
#声明是否将自己的信息注册到Eureka 服务器,默认值为true
eureka.client.registerWithEureka=true
#是否到Eureka服务器中抓取注册信息
eureka.client.fetchRegistry=true
#当前实例主机名称
eureka.instance.hostname=slave2
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://slave1:8761/eureka/

启动类:

@SpringBootApplication
@EnableEurekaServer
public class SrlRegistryServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SrlRegistryServerApplication.class, args);
    }
}

也可通过此方式进行激活:

public static void main(String[] args) {
	// 读取控制台输入作为端口参数
	Scanner scan = new Scanner(System.in);
	String port = scan.nextLine();
	// 设置启动的服务器端口
	new SpringApplicationBuilder(FirstServiceProvider.class).properties(
			"server.port=" + port).run(args);
}

服务提供者

POM文件如下:

   <!-- 健康检查依赖此模块 -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-actuator</artifactId>
   </dependency>
   <!-- 需要手动添加该web模块 -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
   </dependency>

配置文件:

#端口
server.port=9999
#应用名称
spring.application.name=provider-server
#配置本服务示例的主机名称
eureka.instance.hostname=localhost
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://slave1:8761/eureka/,http://slave2:8762/eureka/

Controller:

@RequestMapping(value = "person/{id}", method = RequestMethod.GET)
public Person select(@PathVariable("id") Integer id, HttpServletRequest request) {
    Person person = new Person();
    person.setId(id);
    person.setAge("张三");
    person.setName("克罗斯");
    //为了查看结果 将请求的 URL 设置到 Person 实例中
    person.setMessage(request.getRequestURL().toString());
    return person;
}

服务调用者

POM主要依赖:

<dependencies>
   <!-- 使用httpclient的依赖 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>

配置文件:

#端口
server.port=9000
#应用名称
spring.application.name=invoker-server
#配置本服务示例的主机名称
eureka.instance.hostname=localhost
#指定服务注册中心地址,为集群时,多个地址以逗号分隔
eureka.client.serviceUrl.defaultZone=http://slave1:8761/eureka/,http://slave2:8762/eureka/

Controller:

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

访问:localhost:9000/router

程序结构: 六次请求的都是请求 服务调用者,然后服务调用者去请求,服务提供者,默认负载均衡,所以 请求三次 8081 三次 8082

image.png