一、微服务工程搭建
1. 注册中心搭建
工程目录:
pom.xml :
<!--1. 引入springboot父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<!--2. springcloud版本-->
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<!--3. Eureka服务端启动器导入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<!--4. springcloud的依赖仓库导入 -->
<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:
server.port=8763
eureka.instance.hostname=localhost
#是否注册到eureka
eureka.client.registerWithEureka=false
#是否从eureka中拉取注册信息
eureka.client.fetchRegistry=false
##暴露eureka服务的地址
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
#自我保护模式,当出现出现网络分区、eureka在短时间内丢失过多客户端时,会进入自我保护模式,即一个服务长时间没有发送心跳,eureka也不会将其删除,默认为true
eureka.server.enable-self-preservation=true
启动类:
package com.lic;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
//开启eureka客户端功能
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
2. 服务提供方
工程目录:
pom.xml
<!--1. 引入springboot父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<!--2. springcloud版本-->
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<!--3.1 引入spring-boot-starter-web依赖(注册方需要发送http请求)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--3.2 Eureka客户端启动器导入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!--4. springcloud的依赖仓库导入 -->
<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:
spring.application.name=springcloud-eureka-client-provider
server.port=8084
eureka.client.serviceUrl.defaultZone=http://localhost:8763/eureka/
启动类:
package com.lic;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
//开启Eureka客户端功能
@EnableEurekaClient
public class EurekaClientProviderApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientProviderApplication.class,args);
}
}
测试类:
package com.lic.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping("/queryUser")
public String queryUser(){
System.out.println("springcloud-eureka-client-provider: Hello World");
return "springcloud-eureka-client-provider: Hello World";
}
}
3. 服务消费方
工程目录:
pom.xml
<!--1. 引入springboot父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<!--2. springcloud版本-->
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<!--3.1 引入spring-boot-starter-web依赖(注册方需要发送http请求)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--3.2 Eureka客户端启动器导入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!--4. springcloud的依赖仓库导入 -->
<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:
spring.application.name=springcloud-eureka-client-consumer
server.port=8085
eureka.client.serviceUrl.defaultZone=http://localhost:8763/eureka/
启动类:
package com.lic;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
//开启Eureka客户端功能
@EnableEurekaClient
public class EurekaClientConsumerApplication {
@Bean
//负载均衡注解
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(EurekaClientConsumerApplication.class);
}
}
测试类:
================================ controller层 =================================
package com.lic.controller;
import com.lic.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("queryUser")
public String queryUser(){
return userService.queryContent();
}
}
================================= service层 ===================================
package com.lic.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
public class UserServiceImpl implements UserService{
public static String SERVIER_NAME = "springcloud-eureka-client-provider";
@Autowired
private RestTemplate restTemplate;
@Override
public String queryContent() {
String result = restTemplate.getForObject("http://"+
SERVIER_NAME + "/queryUser",String.class);
return result;
}
}
4. 测试
1.1 浏览器访问Eureka监控中心: http://localhost:8763/
1.2 启动多个Eureka服务提供方工程:
2. 浏览器访问Eureka服务提供方: http://localhost:8085/user/queryUser
3. Eureka服务提供方控制台输出:
二、Eureka用户认证
连接到 eureka 的时候需要带上连接的用户名和密码
Eureka服务端配置:
1. pom文件中添加启动器:
<!-- 5. Eureka用户认证启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2. 代码配置, 关闭csrf验证:
package com.lic;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//关闭csrf
http.csrf().disable();
//开启认证: URL格式登录必须是httpBasic
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
3. application.properties配置:
#开启basic校验, 设置登录用户名密码
security.basic.enabled=true
spring.security.user.name=admin
spring.security.user.password=admin
Eureka客户端配置:
#与 eureka 连接的时候要带上用户名密码
eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:8763/eureka/
三、服务续约保活
当客户端启动想 eureka 注册了本身服务列表后, 需要隔段时间发送一次心跳给 eureka 服务端来证明自己还活着, 当 eureka 收到这个心跳请求后才会知道客户端还活着, 才会维护该客户端的服务列表信息; 一旦因为某些原因导致客户端没有按时发送心跳给 eureka 服务端, 这时候 eureka 可能会认为你这个客户端已经挂了, 它就有可能把该服务从服务列表中删除掉。
Eureka服务端配置:
#自我保护模式,当出现出现网络分区、eureka在短时间内丢失过多客户端时,会进入自我保护模式,即一个服务长时间没有发送心跳,eureka也不会将其删除,默认为true
eureka.server.enable-self-preservation=true
#Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来
eureka.server.renewal-percent-threshold=0.85
#eureka server 清理无效节点的时间间隔,默认 60000 毫秒,即 60 秒
eureka.server.eviction-interval-timer-in-ms=60000
Eureka客户端配置:
#服务续约,心跳的时间间隔
eureka.instance.lease-renewal-interval-in-seconds=10
#如果从前一次发送心跳时间起,30 秒没接受到新的心跳,就剔除服务
eureka.instance.lease-expiration-duration-in-seconds=30
#表示 eureka client 间隔多久去拉取服务注册信息,默认为 30 秒
eureka.client.registry-fetch-interval-seconds=30
四、Eureka健康监测
Eureka 默认的健康检测只是你校验服务连接是否是 UP 还是 DOWN 的, 然后客户端只会调用状态为 UP 状态的服务, 但是有的情况下, 虽然服务连接是好的, 但是有可能这个服务的某些接口不是正常的, 可能由于需要连接 Redis, mongodb 或者 DB 有问题导致接口调用失败, 所以理论上服务虽然能够正常调用, 但是它不是一个健康的服务; 所以我们就有必要对这种情况做自定义健康检测。
Eureka客户端配置:
1. application.properties配置:
#开启健康检测
eureka.client.healthcheck.enabled=true
2. 自定义健康监测器:
package com.lic.health;
import com.lic.controller.UserController;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EurekaClientHealthIndicator implements HealthIndicator {
@Override
public Health health() {
//根据健康监测返回的结果判断当前服务状态
if (UserController.healthChecked){
return new Health.Builder(Status.UP).build();
}else{
return new Health.Builder(Status.DOWN).build();
}
}
}
package com.lic.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
public static boolean healthChecked = true;
@RequestMapping("healthCheck/{health}")
public void healthCheck(@PathVariable boolean health){
//todo: 针对服务中的某些接口进行检测, 给出健康状态的判断
healthChecked = health;
}
}
3. pom依赖添加:
<!-- 5. 健康监测的jar包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
查看服务健康状态: http://localhost:8084/actuator/health
修改服务的健康状态: http://localhost:8084/healthCheck/false
五、服务下线
比如有些情况是服务主机意外宕机了, 也就意味着服务没办法给 eureka 心跳信息了, 但是eureka 在没有接受到心跳的情况下依赖维护该服务 90s, 在这 90s 之内可能会有客户端调用到该服务, 这就可能会导致调用失败; 所以我们必须要有一个机制能手动的立马把宕机的服务从 eureka 服务列表中清除掉, 避免被服务调用方调用到。
这个接口是调用 eureka 服务端的接口: http://localhost:8763/eureka/apps/EUREKA-CLIENT-PROVIDER/localhost:eureka-client-provider:8084
六、Eureka高可用
整个微服务中存在多个 eureka 服务, 每个 eureka 服务都是相互复制的, 会把客户端注册进来的服务复制到 eureka 集群中的其他节点里面来, 其实简单来说就是 eureka 每个节点相互复制;
Eureka服务端配置:
1. application-8671.properties配置:
server.port=8761
eureka.instance.hostname=eureka8761
#是否注册到eureka
eureka.client.registerWithEureka=true
#是否从eureka中拉取注册信息
eureka.client.fetchRegistry=true
##暴露eureka服务的地址 (当前eureka服务把自己注册到另外一个eureka服务中)
#注意: 如果不配置域名映射(127.0.0.1 ==> eureka8762): eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:8762/eureka/
eureka.client.serviceUrl.defaultZone=http://admin:admin@eureka8762:8762/eureka/
#开启basic校验, 设置登录用户名密码
security.basic.enabled=true
spring.security.user.name=admin
spring.security.user.password=admin
#自我保护模式,当出现出现网络分区、eureka在短时间内丢失过多客户端时,会进入自我保护模式,即一个服务长时间没有发送心跳,eureka也不会将其删除,默认为true
eureka.server.enable-self-preservation=true
#Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来
eureka.server.renewal-percent-threshold=0.85
#eureka server 清理无效节点的时间间隔,默认 60000 毫秒,即 60 秒
eureka.server.eviction-interval-timer-in-ms=60000
2. application-8672.properties配置:
server.port=8762
eureka.instance.hostname=eureka8762
#是否注册到eureka
eureka.client.registerWithEureka=true
#是否从eureka中拉取注册信息
eureka.client.fetchRegistry=true
##暴露eureka服务的地址 (当前eureka服务把自己注册到另外一个eureka服务中)
#注意: 如果不配置域名映射(127.0.0.1 ==> eureka8761): eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:8761/eureka/
eureka.client.serviceUrl.defaultZone=http://admin:admin@eureka8761:8761/eureka/
#开启basic校验, 设置登录用户名密码
security.basic.enabled=true
spring.security.user.name=admin
spring.security.user.password=admin
#自我保护模式,当出现出现网络分区、eureka在短时间内丢失过多客户端时,会进入自我保护模式,即一个服务长时间没有发送心跳,eureka也不会将其删除,默认为true
eureka.server.enable-self-preservation=true
#Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来
eureka.server.renewal-percent-threshold=0.85
#eureka server 清理无效节点的时间间隔,默认 60000 毫秒,即 60 秒
eureka.server.eviction-interval-timer-in-ms=60000
3. hosts域名映射配置:
127.0.0.1 eureka8762
127.0.0.1 eureka8761
Eureka客户端配置:
application-cluster.properties配置:
spring.application.name=eureka-client-provider
server.port=8084
#是否注册到eureka
eureka.client.registerWithEureka=true
#是否从eureka中拉取注册信息
eureka.client.fetchRegistry=true
#注意: 如果不配置域名映射(127.0.0.1 ==> eureka8761,127.0.0.1 ==> eureka8762)
#那么: http://admin:admin@localhost:8761/eureka/,http://admin:admin@localhost:8762/eureka/
eureka.client.serviceUrl.defaultZone=http://admin:admin@eureka8761:8761/eureka/,http://admin:admin@eureka8762:8762/eureka/
#hystrix.stream 开放所有的监控接口
management.endpoints.web.exposure.include=*
#feign开启熔断器
feign.hystrix.enabled=true
#开启feign的压缩功能
feign.compression.request.enabled=true
#feign.compression.request.mime-types=text/xml,application/xml,application/json
#feign.compression.request.min-request-size=2048
feign.compression.response.enabled=true
#全局超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
#hystrix.command.<commandKey>作为前缀,默认是采用Feign的客户端的方法名字作为标识
hystrix.command.saveStudent.execution.isolation.thread.timeoutInMilliseconds=6000
#全局ribbon配置
MICRO-ORDER.ribbon.ConnectTimeout=5000
#could not be registered. A bean with that name has already been defined in file
spring.main.allow-bean-definition-overriding=true
启动:
将eureka服务端工程打包 (> mvn package -Dmaven.skiptest=true), 启动(> java -jar springcloud-1.0.0.RELEASE.jar --spring.profiles.active=8762 > java -jar springcloud-1.0.0.RELEASE.jar --spring.profiles.active=8762)
将eureka客户端工程打包 (mvn package -Dmaven.skiptest=true), 启动(> java -jar springcloud-eureka-client-provider-1.0.0.RELEASE.jar --spring.profiles.active=cluster)
注意: 在启动eureka服务时, 由于两个服务会相互注册, 但是如果两个服务没有同时启动完成, 那么启动好的那个服务向另外一个服务注册时会出现未知主机异常, 当两个服务都启动完成后, 就可以正常注册了
eureka8761服务信息:
eureka8762服务信息: