1、Eureka简介
Eureka是Netflix中的一个子模块,本身是一个Rest服务,用于定位服务,以达到负载均衡和中间层服务故障转移的目的。服务注册与发现对于微服务来说是相当重要的,有了注册中心,就可以通过服务的标识符进行服务之间的调用。(如果之前用过dubbo,以zookeeper作为注册中心,这里可以将zookeeper作为前导学习Eureka)
2、Eureka组件
Eureka包含Eureka Server和Eureka Client。
Eureka Server提供服务注册服务,当需要注册的服务启动后,会在Eureka Server中进行注册,所有服务节点的信息都会保存到Eureka Server的注册表中,服务的节点信息也可以在Eureka Server的界面中进行查看。Eureka Server默认情况下会自动注册到Eureka注册中心,如果是单机版的,需要取消Eureka Server的自动注册。
Eureka Client在应用启动后,向Eureka Server发送心跳,默认周期为30s,若Eureka Server在多个心跳周期内都没有接收到节点的心跳,则该节点将会从Eureka Server的服务注册表中删除,服务的节点移除周期默认90s(这里的90s是90s未收到服务提供者发来的心跳,服务就将会被注销,而这里的注销并不是立即注销,而是在60s后对该时间段内无心跳的服务集中进行注销)。
我们平时的Eureka Client一般会由生产者(Provider)和消费者(Consumer)组成。消费者将自己的服务注册到Eureka Server中,消费者通过从Eureka Server中拉取服务并消费。
3、搭建单机版Eureka Server
我们使用idea创建项目,大概先说一下项目构建规划。新建一个Project(springcloud-eureka-application),其中包含三个module,分别为:springcloud-eureka-server、springcloud-provider、springcloud-consumer,一个公共模块springcloud-common(先打包这个)。最终实现效果,provider与consumer将自己注册到eureka server中,consumer通过从eureka server拉取到provider中的服务进行调用。springcloud-common用来放置公共的代码。
新建项目:File->New->Project
填写项目信息
创建三个Module:选中springcloud-eureka-application->File->Project Structure->Modules->”+”->new module
-
springcloud-eureka-server
-
springcloud-provider
- springcloud-consumer
创建完成之后开始编码部分。
- Springcloud-eureka-application
pom.xml添加如下内容
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- springcloud-eureka-server
pom.xml添加如下内容
<dependencies>
<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>
创建启动类:com.zh.cloud.EurekaServerApplication
package com.zh.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer // 这个应用是一个EurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
创建配置文件:application.yml
server:
port: 8080 # 端口
spring:
application:
name: eureka-server # 设置应用名称,会在Eureka界面中显示
eureka:
client:
register-with-eureka: false # 是否注册自己的信息到EurekaServer,默认是true
fetch-registry: false # false表示自己就是注册中心,并不需要去注册中心获取其他服务地址
service-url: # EurekaServer的地址,单机是自己的地址,如果是集群,需要加上其它Server的地址。
defaultZone: http://127.0.0.1:${server.port}/eureka
然后就可以启动main方法进行测试了:http://ip:端口
下面就是erueka的界面
- springcloud-provider
pom.xml添加如下内容
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
添加启动类:com.zh.cloud.ProviderApplication
package com.zh.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableDiscoveryClient // 开启EurekaClient功能
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
添加配置文件:application.yml
server:
port: 8081 # 端口
spring:
application:
name: provider
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8080/eureka # EurekaServer地址
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
- springcloud-consumer
pom.xml添加如下内容
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
添加启动类com.zh.cloud.ConsumerApplication
package com.zh.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableDiscoveryClient // 开启EurekaClient功能
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
配置文件:application.yml
server:
port: 8082 # 端口
spring:
application:
name: consumer
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8080/eureka # EurekaServer地址
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
分别启动provider与consumer,这时在erueka界面上就可以看到这两个服务注册成功了
到此最基本的注册已经完成了,接下来我们需要调用服务,那么我们来写个接口,做个服务来测试一下功能。
以下开始完成一些测试服务:
- 创建springcloud-common公共模块
File->New->Module->Maven
编写User实体类
package com.zh.cloud.model
;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private Integer age;
private String address;
}
注:必须先将这个打成jar包放在仓库中。
- 在springcloud-provider中添加测试接口,并集成mybatis
pom.xml添加
<dependency>
<groupId>com.zh</groupId>
<artifactId>springcloud-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
application.yml中添加mybatis配置
完整配置如下:
server:
port: 8081 # 端口
# mybatis 别名扫描
mybatis:
type-aliases-package: com.zh.cloud.model
# mapper.xml文件位置,如果没有映射文件,请注释掉
mapper-locations: classpath:mappers/*.xml
spring:
application:
name: provider
datasource:
url: jdbc:mysql://dev.apibutton.top:3306/button-test?useUnicode=true&characterEncoding=utf-8
username: root
password: zhZH940126
driverClassName: com.mysql.jdbc.Driver
druid:
initial-size: 1 #初始化连接数
min-idle: 1 #最小空闲连接
max-active: 20 #最大活动连接
test-on-borrow: true #获取连接时测试是否可用
stat-view-servlet.allow: true #监控页面启动
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8080/eureka # EurekaServer地址
instance:
prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
添加UserMapper
package com.zh.cloud.mapper;
import com.zh.cloud.model.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
public List<User> queryUsers();
}
添加UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zh.cloud.mapper.UserMapper">
<select id="queryUsers" resultType="com.zh.cloud.model.User">
select * from tb_user
</select>
</mapper>
添加UserService
package com.zh.cloud.service;
import com.zh.cloud.model.User;
import java.util.List;
public interface UserService {
public List<User> queryUsers();
}
添加UserServiceImpl
package com.zh.cloud.service.impl;
import com.zh.cloud.mapper.UserMapper;
import com.zh.cloud.model.User;
import com.zh.cloud.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
public List<User> queryUsers() {
return userMapper.queryUsers();
}
}
添加UserController
package com.zh.cloud.controller;
import com.zh.cloud.model.User;
import com.zh.cloud.service.UserService;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/v1/user")
@Scope("prototype")
public class UserController {
@Resource
private UserService userService;
@GetMapping("/list")
public List<User> queryUser() {
return userService.queryUsers();
}
}
启动ProviderApplication进行测试:
http://localhost:8081/v1/user/list
- springcloud-consumer拉群服务并调用
pom.xml添加
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.9.0</version>
</dependency>
配置RestTemplate(使用okhttp3)
package com.zh.cloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
添加UserController
package com.zh.cloud.controller;
import com.zh.cloud.model.User;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
@RestController
@Scope("prototype")
public class UserController {
@Resource
private RestTemplate restTemplate;
@Resource
private DiscoveryClient client;
@GetMapping("/list")
public List<User> getUsers() {
List<ServiceInstance> instances = client.getInstances("provider");
ServiceInstance serviceInstance = instances.get(0);
String url = serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/all";
System.out.println("url: " + url);
return this.restTemplate.getForObject(url, List.class);
}
}
访问查看结果:http://localhost:8082/list
完整代码地址:
https://gitee.com/superbutton/spring-cloud-study/tree/develop/springcloud-eureka-application
4、搭建高可用版Eureka Server
在之前的基础上,增加两个Module项目,springcloud-eureka-server02和springcloud-eureka-server03,这两个Module除了配置文件之外,其余代码相同。
这里我们通过hostname替换ip的方式进行配置注册地址,那么必须得在“hosts”文件中添加。windows中:
C:\Windows\System32\drivers\etc\hosts
添加如下内容:
127.0.0.1 eureka-server-01
127.0.0.1 eureka-server-02
127.0.0.1 eureka-server-03
三个注册中心的application.yml文件内容如下:
eureka-server-01:
server:
port: 8080 # 端口
spring:
application:
name: eureka-server-01 # 设置应用名称,会在Eureka界面中显示
eureka:
instance:
hostname: eureka-server-01
client:
register-with-eureka: false # 是否注册自己的信息到EurekaServer,默认是true
fetch-registry: false # false表示自己就是注册中心,并不需要去注册中心获取其他服务地址
service-url: # EurekaServer的地址,单机是自己的地址,如果是集群,需要加上其它Server的地址。
defaultZone: http://eureka-server-02:8081/eureka,http://eureka-server-03:8082/eureka
eureka-server-02:
server:
port: 8081 # 端口
spring:
application:
name: eureka-server-02 # 设置应用名称,会在Eureka界面中显示
eureka:
instance:
hostname: eureka-server-02
client:
register-with-eureka: false # 是否注册自己的信息到EurekaServer,默认是true
fetch-registry: false # false表示自己就是注册中心,并不需要去注册中心获取其他服务地址
service-url: # EurekaServer的地址,单机是自己的地址,如果是集群,需要加上其它Server的地址。
defaultZone: http://eureka-server-01:8080/eureka,http://eureka-server-03:8082/eureka
eureka-server-03:
server:
port: 8082 # 端口
spring:
application:
name: eureka-server-03 # 设置应用名称,会在Eureka界面中显示
eureka:
instance:
hostname: eureka-server-03
client:
register-with-eureka: false # 是否注册自己的信息到EurekaServer,默认是true
fetch-registry: false # false表示自己就是注册中心,并不需要去注册中心获取其他服务地址
service-url: # EurekaServer的地址,单机是自己的地址,如果是集群,需要加上其它Server的地址。
defaultZone: http://eureka-server-01:8080/eureka,http://eureka-server-02:8081/eureka
修改Provider和Consumer的注册中心地址:
eureka:
client:
service-url:
defaultZone: http://eureka-server-01:8080/eureka,http://eureka-server-02:8081/eureka,http://eureka-server-03:8082/eureka
完整代码如下:
https://gitee.com/superbutton/spring-cloud-study/tree/develop/springcloud-eureka-colony
5、Eureka的自我保护机制
Eureka的自我保护是一种针对网络异常波动的安全保护措施,使用自我保护可以使Eureka集群更加健壮稳定的运行。
Eureka Client默认30s会向Eureka Server发送心跳,若Eureka Server在一定时间内没有收到实例的心跳,就会把该实例从列表中剔除(默认90s)。但是,如果在短时间内没有心跳,便会触发Eureka Server的自我保护机制(Eureka Server会将实例的注册信息保护起来,不予剔除)默认自我保护是出于开启状态的。
关闭Eureka Server自我保护机制的方式
eureka.server.enable-self-preservation: false #关闭eureka自我保护机制false(默认为true)
eureka.server.eviction-interval-timer-in-ms: 2000 # 配置Eureka Server清理无效节点的时间间隔(单位毫秒,默认60*1000毫秒,即60秒)
设置Eureka Client发送心跳时间
eureka.instance.lease-expiration-duration-in-seconds: 90 #eureka服务端在接受到实例的最后一次发出的心跳后,需要等待多久才可以将此删除,单位为秒(默认为90s),超过时间则剔除(客户端会按照此规则向Eureka服务端发送心跳检测包)
eureka.instance.lease-renewal-interval-in-seconds: 2 #eureka客户端需要多长时间发送心跳给eureka服务端,单位为秒(默认为30s),(客户端会按照此规则向Eureka服务端发送心跳检测包)
如果消费者配置了eureka.client.fetch-registry=true,可以配置
eureka.client.registry-fetch-interval-seconds: 5 表示eureka client间隔多久去拉取服务器注册信息,默认为30秒
那么Eureka的自我保护机制开启还是关闭好呢?平时开发中关闭比较好,把我们提到的配置配一下就好,这样的话当我们修改了服务,重新启动之后,最新的服务就会生效,不会受到eureka自我保护的影响。但是生产环境最好开启,也就是不需要配置,默认就是开启的。这样就可以保证生产环境不会因为短时间内网络的波动导致服务的不可用。
6、Eureka优雅停服
这里我们可以借助actuator提供的接口,直接停掉想要停止的服务,Eureka Server的自我保护保持开启即可。(配置需要加在停服的服务里面,这里我们加在provider中)
实现方式:
- 添加actuator的jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- application.yml中配置
management:
endpoints:
web:
exposure:
include: shutdown #暴漏shutdown端点服务
endpoint:
shutdown:
enabled: true
- 启动服务,调用shutdown接口
http://ip:port/actuator/shutdown
注:该接口为post接口,不能在浏览器中直接访问,需要借助工具实现。
4. 验证
在Eureka界面中查看,此时Provider已经停服了。
完整代码如下:
https://gitee.com/superbutton/spring-cloud-study/tree/develop/springcloud-eureka-stop-taking